Received: from malur.postgresql.org ([217.196.149.56]) by arkaria.postgresql.org with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (Exim 4.96) (envelope-from ) id 1x4dga-007IDu-1e for pgsql-bugs@arkaria.postgresql.org; Thu, 10 Sep 2026 12:15:24 +0000 Received: from localhost ([127.0.0.1] helo=malur.postgresql.org) by malur.postgresql.org with esmtp (Exim 4.96) (envelope-from ) id 1x4dgZ-005a1x-0t for pgsql-bugs@arkaria.postgresql.org; Thu, 10 Sep 2026 12:15:23 +0000 Received: from magus.postgresql.org ([2a02:c0:301:0:ffff::29]) by malur.postgresql.org with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (Exim 4.96) (envelope-from ) id 1x3aEc-001xIv-21 for pgsql-bugs@lists.postgresql.org; Mon, 07 Sep 2026 14:22:10 +0000 Received: from mahout.postgresql.org ([2001:4800:3e1:1::227]) by magus.postgresql.org with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (Exim 4.98.2) (envelope-from ) id 1x3aEZ-00000003Q1w-0NSO for pgsql-bugs@lists.postgresql.org; Mon, 07 Sep 2026 14:22:10 +0000 DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=postgresql.org; s=20171124; h=Message-ID:Date:Reply-To:Cc:From:To:Subject: Content-Transfer-Encoding:MIME-Version:Content-Type:Sender:Content-ID: Content-Description:In-Reply-To:References; bh=zSGEGXT7I4bam48xhgUrH+HICHaDvpzJtaSFtHlQJWk=; b=Et2jKfRRXcy3ED63hMJpK0cdSR /4QZF2+9fyvXw4zoePMImJAYhx8El7NI382FCVl3o7Fj0BUv0o715ySMNX1aOAirmX+5m1IA4XmLA ezb1us6We/UQFzvMXtgD1trL3C7zGelNm5aYxQEf4U4hpFf/9ni0Pb4dunB3E/q1U+uzZxpFIXCd6 AeP2g76/5cFGPVRINhJ8pb0fX9sxgtNb3yw3rWTeuJSRXjaCK7oeulS7ElPrGuU6ng+bKfmofvBAd tdghj5pQvg/O+b3i7X5PYAST6v2iFm2TIRddbNHrTkxEYFd2QgpJhUj8EygKk7yB7SiM8LVOna/jV MIO9Ba/w==; Received: from wrigleys.postgresql.org ([2a02:16a8:dc51::60]) by mahout.postgresql.org with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (Exim 4.96) (envelope-from ) id 1x3aEW-00DTfl-3A for pgsql-bugs@lists.postgresql.org; Mon, 07 Sep 2026 14:22:04 +0000 Received: from localhost ([127.0.0.1] helo=wrigleys.postgresql.org) by wrigleys.postgresql.org with esmtp (Exim 4.98.2) (envelope-from ) id 1x3aEV-00000000gOS-2joz for pgsql-bugs@lists.postgresql.org; Mon, 07 Sep 2026 14:22:03 +0000 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Subject: BUG #19678: int16 Overflow in tsquery Phrase Distance After Stopword Removal To: pgsql-bugs@lists.postgresql.org From: PG Bug reporting form Cc: 1950233439@qq.com Reply-To: 1950233439@qq.com, pgsql-bugs@lists.postgresql.org Date: Mon, 07 Sep 2026 14:21:21 +0000 Message-ID: <19678-790bf8888cef8cb5@postgresql.org> X-Auto-Response-Suppress: All Auto-Submitted: auto-generated List-Id: List-Help: List-Subscribe: List-Post: List-Owner: List-Archive: Archived-At: Precedence: bulk The following bug has been logged on the website: Bug reference: 19678 Logged by: Tianyu Shi Email address: 1950233439@qq.com PostgreSQL version: 19beta3 Operating system: Ubuntu22.04 Description: =20 ### 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 +=3D 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 =E2=88=9232768, producing a tsquery with a negative phrase distance. The co= rrupted tsquery causes `TS_phrase_execute()` to emit silent false negatives =E2=80= =94 documents that genuinely satisfy the phrase query are not returned =E2=80= =94 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=3D32768 wraps to -32768) -- Step 2: Confirm false negative =E2=80=94 the document contains both word= s 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 =E2=80=94 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.