From 382c736b599292470b050e112b66945c46a5a7e7 Mon Sep 17 00:00:00 2001 From: Ayush Tiwari Date: Thu, 18 Jun 2026 19:54:54 +0530 Subject: [PATCH] Fix out-of-bounds access on embedded null tokens in dict_int and dict_xsyn pnstrdup() stops at the first null byte, so a token beginning with a null is copied as a 1-byte string. Both dictionaries then reused the original token length against that shorter copy. dintdict_lexize() (dict_int) used it for the "len > maxlen" check and then wrote txt[maxlen] = '\0' past the end of the 1-byte allocation; recompute len from the copy. dxsyn_lexize() (dict_xsyn) passed the original length to str_tolower(), which then read past the copy; pass the token directly instead, dropping the needless copy. Reaching either path needs a token with a null byte, which the normal text-search input path does not produce (it requires SQL_ASCII and a caller passing raw bytes to the lexize method). Reported-by: Yuelin Wang <3020001251@tju.edu.cn> Bug: #19525 --- contrib/dict_int/dict_int.c | 6 ++++++ contrib/dict_xsyn/dict_xsyn.c | 15 +++++++-------- 2 files changed, 13 insertions(+), 8 deletions(-) diff --git a/contrib/dict_int/dict_int.c b/contrib/dict_int/dict_int.c index aafce894977..b4413cfde99 100644 --- a/contrib/dict_int/dict_int.c +++ b/contrib/dict_int/dict_int.c @@ -95,6 +95,12 @@ dintdict_lexize(PG_FUNCTION_ARGS) else txt = pnstrdup(in, len); + /* + * pnstrdup() stops at the first null byte, so recompute len from the copy; + * otherwise the truncation below could write past the allocation. + */ + len = strlen(txt); + if (len > d->maxlen) { if (d->rejectlong) diff --git a/contrib/dict_xsyn/dict_xsyn.c b/contrib/dict_xsyn/dict_xsyn.c index 9e3784e0f47..b375e8f7387 100644 --- a/contrib/dict_xsyn/dict_xsyn.c +++ b/contrib/dict_xsyn/dict_xsyn.c @@ -211,14 +211,13 @@ dxsyn_lexize(PG_FUNCTION_ARGS) if (!length || d->len == 0) PG_RETURN_POINTER(NULL); - /* Create search pattern */ - { - char *temp = pnstrdup(in, length); - - word.key = str_tolower(temp, length, DEFAULT_COLLATION_OID); - pfree(temp); - word.value = NULL; - } + /* + * Pass the token directly to str_tolower(); copying it first with + * pnstrdup() and reusing the original length would read past the copy if + * the token contains a null byte. + */ + word.key = str_tolower(in, length, DEFAULT_COLLATION_OID); + word.value = NULL; /* Look for matching syn */ found = (Syn *) bsearch(&word, d->syn, d->len, sizeof(Syn), compare_syn); -- 2.34.1