Subject: dict_synonym.c: fix truncation of multibyte sequence

From: Jeff Davis <[email-masked]>
Date: Thu, 04 Jun 2026 15:07:25 -0700

If case_sensitive is false and str_tolower() changes the byte length of
the string, then outlen will be incorrect.

Fortunately, pnstrdup() also stops at a NUL terminator, so it will
never overrun; but if outlen is calculated to be too small, then it
could cause truncation. In any case, the input comes from a trusted
source (dictionary configuration), so it's not very serious.

The correct value of outlen is strlen(d->syn[cur].out). But it's only
ever used in one place, which is a call to pnstrdup(). Given that the
string is NUL-terminated anyway, it's easier to fix it by just changing
that to a pstrdup(). Patch attached, backpatch all the way.

Regards,
	Jeff Davis

From cc395fca68100d8bd354edab2ee93251285c943a Mon Sep 17 00:00:00 2001
From: Jeff Davis <[email-masked]>
Date: Wed, 15 Apr 2026 12:56:31 -0700
Subject: [PATCH v1] dict_synonym.c: remove incorrect outlen.

Previously, outlen was miscalculated if case_sensitive was false and
str_tolower() changed the byte length of the string. If outlen was too
large, pnstrdup() would stop at the NUL terminator, preventing
overrun. But if outlen was too small, it would cause truncation,
possibly in the middle of a multibyte sequence.

Fix by just removing outlen. Both strings are NUL-terminated anyway.

Backpatch-through: 14
---
 src/backend/tsearch/dict_synonym.c | 4 +---
 1 file changed, 1 insertion(+), 3 deletions(-)

diff --git a/src/backend/tsearch/dict_synonym.c b/src/backend/tsearch/dict_synonym.c
index 3937f25bcc6..24345767658 100644
--- a/src/backend/tsearch/dict_synonym.c
+++ b/src/backend/tsearch/dict_synonym.c
@@ -24,7 +24,6 @@ typedef struct
 {
 	char	   *in;
 	char	   *out;
-	int			outlen;
 	uint16		flags;
 } Syn;
 
@@ -189,7 +188,6 @@ dsynonym_init(PG_FUNCTION_ARGS)
 			d->syn[cur].out = str_tolower(starto, strlen(starto), DEFAULT_COLLATION_OID);
 		}
 
-		d->syn[cur].outlen = strlen(starto);
 		d->syn[cur].flags = flags;
 
 		cur++;
@@ -237,7 +235,7 @@ dsynonym_lexize(PG_FUNCTION_ARGS)
 		PG_RETURN_POINTER(NULL);
 
 	res = palloc0_array(TSLexeme, 2);
-	res[0].lexeme = pnstrdup(found->out, found->outlen);
+	res[0].lexeme = pstrdup(found->out);
 	res[0].flags = found->flags;
 
 	PG_RETURN_POINTER(res);
-- 
2.43.0

---

From: "Tristan Partin" <[email-masked]>
Date: Fri, 05 Jun 2026 15:57:53 +0000

> that to a pstrdup(). Patch attached, backpatch all the way.

The fix looks and sounds good. Do we have any way to test this, so it 
doesn't regress in the future? Do we need to export a module to test 
through SQL?

-- 
Tristan Partin
PostgreSQL Contributors Team
AWS (https://aws.amazon.com)

---

From: Jeff Davis <[email-masked]>
Date: Fri, 05 Jun 2026 10:37:03 -0700

On Fri, 2026-06-05 at 15:57 +0000, Tristan Partin wrote:
> > In any case, the input comes from a trusted
> doesn't regress in the future?

  -- Ⱥ is 2 bytes, 'ⱥ' is 3 bytes
  $ echo "foo barȺ" > /path/to/postgres/share/tsearch_data/mbtest.syn

  CREATE TEXT SEARCH DICTIONARY mb_syn (
    TEMPLATE = synonym,
    SYNONYMS = mbtest);

  SELECT ts_lexize('mb_syn', 'foo');

  =# SELECT ts_lexize('mb_syn', 'foo'); -- before patch
   ts_lexize 
  -----------
   {bar}
  (1 row)

  =# SELECT ts_lexize('mb_syn', 'foo'); -- after patch
   ts_lexize 
  -----------
   {barⱥ}
  (1 row)

It requires a specially-crafted synonym file, and I'm not sure it's
worth much effort to add a test for this specific path. If we see
similar bugs, it's more likely to be somewhere else that makes the same
faulty assumption.

If you do think we should add tests, we should probably add a set of
dictionary-related files (.syn, .dict, .ths, etc.) that contain a
variety of adversarial Unicode cases.

I'd be inclined to just commit this fix for now. It needs backpatching,
and I don't think we want to backpatch a large set of tests with it.

Regards,
	Jeff Davis

---

From: "Tristan Partin" <[email-masked]>
Date: Fri, 05 Jun 2026 20:46:00 +0000

>   =# SELECT ts_lexize('mb_syn', 'foo'); -- before patch

I would say proceed as you see fit. I guess I am generally of the 
opinion that additional testing is generally always better, but I don't 
want to push for something if others don't see the same value. I'd be 
happy to provide a patch for the test in a subsequent discussion if that 
is a good middle ground?

---

From: Jeff Davis <[email-masked]>
Date: Fri, 05 Jun 2026 14:34:45 -0700

> happy to provide a patch for the test in a subsequent discussion if

Yes, sounds good.

Regards,
	Jeff Davis