Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions NEWS
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,10 @@ PHP NEWS
- Curl:
. Fixed curl_reset() retaining CURLOPT_PRIVATE after reset. (lacatoire)

- DBA:
. Fixed dba_close() not closing a database opened with dba_popen().
(Ilia Alshanetsky)

- DOM:
. Fixed bug GH-25268 (NULL php_libxml_ref_obj dereference in
dom_objects_store_clone_obj when cloning Dom\Notation). (David Carlier)
Expand Down
45 changes: 41 additions & 4 deletions ext/dba/dba.c
Original file line number Diff line number Diff line change
Expand Up @@ -271,6 +271,22 @@ static void dba_close_info(dba_info *info)
info = NULL;
}

static bool dba_info_shares_stream(const dba_info *info)
{
ZEND_HASH_FOREACH_PTR(&EG(persistent_list), zend_resource *le) {
if (le->type != le_pdb || le->ptr == NULL || le->ptr == info) {
continue;
}
const dba_info *other = le->ptr;
if ((info->fp && (info->fp == other->fp || info->fp == other->lock.fp))
|| (info->lock.fp && (info->lock.fp == other->fp || info->lock.fp == other->lock.fp))) {
return true;
}
} ZEND_HASH_FOREACH_END();

return false;
}

static int remove_pconnection_from_list(zval *zv, void *p)
{
zend_resource *le = Z_RES_P(zv);
Expand All @@ -297,6 +313,11 @@ static void dba_close_connection(dba_connection *connection)

if (!persistent) {
dba_close_info(connection->info);
} else if (connection->hash) {
zend_resource *le = zend_hash_find_ptr(&EG(persistent_list), connection->hash);
if (le) {
GC_DELREF(le);
}
}

connection->info = NULL;
Expand Down Expand Up @@ -501,6 +522,15 @@ static dba_info *php_dba_find(const zend_string *path)
}
} ZEND_HASH_FOREACH_END();

ZEND_HASH_FOREACH_PTR(&EG(persistent_list), zend_resource *le) {
if (le->type == le_pdb && GC_REFCOUNT(le) > 1) {
dba_info *info = le->ptr;
if (info && zend_string_equals(path, info->path)) {
return info;
}
}
} ZEND_HASH_FOREACH_END();

return NULL;
}
/* }}} */
Expand Down Expand Up @@ -596,6 +626,7 @@ static void php_dba_open(INTERNAL_FUNCTION_PARAMETERS, bool persistent)
efree(resource_key);
RETURN_FALSE;
}
GC_ADDREF(le);

object_init_ex(return_value, dba_connection_ce);
dba_connection *connection = Z_DBA_CONNECTION_P(return_value);
Expand Down Expand Up @@ -951,13 +982,15 @@ static void php_dba_open(INTERNAL_FUNCTION_PARAMETERS, bool persistent)
connection->info->hnd = hptr;

if (persistent) {
if (zend_register_persistent_resource(resource_key, resource_key_len, connection->info, le_pdb) == NULL) {
zend_resource *le = zend_register_persistent_resource(resource_key, resource_key_len, connection->info, le_pdb);
if (le == NULL) {
php_error_docref(NULL, E_WARNING, "Could not register persistent resource");
efree(resource_key);
dba_close_connection(connection);
zval_ptr_dtor(return_value);
RETURN_FALSE;
}
GC_ADDREF(le);
}

zend_hash_add_new(&DBA_G(connections), connection->hash, return_value);
Expand Down Expand Up @@ -1000,12 +1033,16 @@ PHP_FUNCTION(dba_close)
connection = Z_DBA_CONNECTION_P(id);
CHECK_DBA_CONNECTION(connection->info);

bool persistent = connection->info->flags & DBA_PERSISTENT;
dba_info *info = connection->info;
zend_resource *le = NULL;
if ((info->flags & DBA_PERSISTENT) && connection->hash) {
le = zend_hash_find_ptr(&EG(persistent_list), connection->hash);
}

dba_close_connection(connection);

if (persistent) {
zend_hash_apply_with_argument(&EG(persistent_list), remove_pconnection_from_list, (void *) connection->info);
if (le && GC_REFCOUNT(le) == 1 && !dba_info_shares_stream(info)) {
zend_hash_apply_with_argument(&EG(persistent_list), remove_pconnection_from_list, (void *) info);
}
}
/* }}} */
Expand Down
70 changes: 70 additions & 0 deletions ext/dba/tests/dba_popen_close.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
--TEST--
dba_close() releases a persistent connection once no other connection uses it
--EXTENSIONS--
dba
--SKIPIF--
<?php
$handler = "flatfile";
require_once(__DIR__ .'/skipif.inc');
if (PHP_OS_FAMILY === 'Solaris') die("skip Solaris flock() emulation cannot detect a lock held by the same process");
?>
--CONFLICTS--
dba
--FILE--
<?php
$handler = "flatfile";
require_once(__DIR__ .'/test.inc');

function lock_free(string $file): bool {
$db = @dba_open($file, 'wlt', 'flatfile');
if ($db === false) {
return false;
}
dba_close($db);
return true;
}

$db1 = dba_popen($db_filename, 'cl', 'flatfile');
dba_insert('key', 'value', $db1);
$db2 = dba_popen($db_filename, 'cl', 'flatfile');

dba_close($db2);
echo "first still open: ", dba_fetch('key', $db1), "\n";
echo "lock free after closing one of two: "; var_dump(lock_free($db_filename));

dba_close($db1);
echo "lock free after closing both: "; var_dump(lock_free($db_filename));

$db3 = dba_popen($db_filename, 'cl', 'flatfile');
unset($db3);
$db4 = dba_popen($db_filename, 'cl', 'flatfile');
echo "reused after implicit release: ", dba_fetch('key', $db4), "\n";
dba_close($db4);
echo "lock free after closing the reused connection: "; var_dump(lock_free($db_filename));

$db5 = dba_popen($db_filename, 'cl', 'flatfile');
$db6 = dba_popen($db_filename, 'cl', 'flatfile');
dba_close($db5);
var_dump(@dba_open($db_filename, 'wlt', 'flatfile'));
echo "conflict seen after closing the earlier connection: ", error_get_last()['message'], "\n";
dba_close($db6);

$reader1 = dba_popen($db_filename, 'rl', 'flatfile');
$reader2 = dba_popen($db_filename, 'rd', 'flatfile');
dba_close($reader1);
echo "connection sharing the stream still reads: ", dba_fetch('key', $reader2), "\n";
dba_close($reader2);
?>
--CLEAN--
<?php
require(__DIR__ .'/clean.inc');
?>
--EXPECT--
first still open: value
lock free after closing one of two: bool(false)
lock free after closing both: bool(true)
reused after implicit release: value
lock free after closing the reused connection: bool(true)
bool(false)
conflict seen after closing the earlier connection: dba_open(): Driver initialization failed for handler: flatfile: Unable to establish lock (database file already open)
connection sharing the stream still reads: value
Loading