Skip to content

Commit 271b460

Browse files
committed
ext/pdo: Prevent statement modification during fetching
1 parent cdca0f8 commit 271b460

7 files changed

+44
-89
lines changed

ext/pdo/pdo_stmt.c

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -730,6 +730,7 @@ static bool do_fetch(pdo_stmt_t *stmt, zval *return_value, enum pdo_fetch_type h
730730
return true;
731731
}
732732

733+
stmt->in_fetch = true;
733734
switch (how) {
734735
case PDO_FETCH_USE_DEFAULT:
735736
case PDO_FETCH_ASSOC:
@@ -938,6 +939,7 @@ static bool do_fetch(pdo_stmt_t *stmt, zval *return_value, enum pdo_fetch_type h
938939
}
939940
efree(fetch_function_params);
940941
}
942+
stmt->in_fetch = false;
941943

942944
return true;
943945
}
@@ -1742,6 +1744,10 @@ PHP_METHOD(PDOStatement, setFetchMode)
17421744

17431745
PHP_STMT_GET_OBJ;
17441746

1747+
if (stmt->in_fetch) {
1748+
zend_throw_error(NULL, "Cannot change default fetch mode while fetching");
1749+
RETURN_THROWS();
1750+
}
17451751
if (!pdo_stmt_setup_fetch_mode(stmt, fetch_mode, 1, args, num_args)) {
17461752
RETURN_THROWS();
17471753
}

