Commit 407c45debce for php.net

commit 407c45debce31f5544cbd3c62983646d39523436
Author: ndossche <7771979+ndossche@users.noreply.github.com>
Date:   Fri May 15 12:55:39 2026 +0200

    sqlite: fix error checks for column retrieval

    These can return NULL on OOM.
    And for blobs, it can return NULL for empty blobs (so *no* failure,
    just an edge case). Passing NULL to memcpy is UB, so we have to
    check for a NULL pointer there to avoid UB.

    Closes GH-22045.

diff --git a/NEWS b/NEWS
index 7aa9ec33744..faeb27fdf6d 100644
--- a/NEWS
+++ b/NEWS
@@ -36,6 +36,9 @@ PHP                                                                        NEWS
     Phar::addEmptyDir() for paths starting with "/.phar", while allowing
     non-magic directory names that merely share the ".phar" prefix. (Weilin Du)

+- Sqlite:
+  . Fix error checks for column retrieval. (ndossche)
+
 - Zlib:
   . Fixed memory leak if deflate initialization fails and there is a dict.
     (ndossche)
diff --git a/ext/sqlite3/sqlite3.c b/ext/sqlite3/sqlite3.c
index 100cf32680e..1bb93e0e267 100644
--- a/ext/sqlite3/sqlite3.c
+++ b/ext/sqlite3/sqlite3.c
@@ -627,7 +627,7 @@ PHP_METHOD(SQLite3, query)
 }
 /* }}} */

-static void sqlite_value_to_zval(sqlite3_stmt *stmt, int column, zval *data) /* {{{ */
+static void sqlite_value_to_zval(php_sqlite3_db_object *db_obj, sqlite3_stmt *stmt, int column, zval *data) /* {{{ */
 {
 	sqlite3_int64 val;

@@ -636,7 +636,13 @@ static void sqlite_value_to_zval(sqlite3_stmt *stmt, int column, zval *data) /*
 			val = sqlite3_column_int64(stmt, column);
 #if LONG_MAX <= 2147483647
 			if (val > ZEND_LONG_MAX || val < ZEND_LONG_MIN) {
-				ZVAL_STRINGL(data, (char *)sqlite3_column_text(stmt, column), sqlite3_column_bytes(stmt, column));
+				const char *text = (const char *) sqlite3_column_text(stmt, column);
+				if (UNEXPECTED(text == NULL)) {
+					php_sqlite3_error(db_obj, SQLITE_NOMEM, "Failed to retrieve column value due to out of memory");
+					ZVAL_NULL(data);
+				} else {
+					ZVAL_STRINGL(data, text, sqlite3_column_bytes(stmt, column));
+				}
 			} else {
 #endif
 				ZVAL_LONG(data, (zend_long) val);
@@ -653,13 +659,33 @@ static void sqlite_value_to_zval(sqlite3_stmt *stmt, int column, zval *data) /*
 			ZVAL_NULL(data);
 			break;

-		case SQLITE3_TEXT:
-			ZVAL_STRING(data, (char*)sqlite3_column_text(stmt, column));
+		case SQLITE3_TEXT: {
+			const char *text = (const char *) sqlite3_column_text(stmt, column);
+			if (UNEXPECTED(text == NULL)) {
+				php_sqlite3_error(db_obj, SQLITE_NOMEM, "Failed to retrieve column value due to out of memory");
+				ZVAL_NULL(data);
+			} else {
+				ZVAL_STRING(data, text);
+			}
 			break;
+		}

 		case SQLITE_BLOB:
-		default:
-			ZVAL_STRINGL(data, (char*)sqlite3_column_blob(stmt, column), sqlite3_column_bytes(stmt, column));
+		default: {
+			const char *blob = (const char *) sqlite3_column_blob(stmt, column);
+			if (UNEXPECTED(blob == NULL)) {
+				if (sqlite3_errcode(sqlite3_db_handle(stmt)) == SQLITE_NOMEM) {
+					php_sqlite3_error(db_obj, SQLITE_NOMEM, "Failed to retrieve column value due to out of memory");
+					ZVAL_NULL(data);
+				} else {
+					/* Zero-length BLOB */
+					ZVAL_EMPTY_STRING(data);
+				}
+			} else {
+				ZVAL_STRINGL(data, blob, sqlite3_column_bytes(stmt, column));
+			}
+			break;
+		}
 	}
 }
 /* }}} */
@@ -709,13 +735,13 @@ PHP_METHOD(SQLite3, querySingle)
 		case SQLITE_ROW: /* Valid Row */
 		{
 			if (!entire_row) {
-				sqlite_value_to_zval(stmt, 0, return_value);
+				sqlite_value_to_zval(db_obj, stmt, 0, return_value);
 			} else {
 				int i = 0;
 				array_init(return_value);
 				for (i = 0; i < sqlite3_data_count(stmt); i++) {
 					zval data;
-					sqlite_value_to_zval(stmt, i, &data);
+					sqlite_value_to_zval(db_obj, stmt, i, &data);
 					add_assoc_zval(return_value, (char*)sqlite3_column_name(stmt, i), &data);
 				}
 			}
@@ -1973,7 +1999,7 @@ PHP_METHOD(SQLite3Result, fetchArray)
 			for (i = 0; i < n_cols; i++) {
 				zval data;

-				sqlite_value_to_zval(result_obj->stmt_obj->stmt, i, &data);
+				sqlite_value_to_zval(result_obj->db_obj, result_obj->stmt_obj->stmt, i, &data);

 				if (mode & PHP_SQLITE3_NUM) {
 					add_index_zval(return_value, i, &data);