]> 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 ecd8268b23b51282a4ba847bba6d1bef27fa54f4..85db92411f0d54d17e0618659235fcdc423ecae6 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 516b668f361e19f7ab83928c36484853b06affb6..ba64c999d50767d0e3bf53d2863e081a33a51b5a 100644 (file)
@@ -1856,6 +1856,12 @@ SELECT translate('12345', '14', 'ax');
  a23x5
 (1 row)
 
+SELECT translate('12345', '134', 'a');
+ translate 
+-----------
+ a25
+(1 row)
+
 SELECT ascii('x');
  ascii 
 -------
index a736a94ce135e7c6a34ed5401257ae2b6c062eae..8589712d41b2291557e8762767af1b7a0787101e 100644 (file)
@@ -645,6 +645,7 @@ SELECT ltrim('zzzytrim', 'xyz');
 
 SELECT translate('', '14', 'ax');
 SELECT translate('12345', '14', 'ax');
+SELECT translate('12345', '134', 'a');
 
 SELECT ascii('x');
 SELECT ascii('');