Avoid fetching one past the end of translate()'s "to" parameter.

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
This commit is contained in:
Tom Lane 2023-03-01 11:30:17 -05:00
parent 11290c89bb
commit b162660d3a
3 changed files with 14 additions and 5 deletions

View 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);

View 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
-------

View 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('');