[php-src] Issue #24641: ext/dba: ndbm backend never checks dbm_open() return value, NULL deref on first use after a failed open
| From: | kunwaratulhax0r | Date: | Sat, 24 Oct 2026 12:50:07 +0000 |
| Subject: | [php-src] Issue #24641: ext/dba: ndbm backend never checks dbm_open() return value, NULL deref on first use after a failed open | ||
| Groups: | php.bugs | ||
| Request: | Send a blank email to php-bugs+get-253157@lists.php.net to get a copy of this message | ||
Issue: https://github.com/php/php-src/issues/24641
Author: kunwaratulhax0r
DBA_OPEN_FUNC(ndbm) (ext/dba/dba_ndbm.c) never checks whether
dbm_open() actually succeeded before returning SUCCESS:
```c
dbf = dbm_open(ZSTR_VAL(info->path), gmode, filemode);
pinfo->dbf = dbf;
return SUCCESS;
```
dbm_open() returns NULL on failure (invalid format, permission issue,
etc.). Every other dba backend checks this correctly before reporting success:
```c
// dba_gdbm.c, dba_qdbm.c, dba_tcadb.c, dba_db1.c..db4.c:
if (dbf) {
...
return SUCCESS;
}
return FAILURE;
```
ndbm is the only one that skips it, so info->dbf ends up
NULL while dba_open() still reports success. Any subsequent operation
(dba_firstkey, dba_fetch, etc.) calls directly into the NDBM library with
a NULL handle.
**Repro:**
```php
<?php
file_put_contents('/tmp/invalid_ndbm_file', 'not a valid ndbm/gdbm database, just
plain text');
$db = dba_open('/tmp/invalid_ndbm_file', 'r', 'ndbm');
var_dump($db); // truthy -- reports SUCCESS despite the invalid format
dba_firstkey($db); // SEGV
```
**ASan output:**
```
AddressSanitizer: SEGV on unknown address 0x000000000000
READ memory access (NULL dereference)
dbm_firstkey (libgdbm_compat.so.4)
dba_firstkey_ndbm ext/dba/dba_ndbm.c:122
zif_dba_firstkey ext/dba/dba.c:1164
```
Address 0x000000000000 confirms a plain NULL-pointer dereference.
Built/tested with --enable-dba --with-ndbm (Ubuntu libgdbm-compat-dev,
CFLAGS="-fsanitize=address -g -O0").
Note on reachability: PHP's own generic dispatcher (ext/dba/dba.c,
php_stream_open_wrapper()) already blocks a path that doesn't exist at all before
it reaches the ndbm handler. This crash needs the path to already exist as a real file,
just not a valid NDBM/GDBM database — an ordinary real-world state (wrong file, corrupted cache),
not something requiring deliberate construction.
**Suggested fix:** add the same if (!dbf) return FAILURE; check
ndbm's sibling backends already have.