ext/pdo/php_pdo_driver.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -568,7 +568,9 @@ struct _pdo_stmt_t {
568568
* emulate prepare and bind on its behalf */
569569
unsigned supports_placeholders:2;
570570

571-
unsigned _reserved:29;
571+
/* If true we are in a do_fetch() call, and modification to the statement must be prevented */
572+
unsigned in_fetch:1;
573+
unsigned _reserved:28;
572574

573575
/* the number of columns in the result set; not valid until after
574576
* the statement has been executed at least once. In some cases, might

ext/pdo/tests/pdo_fetch_class_change_ctor_args_during_fetch1.phpt

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,12 @@ $stmt = $db->prepare('SELECT val1, val2 FROM pdo_fetch_class_change_ctor_one');
3737
$stmt->setFetchMode(PDO::FETCH_CLASS, 'Test', [$stmt]);
3838

3939
$stmt->execute();
40-
var_dump($stmt->fetch());
40+
41+
try {
42+
var_dump($stmt->fetch());
43+
} catch (\Throwable $e) {
44+
echo $e::class, ': ', $e->getMessage(), \PHP_EOL;
45+
}
4146

4247
?>
4348
--CLEAN--
@@ -51,9 +56,4 @@ object(PDOStatement)#%d (1) {
5156
["queryString"]=>
5257
string(54) "SELECT val1, val2 FROM pdo_fetch_class_change_ctor_one"
5358
}
54-
object(Test)#%d (2) {
55-
["val1"]=>
56-
string(1) "A"
57-
["val2"]=>
58-
string(5) "alpha"
59-
}
59+
Error: Cannot change default fetch mode while fetching

ext/pdo/tests/pdo_fetch_class_change_ctor_args_during_fetch2.phpt

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,12 @@ $db->exec("INSERT INTO pdo_fetch_class_change_ctor_two VALUES(4, 'D', 'delta')")
3737
$stmt = $db->prepare('SELECT val1, val2 FROM pdo_fetch_class_change_ctor_two');
3838

3939
$stmt->execute();
40-
var_dump($stmt->fetchObject('Test', [$stmt]));
40+
41+
try {
42+
var_dump($stmt->fetchObject('Test', [$stmt]));
43+
} catch (\Throwable $e) {
44+
echo $e::class, ': ', $e->getMessage(), \PHP_EOL;
45+
}
4146

4247
?>
4348
--CLEAN--
@@ -51,9 +56,4 @@ object(PDOStatement)#%s (1) {
5156
["queryString"]=>
5257
string(54) "SELECT val1, val2 FROM pdo_fetch_class_change_ctor_two"
5358
}
54-
object(Test)#%s (2) {
55-
["val1"]=>
56-
string(1) "A"
57-
["val2"]=>
58-
string(5) "alpha"
59-
}
59+
Error: Cannot change default fetch mode while fetching

ext/pdo/tests/pdo_fetch_class_change_ctor_args_during_fetch3.phpt

Lines changed: 7 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,12 @@ $stmt = $db->prepare('SELECT val1, val2 FROM pdo_fetch_class_change_ctor_three')
3737
$stmt->setFetchMode(PDO::FETCH_CLASS, 'Test', [$stmt]);
3838

3939
$stmt->execute();
40-
var_dump($stmt->fetchAll());
40+
41+
try {
42+
var_dump($stmt->fetchAll());
43+
} catch (\Throwable $e) {
44+
echo $e::class, ': ', $e->getMessage(), \PHP_EOL;
45+
}
4146

4247
?>
4348
--CLEAN--
@@ -51,36 +56,4 @@ object(PDOStatement)#%d (1) {
5156
["queryString"]=>
5257
string(56) "SELECT val1, val2 FROM pdo_fetch_class_change_ctor_three"
5358
}
54-
string(5) "alpha"
55-
string(5) "alpha"
56-
string(5) "alpha"
57-
array(4) {
58-
[0]=>
59-
object(Test)#%d (2) {
60-
["val1"]=>
61-
string(1) "A"
62-
["val2"]=>
63-
string(5) "alpha"
64-
}
65-
[1]=>
66-
object(Test)#%d (2) {
67-
["val1"]=>
68-
string(1) "B"
69-
["val2"]=>
70-
string(4) "beta"
71-
}
72-
[2]=>
73-
object(Test)#%d (2) {
74-
["val1"]=>
75-
string(1) "C"
76-
["val2"]=>
77-
string(5) "gamma"
78-
}
79-
[3]=>
80-
object(Test)#%d (2) {
81-
["val1"]=>
82-
string(1) "D"
83-
["val2"]=>
84-
string(5) "delta"
85-
}
86-
}
59+
Error: Cannot change default fetch mode while fetching

ext/pdo/tests/pdo_fetch_class_change_ctor_args_during_fetch4.phpt

Lines changed: 7 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,12 @@ $db->exec("INSERT INTO pdo_fetch_class_change_ctor_four VALUES(4, 'D', 'delta')"
3636
$stmt = $db->prepare('SELECT val1, val2 FROM pdo_fetch_class_change_ctor_four');
3737

3838
$stmt->execute();
39-
var_dump($stmt->fetchAll(PDO::FETCH_CLASS, 'Test', [$stmt]));
39+
40+
try {
41+
var_dump($stmt->fetchAll(PDO::FETCH_CLASS, 'Test', [$stmt]));
42+
} catch (\Throwable $e) {
43+
echo $e::class, ': ', $e->getMessage(), \PHP_EOL;
44+
}
4045

4146
?>
4247
--CLEAN--
@@ -50,36 +55,4 @@ object(PDOStatement)#%d (1) {
5055
["queryString"]=>
5156
string(55) "SELECT val1, val2 FROM pdo_fetch_class_change_ctor_four"
5257
}
53-
string(5) "alpha"
54-
string(5) "alpha"
55-
string(5) "alpha"
56-
array(4) {
57-
[0]=>
58-
object(Test)#%d (2) {
59-
["val1"]=>
60-
string(1) "A"
61-
["val2"]=>
62-
string(5) "alpha"
63-
}
64-
[1]=>
65-
object(Test)#%d (2) {
66-
["val1"]=>
67-
string(1) "B"
68-
["val2"]=>
69-
string(4) "beta"
70-
}
71-
[2]=>
72-
object(Test)#%d (2) {
73-
["val1"]=>
74-
string(1) "C"
75-
["val2"]=>
76-
string(5) "gamma"
77-
}
78-
[3]=>
79-
object(Test)#%d (2) {
80-
["val1"]=>
81-
string(1) "D"
82-
["val2"]=>
83-
string(5) "delta"
84-
}
85-
}
58+
Error: Cannot change default fetch mode while fetching

ext/pdo/tests/pdo_fetch_class_change_ctor_args_during_fetch5.phpt

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,11 @@ function stuffingErrorHandler(int $errno, string $errstr, string $errfile, int $
3838
}
3939
set_error_handler(stuffingErrorHandler(...));
4040

41-
var_dump($stmt->fetchAll(PDO::FETCH_CLASS|PDO::FETCH_SERIALIZE, 'B', [$stmt]));
41+
try {
42+
var_dump($stmt->fetchAll(PDO::FETCH_CLASS|PDO::FETCH_SERIALIZE, 'B', [$stmt]));
43+
} catch (\Throwable $e) {
44+
echo $e::class, ': ', $e->getMessage(), \PHP_EOL;
45+
}
4246

4347
?>
4448
--CLEAN--
@@ -47,9 +51,6 @@ require_once getenv('REDIR_TEST_DIR') . 'pdo_test.inc';
4751
$db = PDOTest::factory();
4852
PDOTest::dropTableIfExists($db, "pdo_fetch_class_change_ctor_five");
4953
?>
50-
--EXPECTF--
54+
--EXPECT--
5155
PDOStatement::fetchAll(): The PDO::FETCH_SERIALIZE mode is deprecated
52-
PDOStatement::fetchAll(): SQLSTATE[HY000]: General error: cannot unserialize class
53-
PDOStatement::fetchAll(): SQLSTATE[HY000]: General error%S
54-
array(0) {
55-
}
56+
Error: Cannot change default fetch mode while fetching

0 commit comments

Comments
 (0)