Skip to content

Commit 7100162

Browse files
committed
Fix GH-11878: SQLite3 callback functions cause a memory leak with a callable array
In this test file, the free_obj handler is called with a refcount of 2, caused by the fact we do a GC_ADDREF() to increase its refcount while its refcount is still 1 because the Foo object hasn't been destroyed yet (due to the cycle caused by the sqlite function callback). Solve this by introducing a get_gc handler. Closes GH-11881.
1 parent 748adf1 commit 7100162

File tree

2 files changed

+55
-0
lines changed

2 files changed

+55
-0
lines changed

ext/sqlite3/sqlite3.c

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2194,6 +2194,37 @@ static void php_sqlite3_object_free_storage(zend_object *object) /* {{{ */
21942194
}
21952195
/* }}} */
21962196

2197+
static HashTable *php_sqlite3_get_gc(zend_object *object, zval **table, int *n)
2198+
{
2199+
php_sqlite3_db_object *intern = php_sqlite3_db_from_obj(object);
2200+
2201+
if (intern->funcs == NULL && intern->collations == NULL) {
2202+
/* Fast path without allocations */
2203+
*table = NULL;
2204+
*n = 0;
2205+
} else {
2206+
zend_get_gc_buffer *gc_buffer = zend_get_gc_buffer_create();
2207+
2208+
php_sqlite3_func *func = intern->funcs;
2209+
while (func != NULL) {
2210+
zend_get_gc_buffer_add_zval(gc_buffer, &func->func);
2211+
zend_get_gc_buffer_add_zval(gc_buffer, &func->step);
2212+
zend_get_gc_buffer_add_zval(gc_buffer, &func->fini);
2213+
func = func->next;
2214+
}
2215+
2216+
php_sqlite3_collation *collation = intern->collations;
2217+
while (collation != NULL) {
2218+
zend_get_gc_buffer_add_zval(gc_buffer, &collation->cmp_func);
2219+
collation = collation->next;
2220+
}
2221+
2222+
zend_get_gc_buffer_use(gc_buffer, table, n);
2223+
}
2224+
2225+
return NULL;
2226+
}
2227+
21972228
static void php_sqlite3_stmt_object_free_storage(zend_object *object) /* {{{ */
21982229
{
21992230
php_sqlite3_stmt *intern = php_sqlite3_stmt_from_obj(object);
@@ -2327,6 +2358,7 @@ PHP_MINIT_FUNCTION(sqlite3)
23272358
sqlite3_object_handlers.offset = XtOffsetOf(php_sqlite3_db_object, zo);
23282359
sqlite3_object_handlers.clone_obj = NULL;
23292360
sqlite3_object_handlers.free_obj = php_sqlite3_object_free_storage;
2361+
sqlite3_object_handlers.get_gc = php_sqlite3_get_gc;
23302362
php_sqlite3_sc_entry = register_class_SQLite3();
23312363
php_sqlite3_sc_entry->create_object = php_sqlite3_object_new;
23322364

ext/sqlite3/tests/gh11878.phpt

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
--TEST--
2+
GH-11878 (SQLite3 callback functions cause a memory leak with a callable array)
3+
--EXTENSIONS--
4+
sqlite3
5+
--FILE--
6+
<?php
7+
class Foo {
8+
public $sqlite;
9+
public function __construct() {
10+
$this->sqlite = new SQLite3(":memory:");
11+
$this->sqlite->createAggregate("indexes", array($this, "SQLiteIndex"), array($this, "SQLiteFinal"), 0);
12+
$this->sqlite->createFunction("func", array($this, "SQLiteIndex"), 0);
13+
$this->sqlite->createCollation("collation", array($this, "SQLiteIndex"));
14+
}
15+
public function SQLiteIndex() {}
16+
public function SQLiteFinal() {}
17+
}
18+
19+
$x = new Foo;
20+
?>
21+
Done
22+
--EXPECT--
23+
Done

0 commit comments

Comments
 (0)