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 1watK3-0028fB-1p for pgsql-bugs@arkaria.postgresql.org; Sat, 20 Jun 2026 10:53:11 +0000 Received: from localhost ([127.0.0.1] helo=malur.postgresql.org) by malur.postgresql.org with esmtp (Exim 4.96) (envelope-from ) id 1watK2-001Ri4-1a for pgsql-bugs@arkaria.postgresql.org; Sat, 20 Jun 2026 10:53:10 +0000 Received: from makus.postgresql.org ([2001:4800:3e1:1::229]) by malur.postgresql.org with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (Exim 4.96) (envelope-from ) id 1waR2T-00ExQ1-2i for pgsql-bugs@lists.postgresql.org; Fri, 19 Jun 2026 04:41:10 +0000 Received: from mahout.postgresql.org ([2001:4800:3e1:1::227]) by makus.postgresql.org with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (Exim 4.98.2) (envelope-from ) id 1waR2R-000000015XR-1xTM for pgsql-bugs@lists.postgresql.org; Fri, 19 Jun 2026 04:41:08 +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=LNZdO6hMckbYL9yWwBQgQ4YD51jNq0wRYUNbtLP0I7c=; b=QEbbP5p3BHXeHwcIV0aCSRwFyF o05CgVFfVQTuajGVyjaRtcvhAGSk5MZFDToWZUjCC0+MFflPDClkbUMeIbt+D1Z7y571A5l5ozEch hRUzYevY3sgkvlYGd7xHbiTdGnEVFM35l6L+Sz/7Zls8ibUX17O1vHvvq1LJMd2UxVIqQ254vM/a7 aXN18QV6U5yNHyEtKez1SxCVYZcP+VCpxucYWhIfJZiKrRXvy4CsHT82yeMU2ECfuyYFAx9IIqi5X zGB1muQquTwvhSo+9nc1FajXxuj+yut/DrProdnL2YG3qS8xsT1UqYW9h/k2tO+97OPiydJTJ1tD7 8g4C0Dgw==; 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 1waR2N-003HFF-2e for pgsql-bugs@lists.postgresql.org; Fri, 19 Jun 2026 04:41:06 +0000 Received: from localhost ([127.0.0.1] helo=wrigleys.postgresql.org) by wrigleys.postgresql.org with esmtp (Exim 4.96) (envelope-from ) id 1waR2M-007OJ5-2y for pgsql-bugs@lists.postgresql.org; Fri, 19 Jun 2026 04:41:03 +0000 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Subject: BUG #19528: Assert failure in generate_normalized_query() via Squashed Array Literals To: pgsql-bugs@lists.postgresql.org From: PG Bug reporting form Cc: 3020001251@tju.edu.cn Reply-To: 3020001251@tju.edu.cn, pgsql-bugs@lists.postgresql.org Date: Fri, 19 Jun 2026 04:40:56 +0000 Message-ID: <19528-7290dd7e6f7dcc22@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: 19528 Logged by: Yuelin Wang Email address: 3020001251@tju.edu.cn PostgreSQL version: 19beta1 Operating system: Linux (Ubuntu 24.04, x86_64) Description: =20 The buffer is allocated at line 2841 with a per-location budget of 10 bytes: ```c norm_query_buflen =3D query_len + jstate->clocations_count * 10; ``` The comment explains the budget: a `$n` placeholder is at most 11 bytes and the original constant is at least 1 byte, so net expansion is at most 10 bytes per location. This holds for non-squashed constants. For squashed array elements, the sprintf at line 2883 writes `"$N /*, ... */"` instead of just `"$N"`: ```c n_quer_loc +=3D sprintf(norm_query + n_quer_loc, "$%d%s", num_constants_replaced + 1 + jstate->highest_extern_param_id, locs[i].squashed ? " /*, ... */" : ""); ``` `" /*, ... */"` is 11 bytes, so a squashed entry writes `"$N"` (2 bytes) plus `" /*, ... */"` (11 bytes) =3D 13 bytes to replace a 1-byte constant, a net expansion of 12 bytes. The budget of 10 bytes is exceeded by 2 bytes per squashed location. With N squashed array elements the buffer overflows by 2N bytes. Any `ARRAY[a,b]` literal records its second element as a squashed `clocations` entry. Ten such arrays produce a 20-byte overflow, which is sufficient to trigger the Assert at line 2908: ``` TRAP: failed Assert("n_quer_loc <=3D norm_query_buflen"), File: "pg_stat_statements.c", Line: 2908 ``` In a Release build (assertions disabled) the overflow is a true heap write past the `palloc` allocation, corrupting adjacent allocator metadata. ### Reproduction `pg_stat_statements` must be listed in `shared_preload_libraries`. The triggering role holds no special privileges beyond `LOGIN`. ```sql CREATE EXTENSION IF NOT EXISTS pg_stat_statements; CREATE ROLE vuln_004_lowpriv LOGIN PASSWORD 'vuln004'; GRANT pg_read_all_stats TO vuln_004_lowpriv; SET ROLE vuln_004_lowpriv; SELECT ARRAY[1,2], ARRAY[1,2], ARRAY[1,2], ARRAY[1,2], ARRAY[1,2], ARRAY[1,2], ARRAY[1,2], ARRAY[1,2], ARRAY[1,2], ARRAY[1,2]; ``` ### Observed Output psql output: ``` SET server closed the connection unexpectedly This probably means the server terminated abnormally before or while processing the request. connection to server was lost ``` Server log (PostgreSQL 19beta1, cassert build, 2026-06-19): ``` TRAP: failed Assert("n_quer_loc <=3D norm_query_buflen"), File: "pg_stat_statements.c", Line: 2908, PID: 2671635 postgres: yuelinwang postgres [local] SELECT(ExceptionalCondition+0x103)[0x5602a92f55b8] /data/.../pg_stat_statements.so(+0x655b)[0x7d147957e55b] /data/.../pg_stat_statements.so(+0x9ef7)[0x7d1479581ef7] /data/.../pg_stat_statements.so(+0xb3ba)[0x7d14795833ba] postgres: yuelinwang postgres [local] SELECT(parse_analyze_fixedparams+0x120)[...] postgres: yuelinwang postgres [local] SELECT(PostgresMain+0x1142)[...] LOG: client backend (PID 2671635) was terminated by signal 6: Aborted DETAIL: Failed process was running: SELECT ARRAY[1,2], ARRAY[1,2], ARRAY[1,2], ARRAY[1,2], ARRAY[1,2], ARRAY[1,2], ARRAY[1,2], ARRAY[1,2], ARRAY[1,2], ARRAY[1,2]; LOG: terminating any other active server processes LOG: all server processes terminated; reinitializing LOG: database system was interrupted; last known up at 2026-06-19 12:34:57 +08 LOG: database system was not properly shut down; automatic recovery in progress LOG: database system is ready to accept connections ``` ### Expected vs Actual | Step | Expected | Actual | |---|---|---| | SELECT with 10 ARRAY[1,2] literals | returns one row of 10 arrays | backend crashes (SIGABRT signal 6) | | Server log | nothing | `TRAP: failed Assert("n_quer_loc <=3D norm_query_buflen"), File: "pg_stat_statements.c", Line: 2908` | | Server state | unaffected | crash recovery triggered, postmaster reinitializes | | Connection | stays open | `server closed the connection unexpectedly` | ### Fix Account for the extra 11 bytes appended when a squashed suffix is written. One correct approach: ```c norm_query_buflen =3D query_len + jstate->clocations_count * (10 + (jstate->has_squashed_lists ? 11 : 0)); ``` A simpler conservative fix that covers the worst case (`"$2147483648 /*, ... */"` =3D 22 bytes replacing a 1-byte constant, net 21 bytes): ```c norm_query_buflen =3D query_len + jstate->clocations_count * 22; ```