Skip to content

Fix out-of-bounds read / size_t underflow in dkimf_db_datasplit() - #425

Merged
thegushi merged 1 commit into
trusteddomainproject:developfrom
thegushi:fix/lmdb-datasplit-oob-read
Jul 22, 2026
Merged

Fix out-of-bounds read / size_t underflow in dkimf_db_datasplit()#425
thegushi merged 1 commit into
trusteddomainproject:developfrom
thegushi:fix/lmdb-datasplit-oob-read

Conversation

@thegushi

Copy link
Copy Markdown
Collaborator

Summary

dkimf_db_datasplit() (opendkim/opendkim-db.c) splits a colon-delimited DB value into fields. Its delimiter search used strchr(p, ':'), which scans until it finds ':' or a NUL byte -- ignoring the remain byte budget it's supposed to respect.

For the Berkeley DB backend this happens to be harmless: the destination buffer is memset to all-zero for its full length before a DB_DBT_USERMEM fetch that only ever writes up to buflen bytes, so there's always a guaranteed trailing NUL just past the real data.

The LMDB backend has no such guarantee: mdb_get()/mdb_cursor_get() return a pointer directly into LMDB's memory-mapped pages, with no NUL-termination promise. If a stored value has no ':' within its real length, strchr() reads past the value into adjacent mapped memory, clen = q - p can exceed remain, and remain -= (clen + 1) underflows the size_t to a huge value -- the loop then keeps running with a corrupted, out-of-bounds pointer. This affects any DataSet/SigningTable-style DB backed by LMDB that uses colon-delimited multi-field values (reqnum > 1, non-binary fields).

I traced every caller of dkimf_db_datasplit() (Berkeley DB, LMDB dkimf_db_get(), and the LMDB cursor-walk path) to confirm the Berkeley DB call sites are safe by accident and the LMDB ones are not.

Changes

  • Replace strchr(p, ':') with memchr(p, ':', remain), which respects the remain window by construction and can never return a pointer past p + remain, so the subsequent subtraction can no longer underflow.

Test plan

  • autoreconf -fi && ./configure && gmake builds clean
  • gmake check: 174/174 tests pass
  • No new automated test added (would require standing up an LMDB-backed DB with a multi-field colon-delimited value at exactly the mapped-page boundary); fix verified by code inspection. Happy to add a targeted LMDB test if maintainers want one.

In the colon-delimited-field branch, dkimf_db_datasplit() located the
next delimiter with strchr(p, ':'), which scans until it finds ':' or
a NUL byte, ignoring the "remain" byte budget entirely.

For the Berkeley DB backend this happened to be harmless: the
destination buffer is memset to all-zero for its full length before a
DB_DBT_USERMEM fetch that only ever writes up to buflen bytes, so
there's always a guaranteed trailing NUL just past the data.

The LMDB backend has no such guarantee: mdb_get()/mdb_cursor_get()
return a pointer directly into LMDB's memory-mapped pages with no
NUL-termination promise. If a stored value has no ':' within its real
length, strchr() reads past the value into adjacent mapped memory,
"clen = q - p" can exceed "remain", and "remain -= (clen + 1)"
underflows the size_t to a huge value -- the loop then keeps running
with a corrupted, out-of-bounds pointer. This affects any
DataSet/SigningTable-style DB backed by LMDB that uses colon-delimited
multi-field values.

Fixed by using memchr(p, ':', remain) instead, which respects the
"remain" window by construction and can never return a pointer past
p + remain, so the subtraction can no longer underflow.
@thegushi
thegushi merged commit 1c9d450 into trusteddomainproject:develop Jul 22, 2026
2 checks passed
@thegushi
thegushi deleted the fix/lmdb-datasplit-oob-read branch July 22, 2026 02:16
thegushi added a commit that referenced this pull request Jul 22, 2026
thegushi added a commit that referenced this pull request Jul 22, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant