Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
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 76e666474e847c2e227d59a1f6af347ca22a5187..60d2d4cc768b977b26bfc44eca4087e1671c9a6b 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 90ab198223f50c5e8c6d95f441bf405fd4913eeb..2efda79aa20672ebf69d253b1bc6f87a2e9e6dc2 100644 (file)
@@ -1988,6 +1988,12 @@ SELECT translate('12345', '14', 'ax');
  a23x5
 (1 row)
 
+SELECT translate('12345', '134', 'a');
+ translate 
+-----------
+ a25
+(1 row)
+
 SELECT ascii('x');
  ascii 
 -------
index 493547dd74f60880b8343a2849df8049f099d636..4b139cfdc5043a2e6db01ef80d3a5466d5742680 100644 (file)
@@ -679,6 +679,7 @@ SELECT ltrim('zzzytrim', 'xyz');
 
 SELECT translate('', '14', 'ax');
 SELECT translate('12345', '14', 'ax');
+SELECT translate('12345', '134', 'a');
 
 SELECT ascii('x');
 SELECT ascii('');