Avoid fetching one past the end of translate()'s "to" parameter.
authorTom Lane
Wed, 1 Mar 2023 16:30:17 +0000 (11:30 -0500)
committerTom Lane
Wed, 1 Mar 2023 16:30:17 +0000 (11:30 -0500)
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
src/test/regress/expected/strings.out
src/test/regress/sql/strings.sql

index f737aa6fbde73763eb5c70be5ac3287652acac9c..bd9e5f9e243d8dbaded74ad9f322c30454f9a71f 100644 (file)
@@ -797,7 +797,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,
@@ -819,6 +820,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
@@ -852,16 +854,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);
index 91aa8198045e72332dc4504862dddfd43b9f52a2..99b0eb3f688dda352451bce29c198507fee3b44c 100644 (file)
@@ -2116,6 +2116,12 @@ SELECT translate('12345', '14', 'ax');
  a23x5
 (1 row)
 
+SELECT translate('12345', '134', 'a');
+ translate 
+-----------
+ a25
+(1 row)
+
 SELECT ascii('x');
  ascii 
 -------
index 2c502534c2b679ebf8220e4374300a7cdc8b7e72..63ddce67c54b7afb472d9ae15685e3051a9dd9b1 100644 (file)
@@ -719,6 +719,7 @@ SELECT ltrim('zzzytrim', 'xyz');
 
 SELECT translate('', '14', 'ax');
 SELECT translate('12345', '14', 'ax');
+SELECT translate('12345', '134', 'a');
 
 SELECT ascii('x');
 SELECT ascii('');