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 1whLlV-000AZf-2P for pgsql-bugs@arkaria.postgresql.org; Wed, 08 Jul 2026 06:28:14 +0000 Received: from localhost ([127.0.0.1] helo=malur.postgresql.org) by malur.postgresql.org with esmtp (Exim 4.96) (envelope-from ) id 1whLlU-00528a-0m for pgsql-bugs@arkaria.postgresql.org; Wed, 08 Jul 2026 06:28:12 +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 1whLlT-00528Q-2m for pgsql-bugs@lists.postgresql.org; Wed, 08 Jul 2026 06:28:12 +0000 Received: from mail-lf1-x134.google.com ([2a00:1450:4864:20::134]) by makus.postgresql.org with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 (Exim 4.98.2) (envelope-from ) id 1whLlR-000000006lS-4C4x for pgsql-bugs@lists.postgresql.org; Wed, 08 Jul 2026 06:28:11 +0000 Received: by mail-lf1-x134.google.com with SMTP id 2adb3069b0e04-5aeb11c7347so214745e87.0 for ; Tue, 07 Jul 2026 23:28:10 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1783492089; cv=none; d=google.com; s=arc-20260327; b=e5PKWV6eOEwtEUGgmbNCgDj32fwqB25DyfiKqokJrTcBN6dxCEXpbFXgQWbR92Cmuj xbPjmwEsp5pW3StbWE/E5xHXclOjSfAv1zclSlkddL7jPNRyZncznaEXtaKgSEFrN/kI mnp6RDoITNNiCVqO4mcLqPHbXIl+KXHRfCv/iYt2udsLkq2imZMzHycTeVyhzSeVXA2z u6ML10vQx3D4gt+uEy/SfNayYi3otJUsVFQZ3pKI8GFGUpzvzzWL0AtB9UH0SA2ow4zF IhI9yK1IM03gHfW2nVArwvCWUGDTafxijOLgWuBPpjhIBg4n8nb7UD/ydu7sG7pEXGAk AdtA== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20260327; h=to:subject:message-id:date:from:in-reply-to:references:mime-version :dkim-signature; bh=mdQ5vw0zALKGflw6K8gniMrWqYATuVz5uhegxYTPloY=; fh=bfGZdQUBZEPtmBmlFYqVhk+8t0H43jRSJk+vuY3DKT8=; b=okbtwaMKVX2mLo44Gosuxx9niNJGKLGGwzWHKHzONbmu3u5JztDCyt7TFcnplGYEsg lAwiIf47/wEYoXr/QAGmC1zTZcCXizo8LRMddw906Fa+JC6jglDzYaTlNcAWr6LE6o2j YONUTdRWC0zTLg1ldHdBYDbqNghP9F4yG/odnTbcEu0NN/jbJDQV3h9HX/V3KhDJz3uo 4qU9CS7pK1lsZn7uOmz2LA+4Z541wiaI/gH52MFf24CV9YSqeKWTU8d6Kg6rYs1TZMrK 034sJ8CDKGj0nJ5zClnhXbqtWvgqHLpSL8l4FbbZmmyg4a0G4ZgLa9lf6K2ZEkFf2be5 UD/w==; darn=lists.postgresql.org ARC-Authentication-Results: i=1; mx.google.com; arc=none DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1783492089; x=1784096889; darn=lists.postgresql.org; h=to:subject:message-id:date:from:in-reply-to:references:mime-version :from:to:cc:subject:date:message-id:reply-to; bh=mdQ5vw0zALKGflw6K8gniMrWqYATuVz5uhegxYTPloY=; b=ExaHBi3d7vh0HzpZ8LkCUTDe9cKV1I9TNpAminZNFp2J650GkeFmU/BBBFNgZCfZxF U3xNi7DgSCjPdwxHb33uWYkiBoAREfmFC7lwxzxHoz86c9pMaWk83KS6QIuAuNbHM9C9 /ecoeM+3DkQUCuhZGvSWrS9F19eCAyKz6aMUlkAIaiNJQWSKTSaUhdzYOijm+YV8og5x oaD9AATrsmbWjOOqJRJSAK+nXFKDTfgzxEqsaxzfJ3JU6jT7w1u869vnSiEUG/aCMoY2 v8Xt6XWpt1/DqsFpjBcLS+dv5cnt+zIc0Xi+J3NLzRHz4gSjHcGMqDRargaZiz1igCh4 pGnw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1783492089; x=1784096889; h=to:subject:message-id:date:from:in-reply-to:references:mime-version :x-gm-gg:x-gm-message-state:from:to:cc:subject:date:message-id :reply-to; bh=mdQ5vw0zALKGflw6K8gniMrWqYATuVz5uhegxYTPloY=; b=GpSqm5jT2p4JTdEEnK5Jnj0TSULazj6IxPVcUZDfUFaTUwVqN86dDHyycVBbwCWvjc Ojo23bA3azAdWkqdJgdffgz5T5lYZvETZKH7HFQ1jgsAME4a2KU9IA71TZZzOUnSTDE7 u+yYGjnwZhGmTTU0bruMbvqsTTK+7uZrka/fBRyLkYIyF3S+b4JT2Up1nAGK64hOkZEA kW1wZrRTyivLlbYp6bkEi0oaZ9KP9Shd0PI766acHSYZvmOF9a/E0QEv3hLk4oEBWQnM kWwD81ot0vU96DIE6H8VTp4oYvzqo2+SKs55QxB7hyo8s03zBtqyg4vxYmYLzfOUbyXs WwuQ== X-Forwarded-Encrypted: i=1; AHgh+RqRMVQir24wJjoUaR18rUMmwKR6laFf4yit5OqLH+z5DdgCQkblWdKZgB20xGsuE+prHxuTPIRcqNLE@lists.postgresql.org X-Gm-Message-State: AOJu0YxXs6/rwbzF7nqfN1vOhzNYx/ttpa+BFqrbEKtjuNicpfxKa4l7 8HsOXkS8SiFIYEE9Oe+X1GEGcBBVlXh9C67S4D4GBU7Im+1MnU9yS+FZhzu/5vb6Q67z3yxuEjb XBPrEzzlRqPGqVmJeccnIw3RGWVSDz/a8ocmzZzV0Pw== X-Gm-Gg: AfdE7cnN75t/Zixm0Qc5YLD6fb9GmMVUWxpnbilAFKsMp5MT5cSU2+8BhBFRCZIK9r+ qI9OOW6B3iv0ESnRiei0lgl5ie3Oo3gnyrZ7DKePcGLEUGhqDa5+8yOdRJ0UMZvbkqnLHxg4Kn0 IfvObhsgCinYmox3wMjLb9bcSDBtr0H51V4IiDAbCREjTtamSf89nbN1XQFsW/F0jRdS/Cef3Vw JbaO5p+xZTsN+/wJ1BrckRcHvV4AgaK0lxtGiPBLpIFYeJMu5f1FY/w1xNyOgg+7mxzvsgAX1ox 1j4BTUwygf06QFvuC3I0Wo5JPSdBwaguxBUUhRVkP81OwenhYbzL4ogWPzbgeZCzXMSxGA== X-Received: by 2002:a05:6512:6890:20b0:5ae:bf95:38e0 with SMTP id 2adb3069b0e04-5b01144a012mr181424e87.29.1783492088321; Tue, 07 Jul 2026 23:28:08 -0700 (PDT) MIME-Version: 1.0 References: <19545-0f25b7e47351e8fc@postgresql.org> In-Reply-To: <19545-0f25b7e47351e8fc@postgresql.org> From: Ewan Young Date: Wed, 8 Jul 2026 14:27:55 +0800 X-Gm-Features: AVVi8CfV3hCkkE9oGXxhK4ZMuOvGL5YNK_GFQPEmuWDFoYzH0z2RDBnymTtHSmw Message-ID: Subject: Re: BUG #19545: Integer truncation of `GinTuple.keylen` causes out-of-bounds read in parallel GIN index build To: 1217816127@qq.com, pgsql-bugs@lists.postgresql.org Content-Type: multipart/mixed; boundary="000000000000626f720656139c1f" List-Id: List-Help: List-Subscribe: List-Post: List-Owner: List-Archive: Archived-At: Precedence: bulk --000000000000626f720656139c1f Content-Type: text/plain; charset="UTF-8" Content-Transfer-Encoding: quoted-printable Hi Yuelin, Thanks for the very precise report -- I reproduced it on master and your analysis is exactly right. _gin_build_tuple() builds the whole GinTuple (palloc size, key memcpy, TID-list offset) from the int keylen, but the stored GinTuple.keylen is uint16, so a key wider than 65535 bytes has its stored length truncated. On read-back GinTupleGetFirst() and _gin_parse_tuple_items() recompute the posting-list offset from the truncated value, and ginPostingListDecodeAllSegments() then walks the key bytes, aborting (or reading past the allocation on non-assert builds) exactly as you saw. It's parallel-only because only the parallel path serializes a GinTuple. I went with your fix A -- widening keylen to uint32 (attached). It's the minimal root-cause fix: the stored length now matches the length the rest of the function already uses. I preferred it over an explicit ereport at UINT16_MAX, since 65535 isn't a meaningful GIN limit -- the entry-tree item limit is much smaller and is applied to the (compressed) tuple by GinFormTuple() -- so rejecting there would be an arbitrary cutoff. Verified on master with your PoC: - Before: the parallel build aborts in ginPostingListDecodeAllSegments (Assert at ginpostinglist.c:324), as reported. - After: the parallel build succrrect results -- your 100000-byte element compresses well under the entry-tree item limit, so it irial build. - A genuinely incompressible key wider than the item limit now fails cleanly ("index row requires N) in both the parallel and serial paths, instead of crashing. I didn't add a regression test -- forcing parallel maintenance workers plus a >64 kB key deterministically in -- but I'm happy to add one if preferred. Attaching v1 against master; this should go back to the branches that have parallel GIN builds. On Wed, Jul 8, 2026 at 7:58=E2=80=AFAM PG Bug reporting form wrote: > > The following bug has been logged on the website: > > Bug reference: 19545 > Logged by: Yuelin Wang > Email address: 1217816127@qq.com > PostgreSQL version: 19beta1 > Operating system: Linux (Ubuntu 24.04, x86_64) > Description: > > ### Summary > > `GinTuple.keylen` is declared `uint16` (max 65535), but in > `_gin_build_tuple()` the local `keylen` is an `int` taken from > `VARSIZE_ANY(key)` (up to ~1 GB for a varlena `NORM_KEY`). The store > `tuple->keylen =3D keylen` **truncates** when the key exceeds 65535 bytes= . The > tuple's *memory layout* (palloc size, key memcpy, TID-array pointer) uses > the full `int` keylen and is correct. Only the stored field is truncated.= On > read-back the truncated value mis-computes the posting-list region, so th= e > decoder walks attacker-controlled bytes past the allocation, which is a h= eap > out-of-bounds read. > > ### Details > > ```c > int keylen; /* 2251 */ > keylen =3D VARSIZE_ANY(DatumGetPointer(key)); /* 2278: up to ~1GB */ > tuple->keylen =3D keylen; /* 2327: stored into uint16 -> > truncation */ > ``` > > On read-back `_gin_parse_tuple_items()` (2419-2420) uses the truncated > `a->keylen`, so the posting-list pointer lands ~64 KB early inside the > attacker's key content and `ginPostingListDecodeAllSegments()` decodes > attacker-controlled bytes off the end of the allocation. This is > parallel-only because the `GinMaxItemSize` guard lives in the leader's > `GinFormTuple`, whereas a parallel worker reparses the serialized `GinTup= le` > *before* any size gate (present in 19devel). > > ### Proof of Concept > > `array_ops`' `extractValue` returns full-size array-element datums, so a > `text[]` element > 65535 bytes flows in as `NORM_KEY`: > > ```sql > CREATE SCHEMA vuln_001_sch; > CREATE TABLE vuln_001_sch.vuln_001_t (a text[]); > > -- one 100000-byte element per row (> 65535 -> keylen truncation), enough > rows for real sort work > INSERT INTO vuln_001_sch.vuln_001_t > SELECT ARRAY[repeat('x', 100000)] FROM generate_series(1, 300); > > -- force a parallel maintenance build (all settable by a non-superuser) > ALTER TABLE vuln_001_sch.vuln_001_t SET (parallel_workers =3D 4); > SET max_parallel_maintenance_workers =3D 4; > SET min_parallel_table_scan_size =3D '0'; > > CREATE INDEX vuln_001_idx ON vuln_001_sch.vuln_001_t USING gin (a); > ``` > > Run: > > ``` > psql -h /tmp -p 36901 -U ylwang -d postgres -f vuln_001.sql > ``` > > ### Result > > The parallel build crashed 3 workers (PIDs 114475 to 114477) during `CREA= TE > INDEX ... USING gin`, on exactly the predicted path > (`ginPostingListDecodeAllSegments`): > > ``` > DEBUG: building index "vuln_001_idx" ... with request for 4 parallel > workers > server closed the connection unexpectedly > parallel worker ... ExceptionalCondition ... > parallel worker ... ginPostingListDecodeAllSegments+0x163 > TRAP: failed > Assert("OffsetNumberIsValid(ItemPointerGetOffsetNumber(&segment->first))"= ), > File: "ginpostinglist.c", Line: 324 > ``` > > ### Fix > > Widen the field to hold real key lengths by declaring `GinTuple.keylen` a= s > `int`/`uint32` in `gin_tuple.h`. Alternatively, reject oversized keys > explicitly in `_gin_build_tuple()` by calling `ereport(ERROR)` when `keyl= en > > UINT16_MAX`, so the parallel path fails as cleanly as the serial one. > > > > --=20 Regards, Ewan Young --000000000000626f720656139c1f Content-Type: application/octet-stream; name="v1-0001-Widen-GinTuple.keylen-to-uint32-to-fix-parallel-G.patch" Content-Disposition: attachment; filename="v1-0001-Widen-GinTuple.keylen-to-uint32-to-fix-parallel-G.patch" Content-Transfer-Encoding: base64 Content-ID: X-Attachment-Id: f_mrbp3wec0 RnJvbSBiNzhiNmI2ZjBlNzE0MDNjZjkzZmIxNWQ0ZWQ0NDcwYTdkYmI5YWQ5IE1vbiBTZXAgMTcg MDA6MDA6MDAgMjAwMQpGcm9tOiBFd2FuIFlvdW5nIDxrZGJhc2UuaGFja0BnbWFpbC5jb20+CkRh dGU6IFdlZCwgOCBKdWwgMjAyNiAyMjoxODo1NiArMDgwMApTdWJqZWN0OiBbUEFUQ0ggdjFdIFdp ZGVuIEdpblR1cGxlLmtleWxlbiB0byB1aW50MzIgdG8gZml4IHBhcmFsbGVsIEdJTiBidWlsZHMK IHdpdGggbGFyZ2Uga2V5cwoKRHVyaW5nIGEgcGFyYWxsZWwgR0lOIGluZGV4IGJ1aWxkLCBfZ2lu X2J1aWxkX3R1cGxlKCkgY29tcHV0ZXMgdGhlIGtleQpsZW5ndGggYXMgYW4gaW50IGZyb20gVkFS U0laRV9BTlkoKSAodXAgdG8gfjFHQiBmb3IgYSB2YXJsZW5hIGtleSkgYW5kCmxheXMgb3V0IHRo ZSBzZXJpYWxpemVkIEdpblR1cGxlIC0tIGl0cyBwYWxsb2Mgc2l6ZSwgdGhlIGtleSBtZW1jcHks IGFuZAp0aGUgb2Zmc2V0IG9mIHRoZSBjb21wcmVzc2VkIFRJRCBsaXN0IC0tIHVzaW5nIHRoYXQg ZnVsbCBsZW5ndGguICBCdXQKR2luVHVwbGUua2V5bGVuIHdhcyBkZWNsYXJlZCB1aW50MTYsIHNv IHRoZSBzdG9yZWQgZmllbGQgd3JhcHBlZCBhcm91bmQKZm9yIGtleXMgbGFyZ2VyIHRoYW4gNjU1 MzUgYnl0ZXMgd2hpbGUgdGhlIHJlc3Qgb2YgdGhlIHR1cGxlIHVzZWQgdGhlCnVudHJ1bmNhdGVk IGxlbmd0aC4KCk9uIHJlYWQtYmFjayBHaW5UdXBsZUdldEZpcnN0KCkgYW5kIF9naW5fcGFyc2Vf dHVwbGVfaXRlbXMoKSByZWNvbXB1dGUKdGhlIFRJRC1saXN0IG9mZnNldCBmcm9tIHRoZSB0cnVu Y2F0ZWQga2V5bGVuLCBzbyBpdCBwb2ludHMgaW50byB0aGUga2V5CmRhdGEgaW5zdGVhZCBvZiBh dCB0aGUgY29tcHJlc3NlZCBwb3N0aW5nIGxpc3QuCmdpblBvc3RpbmdMaXN0RGVjb2RlQWxsU2Vn bWVudHMoKSB0aGVuIGRlY29kZXMgYXJiaXRyYXJ5IGtleSBieXRlcywgd2hpY2gKYWJvcnRzIHRo ZSBidWlsZCAoQXNzZXJ0IGluIGdpbnBvc3RpbmdsaXN0LmMpIG9yIHJlYWRzIHBhc3QgdGhlCmFs bG9jYXRpb24gb24gbm9uLWFzc2VydCBidWlsZHMuICBBbnkgdXNlciBhYmxlIHRvIHJ1biBDUkVB VEUgSU5ERVggd2l0aApwYXJhbGxlbCB3b3JrZXJzIGNhbiBoaXQgdGhpcywgZS5nLiBhIGdpbiBp bmRleCBvbiBhIHRleHRbXSBjb2x1bW4gd2l0aAphbiBlbGVtZW50IHdpZGVyIHRoYW4gNjU1MzUg Ynl0ZXMuCgpUaGlzIGlzIHNwZWNpZmljIHRvIHBhcmFsbGVsIGJ1aWxkczogdGhlIHNlcmlhbCBw YXRoIG5ldmVyIG1hdGVyaWFsaXplcyBhCkdpblR1cGxlLiAgVGhlIGdlbmVyYWwgaW5kZXgtdHVw bGUgc2l6ZSBsaW1pdCBpcyBzdGlsbCBlbmZvcmNlZCBieQpHaW5Gb3JtVHVwbGUoKSBpbiBib3Ro IGNhc2VzLgoKV2lkZW4ga2V5bGVuIHRvIHVpbnQzMiBzbyB0aGUgc3RvcmVkIGxlbmd0aCBtYXRj aGVzIHRoZSBsZW5ndGggdXNlZCB0bwpidWlsZCB0aGUgdHVwbGUuICBBIHBhcmFsbGVsIGJ1aWxk IHRoZW4gY29ycmVjdGx5IGluZGV4ZXMgbGFyZ2Uga2V5cyB0aGF0CmNvbXByZXNzIGJlbG93IHRo ZSBlbnRyeS10cmVlIGl0ZW0gbGltaXQgKGFzIGEgc2VyaWFsIGJ1aWxkIGFscmVhZHkgZG9lcyks CmFuZCBzdGlsbCByZWplY3RzIGdlbnVpbmVseSBvdmVyc2l6ZWQga2V5cyBjbGVhbmx5IHZpYSBH aW5Gb3JtVHVwbGUoKQppbnN0ZWFkIG9mIGNyYXNoaW5nLgoKUmVwb3J0ZWQtYnk6IFl1ZWxpbiBX YW5nIDwxMjE3ODE2MTI3QHFxLmNvbT4KQnVnOiAjMTk1NDUKLS0tCiBzcmMvaW5jbHVkZS9hY2Nl c3MvZ2luX3R1cGxlLmggfCAyICstCiAxIGZpbGUgY2hhbmdlZCwgMSBpbnNlcnRpb24oKyksIDEg ZGVsZXRpb24oLSkKCmRpZmYgLS1naXQgYS9zcmMvaW5jbHVkZS9hY2Nlc3MvZ2luX3R1cGxlLmgg Yi9zcmMvaW5jbHVkZS9hY2Nlc3MvZ2luX3R1cGxlLmgKaW5kZXggN2JkZTA1ZTJkZTQuLmExMTZm M2ExYTc3IDEwMDY0NAotLS0gYS9zcmMvaW5jbHVkZS9hY2Nlc3MvZ2luX3R1cGxlLmgKKysrIGIv c3JjL2luY2x1ZGUvYWNjZXNzL2dpbl90dXBsZS5oCkBAIC0yMyw3ICsyMyw3IEBAIHR5cGVkZWYg c3RydWN0IEdpblR1cGxlCiB7CiAJaW50CQkJdHVwbGVuOwkJCS8qIGxlbmd0aCBvZiB0aGUgd2hv bGUgdHVwbGUgKi8KIAlPZmZzZXROdW1iZXIgYXR0cm51bTsJCS8qIGF0dG51bSBvZiBpbmRleCBr ZXkgKi8KLQl1aW50MTYJCWtleWxlbjsJCQkvKiBieXRlcyBpbiBkYXRhIGZvciBrZXkgdmFsdWUg Ki8KKwl1aW50MzIJCWtleWxlbjsJCQkvKiBieXRlcyBpbiBkYXRhIGZvciBrZXkgdmFsdWUgKi8K IAlpbnQxNgkJdHlwbGVuOwkJCS8qIHR5cGxlbiBmb3Iga2V5ICovCiAJYm9vbAkJdHlwYnl2YWw7 CQkvKiB0eXBieXZhbCBmb3Iga2V5ICovCiAJc2lnbmVkIGNoYXIgY2F0ZWdvcnk7CQkvKiBjYXRl Z29yeTogbm9ybWFsIG9yIE5VTEw/ICovCi0tIAoyLjQ3LjMKCg== --000000000000626f720656139c1f--