-
Notifications
You must be signed in to change notification settings - Fork 26
Description
Description
pp_exec_rset() in dbdimp.c leaks the refcount of the user's bound inout SV when a returned SYS_REFCURSOR was never opened (the OCI_STMT_STATE_INITIALIZED path added for RT 82663).
Root Cause
When bind_param_inout is called for a cursor output parameter, dbd_bind_ph() stores the user's live Perl variable in phs->sv with an incremented refcount:
phs->sv = SvREFCNT_inc(newvalue); /* point to live var */In pp_exec_rset() post-execute, when the cursor was never opened (stmt_state == OCI_STMT_STATE_INITIALIZED), the code does:
phs->sv = newSV(0); /* undef */This overwrites the phs->sv pointer without decrementing the refcount on the original SV. The user's variable (e.g., $cursor from bind_param_inout(':cursor', \$cursor, ...)) leaks its refcount and is never freed.
Additionally, replacing the pointer breaks the inout binding — if the same statement is re-executed, the cursor handle would be written into the orphaned newSV(0) instead of the user's original variable.
Impact
- Memory leak: one SV leaked per execute when the cursor is not opened
- Repeated re-execution of the same prepared statement with cursor output params compounds the leak
- The user's bound variable becomes disconnected from the placeholder after the first such execute
Suggested Fix
Instead of replacing the phs->sv pointer, set the existing SV to undef:
if (phs->sv && phs->sv != &PL_sv_undef)
(void)SvOK_off(phs->sv);This preserves the inout binding, properly communicates "no cursor returned" to the caller, and avoids the refcount leak.
Reproducer
my $dbh = DBI->connect(...);
my $sth = $dbh->prepare(q{
BEGIN
IF :flag = 0 THEN
:cursor := NULL; -- cursor never opened
ELSE
OPEN :cursor FOR SELECT 1 FROM dual;
END IF;
END;
});
my $cursor;
$sth->bind_param(':flag', 0);
$sth->bind_param_inout(':cursor', \$cursor, 0, { ora_type => ORA_RSET });
# Each execute leaks one SV refcount
for (1..10000) {
$sth->execute;
}Related
- RT 82663 (the original fix that introduced this code path)