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

Attachment: v1-0001-Fix-crash-when-describing-a-FETCH-statement-wh.patch
Description: Binary data

Reply via email to