Skip to content

Commit 22237f2

Browse files
committed
ext/pdo: Release driver options after bindParam and bindColumn
bindParam() and bindColumn() copied the driver options zval and then addref'd it again. The extra reference kept the value alive after the statement was destroyed. Keep the single reference from the copy. Closes GH-23936
1 parent beee995 commit 22237f2

3 files changed

Lines changed: 41 additions & 4 deletions

File tree

‎NEWS‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,8 @@ PHP NEWS
106106
whose constructor arguments it rejects. (Ilia Alshanetsky)
107107
. Fixed PDO::ATTR_STATEMENT_CLASS constructor_args type errors reporting
108108
"array given" regardless of the value passed. (Ilia Alshanetsky)
109+
. Fixed PDOStatement::bindParam() and bindColumn() leaking the driver
110+
options value. (Ilia Alshanetsky)
109111

110112
- PDO_Firebird:
111113
. Fixed bug GH-23758 (PDO_Firebird returns null for non-null empty BLOBs).

‎ext/pdo/pdo_stmt.c‎

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -287,10 +287,6 @@ static bool really_register_bound_param(struct pdo_bound_param_data *param, pdo_
287287
param->stmt = stmt;
288288
param->is_param = is_param;
289289

290-
if (Z_REFCOUNTED(param->driver_params)) {
291-
Z_ADDREF(param->driver_params);
292-
}
293-
294290
if (!is_param && param->name && stmt->columns) {
295291
/* try to map the name to the column */
296292
int i;
@@ -374,6 +370,7 @@ static bool really_register_bound_param(struct pdo_bound_param_data *param, pdo_
374370
} else {
375371
zend_hash_index_del(hash, pparam->paramno);
376372
}
373+
ZVAL_UNDEF(&param->driver_params);
377374
/* param->parameter is freed by hash dtor */
378375
ZVAL_UNDEF(&param->parameter);
379376
return 0;
@@ -1462,6 +1459,9 @@ static void register_bound_param(INTERNAL_FUNCTION_PARAMETERS, int is_param) /*
14621459
if (!Z_ISUNDEF(param.parameter)) {
14631460
zval_ptr_dtor(&(param.parameter));
14641461
}
1462+
if (!Z_ISUNDEF(param.driver_params)) {
1463+
zval_ptr_dtor(&param.driver_params);
1464+
}
14651465

14661466
RETURN_FALSE;
14671467
}
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
--TEST--
2+
PDO SQLite releases driverOptions objects after binding or failed registration
3+
--EXTENSIONS--
4+
pdo_sqlite
5+
--FILE--
6+
<?php
7+
class Tracked {}
8+
9+
$db = new PDO('sqlite::memory:');
10+
11+
$stmt = $db->prepare('SELECT ? AS value');
12+
$value = 1;
13+
$driverOptions = [new Tracked()];
14+
$weakReference = WeakReference::create($driverOptions[0]);
15+
var_dump($stmt->bindParam(1, $value, PDO::PARAM_STR, 0, $driverOptions));
16+
unset($driverOptions, $value, $stmt);
17+
gc_collect_cycles();
18+
var_dump($weakReference->get());
19+
20+
$stmt = $db->prepare('SELECT ? AS value');
21+
$stmt->execute();
22+
$db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_SILENT);
23+
$value = null;
24+
$driverOptions = [new Tracked()];
25+
$weakReference = WeakReference::create($driverOptions[0]);
26+
var_dump(@$stmt->bindColumn('missing', $value, PDO::PARAM_STR, 0, $driverOptions));
27+
unset($driverOptions);
28+
var_dump($weakReference->get());
29+
unset($value, $stmt);
30+
?>
31+
--EXPECT--
32+
bool(true)
33+
NULL
34+
bool(false)
35+
NULL

0 commit comments

Comments
 (0)