diff --git a/ext/sqlite3/sqlite3.c b/ext/sqlite3/sqlite3.c index de6d55a3879fb..61159de3f257a 100644 --- a/ext/sqlite3/sqlite3.c +++ b/ext/sqlite3/sqlite3.c @@ -2194,6 +2194,42 @@ static void php_sqlite3_object_free_storage(zend_object *object) /* {{{ */ } /* }}} */ +static HashTable *php_sqlite3_get_gc(zend_object *object, zval **table, int *n) +{ + php_sqlite3_db_object *intern = php_sqlite3_db_from_obj(object); + + if (intern->funcs == NULL && intern->collations == NULL) { + /* Fast path without allocations */ + *table = NULL; + *n = 0; + return zend_std_get_gc(object, table, n); + } else { + zend_get_gc_buffer *gc_buffer = zend_get_gc_buffer_create(); + + php_sqlite3_func *func = intern->funcs; + while (func != NULL) { + zend_get_gc_buffer_add_zval(gc_buffer, &func->func); + zend_get_gc_buffer_add_zval(gc_buffer, &func->step); + zend_get_gc_buffer_add_zval(gc_buffer, &func->fini); + func = func->next; + } + + php_sqlite3_collation *collation = intern->collations; + while (collation != NULL) { + zend_get_gc_buffer_add_zval(gc_buffer, &collation->cmp_func); + collation = collation->next; + } + + zend_get_gc_buffer_use(gc_buffer, table, n); + + if (object->properties == NULL && object->ce->default_properties_count == 0) { + return NULL; + } else { + return zend_std_get_properties(object); + } + } +} + static void php_sqlite3_stmt_object_free_storage(zend_object *object) /* {{{ */ { php_sqlite3_stmt *intern = php_sqlite3_stmt_from_obj(object); @@ -2327,6 +2363,7 @@ PHP_MINIT_FUNCTION(sqlite3) sqlite3_object_handlers.offset = XtOffsetOf(php_sqlite3_db_object, zo); sqlite3_object_handlers.clone_obj = NULL; sqlite3_object_handlers.free_obj = php_sqlite3_object_free_storage; + sqlite3_object_handlers.get_gc = php_sqlite3_get_gc; php_sqlite3_sc_entry = register_class_SQLite3(); php_sqlite3_sc_entry->create_object = php_sqlite3_object_new; diff --git a/ext/sqlite3/tests/gh11878.phpt b/ext/sqlite3/tests/gh11878.phpt new file mode 100644 index 0000000000000..4a8a408a2679b --- /dev/null +++ b/ext/sqlite3/tests/gh11878.phpt @@ -0,0 +1,31 @@ +--TEST-- +GH-11878 (SQLite3 callback functions cause a memory leak with a callable array) +--EXTENSIONS-- +sqlite3 +--FILE-- +sqlite = new SQLite3(":memory:"); + if ($aggregates) { + $this->sqlite->createAggregate("indexes", array($this, "SQLiteIndex"), array($this, "SQLiteFinal"), 0); + } + if ($normalFunctions) { + $this->sqlite->createFunction("func", array($this, "SQLiteIndex"), 0); + $this->sqlite->createCollation("collation", array($this, "SQLiteIndex")); + } + } + public function SQLiteIndex() {} + public function SQLiteFinal() {} +} + +// Test different combinations to check for null pointer derefs +$x = new Foo(true, true); +$y = new Foo(false, true); +$z = new Foo(true, false); +$w = new Foo(false, false); +?> +Done +--EXPECT-- +Done