https://bugs.openldap.org/show_bug.cgi?id=10642
Issue ID: 10642
Summary: unaligned MDB_db access in mdb_subdb_adjust()
Product: LMDB
Version: unspecified
Hardware: All
OS: All
Status: UNCONFIRMED
Keywords: needs_review
Severity: normal
Priority: ---
Component: liblmdb
Assignee: bugs(a)openldap.org
Reporter: ledvinap(a)gmail.com
Target Milestone: ---
Commit b048b7e (ITS#9388) introduced unaligned MDB_db* access.
Cause: mdb_subdb_adjust dereferences an MDB_db * from NODEDATA(), which is not
aligned
Impact: undefined behavior in C. Incorrect page counts on ARM920T
Fix: trivial, pass old as const void*, memcpy() into aligned stack variable
Spotted/fixed by AI, so diff is not acceptable according to contribution rules.
This report is fully authored by me.
--
You are receiving this mail because:
You are on the CC list for the issue.
https://bugs.openldap.org/show_bug.cgi?id=10603
Issue ID: 10603
Summary: slapd 2.6.10 aborts in mdb_opinfo_get (id2entry.c:828)
serving syncrepl refresh - thread-cached reader txn
still active, mdb_txn_renew returns EINVAL
Product: OpenLDAP
Version: 2.6.10
Hardware: x86_64
OS: Linux
Status: UNCONFIRMED
Keywords: needs_review
Severity: normal
Priority: ---
Component: slapd
Assignee: bugs(a)openldap.org
Reporter: vojtech(a)dusatko.org
Target Milestone: ---
Created attachment 1215
--> https://bugs.openldap.org/attachment.cgi?id=1215&action=edit
thread apply all bt (coredump + gdb)
Three-node multiprovider cluster (olcMultiProvider on {0}config and the data
DB),
back-mdb, replicating one suffix. Overlay stack on the data DB in insertion
order:
auditlog, memberof, refint, syncprov, ppolicy, unique. syncprov had no
checkpoint/sessionlog configured. Consumers use refreshAndPersist, retry "5 20
300 +".
Binary: Debian 13 (trixie) official package slapd 2.6.10+dfsg-1 from
deb.debian.org (build "May 29 2025 23:41:48", Debian OpenLDAP Maintainers);
symbols from slapd-dbgsym 2.6.10+dfsg-1 (debian-debug archive). No local
patches.
Symptom: whenever a provider slapd is (re)started and a consumer reconnects and
begins its refresh (present phase; consumer search is base=<suffix> scope=sub
deref=0 filter=(objectClass=*) attrs=* +), the provider aborts within seconds:
slapd: ../../../../../servers/slapd/back-mdb/id2entry.c:828: mdb_opinfo_get:
Assertion `!rc' failed.
Reproduced 5+ times over two days; effectively every provider restart followed
by
a consumer refresh crashes it. Environment is small and healthy: data.mdb 1.5
MB,
olcDbMaxSize 1 GiB, olcDbMaxReaders default, >20 GB free disk, no other process
opens the LMDB env (verified). The DB had been slapindex'ed offline (slapd
stopped) before the first occurrence; the crash also reproduces on a freshly
started slapd. Loglevel "stats acl filter" during the captured crash.
Analysis from the core: id2entry.c:828 is the mdb_txn_renew() of the thread's
cached reader transaction inside mdb_opinfo_get() (rdonly=1, renew=1). The
cached
txn retrieved via the thread-pool key has mt_flags = 0x20000 (MDB_TXN_RDONLY
set,
MDB_TXN_FINISHED NOT set), i.e. it was still active, not reset - so
mdb_txn_renew() returned EINVAL and the assert fired. The env is healthy:
me_flags = 0x30000000 (MDB_ENV_ACTIVE|MDB_ENV_TXKEY, no MDB_FATAL_ERROR).
The failing call is syncprov's internal findbase search nested on the same
worker
thread under the consumer's refresh search; the fresh stack-local mdb_op_info
(moi_ref = 0, reader flag) borrowed the thread's cached reader txn while it was
still in use - reentrant/unbalanced use of the per-thread cached read
transaction
(an outer op holding it active, or an earlier op missing its mdb_txn_reset).
Relevant locals at frame 4 (mdb_opinfo_get):
renew = 1
data = 0x7f3134105580 (thread-pool cached txn)
moi = 0x7f313b7ebc50 {moi_txn = 0x7f3134105580, moi_ref = 0,
moi_flag = 1}
moi->moi_txn->mt_flags = 0x20000 (RDONLY, not FINISHED ->
mdb_txn_renew EINVAL)
mdb->mi_dbenv->me_flags = 0x30000000 (no MDB_FATAL_ERROR)
Backtrace of the aborting thread (full "thread apply all bt" attached):
#4 mdb_opinfo_get (op=0x7f313b7fc070, mdb=0x7f31811e9010, rdonly=1,
moip=0x7f313b7ebb48) at back-mdb/id2entry.c:828
#5 mdb_search (op=0x7f313b7fc070, rs=0x7f313b7fc000) at back-mdb/search.c:449
#6 overlay_op_walk (op=0x7f313b7fc070, ...) at backover.c:706
#7 over_op_func (op=0x7f313b7fc070, ...) at backover.c:766
#8 syncprov_findbase (op=0x7f3134103be0, fc=0x7f313b7fc330) at
overlays/syncprov.c:530
#9 syncprov_op_search (op=0x7f3134103be0, rs=0x7f313b7fd9a0) at
overlays/syncprov.c:3175
#10 overlay_op_walk (op=0x7f3134103be0, ...) at backover.c:691
#11 over_op_func (op=0x7f3134103be0, ...) at backover.c:766
#12 fe_op_search (op=0x7f3134103be0, ...) at search.c:426
#13 do_search ... at search.c:267
#14 connection_operation ... at connection.c:1115
#15 connection_read_thread ... at connection.c:1267
Note the two distinct op pointers: the consumer's search op (0x7f3134103be0,
frames 8-15) and syncprov's internal findbase op (0x7f313b7fc070, frames 4-7)
on
the same worker thread. At crash time another worker thread was serving an
unrelated cn=config subtree search (config_back_search) and a third was blocked
in syslog(3) writing filter-level debug output, in case concurrency is
relevant.
slapd log tail before one abort:
conn=1355 fd=18 ACCEPT from IP=<consumer-ip>:48480 (IP=<provider-ip>:1389)
conn=1355 op=0 BIND dn="cn=admin,<suffix>" mech=SIMPLE ssf=0
conn=1355 op=1 SRCH base="<suffix>" scope=2 deref=0 filter="(objectClass=*)"
conn=1355 op=1 SRCH attr=* +
slapd: ../../../../../servers/slapd/back-mdb/id2entry.c:828: mdb_opinfo_get:
Assertion `!rc' failed.
Reproduction: restart slapd on a provider; wait for a consumer's syncrepl retry
to reconnect and start refresh (seconds to ~1 min). Ordinary application
traffic
only, no special load.
Timeline note: crashes began after a config deploy that (a) added a
slapo-unique
instance to the overlay stack, (b) added one new eq index followed by an
offline
slapindex, (c) changed loglevel to "stats acl filter". We cannot exclude the
changed overlay stack being a precondition rather than coincidence.
--
You are receiving this mail because:
You are on the CC list for the issue.
https://bugs.openldap.org/show_bug.cgi?id=10597
Issue ID: 10597
Summary: slapo-accesslog frees a shared `entryUUID` value from
operations that do not hold the mutex guarding it,
causing a double free and heap corruption. Introduced
in 2.6.14 by the fix for ITS#10482.
Product: OpenLDAP
Version: 2.6.14
Hardware: All
OS: All
Status: UNCONFIRMED
Keywords: needs_review
Severity: normal
Priority: ---
Component: overlays
Assignee: bugs(a)openldap.org
Reporter: bohdan.kmit(a)kiteworks.com
Target Milestone: ---
Created attachment 1212
--> https://bugs.openldap.org/attachment.cgi?id=1212&action=edit
Patch
# Symptom
`slapd` terminates with SIGSEGV under concurrent write and search load. glibc
reports the corruption at whatever the process allocates next, so the message
varies:
```
double free or corruption (out)
malloc(): unaligned fastbin chunk detected
malloc(): unaligned tcache chunk detected
free(): invalid pointer
```
Of ten cores collected from one instance, nine abort inside `malloc()` or
`calloc()` at unrelated allocation sites and carry no information about the
origin. One caught the faulting free itself:
```
#4 free ()
#5 accesslog_response (op=<optimized out>, rs=...) at accesslog.c:2034
#6 ...
#8 slap_send_ldap_result ()
```
(line 2034 in 2.6.15; the equivalent is 2176 in current master.)
# Analysis
`accesslog_response()` ends with:
```c
skip:
if ( !BER_BVISNULL( &li->li_uuid ) ) {
ber_memfree( li->li_uuid.bv_val );
BER_BVZERO( &li->li_uuid );
}
if ( lo->mask & LOG_OP_WRITES ) {
/* We haven't transitioned to li_log_mutex yet */
ldap_pvt_thread_mutex_unlock( &li->li_op_rmutex );
}
return SLAP_CB_CONTINUE;
```
`li_uuid` is a field of the shared `log_info` instance, not of the operation.
Access to it is asymmetric:
* Write operations (`bi_op_add`, `bi_op_delete`, `bi_op_modify`, `bi_op_modrdn`
→ `accesslog_op_mod()`) acquire `li_op_rmutex` and populate `li_uuid` with
`ber_dupbv()` while holding it. They still hold it on entry to
`accesslog_response()`, free under it at `skip:`, and release it immediately
after. This is correct.
* Non-write operations (`bi_op_bind`, `bi_op_compare`, `bi_op_search`,
`bi_extended` → `accesslog_op_misc()`) take no lock at all.
`accesslog_response()` acquires `li_op_rmutex` for them only part-way through
the function, and **all four `goto skip` sites precede that acquisition**
(master: 1656, 1661, 1671, 1683 versus the lock at 1693). Such an operation
therefore reaches `skip:` holding no lock, and frees a pointer it never set.
The `goto skip` conditions reachable this way are:
| site | condition |
|------|-----------------------------------------------------|
| 1656 | log database absent or not open |
| 1661 | `op->o_dont_replicate` |
| 1671 | `li_success` configured and the operation failed |
| 1683 | operation not in `li_ops` and no matching `logbase` |
Two consequences follow:
1. Two non-write operations completing concurrently both observe a non-NULL
`li_uuid` and both call `ber_memfree()` on it — a double free.
2. A non-write operation can free it in the window between a write operation
storing the value and that write operation taking ownership of it (master
1716–1718, under both mutexes). The write path then works with, and frees, a
dangling pointer.
Either way the allocator's free lists are corrupted, and the failure surfaces
later at an unrelated allocation, which is why almost every core points
somewhere innocent.
The exposure depends strongly on configuration. With `olcAccessLogOps: writes`,
searches are not in `li_ops`, so **every search** fails the test at 1673 and
takes `goto skip` at 1683. A read-heavy workload therefore executes the
unlocked free at close to search rate. Note that 1683 is unreachable for write
operations, since `accesslog_op_mod()` applies the same test before registering
the callback.
Access to the tree of related state is otherwise correct: every use of
`li_mincsn`, `li_sids` and `li_numcsns` in `log_old_lookup()`,
`accesslog_purge()`, `accesslog_response()` and `accesslog_db_root()` is
serialised by `li_log_mutex`, and no locked region in the file contains a
`return` or `goto` that bypasses its unlock. `li_uuid` is the only unserialised
mutation.
# Regression
The unconditional free was added by:
```
5c4e7f2f1a ITS#10482 slapo-accesslog: do not leak entryUUID (2026-03-31)
ab4e53e54b same, backported to the 2.6 branch
```
That commit consists solely of those four lines. It plugs a genuine leak but
places the release on a path reachable without the mutex.
# Affected versions
| release | affected |
|--------------|----------|
| 2.6.13 | no |
| 2.6.14 | yes |
| 2.6.15 | yes |
| 2.7.0, 2.7.1 | yes |
| master | yes |
Observed on 2.6.15, x86-64, glibc, back-mdb, with `syncprov` and `accesslog` on
the same database and delta-syncrepl consumers reading the accesslog.
# Reproduction
Configuration: `accesslog` over back-mdb with `olcAccessLogOps: writes` and
`olcAccessLogSuccess: TRUE`.
Load: six concurrent MODIFY streams cycling over four DNs (to maximise
same-target concurrency), two more spread over twenty DNs, one stream of ADDs
of existing entries (returning `entryAlreadyExists`), and twelve concurrent
search streams over the same subtree.
Result: SIGSEGV within **13–24 seconds**, repeatably — effectively one crash
per load application.
Two observations that isolate the mechanism:
* The **same write load with no search streams ran 300 seconds without a
fault.** Searches are required, because only non-write operations take the
unlocked path.
* Instances receiving the identical replicated write stream but carrying **no
local search traffic** ran 68 minutes without a fault, while an instance with
local searches plus the same replicated writes crashed.
`MALLOC_CHECK_` does not help: by the time of the second free the chunk has
typically been handed to another allocation, so it is a valid live chunk and
nothing is flagged at the free.
# Proposed fix
Free the value only on the path that owns it and already holds the mutex:
```diff
skip:
- if ( !BER_BVISNULL( &li->li_uuid ) ) {
- ber_memfree( li->li_uuid.bv_val );
- BER_BVZERO( &li->li_uuid );
- }
if ( lo->mask & LOG_OP_WRITES ) {
+ /* Only this path holds li_op_rmutex, which guards li_uuid */
+ if ( !BER_BVISNULL( &li->li_uuid ) ) {
+ ber_memfree( li->li_uuid.bv_val );
+ BER_BVZERO( &li->li_uuid );
+ }
/* We haven't transitioned to li_log_mutex yet */
ldap_pvt_thread_mutex_unlock( &li->li_op_rmutex );
}
```
Non-write operations never populate `li_uuid`, so they have nothing to release;
the leak ITS#10482 addressed remains fixed for the write path, which is the
only producer. Because every `goto skip` precedes the mutex acquisition, no
path can now reach the label holding the lock without releasing it.
# Related observation, not addressed by this patch
`li_old` has the same missing release. `accesslog_op_mod()` assigns `li->li_old
= entry_dup( e )` without freeing any previous value, `accesslog_response()`
moves it out only on the non-skip path, and neither `skip:` nor
`accesslog_db_destroy()` releases it. A write operation whose response takes
`goto skip` therefore strands an `Entry`, which the next write overwrites and
leaks. This requires `olcAccessLogOld` to be configured and is a leak only — no
unserialised free, so no corruption. Reported for completeness rather than
fixed here.
--
You are receiving this mail because:
You are on the CC list for the issue.
https://bugs.openldap.org/show_bug.cgi?id=10236
Issue ID: 10236
Summary: fragmentation makes mdb_page_alloc slow
Product: LMDB
Version: 0.9.31
Hardware: All
OS: All
Status: UNCONFIRMED
Keywords: needs_review
Severity: normal
Priority: ---
Component: liblmdb
Assignee: bugs(a)openldap.org
Reporter: aalekseyev(a)janestreet.com
Target Milestone: ---
Created attachment 1022
--> https://bugs.openldap.org/attachment.cgi?id=1022&action=edit
patch is relative to LMDB_0.9.31
It's a known problem that mdb_page_alloc can be slow
when the free list is large and fragmented. [1] [2] [3]
I'm not sure it's known *how* slow it can be.
In our workload we saw a fragmented freelist leading
to a pathological O(n^2) behavior.
To handle a multi-page allocation we iterate loading chunks of the
free list one by one, and at every iteration we do O(n) work to check
if the allocation can succeed.
Even small-ish allocations (tens of pages) are repeatedly hitting
this edge case, with free list growing to ~1000000, and the outer loop
taking ~2000 iterations (10^9 worth of work in total, just to allocate a
few pages).
Even though I'm sure there are ways to avoid hitting this pathological
scenario so much (avoid values larger than 4k, or fix whatever causes
fragmentation), it seems unacceptable to have a performance cliff this bad.
I made a patch to make the allocation take ~O(n*log(n)), by loading
and merging multiple chunks at once instead of doing it one-by-one.
I'd appreciate it if someone could review the patch (attached), improve it,
and/or come up with an alternative fix.
The code in `midl.c` is kinda meme-y, including a contribution from GPT-4o, but
it performs well enough to speed up our pathological workload by ~20x (which is
still ~3x away from the non-fragmented case).
Anyway, the main thing that warrants scrutiny is the change in `mdb.c`:
I understand very little about lmdb internals and I worry that loading
multiple pages at once instead of one-by-one might break something.
[1] issue #8664
[2]
https://lists.openldap.org/hyperkitty/list/openldap-bugs@openldap.org/threa…
[3]
https://lists.openldap.org/hyperkitty/list/openldap-technical@openldap.org/…
--
You are receiving this mail because:
You are on the CC list for the issue.
https://bugs.openldap.org/show_bug.cgi?id=5738
Howard Chu <hyc(a)openldap.org> changed:
What |Removed |Added
----------------------------------------------------------------------------
Status|CONFIRMED |IN_PROGRESS
--- Comment #5 from Howard Chu <hyc(a)openldap.org> ---
https://git.openldap.org/openldap/openldap/-/merge_requests/941
Down the road we should also add support for deleting / unloading backends.
The deleter can check to see if any databases have the backend in use, and
return a failure in that case. Otherwise, it would be safe to unlink it from
the list of backends and unload the module.
--
You are receiving this mail because:
You are on the CC list for the issue.
https://bugs.openldap.org/show_bug.cgi?id=8617
Quanah Gibson-Mount <quanah(a)openldap.org> changed:
What |Removed |Added
----------------------------------------------------------------------------
Resolution|--- |TEST
Status|IN_PROGRESS |RESOLVED
--- Comment #6 from Quanah Gibson-Mount <quanah(a)openldap.org> ---
RE27:
bc937c33
by Ondřej Kuzník at 2026-10-14T22:44:08+00:00
ITS#8617 slapo-rwm: Never instantiate under back-meta
--
You are receiving this mail because:
You are on the CC list for the issue.
https://bugs.openldap.org/show_bug.cgi?id=10617
Issue ID: 10617
Summary: Add an option to throttle when qtime exceeds a
threshold
Product: OpenLDAP
Version: 2.7.0
Hardware: All
OS: All
Status: UNCONFIRMED
Keywords: needs_review
Severity: enhancement
Priority: ---
Component: slapd
Assignee: bugs(a)openldap.org
Reporter: hyc(a)openldap.org
Target Milestone: ---
The first sign that a server is overloaded is that ops start spending longer
time being queued before being executed. If the incoming load continues, the
qtimes will continue growing. As such, an overload can be short-circuited
before it gets completely out of hand by rejecting operations once their qtime
exceeds a given threshold.
We should add a config keyword to set a qtime threshold. When an operation hits
the threshold it should be rejected.
Note that we already implemented a feature like this experimentally for a
private user, so we already know it is effective. In practice, qtimes on a
healthy server are much less than 1msec. A qtime of 1msec would already
indicate an overload condition.
--
You are receiving this mail because:
You are on the CC list for the issue.
https://bugs.openldap.org/show_bug.cgi?id=10581
Issue ID: 10581
Summary: Mismatch of crypt() prototype on PPC Mac OS X 10.5.8,
Leopard
Product: OpenLDAP
Version: 2.6.14
Hardware: Other
OS: Mac OS
Status: UNCONFIRMED
Keywords: needs_review
Severity: normal
Priority: ---
Component: build
Assignee: bugs(a)openldap.org
Reporter: Peter_Dyballa(a)Web.DE
Target Milestone: ---
This happens with OpenLDAP versions 2.6.13 and 2.61.4:
/opt/local/bin/gcc-mp-15 -pipe -Os -arch ppc -I../../include
-I../../include -isystem/opt/local/include/LegacySupport -I/opt/local/include
-I/opt/local/include/db48 -I/opt/local/include/openssl -DBIND_8_COMPAT
-DMDB_FDATASYNC=fsync -DMDB_DSYNC=O_SYNC -c -o passwd.o passwd.c
In file included from passwd.c:38:
../../include/ac/crypt.h:26:23: error: conflicting types for 'crypt'; have
'char *(void)'
26 | extern char *(crypt)();
| ^~~~~
In file included from /opt/local/include/LegacySupport/unistd.h:87,
from ../../include/ac/unistd.h:25,
from passwd.c:34:
/usr/include/unistd.h:424:10: note: previous declaration of 'crypt' with type
'char *(const char *, const char *)'
424 | char *crypt(const char *, const char *);
| ^~~~~
passwd.c: In function 'lutil_crypt':
passwd.c:627:20: error: too many arguments to function 'crypt'; expected 0,
have 2
627 | char *cr = crypt( key, salt );
| ^~~~~ ~~~
../../include/ac/crypt.h:26:23: note: declared here
26 | extern char *(crypt)();
| ^~~~~
make[2]: *** [passwd.o] Error 1
make[2]: Leaving directory
`/opt/local/var/macports/build/openldap-afe672a1/work/openldap-2.6.14/libraries/liblutil'
make[1]: *** [all-common] Error 1
make[1]: Leaving directory
`/opt/local/var/macports/build/openldap-afe672a1/work/openldap-2.6.14/libraries'
make: *** [all-common] Error 1
make: Leaving directory
`/opt/local/var/macports/build/openldap-afe672a1/work/openldap-2.6.14'
Command failed: cd
"/opt/local/var/macports/build/openldap-afe672a1/work/openldap-2.6.14" &&
/usr/bin/make -w all
Exit code: 2
On Leopard /usr/include/unistd.h has:
char *crypt(const char *, const char *);
So I ended applying this simple patch:
--- include/ac/crypt.h~ 2026-08-06 18:45:16.000000000 +0200
+++ include/ac/crypt.h 2026-08-30 11:21:21.000000000 +0200
@@ -22,8 +22,6 @@
/* crypt() may be defined in a separate include file */
#ifdef HAVE_CRYPT_H
# include <crypt.h>
-#else
- extern char *(crypt)();
#endif
#endif /* _AC_CRYPT_H */
This happens when using the MacPorts package manager. The issue can be viewed
(and also commented) here: https://trac.macports.org/ticket/73673.
--
You are receiving this mail because:
You are on the CC list for the issue.
https://bugs.openldap.org/show_bug.cgi?id=10621
Issue ID: 10621
Summary: back-sock: sock_over_op strips all callbacks
Product: OpenLDAP
Version: 2.6.15
Hardware: All
OS: All
Status: UNCONFIRMED
Keywords: needs_review
Severity: normal
Priority: ---
Component: backends
Assignee: bugs(a)openldap.org
Reporter: ondra(a)mistotebe.net
Target Milestone: ---
overlays set up callbacks to manage their own resources or undo changes to *op,
this quietly skips them and might(/will) leave *op contents inconsistent.
e.g. when coupled with pcache, ors_attrs ends up pointing to pcache-owned
memory, freeing which out of turn is deadly.
--
You are receiving this mail because:
You are on the CC list for the issue.
https://bugs.openldap.org/show_bug.cgi?id=10620
Issue ID: 10620
Summary: pcache: rejected olcPcacheTemplate value leaves a
freed template in the template list
Product: OpenLDAP
Version: 2.6.15
Hardware: All
OS: All
Status: UNCONFIRMED
Keywords: needs_review
Severity: normal
Priority: ---
Component: overlays
Assignee: bugs(a)openldap.org
Reporter: ondra(a)mistotebe.net
Target Milestone: ---
If the template fails parsing on the ttl/r side, temp is already put into
qm->templates but it's never removed. But it is also freed, leaving it
accessible...
A fix is coming.
--
You are receiving this mail because:
You are on the CC list for the issue.