| From: | Zsolt Parragi <zsolt(dot)parragi(at)percona(dot)com> |
|---|---|
| To: | Andrew Dunstan <andrew(at)dunslane(dot)net> |
| Cc: | David Rowley <dgrowleyml(at)gmail(dot)com>, Andrey Rachitskiy <pl0h0yp1(at)gmail(dot)com>, Vladimir Savin <vladimir(at)encord(dot)com>, pgsql-bugs(at)lists(dot)postgresql(dot)org |
| Subject: | Re: PG18: use-after-free in exec partition pruning after an EPQ recheck in LockRows |
| Date: | 2026-10-10 11:51:29 |
| Message-ID: | CAN4CZFPZ6qKnv6FPmq1RQZ49cHALCwLBu_NJdqUrkZNg0z81JQ@mail.gmail.com |
| Views: | Whole Thread | Raw Message | Download mbox | Resend email |
| Thread: | |
| Lists: | pgsql-bugs |
Hello
An automated claude review pointed out a leftover issue after the fix.
> per buildfarm, this is an ABI break on release 18, apparently needs an
> update to .abi-compliance-history
The attached patch fixing that removes the initialized field again,
which should also take care of this.
EPQ EState has another problem, which the initialized flag doesn't
address: the ExprContext of the PartitionPruneState is created by
CreatePartitionPruneState() for the parent EState, so exec pruning in
the EPQ reads the parent's es_param_exec_vals. If the pruning depends
on a value from the re-fetched row, the EPQ prunes with the value from
the old row version, and can remove the partition it actually needs.
This isn't caused by 1b5dd3a24. Before it the EPQ re-initialized the
exec contexts, but with the same parent ExprContext, so this goes back
to 8741e48e5.
Repro:
CREATE TABLE p (a int, b int) PARTITION BY LIST (a);
CREATE TABLE p1 PARTITION OF p FOR VALUES IN (1);
CREATE TABLE p2 PARTITION OF p FOR VALUES IN (2);
INSERT INTO p VALUES (1, 100), (2, 200);
CREATE TABLE t (id int PRIMARY KEY, k int, v int, done bool DEFAULT false);
INSERT INTO t VALUES (1, 1, 100);
-- s1
BEGIN;
UPDATE t SET k = 2, v = 200 WHERE id = 1;
-- s2, blocks
UPDATE t SET done = true WHERE t.v = (SELECT p.b FROM p WHERE p.a =
t.k) RETURNING *;
-- s1
COMMIT;
s2 returns UPDATE 0. The recheck sees k = 2, the SubPlan sets $0 = 2
in the EPQ EState, but pruning still sees $0 = 1 from the parent, so
only p1 is kept, the scan finds nothing, and the subquery returns
NULL.
With enable_partition_pruning = off the row is updated as expected.
As David said upthread, before bb3ec16e1 the EPQ had its own
PartitionPruneStates, the sharing only came with 8741e48e5, which
fixed the missing states after that commit. The attached patch goes
back to separate states: it gives the EPQ EState its own
PartitionPruneStates, created from the parent's PartitionPruneInfos,
and keeps borrowing es_part_prune_results so the same subplans are
initialized, which was the reason for the sharing. As nothing is
shared anymore, I removed the initialized flag again. This also covers
the use-after-free, nothing in the parent state is touched by the EPQ
anymore, the existing test still passes, and so does Vladimir's
epq-prune-repro.sql.
The patch also adds the case above to the
eval-plan-qual-partition-prune isolation test.
| Attachment | Content-Type | Size |
|---|---|---|
| v1-0001-Fix-EPQ-run-time-pruning-using-the-parent-s-PARAM.patch | application/octet-stream | 8.1 KB |
| From | Date | Subject | |
|---|---|---|---|
| Previous Message | Zsolt Parragi | 2026-10-10 09:47:11 | Re: Do we want to solve reload/config races more generally? (was: Postmaster crashes on SIGHUP when oauth_validator_libraries holds only whitespace) |