From f28fb1ac85765c2a6c3acffbf69571341e9db1d2 Mon Sep 17 00:00:00 2001
From: Andrey Rachitskiy <pl0h0yp1@gmail.com>
Date: Thu, 10 Sep 2026 18:08:27 +0500
Subject: [PATCH] Reject out-of-range Hunspell AF alias counts.

NIImportOOAffixes() parsed the AF alias count with atoi().  A value
such as 4294967297 overflows a 32-bit int to 1 and was accepted when
the wrapped count matched the number of alias lines.

Parse the count with strtol() and reject overflow, non-numeric input,
and values that cannot be incremented to reserve the empty flag-set
slot.

Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
 src/backend/tsearch/Makefile                        |  3 ++-
 src/backend/tsearch/dicts/hunspell_afoverflow.affix |  5 +++++
 src/backend/tsearch/dicts/hunspell_afoverflow.dict  |  1 +
 src/backend/tsearch/spell.c                         | 11 +++++++++--
 src/test/regress/expected/tsdicts.out               |  7 +++++++
 src/test/regress/sql/tsdicts.sql                    |  7 +++++++
 6 files changed, 31 insertions(+), 3 deletions(-)
 create mode 100644 src/backend/tsearch/dicts/hunspell_afoverflow.affix
 create mode 100644 src/backend/tsearch/dicts/hunspell_afoverflow.dict

diff --git a/src/backend/tsearch/Makefile b/src/backend/tsearch/Makefile
index 4a436150109..64d4ad0b0d8 100644
--- a/src/backend/tsearch/Makefile
+++ b/src/backend/tsearch/Makefile
@@ -18,7 +18,8 @@ DICTFILES=synonym_sample.syn thesaurus_sample.ths \
 	hunspell_sample.affix \
 	ispell_sample.affix ispell_sample.dict \
 	hunspell_sample_long.affix hunspell_sample_long.dict \
-	hunspell_sample_num.affix hunspell_sample_num.dict
+	hunspell_sample_num.affix hunspell_sample_num.dict \
+	hunspell_afoverflow.affix hunspell_afoverflow.dict
 
 # Local paths to dictionaries files
 DICTFILES_PATH=$(addprefix dicts/,$(DICTFILES))
diff --git a/src/backend/tsearch/dicts/hunspell_afoverflow.affix b/src/backend/tsearch/dicts/hunspell_afoverflow.affix
new file mode 100644
index 00000000000..64a3cd2b89c
--- /dev/null
+++ b/src/backend/tsearch/dicts/hunspell_afoverflow.affix
@@ -0,0 +1,5 @@
+SET UTF-8
+AF 4294967297
+AF A
+PFX A Y 1
+PFX A 0 re .
diff --git a/src/backend/tsearch/dicts/hunspell_afoverflow.dict b/src/backend/tsearch/dicts/hunspell_afoverflow.dict
new file mode 100644
index 00000000000..dcf874345d9
--- /dev/null
+++ b/src/backend/tsearch/dicts/hunspell_afoverflow.dict
@@ -0,0 +1 @@
+book/1
diff --git a/src/backend/tsearch/spell.c b/src/backend/tsearch/spell.c
index f2c6e064218..b2624eba62b 100644
--- a/src/backend/tsearch/spell.c
+++ b/src/backend/tsearch/spell.c
@@ -1382,13 +1382,20 @@ NIImportOOAffixes(IspellDict *Conf, const char *filename)
 			/* First line is the number of aliases */
 			if (!Conf->useFlagAliases)
 			{
+				char	   *end;
+				long		naliases;
+
 				Conf->useFlagAliases = true;
-				naffix = atoi(sflag);
-				if (naffix <= 0)
+				errno = 0;
+				naliases = strtol(sflag, &end, 10);
+				if (sflag == end || errno == ERANGE ||
+					naliases <= 0 || naliases >= INT_MAX)
 					ereport(ERROR,
 							(errcode(ERRCODE_CONFIG_FILE_ERROR),
 							 errmsg("invalid number of flag vector aliases")));
 
+				naffix = (int) naliases;
+
 				/* Also reserve place for empty flag set */
 				naffix++;
 
diff --git a/src/test/regress/expected/tsdicts.out b/src/test/regress/expected/tsdicts.out
index 0bbf2ff4ca2..01a873b682c 100644
--- a/src/test/regress/expected/tsdicts.out
+++ b/src/test/regress/expected/tsdicts.out
@@ -447,6 +447,13 @@ CREATE TEXT SEARCH DICTIONARY hunspell_err (
 						AffFile=hunspell_sample_long
 );
 ERROR:  invalid affix alias "302,301,202,303"
+-- Over-wide AF alias count (4294967297) must be rejected.
+CREATE TEXT SEARCH DICTIONARY hunspell_afoverflow (
+						Template=ispell,
+						DictFile=hunspell_afoverflow,
+						AffFile=hunspell_afoverflow
+);
+ERROR:  invalid number of flag vector aliases
 -- Synonym dictionary
 CREATE TEXT SEARCH DICTIONARY synonym (
 						Template=synonym,
diff --git a/src/test/regress/sql/tsdicts.sql b/src/test/regress/sql/tsdicts.sql
index cf08410bb2d..d2dd76698b0 100644
--- a/src/test/regress/sql/tsdicts.sql
+++ b/src/test/regress/sql/tsdicts.sql
@@ -138,6 +138,13 @@ CREATE TEXT SEARCH DICTIONARY hunspell_err (
 						AffFile=hunspell_sample_long
 );
 
+-- Over-wide AF alias count (4294967297) must be rejected.
+CREATE TEXT SEARCH DICTIONARY hunspell_afoverflow (
+						Template=ispell,
+						DictFile=hunspell_afoverflow,
+						AffFile=hunspell_afoverflow
+);
+
 -- Synonym dictionary
 CREATE TEXT SEARCH DICTIONARY synonym (
 						Template=synonym,
-- 
2.53.0

