postgres.git / summary / log / commit / refs

commit    b1a9d8ef254fab59c35a504490e14b2d9e1bbb92
Author:   Tom Lane <tgl@sss.pgh.pa.us>
Date:     Wed Mar 01 16:30:17 2023 +0000
First released in: 16.0, 11.20, 12.15, 13.11, 14.8, 15.3

    Avoid fetching one past the end of translate()'s "to" parameter.
    
    This is usually harmless, but if you were very unlucky it could
    provoke a segfault due to the "to" string being right up against
    the end of memory.  Found via valgrind testing (so we might've
    found it earlier, except that our regression tests lacked any
    exercise of translate()'s deletion feature).
    
    Fix by switching the order of the test-for-end-of-string and
    advance-pointer steps.  While here, compute "to_ptr + tolen"
    just once.  (Smarter compilers might figure that out for
    themselves, but let's just make sure.)
    
    Report and fix by Daniil Anisimov, in bug #17816.
    
    Discussion: https://postgr.es/m/17816-70f3d2764e88a108@postgresql.org


src/backend/utils/adt/oracle_compat.c | 12 +++++++----- src/test/regress/expected/strings.out | 6 ++++++ src/test/regress/sql/strings.sql | 1 + 3 files changed, 14 insertions(+), 5 deletions(-) diff --git a/src/backend/utils/adt/oracle_compat.c b/src/backend/utils/adt/oracle_compat.c index 2d94f8b0da5..6e44cc306c9 100644 --- a/src/backend/utils/adt/oracle_compat.c +++ b/src/backend/utils/adt/oracle_compat.c @@ -723,7 +723,8 @@ translate(PG_FUNCTION_ARGS) text *to = PG_GETARG_TEXT_PP(2); text *result; char *from_ptr, - *to_ptr; + *to_ptr, + *to_end; char *source, *target; int m, @@ -745,6 +746,7 @@ translate(PG_FUNCTION_ARGS) from_ptr = VARDATA_ANY(from); tolen = VARSIZE_ANY_EXHDR(to); to_ptr = VARDATA_ANY(to); + to_end = to_ptr + tolen; /* * The worst-case expansion is to substitute a max-length character for a @@ -778,16 +780,16 @@ translate(PG_FUNCTION_ARGS) } if (i < fromlen) { - /* substitute */ + /* substitute, or delete if no corresponding "to" character */ char *p = to_ptr; for (i = 0; i < from_index; i++) { - p += pg_mblen(p); - if (p >= (to_ptr + tolen)) + if (p >= to_end) break; + p += pg_mblen(p); } - if (p < (to_ptr + tolen)) + if (p < to_end) { len = pg_mblen(p); memcpy(target, p, len); diff --git a/src/test/regress/expected/strings.out b/src/test/regress/expected/strings.out index 6fbaf152cd1..213b236a145 100644 --- a/src/test/regress/expected/strings.out +++ b/src/test/regress/expected/strings.out @@ -1771,6 +1771,12 @@ SELECT translate('12345', '14', 'ax'); a23x5 (1 row) +SELECT translate('12345', '134', 'a'); + translate +----------- + a25 +(1 row) + SELECT ascii('x'); ascii ------- diff --git a/src/test/regress/sql/strings.sql b/src/test/regress/sql/strings.sql index 8eb6bf30af5..7cb31859e4b 100644 --- a/src/test/regress/sql/strings.sql +++ b/src/test/regress/sql/strings.sql @@ -608,6 +608,7 @@ SELECT ltrim('zzzytrim', 'xyz'); SELECT translate('', '14', 'ax'); SELECT translate('12345', '14', 'ax'); +SELECT translate('12345', '134', 'a'); SELECT ascii('x'); SELECT ascii(''); [parent: 73e779b3807d]