]> git.ipfire.org Git - thirdparty/postgresql.git/commitdiff
Avoid fetching one past the end of translate()'s "to" parameter.
authorTom Lane <tgl@sss.pgh.pa.us>
Wed, 1 Mar 2023 16:30:17 +0000 (11:30 -0500)
committerTom Lane <tgl@sss.pgh.pa.us>
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 2d94f8b0da59a5046dbed96dfc9caadf9836a270..6e44cc306c9ecedb2ff37d55ea6053dab4280b0b 100644 (file)
@@ -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);
index 6fbaf152cd144981e58e5f6bd19486f5eff7d081..213b236a145018c818b8e2a72e87086b7b9a6a15 100644 (file)
@@ -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 
 -------
index 8eb6bf30af5622180663393a9a4fcf7dd59a6151..7cb31859e4bf6556bb1e24506c58689c9ae5b75d 100644 (file)
@@ -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('');