| From: | PG Bug reporting form <noreply(at)postgresql(dot)org> |
|---|---|
| To: | pgsql-bugs(at)lists(dot)postgresql(dot)org |
| Cc: | 1950233439(at)qq(dot)com |
| Subject: | BUG #19678: int16 Overflow in tsquery Phrase Distance After Stopword Removal |
| Date: | 2026-09-07 14:21:21 |
| Message-ID: | 19678-790bf8888cef8cb5@postgresql.org |
| Views: | Whole Thread | Raw Message | Download mbox | Resend email |
| Thread: | |
| Lists: | pgsql-bugs |
The following bug has been logged on the website:
Bug reference: 19678
Logged by: Tianyu Shi
Email address: 1950233439(at)qq(dot)com
PostgreSQL version: 19beta3
Operating system: Ubuntu22.04
Description:
### Summary
In `clean_stopword_intree()` (`src/backend/utils/adt/tsquery_cleanup.c`,
line 344), when a stopword is removed from between two `OP_PHRASE`
operators, the child operator's accumulated distance is added back to the
surviving parent via `node->valnode->qoperator.distance += lradd + rladd;`.
Because `qoperator.distance` is `int16`, the addition overflows silently
when the combined distances exceed INT16_MAX (32767). With two operators
each at the maximum valid input distance of 16384, the sum 32768 wraps to
−32768, producing a tsquery with a negative phrase distance. The corrupted
tsquery causes `TS_phrase_execute()` to emit silent false negatives —
documents that genuinely satisfy the phrase query are not returned — which
can subvert keyword-based security filters or content-gating logic without
raising any error.
### PoC
Reproducible by any user with the default PUBLIC privilege to call
`to_tsquery()`; no superuser or special role required.
```sql
-- Connect to any database
-- ./build/bin/psql -h ./build/run -p 55433 -U postgres postgres
-- Step 1: Observe the corrupted tsquery (distance becomes negative due to
int16 overflow)
SELECT to_tsquery('english', 'cat <16384> the <16384> dog') AS
corrupted_query;
-- Expected: 'cat' <32768> 'dog' (or a valid large distance)
-- Actual: 'cat' <-32768> 'dog' (int16 overflow: 16384+16384=32768 wraps
to -32768)
-- Step 2: Confirm false negative — the document contains both words but the
match fails
SELECT
to_tsvector('english', 'cat the dog')
@@
to_tsquery('english', 'cat <16384> the <16384> dog') AS matches;
-- Expected: t
-- Actual: f (false negative caused by corrupted negative distance)
-- Step 3: Sanity check — small positive distance still works
SELECT
to_tsvector('english', 'cat the dog')
@@
to_tsquery('english', 'cat <2> dog') AS sanity;
-- Expected and actual: t
```
### Result
`to_tsquery('english', 'cat <16384> the <16384> dog')` returns a tsquery
with a negative phrase distance instead of the mathematically correct
positive value:
```
corrupted_query
----------------------
'cat' <-32768> 'dog'
```
The corrupted distance causes a false negative in phrase matching:
```
matches_large_distance
------------------------
f
```
where `t` is expected (both words are present in the document). The overflow
is confirmed by extracting the distance directly:
```
actual_distance | mathematically_correct_distance | analysis
-----------------+---------------------------------+----------------------------------------------
-32768 | 32768 | BUG: distance is
negative, expected positive.
| | int16 overflow
confirmed.
```
No error or warning is emitted; the corrupted tsquery is silently accepted
and stored as a valid datum, making the failure invisible to callers.
| From | Date | Subject | |
|---|---|---|---|
| Next Message | Tom Lane | 2026-09-07 15:08:39 | Re: BUG #19653: "variable not found in subplan target list" during planning with parallel parameterized nested loop, |
| Previous Message | PG Bug reporting form | 2026-09-07 14:19:49 | BUG #19677: Silent int64→int32 Truncation in timestamp_izone() Produces Wrong Timestamps |