From 10cc5aa96a6df7cb1d7c47e978cc94f01f785cb7 Mon Sep 17 00:00:00 2001 From: Tom Lane Date: Wed, 7 Oct 2026 11:49:30 -0400 Subject: [PATCH] Tighten data validity checks in tsvectorrecv(). Insist that the input lexemes be already sorted and non-duplicate. There's no good reason for them not to be, since the required ordering is simple and platform-independent. The previous coding was willing to re-sort them, but that left a gap: it didn't de-duplicate them. De-duplicating would potentially require merging position lists, which would be painful; we can't easily re-use tsvectorin()'s logic for that, since it uses a different temporary representation. Let's just dodge the problem by insisting the lexeme list be fully correct as-presented, as we were already doing for the position lists. Also, there was an oversight in the position-list checking: it did not verify that the first position isn't zero. tsvectorin() rejects that, so this should too. While these problems allow tsvectorrecv() to construct tsvectors that would not be generated internally, the vectors aren't so broken that we can't work with them. Therefore, the best course of action seems to be to apply these tighter rules in master but not back-patch. Back-patching might break application workflows that aren't causing any serious problems. Any such users will need to clean their data before loading it into PG >= v20, but they'd face that issue anyway, and a major update is a better time for it than a minor update. Bug: #19748 Reported-by: Ke Author: Tom Lane Discussion: https://postgr.es/m/19748-6075f0508fa1ffc0@postgresql.org --- src/backend/utils/adt/tsvector.c | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/src/backend/utils/adt/tsvector.c b/src/backend/utils/adt/tsvector.c index 21d29a7e4f6..2fe782374e7 100644 --- a/src/backend/utils/adt/tsvector.c +++ b/src/backend/utils/adt/tsvector.c @@ -454,7 +454,6 @@ tsvectorrecv(PG_FUNCTION_ARGS) * WordEntries */ Size hdrlen; Size len; /* allocated size of vec */ - bool needSort = false; nentries = pq_getmsgint(buf, sizeof(int32)); @@ -512,16 +511,18 @@ tsvectorrecv(PG_FUNCTION_ARGS) datalen += lex_len; + /* Verify correct sorting of lexemes, with no duplicates */ if (i > 0 && compareentry(&vec->entries[i], &vec->entries[i - 1], STRPTR(vec)) <= 0) - needSort = true; + elog(ERROR, "tsvector lexemes are misordered"); /* Receive positions */ if (npos > 0) { uint16 j; WordEntryPos *wepptr; + WordEntryPos lastpos = 0; /* * Pad to 2-byte alignment if necessary. Though we used palloc0 @@ -539,9 +540,13 @@ tsvectorrecv(PG_FUNCTION_ARGS) wepptr = POSDATAPTR(vec, &vec->entries[i]); for (j = 0; j < npos; j++) { - wepptr[j] = (WordEntryPos) pq_getmsgint(buf, sizeof(WordEntryPos)); - if (j > 0 && WEP_GETPOS(wepptr[j]) <= WEP_GETPOS(wepptr[j - 1])) + WordEntryPos thispos; + + thispos = (WordEntryPos) pq_getmsgint(buf, sizeof(WordEntryPos)); + /* Verify positions are sorted, nonduplicate, and not zero */ + if (WEP_GETPOS(thispos) <= WEP_GETPOS(lastpos)) elog(ERROR, "position information is misordered"); + wepptr[j] = lastpos = thispos; } datalen += sizeof(uint16) + npos * sizeof(WordEntryPos); @@ -559,9 +564,5 @@ tsvectorrecv(PG_FUNCTION_ARGS) SET_VARSIZE(vec, hdrlen + datalen); - if (needSort) - qsort_arg(ARRPTR(vec), vec->size, sizeof(WordEntry), - compareentry, STRPTR(vec)); - PG_RETURN_TSVECTOR(vec); }