https://bugs.openldap.org/show_bug.cgi?id=10538
Issue ID: 10538 Summary: On Windows, a large value write can get silently lost Product: LMDB Version: 0.9.35 Hardware: x86_64 OS: Windows Status: UNCONFIRMED Keywords: needs_review Severity: normal Priority: --- Component: liblmdb Assignee: bugs@openldap.org Reporter: github@nicwatson.org Target Milestone: ---
On Windows, mdb_page_flush() writes each overflow page with a single WriteFile() whose write bytes is a 32-bit DWORD. A value whose overflow extent (psize * mp_pages) reaches 2**32 has its length truncated. An extent of exactly 2**32 truncates to 0, so WriteFile() writes nothing and the value is silently lost. The commit still "succeeds" (a 0-byte WriteFile returns TRUE), so the bug shows up as a value that reads back as zeroes instead of its contents.
Here's a reproducer. This only works on Windows 64-bit.
---
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <direct.h> #define MKDIR(d) _mkdir(d)
#include "lmdb.h"
#define CHK(expr) do { int rc_ = (expr); if (rc_) { \ fprintf(stderr, "%s:%d: %s: %s\n", __FILE__, __LINE__, #expr, \ mdb_strerror(rc_)); return 2; } } while (0)
int main(int argc, char **argv) { const char *dir = argc > 1 ? argv[1] : "lw-repro-db"; const size_t VALSIZE = (size_t)0xFFFFF000UL; MDB_env *env; MDB_txn *txn; MDB_dbi dbi; MDB_val key, val, got; unsigned char *p; char *buf;
MKDIR(dir);
buf = (char *)malloc(VALSIZE); if (!buf) { fprintf(stderr, "malloc(%zu) failed\n", VALSIZE); return 2; } memset(buf, 'x', VALSIZE); memcpy(buf, "HEAD", 4); memcpy(buf + VALSIZE - 4, "TAIL", 4);
CHK(mdb_env_create(&env)); CHK(mdb_env_set_mapsize(env, VALSIZE + (256UL << 20))); CHK(mdb_env_open(env, dir, 0, 0664)); CHK(mdb_txn_begin(env, NULL, 0, &txn)); CHK(mdb_dbi_open(txn, NULL, 0, &dbi)); key.mv_data = (void *)"big"; key.mv_size = 3; val.mv_data = buf; val.mv_size = VALSIZE; CHK(mdb_put(txn, dbi, &key, &val, 0)); CHK(mdb_txn_commit(txn)); /* the overflow-page WriteFile happens here */ free(buf);
CHK(mdb_txn_begin(env, NULL, MDB_RDONLY, &txn)); CHK(mdb_get(txn, dbi, &key, &got)); p = (unsigned char *)got.mv_data; if (got.mv_size != VALSIZE || memcmp(p, "HEAD", 4) != 0 || memcmp(p + VALSIZE - 4, "TAIL", 4) != 0) { fprintf(stderr, "BUG REPRODUCED: value truncated/lost: size=%zu " "head=%02x%02x%02x%02x tail=%02x%02x%02x%02x\n", got.mv_size, p[0], p[1], p[2], p[3], p[VALSIZE - 4], p[VALSIZE - 3], p[VALSIZE - 2], p[VALSIZE - 1]); mdb_txn_abort(txn); mdb_env_close(env); return 1; } mdb_txn_abort(txn); mdb_env_close(env);
printf("OK: committed and read back %zu-byte value intact\n", VALSIZE); return 0; }
https://bugs.openldap.org/show_bug.cgi?id=10538
Howard Chu hyc@openldap.org changed:
What |Removed |Added ---------------------------------------------------------------------------- Status|UNCONFIRMED |RESOLVED Resolution|--- |DUPLICATE
--- Comment #1 from Howard Chu hyc@openldap.org ---
*** This issue has been marked as a duplicate of issue 10054 ***
https://bugs.openldap.org/show_bug.cgi?id=10538
--- Comment #2 from Howard Chu hyc@openldap.org --- Note that the documentation already specifies that values may only be up to 0xffffffff bytes long (2**32 - 1). A value of 2**32 bytes long is not valid.
https://bugs.openldap.org/show_bug.cgi?id=10538
--- Comment #3 from github@nicwatson.org github@nicwatson.org ---
Note that the documentation already specifies that values may only be up to 0xffffffff bytes long (2**32 - 1). A value of 2**32 bytes long is not valid.
As demonstrated in the reproducer, since the length is rounded up, a value of length slightly less than 2**32 will cause the WriteFile with value 2**32.
https://bugs.openldap.org/show_bug.cgi?id=10538
--- Comment #4 from github@nicwatson.org github@nicwatson.org --- Looking at the patch, it doesn't fix this particular issue but a similar one (the other bug right before this I reported) on Linux. This only happens on Windows, and it isn't caused by a short write, but an integer overflow of the 32-bit length parameter to WriteFile.
https://bugs.openldap.org/show_bug.cgi?id=10538
Quanah Gibson-Mount quanah@openldap.org changed:
What |Removed |Added ---------------------------------------------------------------------------- Keywords|needs_review | Target Milestone|--- |1.0.1 Assignee|bugs@openldap.org |hyc@openldap.org Status|RESOLVED |CONFIRMED Ever confirmed|0 |1 Resolution|DUPLICATE |---
https://bugs.openldap.org/show_bug.cgi?id=10538
Howard Chu hyc@openldap.org changed:
What |Removed |Added ---------------------------------------------------------------------------- Resolution|--- |TEST Status|CONFIRMED |RESOLVED
--- Comment #5 from Howard Chu hyc@openldap.org --- Fixed in git 36e581af138a30a88669b8b17724d5e54d73eda7
https://bugs.openldap.org/show_bug.cgi?id=10538
--- Comment #6 from Quanah Gibson-Mount quanah@openldap.org --- mdb.master3:
• be8f6148 by Howard Chu at 2026-07-16T16:57:07+01:00 ITS#10538 lmdb: fix large writes on Windows
mdb.RE/1.0:
• 36e581af by Howard Chu at 2026-07-16T16:57:23+01:00 ITS#10538 lmdb: fix large writes on Windows
https://bugs.openldap.org/show_bug.cgi?id=10538
--- Comment #7 from Quanah Gibson-Mount quanah@openldap.org --- mdb.RE/0.9:
• 810398ec by Howard Chu at 2026-07-16T17:17:45+01:00 ITS#10538 lmdb: fix large writes on Windows
https://bugs.openldap.org/show_bug.cgi?id=10538
Quanah Gibson-Mount quanah@openldap.org changed:
What |Removed |Added ---------------------------------------------------------------------------- Status|RESOLVED |VERIFIED Resolution|TEST |FIXED