diff --git a/ext/pdo_firebird/firebird_statement.c b/ext/pdo_firebird/firebird_statement.c index 04a911c0c76c5..5b2971647fbf4 100644 --- a/ext/pdo_firebird/firebird_statement.c +++ b/ext/pdo_firebird/firebird_statement.c @@ -30,6 +30,37 @@ #define RECORD_ERROR(stmt) _firebird_error(NULL, stmt, __FILE__, __LINE__) +#define READ_AND_RETURN_USING_MEMCPY(type, sqldata) do { \ + type ret; \ + memcpy(&ret, sqldata, sizeof(ret)); \ + return ret; \ + } while (0); + +static zend_always_inline ISC_INT64 get_isc_int64_from_sqldata(const ISC_SCHAR *sqldata) +{ + READ_AND_RETURN_USING_MEMCPY(ISC_INT64, sqldata); +} + +static zend_always_inline ISC_LONG get_isc_long_from_sqldata(const ISC_SCHAR *sqldata) +{ + READ_AND_RETURN_USING_MEMCPY(ISC_LONG, sqldata); +} + +static zend_always_inline double get_double_from_sqldata(const ISC_SCHAR *sqldata) +{ + READ_AND_RETURN_USING_MEMCPY(double, sqldata); +} + +static zend_always_inline ISC_TIMESTAMP get_isc_timestamp_from_sqldata(const ISC_SCHAR *sqldata) +{ + READ_AND_RETURN_USING_MEMCPY(ISC_TIMESTAMP, sqldata); +} + +static zend_always_inline ISC_QUAD get_isc_quad_from_sqldata(const ISC_SCHAR *sqldata) +{ + READ_AND_RETURN_USING_MEMCPY(ISC_QUAD, sqldata); +} + /* free the allocated space for passing field values to the db and back */ static void free_sqlda(XSQLDA const *sqlda) /* {{{ */ { @@ -377,10 +408,10 @@ static int firebird_stmt_get_col( n = *(short*)var->sqldata; break; case SQL_LONG: - n = *(ISC_LONG*)var->sqldata; + n = get_isc_long_from_sqldata(var->sqldata); break; case SQL_INT64: - n = *(ISC_INT64*)var->sqldata; + n = get_isc_int64_from_sqldata(var->sqldata); break; case SQL_DOUBLE: break; @@ -388,7 +419,7 @@ static int firebird_stmt_get_col( } if ((var->sqltype & ~1) == SQL_DOUBLE) { - str = zend_strpprintf(0, "%.*F", -var->sqlscale, *(double*)var->sqldata); + str = zend_strpprintf(0, "%.*F", -var->sqlscale, get_double_from_sqldata(var->sqldata)); } else if (n >= 0) { str = zend_strpprintf(0, "%" LL_MASK "d.%0*" LL_MASK "d", n / f, -var->sqlscale, n % f); @@ -414,13 +445,13 @@ static int firebird_stmt_get_col( ZVAL_LONG(result, *(short*)var->sqldata); break; case SQL_LONG: - ZVAL_LONG(result, *(ISC_LONG*)var->sqldata); + ZVAL_LONG(result, get_isc_long_from_sqldata(var->sqldata)); break; case SQL_INT64: #if SIZEOF_ZEND_LONG >= 8 - ZVAL_LONG(result, *(ISC_INT64*)var->sqldata); + ZVAL_LONG(result, get_isc_int64_from_sqldata(var->sqldata)); #else - ZVAL_STR(result, zend_strpprintf(0, "%" LL_MASK "d", *(ISC_INT64*)var->sqldata)); + ZVAL_STR(result, zend_strpprintf(0, "%" LL_MASK "d", get_isc_int64_from_sqldata(var->sqldata))); #endif break; case SQL_FLOAT: @@ -429,7 +460,7 @@ static int firebird_stmt_get_col( break; case SQL_DOUBLE: /* TODO: Why is this not returned as the native type? */ - ZVAL_STR(result, zend_strpprintf(0, "%F", *(double*)var->sqldata)); + ZVAL_STR(result, zend_strpprintf(0, "%F", get_double_from_sqldata(var->sqldata))); break; #ifdef SQL_BOOLEAN case SQL_BOOLEAN: @@ -445,7 +476,10 @@ static int firebird_stmt_get_col( fmt = S->H->time_format ? S->H->time_format : PDO_FB_DEF_TIME_FMT; } else if (0) { case SQL_TIMESTAMP: - isc_decode_timestamp((ISC_TIMESTAMP*)var->sqldata, &t); + { + ISC_TIMESTAMP timestamp = get_isc_timestamp_from_sqldata(var->sqldata); + isc_decode_timestamp(×tamp, &t); + } fmt = S->H->timestamp_format ? S->H->timestamp_format : PDO_FB_DEF_TIMESTAMP_FMT; } /* convert the timestamp into a string */ @@ -453,8 +487,10 @@ static int firebird_stmt_get_col( size_t len = strftime(buf, sizeof(buf), fmt, &t); ZVAL_STRINGL(result, buf, len); break; - case SQL_BLOB: - return firebird_fetch_blob(stmt, colno, result, (ISC_QUAD*)var->sqldata); + case SQL_BLOB: { + ISC_QUAD quad = get_isc_quad_from_sqldata(var->sqldata); + return firebird_fetch_blob(stmt, colno, result, &quad); + } } } } @@ -607,7 +643,12 @@ static int firebird_stmt_param_hook(pdo_stmt_t *stmt, struct pdo_bound_param_dat *var->sqlind = -1; return 1; } - return firebird_bind_blob(stmt, (ISC_QUAD*)var->sqldata, parameter); + ISC_QUAD quad = get_isc_quad_from_sqldata(var->sqldata); + if (firebird_bind_blob(stmt, &quad, parameter) != 0) { + memcpy(var->sqldata, &quad, sizeof(quad)); + return 1; + } + return 0; } } diff --git a/ext/pdo_firebird/tests/gh10908.phpt b/ext/pdo_firebird/tests/gh10908.phpt new file mode 100644 index 0000000000000..f64bb7c3f71c1 --- /dev/null +++ b/ext/pdo_firebird/tests/gh10908.phpt @@ -0,0 +1,155 @@ +--TEST-- +GH-10908 (Bus error with PDO Firebird on RPI with 64 bit kernel and 32 bit userland) +--EXTENSIONS-- +pdo_firebird +--SKIPIF-- + +--ENV-- +LSAN_OPTIONS=detect_leaks=0 +--FILE-- +exec($sql); +$dbh->exec("INSERT INTO gh10908 VALUES(1, 'ABC', 12.34, 1.0, 2.0, '2023-03-24 17:39', '2023-03-24', '17:39', 'abcdefg', 'ab', 'a', 32767, 200000, 'azertyuiop', 'ab', false);"); + +function query_and_dump($dbh, $sql) { + foreach ($dbh->query($sql) as $row) { + print_r($row); + print("\n"); + } +} + +query_and_dump($dbh, "SELECT CODE FROM gh10908"); // works fine +query_and_dump($dbh, "SELECT ID FROM gh10908"); // Used to "bus error" +query_and_dump($dbh, "SELECT NUM FROM gh10908"); // Used to "bus error" +query_and_dump($dbh, "SELECT DBL FROM gh10908"); // Used to "bus error" +query_and_dump($dbh, "SELECT TS FROM gh10908"); // Used to "bus error" +query_and_dump($dbh, "SELECT MYBLOB FROM gh10908"); // Used to "bus error" +query_and_dump($dbh, "SELECT * FROM gh10908"); // Used to "bus error" + +query_and_dump($dbh, "SELECT CAST(NUM AS NUMERIC(9, 3)) FROM gh10908"); // works fine +query_and_dump($dbh, "SELECT CAST(ID AS INTEGER) FROM gh10908"); // works fine +query_and_dump($dbh, "SELECT CAST(ID AS BIGINT) FROM gh10908"); // Used to "bus error" + +echo "Did not crash\n"; + +?> +--CLEAN-- +exec("DROP TABLE gh10908"); +?> +--EXPECT-- +Array +( + [CODE] => ABC + [0] => ABC +) + +Array +( + [ID] => 1 + [0] => 1 +) + +Array +( + [NUM] => 12.340 + [0] => 12.340 +) + +Array +( + [DBL] => 1.000000 + [0] => 1.000000 +) + +Array +( + [TS] => 2023-03-24 17:39:00 + [0] => 2023-03-24 17:39:00 +) + +Array +( + [MYBLOB] => abcdefg + [0] => abcdefg +) + +Array +( + [ID] => 1 + [0] => 1 + [CODE] => ABC + [1] => ABC + [NUM] => 12.340 + [2] => 12.340 + [DBL] => 1.000000 + [3] => 1.000000 + [FLT] => 2.000000 + [4] => 2.000000 + [TS] => 2023-03-24 17:39:00 + [5] => 2023-03-24 17:39:00 + [MYDATE] => 2023-03-24 + [6] => 2023-03-24 + [MYTIME] => 17:39:00 + [7] => 17:39:00 + [MYBLOB] => abcdefg + [8] => abcdefg + [MYBINARY] => ab + [9] => ab + [MYVARBINARY] => a + [10] => a + [MYSMALLINT] => 32767 + [11] => 32767 + [MYINT] => 200000 + [12] => 200000 + [MYCHAR] => azertyuiop + [13] => azertyuiop + [MYVARCHAR] => ab + [14] => ab + [MYBOOL] => + [15] => +) + +Array +( + [CAST] => 12.340 + [0] => 12.340 +) + +Array +( + [CAST] => 1 + [0] => 1 +) + +Array +( + [CAST] => 1 + [0] => 1 +) + +Did not crash