From 802f51b694802705561ca22cbf8a9e14c1c3c355 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michael=20Vo=C5=99=C3=AD=C5=A1ek?= Date: Fri, 30 Oct 2026 17:42:43 +0100 Subject: [PATCH 1/2] Fix large int corruption to/from pdo_sqlite UDF --- ext/pdo_sqlite/sqlite_driver.c | 8 ++++++++ ext/pdo_sqlite/tests/gh25021-udf.phpt | 28 +++++++++++++++++++++++++++ ext/sqlite3/tests/gh25021-udf.phpt | 27 ++++++++++++++++++++++++++ 3 files changed, 63 insertions(+) create mode 100644 ext/pdo_sqlite/tests/gh25021-udf.phpt create mode 100644 ext/sqlite3/tests/gh25021-udf.phpt diff --git a/ext/pdo_sqlite/sqlite_driver.c b/ext/pdo_sqlite/sqlite_driver.c index 03b212bf75c1..48ccf1d3f8d5 100644 --- a/ext/pdo_sqlite/sqlite_driver.c +++ b/ext/pdo_sqlite/sqlite_driver.c @@ -368,7 +368,11 @@ static int do_callback(struct pdo_sqlite_fci *fc, zval *cb, /* get the value */ switch (sqlite3_value_type(argv[i])) { case SQLITE_INTEGER: +#if ZEND_LONG_MAX > 2147483647 + ZVAL_LONG(&zargs[i + is_agg], sqlite3_value_int64(argv[i])); +#else ZVAL_LONG(&zargs[i + is_agg], sqlite3_value_int(argv[i])); +#endif break; case SQLITE_FLOAT: @@ -410,7 +414,11 @@ static int do_callback(struct pdo_sqlite_fci *fc, zval *cb, if (!Z_ISUNDEF(retval)) { switch (Z_TYPE(retval)) { case IS_LONG: +#if ZEND_LONG_MAX > 2147483647 + sqlite3_result_int64(context, Z_LVAL(retval)); +#else sqlite3_result_int(context, Z_LVAL(retval)); +#endif break; case IS_NULL: diff --git a/ext/pdo_sqlite/tests/gh25021-udf.phpt b/ext/pdo_sqlite/tests/gh25021-udf.phpt new file mode 100644 index 000000000000..3d6a9fd90a0a --- /dev/null +++ b/ext/pdo_sqlite/tests/gh25021-udf.phpt @@ -0,0 +1,28 @@ +--TEST-- +GH-25021 UDF isn't using sqlite3_*_int64 API +--EXTENSIONS-- +pdo_sqlite +--FILE-- +sqliteCreateFunction('my_udf_pass_int', static fn(int $v): int => $v); + +foreach ([ + 10, + PHP_INT_MIN, + PHP_INT_MAX, +] as $v) { + $stmt = $conn->prepare('select my_udf_pass_int(:v)'); + $stmt->bindValue(':v', $v, PDO::PARAM_INT); + $stmt->execute(); + $result = $stmt->fetch(); + + var_dump($result[0] === $v); +} +?> +--EXPECT-- +bool(true) +bool(true) +bool(true) diff --git a/ext/sqlite3/tests/gh25021-udf.phpt b/ext/sqlite3/tests/gh25021-udf.phpt new file mode 100644 index 000000000000..4ff8d07a935c --- /dev/null +++ b/ext/sqlite3/tests/gh25021-udf.phpt @@ -0,0 +1,27 @@ +--TEST-- +GH-25021 UDF isn't using sqlite3_*_int64 API +--EXTENSIONS-- +sqlite3 +--FILE-- +createFunction('my_udf_pass_int', static fn(int $v): int => $v); + +foreach ([ + 10, + PHP_INT_MIN, + PHP_INT_MAX, +] as $v) { + $stmt = $conn->prepare('select my_udf_pass_int(:v)'); + $stmt->bindValue(':v', $v, SQLITE3_INTEGER); + $result = $stmt->execute()->fetchArray(); + + var_dump($result[0] === $v); +} +?> +--EXPECT-- +bool(true) +bool(true) +bool(true) From a0061320bdc8cc959f8e6ea4bf5e5ff35e8e59d4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michael=20Vo=C5=99=C3=AD=C5=A1ek?= Date: Fri, 30 Oct 2026 20:34:30 +0100 Subject: [PATCH 2/2] address review --- ext/pdo_sqlite/sqlite_driver.c | 4 ++-- ext/pdo_sqlite/sqlite_statement.c | 2 +- ext/pdo_sqlite/tests/gh25021-udf.phpt | 2 +- ext/sqlite3/sqlite3.c | 10 +++++----- ext/sqlite3/tests/gh25021-udf.phpt | 2 +- 5 files changed, 10 insertions(+), 10 deletions(-) diff --git a/ext/pdo_sqlite/sqlite_driver.c b/ext/pdo_sqlite/sqlite_driver.c index 48ccf1d3f8d5..1dd04db5e638 100644 --- a/ext/pdo_sqlite/sqlite_driver.c +++ b/ext/pdo_sqlite/sqlite_driver.c @@ -368,7 +368,7 @@ static int do_callback(struct pdo_sqlite_fci *fc, zval *cb, /* get the value */ switch (sqlite3_value_type(argv[i])) { case SQLITE_INTEGER: -#if ZEND_LONG_MAX > 2147483647 +#ifdef ZEND_ENABLE_ZVAL_LONG64 ZVAL_LONG(&zargs[i + is_agg], sqlite3_value_int64(argv[i])); #else ZVAL_LONG(&zargs[i + is_agg], sqlite3_value_int(argv[i])); @@ -414,7 +414,7 @@ static int do_callback(struct pdo_sqlite_fci *fc, zval *cb, if (!Z_ISUNDEF(retval)) { switch (Z_TYPE(retval)) { case IS_LONG: -#if ZEND_LONG_MAX > 2147483647 +#ifdef ZEND_ENABLE_ZVAL_LONG64 sqlite3_result_int64(context, Z_LVAL(retval)); #else sqlite3_result_int(context, Z_LVAL(retval)); diff --git a/ext/pdo_sqlite/sqlite_statement.c b/ext/pdo_sqlite/sqlite_statement.c index 90de059a3b7a..205028fdcb73 100644 --- a/ext/pdo_sqlite/sqlite_statement.c +++ b/ext/pdo_sqlite/sqlite_statement.c @@ -115,7 +115,7 @@ static int pdo_sqlite_stmt_param_hook(pdo_stmt_t *stmt, struct pdo_bound_param_d } } else { convert_to_long(parameter); -#if ZEND_LONG_MAX > 2147483647 +#ifdef ZEND_ENABLE_ZVAL_LONG64 if (SQLITE_OK == sqlite3_bind_int64(S->stmt, param->paramno + 1, Z_LVAL_P(parameter))) { return 1; } diff --git a/ext/pdo_sqlite/tests/gh25021-udf.phpt b/ext/pdo_sqlite/tests/gh25021-udf.phpt index 3d6a9fd90a0a..3a5e94820aff 100644 --- a/ext/pdo_sqlite/tests/gh25021-udf.phpt +++ b/ext/pdo_sqlite/tests/gh25021-udf.phpt @@ -1,5 +1,5 @@ --TEST-- -GH-25021 UDF isn't using sqlite3_*_int64 API +GH-25021 (UDF isn't using sqlite3_*_int64 API) --EXTENSIONS-- pdo_sqlite --FILE-- diff --git a/ext/sqlite3/sqlite3.c b/ext/sqlite3/sqlite3.c index 3bb8a3f87b54..b6adc2091863 100644 --- a/ext/sqlite3/sqlite3.c +++ b/ext/sqlite3/sqlite3.c @@ -623,13 +623,13 @@ static void sqlite_value_to_zval(sqlite3_stmt *stmt, int column, zval *data) /* switch (sqlite3_column_type(stmt, column)) { case SQLITE_INTEGER: val = sqlite3_column_int64(stmt, column); -#if LONG_MAX <= 2147483647 +#ifndef ZEND_ENABLE_ZVAL_LONG64 if (val > ZEND_LONG_MAX || val < ZEND_LONG_MIN) { ZVAL_STRINGL(data, (char *)sqlite3_column_text(stmt, column), sqlite3_column_bytes(stmt, column)); } else { #endif ZVAL_LONG(data, (zend_long) val); -#if LONG_MAX <= 2147483647 +#ifndef ZEND_ENABLE_ZVAL_LONG64 } #endif break; @@ -768,7 +768,7 @@ static int sqlite3_do_callback(struct php_sqlite3_fci *fc, zval *cb, int argc, s for (i = 0; i < argc; i++) { switch (sqlite3_value_type(argv[i])) { case SQLITE_INTEGER: -#if ZEND_LONG_MAX > 2147483647 +#ifdef ZEND_ENABLE_ZVAL_LONG64 ZVAL_LONG(&zargs[i + is_agg], sqlite3_value_int64(argv[i])); #else ZVAL_LONG(&zargs[i + is_agg], sqlite3_value_int(argv[i])); @@ -818,7 +818,7 @@ static int sqlite3_do_callback(struct php_sqlite3_fci *fc, zval *cb, int argc, s if (!Z_ISUNDEF(retval)) { switch (Z_TYPE(retval)) { case IS_LONG: -#if ZEND_LONG_MAX > 2147483647 +#ifdef ZEND_ENABLE_ZVAL_LONG64 sqlite3_result_int64(context, Z_LVAL(retval)); #else sqlite3_result_int(context, Z_LVAL(retval)); @@ -1514,7 +1514,7 @@ static int php_sqlite3_bind_params(php_sqlite3_stmt *stmt_obj) /* {{{ */ switch (param->type) { case SQLITE_INTEGER: convert_to_long(parameter); -#if ZEND_LONG_MAX > 2147483647 +#ifdef ZEND_ENABLE_ZVAL_LONG64 return_code = sqlite3_bind_int64(stmt_obj->stmt, param->param_number, Z_LVAL_P(parameter)); #else return_code = sqlite3_bind_int(stmt_obj->stmt, param->param_number, Z_LVAL_P(parameter)); diff --git a/ext/sqlite3/tests/gh25021-udf.phpt b/ext/sqlite3/tests/gh25021-udf.phpt index 4ff8d07a935c..5e9ad6f0ca6f 100644 --- a/ext/sqlite3/tests/gh25021-udf.phpt +++ b/ext/sqlite3/tests/gh25021-udf.phpt @@ -1,5 +1,5 @@ --TEST-- -GH-25021 UDF isn't using sqlite3_*_int64 API +GH-25021 (UDF isn't using sqlite3_*_int64 API) --EXTENSIONS-- sqlite3 --FILE--