Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions NEWS
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,9 @@ PHP NEWS
Pdo\Pgsql::prepare(…, [ PDO::ATTR_PREFETCH => 0 ]) make fetch() lazy
instead of storing the whole result set in memory (Guillaume Outters)

- PDO_SQLITE:
. throw on null bytes / resolve GH-13952 (divinity76).

- PGSQL:
. Added pg_close_stmt to close a prepared statement while allowing
its name to be reused. (David Carlier)
Expand Down
4 changes: 4 additions & 0 deletions UPGRADING
Original file line number Diff line number Diff line change
Expand Up @@ -222,6 +222,10 @@ PHP 8.5 UPGRADE NOTES
PDO::ATTR_PREFETCH sets to 0 which set to lazy fetch mode.
In this mode, statements cannot be run parallely.

- PDO_SQLITE:
. SQLite PDO::quote() will now throw an exception or emit a warning,
depending on the error mode, if the string contains a null byte.

- PGSQL:
. pg_copy_from also supports inputs as Iterable.
. pg_connect checks if the connection_string argument contains
Expand Down
42 changes: 18 additions & 24 deletions ext/pdo_firebird/firebird_driver.c
Original file line number Diff line number Diff line change
Expand Up @@ -599,13 +599,13 @@ static void firebird_handle_closer(pdo_dbh_t *dbh) /* {{{ */
}

if (H->date_format) {
zend_string_release_ex(H->date_format, false);
pefree(H->date_format, dbh->is_persistent);
}
if (H->time_format) {
zend_string_release_ex(H->time_format, false);
pefree(H->time_format, dbh->is_persistent);
}
if (H->timestamp_format) {
zend_string_release_ex(H->timestamp_format, false);
pefree(H->timestamp_format, dbh->is_persistent);
}

if (H->einfo.errmsg) {
Expand Down Expand Up @@ -1091,9 +1091,11 @@ static bool pdo_firebird_set_attribute(pdo_dbh_t *dbh, zend_long attr, zval *val
return false;
}
if (H->date_format) {
zend_string_release_ex(H->date_format, false);
pefree(H->date_format, dbh->is_persistent);
H->date_format = NULL;
}
H->date_format = str;
H->date_format = pestrndup(ZSTR_VAL(str), ZSTR_LEN(str),dbh->is_persistent);
zend_string_release_ex(str, 0);
}
return true;

Expand All @@ -1104,9 +1106,11 @@ static bool pdo_firebird_set_attribute(pdo_dbh_t *dbh, zend_long attr, zval *val
return false;
}
if (H->time_format) {
zend_string_release_ex(H->time_format, false);
pefree(H->time_format, dbh->is_persistent);
H->time_format = NULL;
}
H->time_format = str;
H->time_format = pestrndup(ZSTR_VAL(str), ZSTR_LEN(str),dbh->is_persistent);
zend_string_release_ex(str, 0);
}
return true;

Expand All @@ -1117,9 +1121,11 @@ static bool pdo_firebird_set_attribute(pdo_dbh_t *dbh, zend_long attr, zval *val
return false;
}
if (H->timestamp_format) {
zend_string_release_ex(H->timestamp_format, false);
pefree(H->timestamp_format, dbh->is_persistent);
H->timestamp_format = NULL;
}
H->timestamp_format = str;
H->timestamp_format = pestrndup(ZSTR_VAL(str), ZSTR_LEN(str),dbh->is_persistent);
zend_string_release_ex(str, 0);
}
return true;

Expand Down Expand Up @@ -1240,27 +1246,15 @@ static int pdo_firebird_get_attribute(pdo_dbh_t *dbh, zend_long attr, zval *val)
return 1;

case PDO_FB_ATTR_DATE_FORMAT:
if (H->date_format) {
ZVAL_STR_COPY(val, H->date_format);
} else {
ZVAL_STRING(val, PDO_FB_DEF_DATE_FMT);
}
ZVAL_STRING(val, H->date_format ? H->date_format : PDO_FB_DEF_DATE_FMT);
return 1;

case PDO_FB_ATTR_TIME_FORMAT:
if (H->time_format) {
ZVAL_STR_COPY(val, H->time_format);
} else {
ZVAL_STRING(val, PDO_FB_DEF_TIME_FMT);
}
ZVAL_STRING(val, H->time_format ? H->time_format : PDO_FB_DEF_TIME_FMT);
return 1;

