From 330c6ff741575b318b6bf00131cbca576d2e2387 Mon Sep 17 00:00:00 2001
From: Manuel Reyes Bravo <manuelreyesbravo@gmail.com>
Date: Fri, 2 Oct 2026 14:44:44 -0300
Subject: [PATCH v3 1/2] Fix out-of-bounds read in pg_trgm word similarity

calc_word_similarity() looks at found[j] once more after its merge
loop.  When neither string has a trigram, as in word_similarity('',
''), found[] has no elements and that read is past its end.

The value never affects the result: with no trigrams in the second
string, iterate_word_similarity() returns 0 without using ulen1.  But
it is undefined behavior, and builds with -fsanitize=undefined abort
on it.  cfbot hit it through a test with stored empty strings in the
fix for bug #19701.

Backpatch-through: 14
Discussion: https://postgr.es/m/19701-c861a62e79bf49ce@postgresql.org
---
 contrib/pg_trgm/expected/pg_word_trgm.out | 7 +++++++
 contrib/pg_trgm/sql/pg_word_trgm.sql      | 3 +++
 contrib/pg_trgm/trgm_op.c                 | 3 ++-
 3 files changed, 12 insertions(+), 1 deletion(-)

diff --git a/contrib/pg_trgm/expected/pg_word_trgm.out b/contrib/pg_trgm/expected/pg_word_trgm.out
index c66a67f30ef..17a3831dfe7 100644
--- a/contrib/pg_trgm/expected/pg_word_trgm.out
+++ b/contrib/pg_trgm/expected/pg_word_trgm.out
@@ -1050,3 +1050,10 @@ select * from test_trgm2 where t ~ '.*$x';
 ---
 (0 rows)
 
+-- No trigrams on either side: must not read past the end of an empty array
+SELECT word_similarity('', ''), strict_word_similarity('', '');
+ word_similarity | strict_word_similarity 
+-----------------+------------------------
+               0 |                      0
+(1 row)
+
diff --git a/contrib/pg_trgm/sql/pg_word_trgm.sql b/contrib/pg_trgm/sql/pg_word_trgm.sql
index d2ada49133a..304409728cc 100644
--- a/contrib/pg_trgm/sql/pg_word_trgm.sql
+++ b/contrib/pg_trgm/sql/pg_word_trgm.sql
@@ -46,3 +46,6 @@ select t,word_similarity('Kabankala',t) as sml from test_trgm2 where t %> 'Kaban
 
 -- test unsatisfiable pattern
 select * from test_trgm2 where t ~ '.*$x';
+
+-- No trigrams on either side: must not read past the end of an empty array
+SELECT word_similarity('', ''), strict_word_similarity('', '');
diff --git a/contrib/pg_trgm/trgm_op.c b/contrib/pg_trgm/trgm_op.c
index 22bcc3c3361..00cc0e5e63e 100644
--- a/contrib/pg_trgm/trgm_op.c
+++ b/contrib/pg_trgm/trgm_op.c
@@ -919,7 +919,8 @@ calc_word_similarity(char *str1, int slen1, char *str2, int slen2,
 			found[j] = true;
 		}
 	}
-	if (found[j])
+	/* With no trigrams at all, found[] is empty and there is no last one */
+	if (len > 0 && found[j])
 		ulen1++;
 
 	/* Run iterative procedure to find maximum similarity with word */
-- 
2.55.0

