0001-pg_stash_advice-reject-overlong-stash-names-when-loa.patch
application/octet-stream
Filename: 0001-pg_stash_advice-reject-overlong-stash-names-when-loa.patch
Type: application/octet-stream
Part: 0
From 1cff6bb7892da64afc7078cfc0e855b5ae598ac0 Mon Sep 17 00:00:00 2001
From: Ayush Tiwari <ayushtiwari.slg01@gmail.com>
Date: Sun, 17 May 2026 11:19:22 +0530
Subject: [PATCH] pg_stash_advice: reject overlong stash names when loading
dump file
The persistence dump file parser did not validate the length of stash
names read from disk before passing them to dshash_find_or_insert(),
which calls dshash_strhash() with a key_size of NAMEDATALEN. That
function asserts strlen(v) < size, so a dump file containing any stash
name of 64 bytes or more caused an assertion failure in
assertion-enabled builds. The resulting SIGABRT was treated by the
postmaster as a crash, leading to a full crash-recovery cycle. Because
the same dump file is read on every restart, this produced an indefinite
crash-restart loop until the file was manually removed. In production
builds the name would be silently truncated to NAMEDATALEN-1 bytes by
the dshash key-copy, allowing distinct names to collide.
SQL-level callers cannot trigger this because pgsa_check_stash_name()
enforces the length limit, but the persistence parser bypassed all of
those checks, so the bug was reachable via a corrupted, hand-edited or
truncated dump file, or potentially by reading a file produced by a
future server version with a longer NAMEDATALEN.
Fix this by validating the parsed stash-name length in the same place
where the parser already raises ERRCODE_DATA_CORRUPTED on other forms
of syntax error. Add a TAP test that writes a poisoned dump file and
verifies that the cluster starts cleanly with a parse-error logged
instead of crashing.
---
contrib/pg_stash_advice/stashpersist.c | 17 +++++++++++++++++
contrib/pg_stash_advice/t/001_persist.pl | 19 +++++++++++++++++++
2 files changed, 36 insertions(+)
diff --git a/contrib/pg_stash_advice/stashpersist.c b/contrib/pg_stash_advice/stashpersist.c
index 00a0a74f04d..949be1836dc 100644
--- a/contrib/pg_stash_advice/stashpersist.c
+++ b/contrib/pg_stash_advice/stashpersist.c
@@ -367,6 +367,23 @@ pgsa_read_from_disk(void)
errmsg("syntax error in file \"%s\" line %u: expected stash name",
PGSA_DUMP_FILE, lineno)));
+ /*
+ * Reject overlong stash names. Names that do not fit in
+ * NAMEDATALEN bytes would otherwise crash an assertion-enabled
+ * build via dshash_strhash() when we try to insert them into
+ * pgsa_stash_dshash, and would be silently truncated (with
+ * possible collisions) in production builds. SQL-level callers
+ * are checked by pgsa_check_stash_name(), but the dump file
+ * parser must perform its own check because the file content may
+ * have been produced by an older or modified server, or
+ * tampered with on disk.
+ */
+ if (strlen(name) >= NAMEDATALEN)
+ ereport(ERROR,
+ (errcode(ERRCODE_DATA_CORRUPTED),
+ errmsg("syntax error in file \"%s\" line %u: stash name is longer than %d bytes",
+ PGSA_DUMP_FILE, lineno, NAMEDATALEN - 1)));
+
/* No further fields are expected. */
if (*cursor != '\0')
ereport(ERROR,
diff --git a/contrib/pg_stash_advice/t/001_persist.pl b/contrib/pg_stash_advice/t/001_persist.pl
index 83e98889f93..7387789edb8 100644
--- a/contrib/pg_stash_advice/t/001_persist.pl
+++ b/contrib/pg_stash_advice/t/001_persist.pl
@@ -89,4 +89,23 @@ ok( !-f $node->data_dir . '/pg_stash_advice.tsv',
$node->stop;
+# A dump file that contains a stash name longer than NAMEDATALEN-1 bytes used
+# to crash the worker via an assertion failure in dshash_strhash(), which in
+# turn led the postmaster to crash-restart the cluster in a loop. Verify
+# that an overlong stash name is now rejected with a clean parse error and
+# that the cluster can start normally.
+my $long_name = 'a' x 64;
+my $dump_path = $node->data_dir . '/pg_stash_advice.tsv';
+open(my $fh, '>', $dump_path) or die "cannot open $dump_path: $!";
+print $fh "stash\t$long_name\n";
+close($fh);
+
+$node->start;
+$node->wait_for_log(qr/stash name is longer than 63 bytes/);
+
+is( $node->safe_psql("postgres", "SELECT 1"),
+ "1", 'cluster is up after rejecting overlong stash name in dump file');
+
+$node->stop;
+
done_testing();
--
2.34.1