Skip to content

Commit f119935

Browse files
committed
ext/dba: end LMDB cursor iteration on write/delete
dba_insert/replace/delete on an LMDB handler started a write txn into LMDB_IT(txn) while dba_firstkey's cursor was still bound to the read txn in that slot, so the next dba_nextkey renewed a freed handle. Close the cursor and abort the read txn before starting the write txn, and bail out of dba_nextkey when no cursor is open.
1 parent 8d0777e commit f119935

3 files changed

Lines changed: 100 additions & 0 deletions

File tree

ext/dba/dba_lmdb.c

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -172,6 +172,12 @@ DBA_UPDATE_FUNC(lmdb)
172172
int rc;
173173
MDB_val k, v;
174174

175+
if (LMDB_IT(cur)) {
176+
mdb_cursor_close(LMDB_IT(cur));
177+
LMDB_IT(cur) = NULL;
178+
mdb_txn_abort(LMDB_IT(txn));
179+
}
180+
175181
rc = mdb_txn_begin(LMDB_IT(env), NULL, 0, &LMDB_IT(txn));
176182
if (rc) {
177183
php_error_docref(NULL, E_WARNING, "%s", mdb_strerror(rc));
@@ -243,6 +249,12 @@ DBA_DELETE_FUNC(lmdb)
243249
int rc;
244250
MDB_val k;
245251

252+
if (LMDB_IT(cur)) {
253+
mdb_cursor_close(LMDB_IT(cur));
254+
LMDB_IT(cur) = NULL;
255+
mdb_txn_abort(LMDB_IT(txn));
256+
}
257+
246258
rc = mdb_txn_begin(LMDB_IT(env), NULL, 0, &LMDB_IT(txn));
247259
if (rc) {
248260
php_error_docref(NULL, E_WARNING, "%s", mdb_strerror(rc));
@@ -314,6 +326,10 @@ DBA_NEXTKEY_FUNC(lmdb)
314326
MDB_val k, v;
315327
zend_string *ret = NULL;
316328

329+
if (!LMDB_IT(cur)) {
330+
return NULL;
331+
}
332+
317333
rc = mdb_txn_renew(LMDB_IT(txn));
318334
if (rc) {
319335
php_error_docref(NULL, E_WARNING, "%s", mdb_strerror(rc));
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
--TEST--
2+
DBA LMDB: dba_nextkey before dba_firstkey returns false
3+
--EXTENSIONS--
4+
dba
5+
--SKIPIF--
6+
<?php
7+
require_once __DIR__ . '/setup/setup_dba_tests.inc';
8+
check_skip('lmdb');
9+
?>
10+
--FILE--
11+
<?php
12+
require_once __DIR__ . '/setup/setup_dba_tests.inc';
13+
$db_file = __DIR__ . '/dba_lmdb_nextkey_without_firstkey.db';
14+
@unlink($db_file);
15+
@unlink($db_file . '-lock');
16+
17+
$db = dba_open($db_file, 'cl', 'lmdb');
18+
var_dump(dba_nextkey($db));
19+
dba_replace('a', '1', $db);
20+
var_dump(dba_nextkey($db));
21+
dba_close($db);
22+
?>
23+
--CLEAN--
24+
<?php
25+
$db_file = __DIR__ . '/dba_lmdb_nextkey_without_firstkey.db';
26+
@unlink($db_file);
27+
@unlink($db_file . '-lock');
28+
@unlink($db_file . '.lck');
29+
?>
30+
--EXPECTF--
31+
Notice: dba_open(): Handler lmdb does locking internally in %s on line %d
32+
bool(false)
33+
bool(false)
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
--TEST--
2+
DBA LMDB: writes during cursor iteration end the iteration cleanly
3+
--EXTENSIONS--
4+
dba
5+
--SKIPIF--
6+
<?php
7+
require_once __DIR__ . '/setup/setup_dba_tests.inc';
8+
check_skip('lmdb');
9+
?>
10+
--FILE--
11+
<?php
12+
require_once __DIR__ . '/setup/setup_dba_tests.inc';
13+
$db_file = __DIR__ . '/dba_lmdb_write_during_iteration.db';
14+
@unlink($db_file);
15+
@unlink($db_file . '-lock');
16+
17+
$db = dba_open($db_file, 'cl', 'lmdb');
18+
foreach (['a' => '1', 'b' => '2', 'c' => '3'] as $k => $v) {
19+
dba_replace($k, $v, $db);
20+
}
21+
22+
var_dump(dba_firstkey($db));
23+
var_dump(dba_nextkey($db));
24+
25+
var_dump(dba_replace('d', '4', $db));
26+
var_dump(dba_nextkey($db));
27+
var_dump(dba_nextkey($db));
28+
29+
var_dump(dba_firstkey($db));
30+
var_dump(dba_delete('a', $db));
31+
var_dump(dba_nextkey($db));
32+
33+
dba_close($db);
34+
?>
35+
--CLEAN--
36+
<?php
37+
$db_file = __DIR__ . '/dba_lmdb_write_during_iteration.db';
38+
@unlink($db_file);
39+
@unlink($db_file . '-lock');
40+
@unlink($db_file . '.lck');
41+
?>
42+
--EXPECTF--
43+
Notice: dba_open(): Handler lmdb does locking internally in %s on line %d
44+
string(1) "a"
45+
string(1) "b"
46+
bool(true)
47+
bool(false)
48+
bool(false)
49+
string(1) "a"
50+
bool(true)
51+
bool(false)

0 commit comments

Comments
 (0)