aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTom Lane <tgl@sss.pgh.pa.us>2023-03-01 11:30:17 -0500
committerTom Lane <tgl@sss.pgh.pa.us>2023-03-01 11:30:17 -0500
commit3b37e844220d4e7cedbb8def7fbcd2b36703d65d (patch)
tree8a2cc5eb4c1592142f2961ceb26695e76fe20a21
parentab5b76c07a0e920f106228a5e54043cb5cd298b7 (diff)
downloadpostgresql-3b37e844220d4e7cedbb8def7fbcd2b36703d65d.tar.gz
postgresql-3b37e844220d4e7cedbb8def7fbcd2b36703d65d.zip
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
-rw-r--r--src/backend/utils/adt/oracle_compat.c12
-rw-r--r--src/test/regress/expected/strings.out6
-rw-r--r--src/test/regress/sql/strings.sql1
3 files changed, 14 insertions, 5 deletions
diff --git a/src/backend/utils/adt/oracle_compat.c b/src/backend/utils/adt/oracle_compat.c
index 76e666474e8..60d2d4cc768 100644
--- a/src/backend/utils/adt/oracle_compat.c
+++ b/src/backend/utils/adt/oracle_compat.c
@@ -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);
diff --git a/src/test/regress/expected/strings.out b/src/test/regress/expected/strings.out
index 90ab198223f..2efda79aa20 100644
--- a/src/test/regress/expected/strings.out
+++ b/src/test/regress/expected/strings.out
@@ -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
-------
diff --git a/src/test/regress/sql/strings.sql b/src/test/regress/sql/strings.sql
index 493547dd74f..4b139cfdc50 100644
--- a/src/test/regress/sql/strings.sql
+++ b/src/test/regress/sql/strings.sql
@@ -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('');