postgres.git / summary / log / commit / refs
commit e91dcfccaaa827f2907307e12891eeb131629948
Author: Masahiko Sawada <msawada@postgresql.org>
Commit: Noah Misch <noah@leadboat.com>
Date: Mon Aug 10 13:38:17 2026 +0000
First released in: 14.24, 15.19, 16.15, 17.11, 18.6
Fix potential buffer overrun in regexp match/split functions.
setup_regexp_matches() sizes the buffer used to convert matched
substrings back from pg_wchar form at the smaller of maxlen*eml and
the original string's byte length, on the assumption that such a
conversion cannot produce more bytes than the string it came
from. That assumption holds only for validly encoded input. But
pg_mb2wchar_with_len() silently accepts bytes that are invalid in the
database encoding, turning each such byte into one pg_wchar, and
converting that back can take more bytes than the input did. A string
made of such bytes therefore overruns the conversion buffer by up to
its own length, corrupting the following memory. regexp_match(),
regexp_matches(), regexp_split_to_table() and regexp_split_to_array()
are all affected.
Fix by dropping the tighter bound and always allocating maxlen*eml + 1
bytes.
Reported-by: Francesco Verardi <frevadiscor89@gmail.com>
Author: Masahiko Sawada <sawada.mshk@gmail.com>
Reviewed-by: Tom Lane <tgl@sss.pgh.pa.us>
Backpatch-through: 14
Security: CVE-2026-14664
src/backend/utils/adt/regexp.c | 25 +++++++++++++------------
1 file changed, 13 insertions(+), 12 deletions(-)
diff --git a/src/backend/utils/adt/regexp.c b/src/backend/utils/adt/regexp.c
index d114407e5c5..637970dcb08 100644
--- a/src/backend/utils/adt/regexp.c
+++ b/src/backend/utils/adt/regexp.c
@@ -1512,7 +1512,7 @@ setup_regexp_matches(text *orig_str, text *pattern, pg_re_flags *re_flags,
/* convert string to pg_wchar form for matching */
orig_len = VARSIZE_ANY_EXHDR(orig_str);
- wide_str = (pg_wchar *) palloc(sizeof(pg_wchar) * (orig_len + 1));
+ wide_str = palloc_array(pg_wchar, orig_len + 1);
wide_len = pg_mb2wchar_with_len(VARDATA_ANY(orig_str), wide_str, orig_len);
/* set up the compiled pattern */
@@ -1648,23 +1648,24 @@ setup_regexp_matches(text *orig_str, text *pattern, pg_re_flags *re_flags,
if (eml > 1)
{
- int64 maxsiz = eml * (int64) maxlen;
int conv_bufsiz;
/*
* Make the conversion buffer large enough for any substring of
- * interest.
+ * interest. We can't use the original string's byte length as a
+ * tighter bound, because that assumes the input is validly encoded;
+ * but pg_mb2wchar_with_len() can accept strings that are invalid in
+ * the database encoding, and converting such a character back to
+ * multibyte form can take more bytes than it did in the input.
*
- * Worst case: assume we need the maximum size (maxlen*eml), but take
- * advantage of the fact that the original string length in bytes is
- * an upper bound on the byte length of any fetched substring (and we
- * know that len+1 is safe to allocate because the varlena header is
- * longer than 1 byte).
+ * This can't overflow, nor exceed what palloc will accept: maxlen is
+ * at most wide_len, which is at most orig_len, and we have already
+ * successfully allocated (orig_len + 1) * sizeof(pg_wchar) bytes for
+ * wide_str. That relies on eml being no more than sizeof(pg_wchar),
+ * which is true of all supported encodings.
*/
- if (maxsiz > orig_len)
- conv_bufsiz = orig_len + 1;
- else
- conv_bufsiz = maxsiz + 1; /* safe since maxsiz < 2^30 */
+ Assert(eml <= sizeof(pg_wchar));
+ conv_bufsiz = maxlen * eml + 1;
matchctx->conv_buf = palloc(conv_bufsiz);
matchctx->conv_bufsiz = conv_bufsiz;
[parent: 8e87bd4731d7]