case PDO_FB_ATTR_TIMESTAMP_FORMAT:
if (H->timestamp_format) {
ZVAL_STR_COPY(val, H->timestamp_format);
} else {
ZVAL_STRING(val, PDO_FB_DEF_TIMESTAMP_FMT);
}
ZVAL_STRING(val, H->timestamp_format ? H->timestamp_format : PDO_FB_DEF_TIMESTAMP_FMT);
return 1;

case PDO_FB_TRANSACTION_ISOLATION_LEVEL:
Expand Down
10 changes: 5 additions & 5 deletions ext/pdo_firebird/firebird_statement.c
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ static int get_formatted_time_tz(pdo_stmt_t *stmt, const ISC_TIME_TZ* timeTz, zv
}
time = fb_encode_time(hours, minutes, seconds, fractions);
isc_decode_sql_time(&time, &t);
fmt = S->H->time_format ? ZSTR_VAL(S->H->time_format) : PDO_FB_DEF_TIME_FMT;
fmt = S->H->time_format ? S->H->time_format : PDO_FB_DEF_TIME_FMT;

size_t len = strftime(timeBuf, sizeof(timeBuf), fmt, &t);
if (len == 0) {
Expand Down Expand Up @@ -123,7 +123,7 @@ static int get_formatted_timestamp_tz(pdo_stmt_t *stmt, const ISC_TIMESTAMP_TZ*
ts.timestamp_time = fb_encode_time(hours, minutes, seconds, fractions);
isc_decode_timestamp(&ts, &t);

fmt = S->H->timestamp_format ? ZSTR_VAL(S->H->timestamp_format) : PDO_FB_DEF_TIMESTAMP_FMT;
fmt = S->H->timestamp_format ? S->H->timestamp_format : PDO_FB_DEF_TIMESTAMP_FMT;

size_t len = strftime(timestampBuf, sizeof(timestampBuf), fmt, &t);
if (len == 0) {
Expand Down Expand Up @@ -546,18 +546,18 @@ static int pdo_firebird_stmt_get_col(
break;
case SQL_TYPE_DATE:
isc_decode_sql_date((ISC_DATE*)var->sqldata, &t);
fmt = S->H->date_format ? ZSTR_VAL(S->H->date_format) : PDO_FB_DEF_DATE_FMT;
fmt = S->H->date_format ? S->H->date_format : PDO_FB_DEF_DATE_FMT;
if (0) {
case SQL_TYPE_TIME:
isc_decode_sql_time((ISC_TIME*)var->sqldata, &t);
fmt = S->H->time_format ? ZSTR_VAL(S->H->time_format) : PDO_FB_DEF_TIME_FMT;
fmt = S->H->time_format ? S->H->time_format : PDO_FB_DEF_TIME_FMT;
} else if (0) {
case SQL_TIMESTAMP:
{
ISC_TIMESTAMP timestamp = php_get_isc_timestamp_from_sqldata(var->sqldata);
isc_decode_timestamp(&timestamp, &t);
}
fmt = S->H->timestamp_format ? ZSTR_VAL(S->H->timestamp_format) : PDO_FB_DEF_TIMESTAMP_FMT;
fmt = S->H->timestamp_format ? S->H->timestamp_format : PDO_FB_DEF_TIMESTAMP_FMT;
}
/* convert the timestamp into a string */
char buf[80];
Expand Down
6 changes: 3 additions & 3 deletions ext/pdo_firebird/php_pdo_firebird_int.h
Original file line number Diff line number Diff line change
Expand Up @@ -73,9 +73,9 @@ typedef struct {
zend_ulong txn_isolation_level;

/* date and time format strings, can be set by the set_attribute method */
zend_string *date_format;
zend_string *time_format;
zend_string *timestamp_format;
char *date_format;
char *time_format;
char *timestamp_format;

unsigned sql_dialect:2;

Expand Down
35 changes: 35 additions & 0 deletions ext/pdo_firebird/tests/gh18276.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
--TEST--
GH-18276 (persistent connection - setAttribute(Pdo\Firebird::ATTR_DATE_FORMAT, ..) results in "zend_mm_heap corrupted")
--EXTENSIONS--
pdo_firebird
--SKIPIF--
<?php require('skipif.inc'); ?>
--XLEAK--
A bug in firebird causes a memory leak when calling `isc_attach_database()`.
See https://github.com/FirebirdSQL/firebird/issues/7849
--FILE--
<?php

require("testdb.inc");
unset($dbh);

for ($i = 0; $i < 2; $i++) {
$dbh = new PDO(
PDO_FIREBIRD_TEST_DSN,
PDO_FIREBIRD_TEST_USER,
PDO_FIREBIRD_TEST_PASS,
[
PDO::ATTR_PERSISTENT => true,
],
);
// Avoid interned
$dbh->setAttribute(PDO::FB_ATTR_DATE_FORMAT, str_repeat('Y----m----d', random_int(1, 1)));
$dbh->setAttribute(PDO::FB_ATTR_TIME_FORMAT, str_repeat('H::::i::::s', random_int(1, 1)));
$dbh->setAttribute(PDO::FB_ATTR_TIMESTAMP_FORMAT, str_repeat('Y----m----d....H::::i::::s', random_int(1, 1)));
unset($dbh);
}

echo 'done!';
?>
--EXPECT--
done!
15 changes: 14 additions & 1 deletion ext/pdo_sqlite/sqlite_driver.c
Original file line number Diff line number Diff line change
Expand Up @@ -226,6 +226,19 @@ static zend_string* sqlite_handle_quoter(pdo_dbh_t *dbh, const zend_string *unqu
if (ZSTR_LEN(unquoted) > (INT_MAX - 3) / 2) {
return NULL;
}

if (UNEXPECTED(zend_str_has_nul_byte(unquoted))) {
if (dbh->error_mode == PDO_ERRMODE_EXCEPTION) {
zend_throw_exception_ex(
php_pdo_get_exception(), 0,
"SQLite PDO::quote does not support null bytes");
} else if (dbh->error_mode == PDO_ERRMODE_WARNING) {
php_error_docref(NULL, E_WARNING,
"SQLite PDO::quote does not support null bytes");
}
return NULL;
}

quoted = safe_emalloc(2, ZSTR_LEN(unquoted), 3);
/* TODO use %Q format? */
sqlite3_snprintf(2*ZSTR_LEN(unquoted) + 3, quoted, "'%q'", ZSTR_VAL(unquoted));
Expand Down Expand Up @@ -741,7 +754,7 @@ static const struct pdo_dbh_methods sqlite_methods = {
pdo_sqlite_request_shutdown,
pdo_sqlite_in_transaction,
pdo_sqlite_get_gc,
pdo_sqlite_scanner
pdo_sqlite_scanner
};

static char *make_filename_safe(const char *filename)
Expand Down
79 changes: 79 additions & 0 deletions ext/pdo_sqlite/tests/gh13952.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
--TEST--
GH-13952 (sqlite PDO::quote handles null bytes correctly)
--EXTENSIONS--
pdo
pdo_sqlite
--FILE--
<?php

$modes = [
'exception' => PDO::ERRMODE_EXCEPTION,
'warning' => PDO::ERRMODE_WARNING,
'silent' => PDO::ERRMODE_SILENT,
];

$test_cases = [
"",
"x",
"\x00",
"a\x00b",
"\x00\x00\x00",
"foobar",
"foo'''bar",
"'foo'''bar'",
"foo\x00bar",
"'foo'\x00'bar'",
"foo\x00\x00\x00bar",
"\x00foo\x00\x00\x00bar\x00",
"\x00\x00\x00foo",
"foo\x00\x00\x00",
"\x80", // << invalid UTF-8
"\x00\x80\x00", // << invalid UTF-8 with null bytes
];

foreach ($modes as $mode_name => $mode) {
echo "Testing error mode: $mode_name\n";
$db = new PDO('sqlite::memory:', null, null, [PDO::ATTR_ERRMODE => $mode]);

foreach ($test_cases as $test) {
$contains_null = str_contains($test, "\x00");

if ($mode === PDO::ERRMODE_EXCEPTION && $contains_null) {
set_error_handler(fn() => throw new PDOException(), E_WARNING);
try {
$db->quote($test);
throw new LogicException("Expected exception not thrown.");
} catch (PDOException) {
// expected
} finally {
restore_error_handler();
}
} else {
set_error_handler(fn() => null, E_WARNING);
$quoted = $db->quote($test);
restore_error_handler();

if ($contains_null) {
if ($quoted !== false) {
throw new LogicException("Expected false, got: " . var_export($quoted, true));
}
} else {
if ($quoted === false) {
throw new LogicException("Unexpected false from quote().");
}
$fetched = $db->query("SELECT $quoted")->fetchColumn();
if ($fetched !== $test) {
throw new LogicException("Data corrupted: expected " . var_export($test, true) . " got " . var_export($fetched, true));
}
}
}
}
}

echo "ok\n";
?>
--EXPECT--
Testing error mode: exception
Testing error mode: warning
Testing error mode: silent
ok
Loading