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=8149
Quanah Gibson-Mount <quanah(a)openldap.org> changed:
What |Removed |Added
----------------------------------------------------------------------------
Status|RESOLVED |VERIFIED
Resolution|TEST |FIXED
--
You are receiving this mail because:
You are on the CC list for the issue.
https://bugs.openldap.org/show_bug.cgi?id=8884
Quanah Gibson-Mount <quanah(a)openldap.org> changed:
What |Removed |Added
----------------------------------------------------------------------------
Resolution|TEST |FIXED
Status|RESOLVED |VERIFIED
--
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 |FIXED
Status|RESOLVED |VERIFIED
--
You are receiving this mail because:
You are on the CC list for the issue.
https://bugs.openldap.org/show_bug.cgi?id=10573
Issue ID: 10573
Summary: OpenLDAP 2.7.0 fails to build with MSVC when
preprocessing symbol version maps
Product: OpenLDAP
Version: 2.7.0
Hardware: All
OS: Windows
Status: UNCONFIRMED
Keywords: needs_review
Severity: normal
Priority: ---
Component: build
Assignee: bugs(a)openldap.org
Reporter: openldap-msvc-673e3a64ee4f43(a)emalupe.com
Target Milestone: ---
OpenLDAP 2.7.0 fails to build with MSVC while generating the liblber symbol
version map. The regression appears to have been introduced by commit
8651f98a08c37bea6ee9be78acececd52bac66a0 (ITS#9739).
The new rules in libraries/liblber/Makefile.in and
libraries/libldap/Makefile.in invoke:
$(CPP) -x c -o $@ $(LT_CPPFLAGS) $(srcdir)/$(SYMBOL_VERSION_SCRIPT).in
The -x c option is specific to GCC/Clang. When Autoconf's configured C
preprocessor is MSVC (cl -E), cl ignores -x and treats the following c as an
input filename:
cl : Command line warning D9002 : ignoring unknown option '-x'
c1: fatal error C1083: Cannot open source file: 'c': No such file or
directory
make[2]: *** [Makefile:301: lber.map] Error 2
This was reproduced with OpenLDAP 2.7.0 and Visual Studio 2026/MSVC 14.51 on
Windows. OpenLDAP 2.6.13 builds successfully with the same compiler setup
because it does not preprocess these map files.
The same non-portable invocation is present in both the liblber and libldap
map-generation rules and remains in the current OPENLDAP_REL_ENG_2_7 and master
branches.
Could the hard-coded compiler language option be removed or replaced with a
configure-detected, compiler-portable way to preprocess the .map.in files?
--
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=10589
Issue ID: 10589
Summary: multiple issues with ppolicy rules and rehash in
ppolicy
Product: OpenLDAP
Version: 2.7.1
Hardware: All
OS: All
Status: UNCONFIRMED
Keywords: needs_review
Severity: normal
Priority: ---
Component: overlays
Assignee: bugs(a)openldap.org
Reporter: david.coutadeur(a)gmail.com
Target Milestone: ---
Created attachment 1202
--> https://bugs.openldap.org/attachment.cgi?id=1202&action=edit
OpenLDAP Configuration
Hello,
I have tested recently the new ppolicy features of OpenLDAP 2.7.1.
Thanks for this great work! The new features sound really exciting.
Nevertheless, I have encountered some issues during testing. As these are new
features, I don't know if this is a misconfiguration problem coming from me, or
if there are bugs.
You can find attached the configuration and data I have used.
1. I noticed that pwdPolicySubentry as static attribute is now deprecated. How
could we assign directly a specific policy to a user in the future?
2. Using olcPPolicyRuleGroupAttr attribute in a scope rule generates a coredump
while OpenLDAP tries to evaluate the assigned ppolicy. (ie during user entry
loading). See the configuration.
3. I tried to configure a regex rule for assigning password policies. OpenLDAP
crashes when trying to compute the assigned ppolicy. (when searching a user
entry matching the policy). Maybe I have not correctly defined the regex rule,
but I found no concrete example of this in documentation or unit tests.
4. Trying to run OpenLDAP in debug mode with TRACE level. (-d -1), whith given
configuration and data makes OpenLDAP crash at startup, with no special log. It
is due to scope and regex rules, as when I remove them, OpenLDAP starts
normally.
5. For pwdRehashOnBind feature, I didn't understand the "If pwdReset is set to
"TRUE"" part in the man page. The current behaviour I observed is that when
pwdReset is TRUE, the password is never rehashed. What is the intent here?
6. When I try to modify a password from a user having a directly assigned
ppolicy (pwdPolicySubentry defined statically), the password is never rehashed.
Thanks in advance for your help!
--
You are receiving this mail because:
You are on the CC list for the issue.
https://bugs.openldap.org/show_bug.cgi?id=10619
Issue ID: 10619
Summary: Add optional comments to ACL clauses
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: ---
For example:
access to filter="(objectclass=person)" attrs=userpassword
by anonymous auth /* anonymous needs authent access */
by self =wx /* users can write their own */
Support a delimited comment per "by" clause.
--
You are receiving this mail because:
You are on the CC list for the issue.
https://bugs.openldap.org/show_bug.cgi?id=10455
Issue ID: 10455
Summary: Allow handling of empty group in
memberof_saveMember_cb()
Product: OpenLDAP
Version: 2.6.12
Hardware: All
OS: All
Status: UNCONFIRMED
Keywords: needs_review
Severity: normal
Priority: ---
Component: overlays
Assignee: bugs(a)openldap.org
Reporter: roger.j.meier(a)gmx.ch
Target Milestone: ---
Created attachment 1117
--> https://bugs.openldap.org/attachment.cgi?id=1117&action=edit
Protect e_attrs with a NULL pointer test instead of an assert() to allow empty
groups
In slapd/overlays/memberof.c, the callback
memberof_saveMember_cb()
uses two assert statements for the sr_entry and its e_attrs pointer in
sequence. This makes the service abort on an empty group. If the use of
rs->sr_entry->e_attrs is just protected by a test of the e_attrs pointer, the
code does not abort and allows empty groups.
Please consider to add this patch to the official source.
It was now several month in production and did not lead to unexpected results.
--
You are receiving this mail because:
You are on the CC list for the issue.
https://bugs.openldap.org/show_bug.cgi?id=10605
Issue ID: 10605
Summary: slapo-auditlog creates the log file with insecure
permissions (0666)
Product: OpenLDAP
Version: unspecified
Hardware: All
OS: All
Status: UNCONFIRMED
Keywords: needs_review
Severity: normal
Priority: ---
Component: overlays
Assignee: bugs(a)openldap.org
Reporter: dns.spiros(a)gmail.com
Target Milestone: ---
Created attachment 1216
--> https://bugs.openldap.org/attachment.cgi?id=1216&action=edit
patch implementing the described change
The auditlog overlay opens its log file with mode 0666:
fd = open(ad->ad_logfile, flags, 0666);
Combined with a typical process umask (e.g. 022), this commonly results in
a file that is world-readable (0644) - and, with a permissive umask, can
remain world-writable as literally requested.
This is a real concern for two independent reasons:
1. Confidentiality: the audit log records the full LDIF of every Add/
Modify/Delete/ModRDN operation, including attribute values. In our own
testing, this included userPassword hashes (SSHA) appearing in clear
LDIF form inside the log file. A world-readable audit log exposes this
to any local user, regardless of the LDAP-level ACLs protecting the
same data inside the directory itself.
2. Integrity: mode 0666 as requested permits world-write. An audit trail
that any local user can modify does not reliably serve its purpose -
tampering or truncation by an unprivileged user should not be possible.
Standard security guidance for audit logging (CIS Benchmarks, DISA STIG,
PCI-DSS Requirement 10.5, ISO/IEC 27001 Annex A.12.4) consistently
requires that audit logs be protected from unauthorized read and write
access - not left to whatever the deploying administrator's umask
happens to be.
Attached is a patch (plain git diff) changing the requested mode to 0640
(owner read/write, group read, no access for others), hardcoded rather
than left to umask, consistent with how other security-sensitive files
(e.g. private keys) are typically created.
Note: this is a behavior change for any existing deployment relying on
broader access to the raw log file (e.g. a non-group member reading it
directly) - such setups would need to adjust group membership or ACLs
accordingly after upgrading.
--
You are receiving this mail because:
You are on the CC list for the issue.
https://bugs.openldap.org/show_bug.cgi?id=10599
Issue ID: 10599
Summary: Compile error on Suse and older GCCs
Product: OpenLDAP
Version: 2.7.0
Hardware: All
OS: Linux
Status: UNCONFIRMED
Keywords: needs_review
Severity: normal
Priority: ---
Component: build
Assignee: bugs(a)openldap.org
Reporter: dstoychev(a)symas.com
Target Milestone: ---
Created attachment 1213
--> https://bugs.openldap.org/attachment.cgi?id=1213&action=edit
Diff of an example solution
When building OpenLDAP on openSUSE 15.6 (which uses gcc 7) I get these errors:
```
tls_o.c: In function ‘tlso_ctx_init’:
tls_o.c:547:7: error: a label can only be part of a statement and a declaration
is not a statement
X509 *cert = OSSL_STORE_INFO_get0_CERT( info );
^~~~
tls_o.c:548:7: error: expected expression before ‘X509_STORE’
X509_STORE *store = SSL_CTX_get_cert_store( ctx );
^~~~~~~~~~
tls_o.c:549:34: error: ‘store’ undeclared (first use in this function)
if ( !X509_STORE_add_cert( store, cert ) ) {
^~~~~
tls_o.c:549:34: note: each undeclared identifier is reported only once for each
function it appears in
tls_o.c:757:6: error: a label can only be part of a statement and a declaration
is not a statement
X509 *cert = OSSL_STORE_INFO_get0_CERT(info);
^~~~
tls_o.c:758:6: error: expected expression before ‘int’
int is_ca = X509_check_ca( cert );
^~~
tls_o.c:759:12: error: ‘is_ca’ undeclared (first use in this function); did you
mean ‘ns_c_2’?
if ( !is_ca && !SSL_CTX_use_certificate( ctx, cert )) {
^~~~~
ns_c_2
tls_o.c:777:6: error: a label can only be part of a statement and a declaration
is not a statement
X509_STORE *x509_s = SSL_CTX_get_cert_store( ctx );
^~~~~~~~~~
```
It does not happen on newer GCCs.
This appears to be because on older GCC it is not allowed to have variable
assignment right after a label and in tls_o.c there are `case` labels followed
by a variable assignment (X509 *cert = OSSL_STO...) .
One way to fix this is by putting the body of the case inside curly braces {}.
Attaching an example diff that works to fix the issue for me.
--
You are receiving this mail because:
You are on the CC list for the issue.
https://bugs.openldap.org/show_bug.cgi?id=10604
Issue ID: 10604
Summary: Consumer silently loses a contiguous block of entries
under sustained write load via standard syncrepl
(refreshAndPersist), N-Way Multi-Master
Product: OpenLDAP
Version: 2.6.10
Hardware: x86_64
OS: Linux
Status: UNCONFIRMED
Keywords: needs_review
Severity: normal
Priority: ---
Component: backends
Assignee: bugs(a)openldap.org
Reporter: dns.spiros(a)gmail.com
Target Milestone: ---
Summary
(Architecture in brief: N-Way Multi-Master with 3 master nodes plus one
read-only consumer in a hub-and-spoke arrangement, each node a separate VM with
4GB RAM / 2 vCPU on VMware Workstation - full detail under "Environment"
below.)
Under a sustained burst of Add operations on a single provider (roughly
500-1000+ operations at a rate of one every 5-50ms), one or more standard
syncrepl consumers, in refreshAndPersist mode, silently fail to apply a
contiguous block of entries - typically a few dozen, occasionally into the low
hundreds. The missing entries are never retried: they are not present in the
consumer's database, contextCSN on the consumer nonetheless advances to a value
consistent with having processed them, and no error is logged.
The condition has been observed to persist indefinitely until the affected
slapd instance is restarted, at which point a full resync corrects it.
The same underlying symptom (entries reported by a consumer as "<csn> not new
enough, ignored" that were, per direct inspection, never actually applied by
that consumer) has also been reproduced in a scenario involving an unclean
shutdown ( kill -9 ) of two of three masters mid-write in an N-Way Multi-Master
mesh, with one provider (captured live in its own log, loglevel: sync )
responding "nothing changed" / an empty-cookie refreshDelete to three
simultaneous consumer sessions while its own local entry count was itself well
behind the true dataset size.
We are not certain of the exact internal root cause. We are reporting this
because the minimal reproduction below is clean, small, and - in our testing
- reliably triggers the symptom at or above roughly 1000 sequential Add
operations on a single provider/single consumer pair, with no crash, no network
impairment, and no other variable we have been able to identify as causal (see
"Ruled out" below).
Environment
slapd version: 2.6.10+dfsg-0ubuntu0.24.04.1 (Ubuntu 24.04 LTS package:
slapd -VV → @(#) $OpenLDAP: slapd 2.6.10+dfsg-0ubuntu0.24.04.1 (Sep 23 2025
16:26:39) $ )
Note on distribution packaging: this is the Ubuntu-maintained package,
not a build from upstream OpenLDAP source. We are aware that issues specific to
a distributor's packaging are generally referred back to the distributor, and
we have not yet reproduced this against a vanilla build from OpenLDAP
Git/release source. We are reporting it here first because the symptom appears
to sit in core slapo-syncprov / syncrepl logic rather than anything
Ubuntu-specific in the packaging, but we cannot rule out a packaging-specific
contribution and would welcome guidance on whether a from-source reproduction
is a prerequisite before this can be considered further.
Backend: mdb (LMDB)
Topology: N-Way Multi-Master, 3 provider/master nodes + 1 olcReadOnly:
TRUE consumer-only node, in a hub-and-spoke arrangement (full mesh -
refreshAndPersist between every pair of masters - with the read-only node as a
spoke carrying three independent syncrepl directives, one toward each master)
Hardware: 4 separate virtual machines (one per node), each 4GB RAM / 2
vCPU, on VMware Workstation
Replication mode: standard syncrepl , type=refreshAndPersist .
delta-syncrepl is deliberately not used.
TLS/auth: mutual TLS, bindmethod=sasl saslmech=EXTERNAL , certificate
CN mapped via olcAuthzRegexp to a per-node identity DN.
Relevant olcSyncRepl directive (one per peer, rid varies):
olcSyncRepl: rid=NNN provider=ldaps://<peer>:636 bindmethod=sasl
saslmech=EXTERNAL searchbase="<basedn>" type=refreshAndPersist retry="5 10 10
30 60 120 300 +" timeout=5 network-timeout=5 keepalive=10:5:2 tls_cacert=<path>
tls_cert=<path> tls_key=<path tls_reqcert=demand attrs="*,+" exattrs="memberOf"
slapo-syncprov overlay parameters:
olcSpSessionlog: 2000000
olcSpCheckpoint: 1000 10
(tested at both the above and a much tighter 50 1 - see "Ruled out")
Other overlays in the stack (order matters, listed innermost first):
memberof (index {1}, olcMemberOfRefInt: FALSE , exattrs="memberOf" on all
syncrepl directives so it is computed locally and not replicated), refint
(index {2}, referential integrity on member /
uniqueMember / manager / owner , explicitly not on memberOf ), nestgroup (index
{3}, search-time only, no writes), ppolicy (index {4}). syncprov sits outermost
(index {0}).
Search/size limits: verified unlimited both per-identity ( olcLimits
matching the mTLS peer DNs used by syncrepl ) and at the frontend database
level ( olcSizeLimit / olcTimeLimit on olcDatabase=
{-1}frontend,cn=config ). Ruled out as a factor (see below).
Minimal reproduction
This is the cleanest form in which we have triggered the symptom:
1. A single provider (one master), a single persistent refreshAndPersist
consumer. The other two masters in the mesh are stopped for this reproduction,
to remove any multi-provider interaction as a variable.
2. Database freshly initialized/empty on both sides (a clean redeploy, not
a preexisting dataset).
3. A namespace never used in any previous run (a fresh, timestamp-based DN
prefix each run), to rule out any interaction with residual state or CSN
history from prior test iterations.
4. From the provider, over a single reused connection ( ldapadd -x -c ),
add N entries of class inetOrgPerson , one every 5ms (results identical at 50ms
- see "Ruled out"):
dn: uid=<prefix>.<i>,ou=People,<basedn> objectClass: top
objectClass: inetOrgPerson
uid: <prefix>.<i> cn: Test User <i> sn: Test
userPassword: {SSHA}...
5. Wait for replication to settle (well past any configured retry
interval), then count entries matching uid=<prefix>.* independently on the
provider and on the consumer ( slapcat -a , not ldapsearch , to rule out any
client-side search limit).
Result across repeated runs: N=500 converges correctly in most runs, but not
all (see "Notes on determinism" below). N=1000 and above have, in our testing,
shown the symptom in the large majority of runs. When it occurs, the consumer
is missing a contiguous range of entries (e.g. entries 474-504 of 2000 in one
run; 1584-1604 of 2000 in a different run on a different consumer node; 299-320
of 1000 in a run using the never-before-used-namespace variant above). The size
and position of the missing range vary between runs; its contiguity does not.
Direct log evidence
1. A consumer receiving, but never applying, an entry - later resolved by
a different peer within the same run (this run happened to be an N-Way
mesh, so a second delivery path existed; in the single-provider minimal
reproduction above, no such second path exists):
syncrepl_message_to_entry: rid=101 DN: uid=crash.user.474,ou=people,dc
(no corresponding be_add / syncrepl_entry follows for rid=101 at this point -
the message is received and converted, but not applied)
Several minutes later, in the same log, from a different provider session on
the same consumer:
syncrepl_message_to_entry: rid=103 DN: uid=crash.user.474,ou=people,dc
syncrepl_entry: rid=103 uid=crash.user.474,ou=people,dc=example,dc=com
syncrepl_entry: rid=103 be_add uid=crash.user.474,ou=people,dc=example
do_syncrep2: rid=101 CSN too old, ignoring 20260921191238.368843Z#0000
do_syncrep2: rid=102 CSN too old, ignoring 20260921191238.368843Z#0000
2. A provider reporting "nothing changed" to three simultaneous consumers,
including one whose own dataset was independently confirmed (via direct slapcat
on that consumer, at the same moment) to be missing 163 of 500 expected
entries:
conn=1000 op=1 syncprov_op_search: no change, skipping log replay conn=1000
op=1 syncprov_op_search: nothing changed, finishing up initi conn=1000 op=1
syncprov_sendinfo: refreshDelete cookie=
conn=1001 op=1 syncprov_op_search: no change, skipping log replay conn=1001
op=1 syncprov_op_search: nothing changed, finishing up initi conn=1001 op=1
syncprov_sendinfo: refreshDelete cookie=
conn=1002 op=1 syncprov_op_search: no change, skipping log replay conn=1002
op=1 syncprov_op_search: nothing changed, finishing up initi conn=1002 op=1
syncprov_sendinfo: refreshDelete cookie=
conn=1002 in this excerpt authenticated as the read-only consumer node
(certificate CN mapped accordingly); the provider told it nothing had changed,
and issued a refreshDelete with an empty cookie value. At the same wall-clock
moment, slapcat -a run directly and locally on the provider itself returned 337
entries against an expected 500. The consumer's subsequent REFRESH_DELETE
phase, taking the provider's "nothing changed
/ here is nothing" at face value, deleted entries the consumer had correctly
received from a different, further-along provider - i.e. the incomplete
provider's state propagated outward and actively destroyed correct data
elsewhere in the mesh, rather than merely failing to advance it.
Ruled out
The following were each tested directly and found not to be causal:
Unclean shutdown / kill -9 : the minimal reproduction above involves no
crash of any kind. (A related, but distinct, amplification of the symptom has
been observed specifically following kill -9 of two masters in a 3-master mesh
- see log evidence #2 above - but the base symptom reproduces without any crash
at all.)
Network conditions: reproduces at full LAN speed with no induced
latency/loss. A WAN emulation profile (25ms±1ms one-way, 0.1% loss via netem )
was used in some runs and disabled in others, with the same outcome either way.
ss -ti on a stalled consumer-side connection during one incident showed a
healthy TCP session (no retransmits, app_limited , RTT sub-millisecond) - the
stall was not at the TCP layer.
olcThreads : lowered from the default to 4 on all nodes; no
change in outcome.
Connection-level timeouts: timeout and network-timeout on the syncrepl
directive (both apply only to initial connect/Bind, per slapd-config(5) , not
to an established session); olcWriteTimeout (set to 30s server-wide);
tcp-user-timeout was considered but not pursued once the TCP-layer health above
was confirmed. None of the above affected the outcome.
Clock skew: wall-clock time compared across all four nodes, dispatched
in parallel via backgrounded SSH; maximum observed spread 123ms, most of which
is attributable to SSH connection overhead itself.
Reuse of DN namespace across test runs: reproduces with a namespace
prefix that has never been used in any prior run (see "Minimal reproduction"
above).
olcLimits /search size limits: verified unlimited , both
per-identity (the mTLS peer DNs used by the syncrepl sessions themselves) and
at olcDatabase={-1}frontend,cn=config (i.e. cluster-default). No change in
outcome with either configuration.
Size of preexisting dataset: reproduces identically against a freshly
initialized, otherwise-empty database.
Write rate: 5ms and 50ms between successive Add operations produced the
same outcome at the same volume thresholds.
syncprov-checkpoint interval: tightened from 1000 10 to 50 1
with the affected pair of masters restarted cleanly beforehand; this did not
measurably change the outcome across repeated runs. (We had hypothesized, based
on the documented behavior that contextCSN is updated in memory on every write
but only persisted to the on-disk checkpoint periodically or on clean shutdown,
that an unclean shutdown recovering from a stale on-disk checkpoint might
explain the crash- correlated variant specifically. Direct testing did not
support this; we record the negative result for completeness.)
Notes on determinism
The symptom is not 100% deterministic at any volume we tested. At 2000
sequential Add operations it has occurred in the substantial majority of our
runs; at 1000 in many but not all; at 500 only in a minority of runs, with most
runs at that volume converging correctly and instantly (sub-second). We have
not identified what distinguishes a failing run from a succeeding one at the
same volume and otherwise-identical configuration; every variable we varied to
try to explain this (see "Ruled out") produced no change we could attribute to
it with confidence. We flag this explicitly because it is the main reason we
have not been able to narrow this report further before submitting it.
Related discussions we are aware of
An openldap-technical mailing list thread from 2008-2009 describes what
appears to be the same class of symptom - entries reported by a consumer as
superseded ("too old") that were, in fact, never applied - attributed there to
out-of-order commit-queue processing on the provider under concurrent writes,
with a partial fix referenced against 2.4.11/2.4.16 and a further related ITS
(#6619) mentioned as still open as of a 2011 follow-up. We have not been able
to confirm whether that specific issue is fully resolved in 2.6.x or whether
what we are seeing is a distinct, related condition.
ITS #9538 (April 2021, "Accesslog entryCSN ordering is not always
monotonous") documents non-monotonic entryCSN ordering under
concurrent operations as a live concern in a more recent timeframe.
We are aware of, and our architecture already follows, the guidance
(Quanah Gibson-Mount, openldap-technical , 24 May 2024) that standard syncrepl
is the recommended, safer replication mechanism for multi-provider environments
in OpenLDAP 2.6+, in preference to delta-syncrepl . We note this because our
minimal reproduction above is, if anything, a simpler case than the
multi-provider scenario that guidance addresses (a single provider, a single
consumer) and so does not appear to be excluded by it.
What we are asking
We are not confident enough in any single hypothesis to assert a root cause,
and would welcome guidance on:
1. Whether this is a known, already-tracked issue (our searches of the
mailing list and public ITS entries did not turn up an exact match, but we may
have missed the right terms).
2. Whether a reproduction against a from-source build of current Git
master (rather than the Ubuntu package) is a prerequisite for further triage,
and if so we are willing to attempt it.
3. Any specific loglevel combination, beyond stats sync (already enabled
throughout our testing), or any debugger/instrumentation approach the team
would recommend to capture the provider-side state at the exact moment an entry
is dropped, which we have not yet managed to capture directly (all evidence
above is inferred from surrounding log lines, not a single line that directly
shows the drop occurring).
We can provide the complete Ansible automation used to build the test
environment and drive these reproductions, full logs from any of the runs
referenced above, and are happy to run additional targeted tests against
specific hypotheses.
--
You are receiving this mail because:
You are on the CC list for the issue.
https://bugs.openldap.org/show_bug.cgi?id=10593
Issue ID: 10593
Summary: VLV response formatting can read past a stack buffer
and crash ldapsearch
Product: OpenLDAP
Version: 2.7.1
Hardware: All
OS: All
Status: UNCONFIRMED
Keywords: needs_review
Severity: normal
Priority: ---
Component: client tools
Assignee: bugs(a)openldap.org
Reporter: simon.pichugin(a)gmail.com
Target Milestone: ---
Created attachment 1209
--> https://bugs.openldap.org/attachment.cgi?id=1209&action=edit
full asan and search results
print_vlv() formats the server-provided VLV contextID into a fixed stack
buffer. If snprintf() truncates the output, its required length is passed to
the LDIF writer, which reads past the end of the buffer.
We reproduced this with an unmodified ldapsearch that explicitly requested
server-side sorting on cn and a VLV window. The server verified both request
controls, returned one valid entry, then returned SSS success and a valid VLV
success response with a large server-defined contextID. A 16 KiB contextID
produced an ASan stack-buffer-overflow. A 64 KiB contextID crashed a
non-sanitized, optimized client with SIGSEGV in all five runs.
This is different from the SSS case. A VLV contextID is an opaque value
selected by the server, and the VLV specification does not set a size limit.
The triggering response does not need to mismatch caller input or violate the
defined control sequence. The client must opt into VLV, and a 64 KiB contextID
is unusual.
Short ASan trace
ERROR: AddressSanitizer: stack-buffer-overflow
READ of size 1
#0 ldif_sput_wrap libraries/libldap/ldif.c:622
#1 ldif_put_wrap libraries/libldap/ldif.c:705
#2 tool_write_ldif clients/tools/common.c:2791
#3 print_vlv clients/tools/common.c:2264
#4 tool_print_ctrls clients/tools/common.c:2781
SUMMARY: AddressSanitizer: stack-buffer-overflow libraries/libldap/ldif.c:622
in ldif_sput_wrap
Short native trace
Program received signal SIGSEGV, Segmentation fault.
#0 ldif_sput_wrap libraries/libldap/ldif.c:579
#1 ldif_put_wrap libraries/libldap/ldif.c:705
#2 tool_write_ldif clients/tools/common.c:2791
#3 print_vlv clients/tools/common.c:2264
#4 tool_print_ctrls clients/tools/common.c:2781
#5 print_result clients/tools/ldapsearch.c:2426
#6 dosearch clients/tools/ldapsearch.c:1840
#7 main clients/tools/ldapsearch.c:1550
--
You are receiving this mail because:
You are on the CC list for the issue.
https://bugs.openldap.org/show_bug.cgi?id=10610
Issue ID: 10610
Summary: If double free if ldapi: socket name is too long
Product: OpenLDAP
Version: 2.6.14
Hardware: All
OS: All
Status: UNCONFIRMED
Keywords: needs_review
Severity: normal
Priority: ---
Component: slapd
Assignee: bugs(a)openldap.org
Reporter: ondra(a)mistotebe.net
Target Milestone: ---
slap_get_listener_addresses() frees the *sal on error and then its caller does
the same. slap_get_listener_addresses() should just zero it afterwards? Also
the error message lacks a trailing newline.
--
You are receiving this mail because:
You are on the CC list for the issue.
https://bugs.openldap.org/show_bug.cgi?id=10612
Issue ID: 10612
Summary: olcLogFile not populated on slaptest -f slapd.conf
Product: OpenLDAP
Version: 2.6.14
Hardware: All
OS: All
Status: UNCONFIRMED
Keywords: needs_review
Severity: normal
Priority: ---
Component: slapd
Assignee: bugs(a)openldap.org
Reporter: ondra(a)mistotebe.net
Target Milestone: ---
Per title, put `logfile /path/to/slapd.log` and check `cn=config` entry,
olcLogFile is missing.
--
You are receiving this mail because:
You are on the CC list for the issue.
https://bugs.openldap.org/show_bug.cgi?id=10606
Issue ID: 10606
Summary: LDAP_OPT_DEFBASE can't be reset to NULL
Product: OpenLDAP
Version: 2.6.14
Hardware: All
OS: All
Status: UNCONFIRMED
Keywords: needs_review
Severity: normal
Priority: ---
Component: libraries
Assignee: bugs(a)openldap.org
Reporter: ondra(a)mistotebe.net
Target Milestone: ---
If there is no ldo_defbase on the global context, it's impossible to unset a
connection's defbase to the default (libldap tries to strdup the NULL pointer
and checks for NULL, but that's the correct value here).
--
You are receiving this mail because:
You are on the CC list for the issue.
https://bugs.openldap.org/show_bug.cgi?id=10590
Issue ID: 10590
Summary: sssvlv rejects multiple sort keys with protocolError
in OpenLDAP 2.6.15
Product: OpenLDAP
Version: 2.6.15
Hardware: x86_64
OS: Linux
Status: UNCONFIRMED
Keywords: needs_review
Severity: normal
Priority: ---
Component: overlays
Assignee: bugs(a)openldap.org
Reporter: christian(a)roessner.email
Target Milestone: ---
Created attachment 1204
--> https://bugs.openldap.org/attachment.cgi?id=1204&action=edit
repro.go: anonymous RootDSE one-key versus two-key sorting (go-ldap v3.4.14)
OpenLDAP 2.6.15 with the sssvlv overlay rejects a valid server-side sorting
control containing two keys with:
LDAP Result Code 2 "Protocol Error": serverSort control: decoding error
A single key succeeds. Two keys fail with and without the simple paged results
control. Reproduced using go-ldap/v3 v3.4.14, including an anonymous RootDSE
base search. Tested pairs: cn + uid and uniqueIdentifier + uid, with
caseIgnoreOrderingMatch explicitly selected for both keys.
The application binds before requesting its initial sorted page, so this
presents to users as a connection failure despite successful authentication.
Environment: OpenLDAP 2.6.15 in a Linux x86_64 container (chrroessner/openldap
LTS), on an AlmaLinux 10.2 host; client Go / macOS x86_64. The configuration
loads sssvlv and enables overlay sssvlv. No sort-key limit override is
configured (default five). A pristine upstream build has not yet been run for
comparison.
REPRODUCTION
The attached repro.go sends only anonymous RootDSE searches, without reading
directory accounts or writing data. Run against a disposable local server with
sssvlv registered and a localhost LDAP listener on port 1389:
mkdir ldap-sort-repro && cd ldap-sort-repro
go mod init example.org/ldap-sort-repro
go get github.com/go-ldap/ldap/v3@v3.4.14
# Copy the attached repro.go into this directory.
go run .
Expected: valid one-key and two-key requests are accepted.
Observed on 2.6.15: the one-key case succeeds; both two-key cases return the
diagnostic above.
SUSPECTED CAUSE
In servers/slapd/overlays/sssvlv.c, build_key(), comparison of upstream tags
OPENLDAP_REL_ENG_2_6_13 and OPENLDAP_REL_ENG_2_6_15 shows the closing
ber_scanf(ber, "}") replaced with:
if (( tag = ber_peek_tag( ber, &len )) != LBER_DEFAULT ) {
rs->sr_text = "serverSort control: decoding error";
rs->sr_err = LDAP_PROTOCOL_ERROR;
return rs->sr_err;
}
The parser still shares the enclosing SortKeyList BER cursor across keys. After
the first valid key, the next key's SEQUENCE remains in the cursor, so this
check rejects a valid second key. This source change matches the observed
diagnostic. The initial sequence handling also changed from ber_scanf to
ber_skip_tag.
The intended validation appears to require enforcing the boundary of the
current SortKey SEQUENCE, while permitting the next key in SortKeyList. Simply
accepting arbitrary trailing BER would not be an appropriate fix.
Source:
https://git.openldap.org/openldap/openldap/-/blob/OPENLDAP_REL_ENG_2_6_15/s…
Potentially related: ITS#10564, which records the sss_parseCtrl tightening
(RE26 commit 6c0323aa). This report concerns rejection of valid sibling sort
keys, not the malformed-input issue reported there.
The release branch and current master source inspected on 2026-09-14 contain
the same build_key() end-of-input check. This is source inspection, not a
master runtime test. No live comparison against 2.6.13 was performed, so this
report does not claim a verified first affected release.
TEMPORARY CLIENT MITIGATION
On the exact diagnostic above, retry only an initial search (no active paging
cookie) with the primary sort key alone. Preserve that key for subsequent pages
and cursor release. This drops the UID tie-breaker for equal primary values.
Other errors remain visible.
Suggested regression coverage: two and three keys, optional
orderingRule/reverseOrder, paging continuation and cursor release, plus
malformed/trailing BER rejection.
--
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.