Skip to content
Open
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
4 changes: 4 additions & 0 deletions NEWS
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,10 @@ PHP NEWS
. Fixed a leak when a persistent connection failed a liveness check
with no other live PDO handle. (iliaal)

- PDO_ODBC:
. Fixed bug GH-23444 (ODBC_ATTR_ASSUME_UTF8 corrupts Unicode data outside
Windows). (Calvin Buckley, Lazizbek Ergashev)

- Phar:
. Fixed bug GH-23418 (Use-after-free when looking up mounted directories).
(Weilin Du)
Expand Down
14 changes: 13 additions & 1 deletion ext/pdo_odbc/odbc_stmt.c
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ enum pdo_odbc_conv_result {

static int pdo_odbc_sqltype_is_unicode(pdo_odbc_stmt *S, SQLSMALLINT sqltype)
{
#ifdef PHP_WIN32
if (!S->assume_utf8) return 0;
switch (sqltype) {
#ifdef SQL_WCHAR
Expand All @@ -51,6 +52,9 @@ static int pdo_odbc_sqltype_is_unicode(pdo_odbc_stmt *S, SQLSMALLINT sqltype)
default:
return 0;
}
#else
return 0;
#endif
}

static int pdo_odbc_utf82ucs2(pdo_stmt_t *stmt, int is_unicode, const char *buf,
Expand Down Expand Up @@ -548,7 +552,15 @@ static int odbc_stmt_param_hook(pdo_stmt_t *stmt, struct pdo_bound_param_data *p
break;
}
} else {
P->len = SQL_LEN_DATA_AT_EXEC(Z_STRLEN_P(parameter));
zend_ulong ulen;
if (pdo_odbc_utf82ucs2(stmt, P->is_unicode,
Z_STRVAL_P(parameter),
Z_STRLEN_P(parameter),
&ulen) == PDO_ODBC_CONV_OK) {
P->len = SQL_LEN_DATA_AT_EXEC(ulen);
} else {
P->len = SQL_LEN_DATA_AT_EXEC(Z_STRLEN_P(parameter));
}
}
}
return 1;
Expand Down
34 changes: 34 additions & 0 deletions ext/pdo_odbc/tests/gh23444.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
--TEST--
GH-23444 (Unicode data is corrupted with ODBC_ATTR_ASSUME_UTF8)
--EXTENSIONS--
pdo_odbc
--SKIPIF--
<?php
require 'ext/pdo/tests/pdo_test.inc';
PDOTest::skip();
?>
--FILE--
<?php
require 'ext/pdo/tests/pdo_test.inc';
$db = PDOTest::test_factory(__DIR__ . '/common.phpt');
$db->exec("CREATE TABLE gh23444 (v NVARCHAR(100))");

// 13 bytes as UTF-8, so an unconverted parameter is an odd number of bytes
$string = "\u{6e2c}\u{8a66}\u{4e2d}\u{1f418}";

$db->setAttribute(PDO::ODBC_ATTR_ASSUME_UTF8, true);
$stmt = $db->prepare("INSERT INTO gh23444 VALUES(?)");
$stmt->execute([$string]);

$stmt = $db->prepare("SELECT v FROM gh23444 WHERE v = ?");
$stmt->execute([$string]);
var_dump($stmt->fetchColumn() === $string);
?>
--CLEAN--
<?php
require 'ext/pdo/tests/pdo_test.inc';
$db = PDOTest::test_factory(__DIR__ . '/common.phpt');
$db->exec("DROP TABLE IF EXISTS gh23444");
?>
--EXPECT--
bool(true)
Loading