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=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=10600
Issue ID: 10600
Summary: Deferred referral work can be attached to the wrong
LDAP request
Product: OpenLDAP
Version: 2.7.1
Hardware: All
OS: All
Status: UNCONFIRMED
Keywords: needs_review
Severity: normal
Priority: ---
Component: libraries
Assignee: bugs(a)openldap.org
Reporter: simon.pichugin(a)gmail.com
Target Milestone: ---
When two operations receive referrals to the same connection while an anonymous
rebind is in progress, lconn_rebind_queue stores URL arrays without retaining
the request that owns each deferred referral. When the queue is drained, it
uses the request context performing the rebind, so a child search can be
attached to the wrong operation.
How to reproduce
Start two asynchronous searches on one LDAP handle. Have the origin return
referrals for both operations to the same target, timing the second referral so
it arrives while the target connection is performing the first anonymous bind.
Let the target accept the bind and return successful results for both referred
searches. Operation A receives its final result, while operation B has no
deliverable final result and reaches its caller-supplied timeout.
We reproduced this using public asynchronous APIs on one LDAP handle. The
target validated both child search IDs and base DNs. Operation A completed, but
operation B had no deliverable final result and timed out.
Short result
client_search_ids=1,2
origin_search_ids=1,2
target_verified_anonymous_bind id=4
origin_sent_b_referral_during_a_bind
target_child_search_ids=3,5
target_child_search_bases=dc=target-a,dc=target-b
operation_a_result=101 errno=0
operation_b_result=0 errno=-5 expected_timeout=-5
The test was generated using AI, so I can't share it here as per OpenLDAP's
Policy on AI Contributions. Hence, describing the process with words.
--
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=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.
https://bugs.openldap.org/show_bug.cgi?id=10611
Issue ID: 10611
Summary: slappasswd with sha2 can generate wrong hashes
Product: OpenLDAP
Version: 2.6.14
Hardware: All
OS: All
Status: UNCONFIRMED
Keywords: needs_review
Severity: normal
Priority: ---
Component: contrib
Assignee: bugs(a)openldap.org
Reporter: ondra(a)mistotebe.net
Target Milestone: ---
Depending on compiler version and build flags, SHA256 and SSHA256 can generate
the wrong hash. e.g. hashing password `test`:
- local build (./servers/slapd/slapd -Tp -o
module-load=./contrib/slapd-modules/passwd/sha2/pw-sha2.la -h '{SHA256}' -s
test): `{SHA256}AFQU1hg1xqCsA68tVBURFs7dJbJ25QC/Ancq/MQQXKM=`
- independent (echo -n 'test' | openssl dgst -sha256 -binary | base64):
`n4bQgYhMfWWaL+qgxVrQFaO/TxsrC4Is0V1sFbDwCgg=`
The issue appears to be pointer aliasing violation that the compiler optimises
through. Other implementations already carry a fix, so we can apply that.
None of the known packages (Symas, LTB, Debian) that I tested were affected,
they emit the correct hash but this appears to have been quietly patched around
by setting `OPT=-fno-strict-aliasing` without a report I can see here[0]
[0].
https://salsa.debian.org/openldap-team/openldap/-/blob/master/debian/rules?…
--
You are receiving this mail because:
You are on the CC list for the issue.
https://bugs.openldap.org/show_bug.cgi?id=10608
Issue ID: 10608
Summary: back-mdb: cached per-thread read txns of a database
deleted via cn=config are never released -> err=80
"internal error" on new databases
Product: OpenLDAP
Version: 2.6.13
Hardware: x86_64
OS: Linux
Status: UNCONFIRMED
Keywords: needs_review
Severity: normal
Priority: ---
Component: backends
Assignee: bugs(a)openldap.org
Reporter: coding(a)markus-mazurczak.de
Target Milestone: ---
Created attachment 1218
--> https://bugs.openldap.org/attachment.cgi?id=1218&action=edit
Patch: 0002-back-mdb-purge-reader-keys-on-db-close
SUMMARY
=======
When a back-mdb database is removed at runtime with an ldapdelete of its
olcDatabase entry in cn=config, only the thread that performs the delete
releases its cached read transaction for that database. Every other worker
thread keeps its cached entry for good: the key is the (now freed) MDB_env
pointer and the value a read txn of the closed environment.
The leak accumulates with every database that is created, read and deleted.
Once a worker thread has collected about 32 such dead entries, operations on
any NEWLY created database fail in that thread with
RESULT tag=97 err=80 qtime=0.000020 etime=0.000082 text=internal error
(on glibc builds the result code is err=12, see below). Databases whose
entry was cached before the slots filled up keep working, so the failure
looks random: it hits only new databases and only some threads.
This is the typical life cycle of a multi-tenant directory that provisions
one mdb database per tenant through cn=config. In our production server the
first bind to a freshly provisioned tenant failed sporadically with err=80
after a few hundred tenant create/delete cycles; restarting slapd made it go
away until the slots filled again.
ANALYSIS
========
mdb_opinfo_get() caches one read txn per worker thread and database in the
thread-pool context, keyed by the environment pointer
(servers/slapd/back-mdb/id2entry.c, openldap-2.6.13):
790 if ( ldap_pvt_thread_pool_getkey( ctx, mdb->mi_dbenv, &data, NULL ) )
{
... mdb_txn_begin( ..., MDB_RDONLY, &moi->moi_txn ) ...
808 if ( ( rc = ldap_pvt_thread_pool_setkey( ctx, mdb->mi_dbenv,
data, mdb_reader_free, NULL, NULL ) ) ) {
...
812 Debug( LDAP_DEBUG_ANY, "mdb_opinfo_get: thread_pool_setkey
failed err (%d)\n", rc );
return rc;
}
} else {
moi->moi_txn = data;
818 renew = 1;
}
...
827 rc = mdb_txn_renew( moi->moi_txn );
828 assert(!rc);
When the database is closed, mdb_db_close() releases only the key of the
calling thread (servers/slapd/back-mdb/init.c):
349 mdb_reader_flush( mdb->mi_dbenv );
mdb_reader_flush() (id2entry.c:687) looks up ldap_pvt_thread_pool_context(),
i.e. the current thread. The only place in back-mdb that clears the key in
all threads is the olcDbDirectory handler (config.c:630), which calls
ldap_pvt_thread_pool_purgekey() -- documented as "May only be called while
the pool is paused" (libraries/libldap/tpool.c:1424).
Deleting a database through cn=config does not purge. config_back_delete()
pauses the server and then calls
bconfig.c:6881 ce->ce_be->bd_info->bi_db_close( ce->ce_be, NULL );
bconfig.c:6884 backend_destroy_one( ce->ce_be, 1);
so mdb_db_close() runs while the pool is paused, but still only flushes the
deleting thread. Every other thread that ever read from that database keeps
a dangling {env pointer -> txn} entry.
Two consequences, both observed:
1. Slot exhaustion. A thread context has MAXKEYS = 32 key slots
(tpool.c:52). Dead entries are never removed, so after roughly 32 deleted
databases per thread, ldap_pvt_thread_pool_setkey() returns ENOMEM
(tpool.c:1412) for every new database. mdb_opinfo_get() returns that value
and mdb_bind() answers with it and "internal error" (bind.c:62). The log
shows
mdb_opinfo_get: thread_pool_setkey failed err (12)
RESULT tag=97 err=12 ... text=internal error
The raw errno ends up as the LDAP result code (12 is
unavailableCriticalExtension), which is a small secondary issue.
2. Stale txn reuse (ABA). With an allocator that hands out a freed address
again right away (musl), the MDB_env of a newly created database often
gets the address of a deleted one. getkey() then finds the dead entry,
mdb_opinfo_get() takes the renew path with a txn that belongs to the closed
environment, and the assert(!rc) after mdb_txn_renew() is compiled out in
release builds. The next access fails; with a debug print in mdb_bind()
we see
bind/dn2entry: rc=MDB_BAD_DBI: The specified DBI handle was
closed/changed unexpectedly(-30780)
RESULT tag=97 err=80 ... text=internal error
This is what production (Alpine) shows. Continuing with a txn object of a
destroyed environment is also a memory-safety problem in itself; we did
not see an AddressSanitizer report in our runs (ASan's quarantine prevents
the address reuse), so we do not claim more than the observed behaviour.
REPRODUCTION
============
Self-contained script: stale-reader-test.sh (attached). It needs slapd,
slapadd, slappasswd and the ldap client tools, and runs in a throw-away
container:
1. start slapd with a cn=config database and olcThreads: 2 (or 16)
2. create a long-lived database "dc=steady" and bind to it once
3. repeat 60 times:
- ldapadd olcDatabase=mdb (olcSuffix dc=cN, own olcDbDirectory)
- add dc=cN and cn=admin,dc=cN (simpleSecurityObject)
- 8 simple binds as cn=admin,dc=cN (spread over the worker threads)
- one bind to dc=steady
- ldapdelete the olcDatabase entry of dc=cN, remove its directory
4. count failed binds
# Alpine 3.23 package build of 2.6.13, back_mdb as module
podman run --rm --entrypoint bash -v ./stale-reader-test.sh:/t.sh:ro,z \
-e CYCLES=60 -e BINDS=8 -e MODULES=1 <image> /t.sh
# source build (back-mdb built in), e.g. with AddressSanitizer
podman run --rm -v ./stale-reader-test.sh:/t.sh:ro,z \
-e CYCLES=60 -e BINDS=8 -e THREADS=4 -e MODULES=0 <image> bash /t.sh
Results. All builds contain the ITS#10607 patch (without it, concurrent
cn=config writes crash slapd); the pairs differ only in the attached patch:
build threads cycles x binds failed binds first
---------------------------- ------- -------------- ------------- -----
Alpine 2.6.13 (musl) 2 60 x 8 137 (err=80) 34
Alpine 2.6.13 (musl) 16 200 x 40 4079 (err=80) 34
Alpine 2.6.13 + patch 16 200 x 40 0 -
Alpine 2.6.13 + patch 2 60 x 8 0 -
source build, glibc + ASan 4 60 x 8 57 (err=12) 51
source build, glibc + ASan + patch 4 60 x 8 0 -
Binds to the long-lived database dc=steady never failed in any run, which is
why the problem is easy to miss: only new databases are affected.
On our production server (Alpine, 16 threads) the same sequence with 80
cycles x 40 binds against the patched build ran 3200 binds without a single
failure and without any err=80 in the log.
The first failure appears after about 30 cycles because every cycle leaves
one dead key in each thread that served a read from that database, and a
thread context also holds a few unrelated keys (e.g. the search stack).
PATCH
=====
In mdb_db_close(), purge the key from all threads when the pool is paused,
which is the case when the database is deleted via cn=config; otherwise keep
the existing per-thread flush (shutdown, slap tools). This mirrors what the
olcDbDirectory handler in back-mdb/config.c already does.
if ( ( slapMode & SLAP_SERVER_MODE ) &&
ldap_pvt_thread_pool_pausing( &connection_pool ) > 0 )
ldap_pvt_thread_pool_purgekey( mdb->mi_dbenv );
else
mdb_reader_flush( mdb->mi_dbenv );
ldap_pvt_thread_pool_purgekey() calls the key's free function
(mdb_reader_free -> mdb_txn_abort) for every thread while the environment is
still open, so the cached read txns are ended before mdb_env_close() instead
of outliving it.
The patch applies with -p1 to openldap-2.6.13 (on its own or on top of the
ITS#10607 patch), to OPENLDAP_REL_ENG_2_6 and to master (hunk offset 30
lines).
Verification with the patch applied:
- stale-reader-test.sh: 0 failed binds (tables above), AddressSanitizer clean
- the concurrent cn=config load test from ITS#10607 (4 parallel
create/overlay/modify/delete loops plus continuous binds to an unrelated
database), which exercises the new purgekey path under concurrency:
Alpine build, 150 s: 13083 database cycles, 0 of 19606 binds rejected,
no crash
glibc + ASan, 120 s: 9579 database cycles, 0 of 14565 binds rejected,
AddressSanitizer clean
- server restart with existing databases and our orphaned-directory start
test: unchanged behaviour
Open points, happy to adjust:
- The check could live in config_back_delete() instead (it knows it is
removing a database while paused); putting it into mdb_db_close() keeps
the knowledge about the key inside back-mdb, like config.c:630.
- mdb_bind() and friends return the raw errno from mdb_opinfo_get() as LDAP
result code (err=12). Mapping it to LDAP_OTHER would be more accurate;
not part of this patch.
--
You are receiving this mail because:
You are on the CC list for the issue.
https://bugs.openldap.org/show_bug.cgi?id=10607
Issue ID: 10607
Summary: slapd: use-after-free in
config_back_delete/_modify/_modrdn with concurrent
cn=config writes
Product: OpenLDAP
Version: 2.6.13
Hardware: x86_64
OS: Linux
Status: UNCONFIRMED
Keywords: needs_review
Severity: normal
Priority: ---
Component: slapd
Assignee: bugs(a)openldap.org
Reporter: coding(a)markus-mazurczak.de
Target Milestone: ---
Created attachment 1217
--> https://bugs.openldap.org/attachment.cgi?id=1217&action=edit
Patch: re-resolve config entry after pause and write lock (against 2.6.13, -p1)
SUMMARY
=======
back-config resolves the target CfEntryInfo *before* it pauses the server.
Another cn=config write runs to completion inside that window and frees the
entry, so the first operation continues on freed memory. With two clients
writing to cn=config concurrently, slapd reliably crashes with SIGSEGV.
This is not a theoretical race. It is the blocking defect for using cn=config
as the control plane of a multi-tenant directory service: every tenant we
create or remove is a handful of cn=config writes, and two overlapping
provisioning flows take the server down.
ANALYSIS
========
config_back_modify, config_back_modrdn and config_back_delete all do:
ce = config_find_base( cfb->cb_root, &op->o_req_ndn, &last, op );
... cheap validation on ce ...
slap_pause_server();
ldap_pvt_thread_rdwr_wlock( &cfb->cb_rwlock );
... use ce ...
In openldap-2.6.13 (servers/slapd/bconfig.c):
function config_find_base slap_pause_server wlock
config_back_modify 6364 6439 6446
config_back_modrdn 6525 6668 6675
config_back_delete 6791 6825 6828
Between the lookup and the pause, this thread holds no lock on the config
tree. A second cn=config write that is already past its own pause completes,
reaches
ce->ce_entry->e_private = NULL;
entry_free( ce->ce_entry );
ch_free( ce ); /* bconfig.c:6946 */
and the first thread then dereferences the freed CfEntryInfo at bconfig.c:6835.
config_back_add is not affected: it resolves the entry after the lock, inside
config_add_internal.
The read paths are correct -- config_back_search and config_back_compare take
cb_rwlock for reading *before* calling config_find_base.
Note that the thread pool itself is fine. PAUSE_ARG(DO_PAUSE) subtracts
ltp_pause, so a second pauser first goes GO_IDLE, waits out the running pause
and only then pauses; assert(!pool->ltp_pause) in handle_pause() holds. The
problem is purely the stale pointer taken before the pause.
REPRODUCTION
============
Build slapd with AddressSanitizer (glibc; ASan does not work under musl):
CFLAGS="-g -O1 -fno-omit-frame-pointer -fsanitize=address" \
LDFLAGS="-fsanitize=address" \
./configure --enable-mdb=yes --enable-dynlist=yes --enable-spasswd \
--with-cyrus-sasl --with-tls=openssl --enable-crypt
make depend && make
Start slapd with a cn=config database, then run four concurrent loops that
each add an olcDatabase=mdb entry, add an olcOverlay=dynlist child, delete
olcRootPW, and delete both again. The crash appears within seconds.
AddressSanitizer output:
==15==ERROR: AddressSanitizer: heap-use-after-free
READ of size 4 at 0x5060000681a0 thread T11
#0 config_back_delete servers/slapd/bconfig.c:6835
#1 fe_op_delete servers/slapd/delete.c:181
#2 do_delete servers/slapd/delete.c:95
#3 connection_operation servers/slapd/connection.c:1137
freed by thread T9 here:
#0 free
#1 ber_memfree_x libraries/liblber/memory.c:152
#2 ch_free servers/slapd/ch_malloc.c:139
#3 config_back_delete servers/slapd/bconfig.c:6946
previously allocated by thread T2 here:
#0 calloc
#1 ber_memcalloc_x libraries/liblber/memory.c:283
#2 ch_calloc servers/slapd/ch_malloc.c:104
#3 config_add_internal servers/slapd/bconfig.c:5615
#4 config_back_add servers/slapd/bconfig.c:5849
Without the sanitizer, the stock 2.6.13 build dies with SIGSEGV after roughly
175 add/delete cycles under the same load. With the patch below applied, the
same load ran 14402 cycles in 150 seconds with no crash and a clean ASan run.
PATCH
=====
Resolve the entry again once the pause and the write lock are held, and answer
noSuchObject if it is gone. The pre-pause lookup is kept, so a request that is
going to fail anyway still does not pause the whole server.
The patch is attached as
0001-cn-config-resolve-entry-after-pause.patch and applies with -p1 to
openldap-2.6.13.
For config_back_modrdn the re-resolved entry's ce_type is compared with the
validated one; ixold is derived from op->o_req_ndn, so it stays consistent.
I am happy to adjust the approach -- taking cb_rwlock for reading around the
pre-pause validation as well would close the (much narrower) window in which
the early checks themselves touch a freed entry.
--
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=10609
Issue ID: 10609
Summary: slapd SIGABRT/core dump when modifying
olcRemoteAuthDNAttribute in cn=config for
olcOverlay={0}remoteauth
Product: OpenLDAP
Version: 2.6.8
Hardware: x86_64
OS: Linux
Status: UNCONFIRMED
Keywords: needs_review
Severity: normal
Priority: ---
Component: slapd
Assignee: bugs(a)openldap.org
Reporter: alain.raimbault(a)thalesgroup.com
Target Milestone: ---
Vendor Escalation Report — OpenLDAP remoteauth crash & AD bind failures
Vendor/Product: OpenLDAP (slapd + remoteauth overlay/module)
Module: /usr/lib64/openldap/remoteauth.so.2.0.200 (packaged by
openldap-servers)
OS: RHEL/CentOS 9 (el9 based; from environment)
Symptoms:
slapd SIGABRT/core dump when modifying olcRemoteAuthDNAttribute in cn=config
for olcOverlay={0}remoteauth
AD authentication fails with LDAP bind error Invalid credentials (49) / AD data
52e for multiple username formats
1. Environment Details
1.1 Installed OpenLDAP RPMs
markdown
content_copy
openldap-2.6.8-4.el9.x86_64
openldap-servers-2.6.8-2.el9.x86_64
openldap-clients-2.6.8-4.el9.x86_64
1.2 remoteauth module packaging
markdown
content_copy
rpm -qf /usr/lib64/openldap/remoteauth.so.2.0.200
openldap-servers-2.6.8-2.el9.x86_64
1.3 Module binary location
markdown
content_copy
/usr/lib64/openldap/remoteauth.so -> remoteauth.so.2.0.200
/usr/lib64/openldap/remoteauth.so.2.0.200
1.4 Shared library dependencies
(From ldd)
libldap.so.2, libsasl2.so.3, libssl.so.3, libcrypto.so.3, libevent-2.1.so.7,
etc.
2. Configuration Under Investigation
2.1 Overlay entry (active config)
Example retrieved via:
markdown
content_copy
ldapsearch -Q -LLL -Y EXTERNAL -H ldapi:/// \
-b 'olcOverlay={0}remoteauth,olcDatabase={2}mdb,cn=config' -s base \
'(objectClass=*)' '*'
Key attributes observed:
olcRemoteAuthDNAttribute: cn
olcRemoteAuthDomainAttribute: associatedDomain (as per your dump)
olcRemoteAuthMapping: ntdsi ldap://laselainfadc01p.ntdsi.airsystems.thales and
mapping to ntdsi.airsystems.thales DC URL
olcRemoteAuthTLS: tls_reqcert=never
olcRemoteAuthStore: FALSE
2.2 User entry (local LDAP)
User searched with:
objectClass: domainRelatedObject
associatedDomain: ntdsi
plus cn and uid values
Verified by:
markdown
content_copy
ldapsearch -x -LLL -D 'cn=ldapadmin,...' -W \
-b 'cn=A0007972-R(a)rah-r.thales,ou=Users,...' \
-s base '(objectClass=*)' objectClass associatedDomain
Result:
objectClass: domainRelatedObject
associatedDomain: ntdsi
3. Reproduction of the Crash (Primary Defect)
3.1 How to reproduce
slapd is running with remoteauth overlay configured (operational at least prior
to this change).
Modify the overlay attribute:
Change: olcRemoteAuthDNAttribute from cn to uid
Example LDIF used:
ldif
content_copy
dn: olcOverlay={0}remoteauth,olcDatabase={2}mdb,cn=config
changetype: modify
replace: olcRemoteAuthDNAttribute
olcRemoteAuthDNAttribute: uid
Applied with:
bash
content_copy
ldapmodify -Q -Y EXTERNAL -H ldapi:/// -f
change-remoteauth-dnattribute-to-uid.ldif
3.2 Result
ldapmodify returns: “Can't contact LDAP server (-1)”
slapd.service fails with core-dump / SIGABRT
4. Crash Evidence (Backtrace)
From coredumpctl info slapd stack trace (repeated across multiple dumps):
Crash location:
remoteauth_cf_gen (remoteauth.so.2 + 0x2aee)
called from: config_set_vals → config_parse_add → remoteauth_cf_gen
Relevant frames:
markdown
content_copy
#5 slap_bv2ad
#6 slap_str2ad
#7 remoteauth_cf_gen (remoteauth.so.2 + 0x2aee)
#8 config_set_vals
#9 config_parse_add
#10 config_modify_add...
Signal:
SIGABRT (signal 6)
aborted by __assert_fail path
4.3 Service status snippet
From systemctl status slapd:
“Active: failed (Result: core-dump) … Main PID dumped, signal=ABRT”
5. Additional Observation: AD Bind Failures (Secondary Issue)
Multiple direct AD DC bind tests all fail with:
ldap_bind: Invalid credentials (49)
Additional info: AcceptSecurityContext error, data 52e
Tests performed against:
ldap://laselainfadc01p.ntdsi.airsystems.thales
Tried DN/username forms:
A0007972-R(a)rah-r.thales
a0007972-r
a0007972-r(a)ntdsi.airsystems.thales
NTDSI\a0007972-r
DN-style bind attempts (e.g., cn=a0007972-r,ou=Users,...)
All failed with data 52e.
6. Evidence of remoteauth module being loaded
From config dump / module list:
cn=module{0},cn=config includes olcModuleLoad: {0}remoteauth
module path: /usr/lib64/openldap
7. Expected vs Actual Behavior
Expected
Changing olcRemoteAuthDNAttribute from cn to uid should:
either succeed cleanly, or
return a validation/config error to LDAP client
slapd should never crash on a config value update.
Actual
slapd SIGABRTs and produces a core dump during config modify.
Crash occurs inside remoteauth_cf_gen() and OpenLDAP attribute string parsing
(slap_str2ad, slap_bv2ad).
8. Impact
Any attempt to update olcRemoteAuthDNAttribute to uid breaks the LDAP server
(core-dump).
This prevents applying the likely correct attribute mapping for AD username
derivation.
In parallel, AD binds return 52e for all tested identifiers/password
combinations, which remains unresolved.
9. Suggested Areas for Vendor Investigation
Bug/defect in remoteauth overlay when processing olcRemoteAuthDNAttribute value
uid
especially during config generation (remoteauth_cf_gen)
should validate/return error instead of triggering assert() and aborting
Potential ABI/config parsing mismatch between:
openldap vs openldap-servers builds (mixed releases)
Validate correct handling of attribute types / syntax in config parsing:
crash path suggests OpenLDAP attribute-name parsing (slap_str2ad / slap_bv2ad)
is being fed invalid/unsupported content during overlay config generation
10. Files/Artifacts to Attach (for faster vendor triage)
coredumpctl info slapd output(s)
stack trace text (as included above)
LDIF used for the config modify (the LDIF file contents)
slapcat -n 0 section around:
olcOverlay={0}remoteauth
cn=module{0},cn=config
rpm -qi for openldap-servers
ldd /usr/lib64/openldap/remoteauth.so.2.0.200 output
system logs around the modify attempt:
journalctl -u slapd -b -n 200 --no-pager
--
You are receiving this mail because:
You are on the CC list for the issue.