Hi,
The following extended-protocol sequence crashes a backend (SIGSEGV
in production builds, assertion failure in assert-enabled ones), and
the postmaster terminates all other sessions for crash recovery:
Query BEGIN
Query DECLARE c CURSOR FOR SELECT id FROM t ORDER BY id
Parse "" "FETCH 5 FROM c"
Bind portal "p", statement ""
Sync
Query CLOSE c
Describe portal "p"
Sync -> connection drops; server log shows
"terminated by signal 11", then
"terminating any other active
server processes"
Describing the prepared statement instead of the portal crashes the
same way. Reproduced on REL_18_STABLE (18.6) and current master,
the code is identical on all supported branches.
At Bind time, PortalStart() copies the cursor's tuple descriptor into
the portal via UtilityTupleDescriptor(), which deliberately tolerates
a missing cursor. But Describe re-resolves the target list through
FetchStatementTargetList() (tcop/pquery.c), whose FetchStmt branch does:
subportal = GetPortalByName(fstmt->portalname);
Assert(PortalIsValid(subportal));
return FetchPortalTargetList(subportal);
With the cursor closed, GetPortalByName() returns NULL and the Assert
is compiled out in production builds, so FetchPortalTargetList()
dereferences NULL->strategy. The statement variant reaches the same
branch via CachedPlanGetTargetList().
The attached patch replaces the Assert with a runtime check returning
NIL, mirroring the guard in UtilityTupleDescriptor(). A regression
test is included.
--
Cheers,
Dirkjan Bussink