BenSpex opened a new pull request, #2475:
URL: https://github.com/apache/age/pull/2475
## Summary
Fixes #2474.
A non-superuser subject to `FORCE ROW LEVEL SECURITY` crashed the backend
with **SIGSEGV** when running `MATCH (n) DETACH DELETE n` on a vertex that has
a connected edge, **if the edge label carried an RLS policy whose `USING`/`WITH
CHECK` qual invokes a function** (e.g. a `STABLE` tenant/owner accessor).
Reproduces on `release_PG18_1.7.0` and current `master`.
## Root cause / mechanism
AGE deletes a vertex's connected edges in `check_for_connected_edges()`
(`src/backend/executor/cypher_delete.c`), which is called from
`end_cypher_delete()` — i.e. **during executor shutdown** (`ExecEndPlan`,
reached via `PortalCleanup` → `ExecutorEnd` when the portal is dropped). By
that point the portal's **active snapshot has already been popped**, so the
active-snapshot stack is empty.
RLS for Cypher DELETE is enforced at the executor level (added in #2309):
`check_for_connected_edges()` compiles the edge label's security quals and
evaluates them per candidate edge via `check_security_quals()` → `ExecQual()`.
When the qual **calls a SQL-language function**, `ExecQual()` dispatches into
`fmgr_sql()` → `postquel_start()`, which runs the function's query and reads
the current snapshot with `GetActiveSnapshot()`. With no snapshot on the stack
that is a **NULL-pointer dereference → SIGSEGV**. (A cassert build trips the
`Assert(ActiveSnapshotSet())` in `postquel_start` first — that assert is
exactly the "caller should have ensured a suitable snapshot is active"
contract.)
Backtrace (assert build) at the crash site:
```
Assertion failed: ActiveSnapshotSet(), functions.c
postquel_start (functions.c)
fmgr_sql
ExecQual -> check_security_quals (cypher_utils.c)
process_edges_by_index (cypher_delete.c)
check_for_connected_edges (cypher_delete.c)
end_cypher_delete (cypher_delete.c)
ExecEndCustomScan -> ExecEndPlan -> standard_ExecutorEnd
```
This explains the full narrowing in the issue:
- **vertex path** (`process_delete_list`) runs during *normal execution*
while a snapshot is active → fine;
- **edge-only `DELETE`** and **node-only `DETACH DELETE`** never evaluate an
edge qual at teardown → fine;
- **superuser** bypasses RLS → fine;
- a **constant-only** edge policy never enters `fmgr_sql` → fine;
- only **`DETACH DELETE` of an edge-connected vertex under a
function-bearing edge policy** trips it.
## Fix
Ensure an active snapshot for the duration of the connected-edge scan in
`check_for_connected_edges()`. `es_snapshot` is still valid there (the `EState`
is not torn down until this returns) and is the correct snapshot for reading
the edges, so push it if none is active and pop it before returning. The scans
themselves already pass `es_snapshot` explicitly, so this only affects RLS qual
evaluation; non-RLS paths are unchanged. On an error path (e.g. an RLS denial
raised mid-scan) transaction abort resets the active-snapshot stack, so the
unpaired push is cleaned up.
```c
if (!ActiveSnapshotSet())
{
PushActiveSnapshot(estate->es_snapshot);
pushed_snapshot = true;
}
...
if (pushed_snapshot)
PopActiveSnapshot();
```
## Test
Adds a regression to the security suite (`PART 11b`): an edge-label policy
whose qual calls a `STABLE` SQL function, then `DETACH DELETE` of an
edge-connected vertex as the RLS-bound role. It must delete the vertex and its
edge (leaving the other endpoint) instead of crashing. The existing `PART 11`
uses a constant-only edge policy, which does not exercise the
snapshot-dependent function path.
## Verification
- **A/B on an identical release-style build** (PG18, `-O2`,
`--enable-cassert` off): unpatched → `client backend ... was terminated by
signal 11: Segmentation fault`; patched → deletes cleanly, no crash.
- `make installcheck` against PG18 (`--enable-cassert`) — **all 43 tests
pass**, including the new case.
- The issue's function-based repro no longer crashes and deletes correctly.
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: [email protected]
For queries about this service, please contact Infrastructure at:
[email protected]