]> 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 018e8c93428a0ad22c64d61eae4ea5f2bb0685dd..6a5ce1cc1024155faeec570bde2897a82fb1a759 100644 (file)
@@ -800,7 +800,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,
@@ -822,6 +823,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
@@ -856,16 +858,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 0f95b9400b697dc69fa11a7ca2cbf7402cf8b114..b72120d48b2d41ab5182c2907016090ec9f59900 100644 (file)
@@ -2506,6 +2506,12 @@ SELECT translate('12345', '14', 'ax');
  a23x5
 (1 row)
 
+SELECT translate('12345', '134', 'a');
+ translate 
+-----------
+ a25
+(1 row)
+
 SELECT ascii('x');
  ascii 
 -------
index 8c379182cb9d608e036c0e071c894b23695c7f52..fd4f1f674aaa0f797ddb3311fa7c7c94ba40294a 100644 (file)
@@ -813,6 +813,7 @@ SELECT ltrim('zzzytrim', 'xyz');
 
 SELECT translate('', '14', 'ax');
 SELECT translate('12345', '14', 'ax');
+SELECT translate('12345', '134', 'a');
 
 SELECT ascii('x');
 SELECT ascii('');