https://bugs.openldap.org/show_bug.cgi?id=10266
Issue ID: 10266
Summary: Adopt broader RFC4511 NoD interpretation on lloadd's
client side
Product: OpenLDAP
Version: unspecified
Hardware: All
OS: All
Status: UNCONFIRMED
Keywords: needs_review
Severity: normal
Priority: ---
Component: lloadd
Assignee: bugs(a)openldap.org
Reporter: ondra(a)mistotebe.net
Target Milestone: ---
Server side, lloadd has long implemented a broad interpretation of NoD
unsolicited response handling: when the message is issued, no new requests are
accepted on the session however the client and server are both free to keep the
session open if there are any operations that have not resolved yet. The server
is still expected to close the connection as soon as no operations are still
pending.
This seems to interoperate with known clients. Those that want to will close
the session immediately, unaware of this possibility, those that also want to
interpret RFC 4511 this way can choose to wait for existing operations to
resolve.
This ticket is to track the lloadd's implementation of the client side of this
- when receiving a NoD message, we don't close the connection
immediately+unconditionally either but are willing to wait.
Related functionality:
- if connection was a bind connection processing a multi-stage SASL bind, the
bind should fail if/when the client attempts to progress it
- clients assigned to this connection through coherence at least 'connection'
are also marked closing
--
You are receiving this mail because:
You are on the CC list for the issue.
https://bugs.openldap.org/show_bug.cgi?id=10502
Issue ID: 10502
Summary: feature proxyauthz should become default
Product: OpenLDAP
Version: unspecified
Hardware: All
OS: All
Status: UNCONFIRMED
Keywords: needs_review
Severity: normal
Priority: ---
Component: lloadd
Assignee: bugs(a)openldap.org
Reporter: ondra(a)mistotebe.net
Target Milestone: ---
Most stock lloadd deployments are against OpenLDAP, but since "feature" keyword
doesn't allow "off-switches", proxyauthz has had to have been an "off by
default" thing. This is insecure and probably a footgun for many.
We'll have to introduce a new configuration option to do this, one that allows
for gentler evolution.
This also allows us to reject a bindconf+no_proxyauthz combinations if we force
the rare admins who genuinely need it (no proxyauthz support in upstreams) to
opt in explicitly.
--
You are receiving this mail because:
You are on the CC list for the issue.
https://bugs.openldap.org/show_bug.cgi?id=10546
Issue ID: 10546
Summary: ldap_explode_dn result is not released in the
interactive test client
Product: OpenLDAP
Version: unspecified
Hardware: All
OS: All
Status: UNCONFIRMED
Keywords: needs_review
Severity: normal
Priority: ---
Component: libraries
Assignee: bugs(a)openldap.org
Reporter: 1489378385(a)qq.com
Target Milestone: ---
URL:
https://github.com/openldap/openldap/blob/8afcbe9e04f1a40f03cea51c57418b05f…
Description:
Source revision:
8afcbe9e04f1a40f03cea51c57418b05f86e8f44
Affected file:
libraries/libldap/test.c
Affected lines:
472-478
Source-level observation:
The interactive test client's E command calls ldap_explode_dn() and stores
the returned NULL-terminated array in exdn. It prints the array elements and
then leaves the switch case without releasing exdn.
According to ldap_get_dn(3), the result returned by ldap_explode_dn() should
be released with ldap_value_free().
The only reference to the allocated array is overwritten the next time the E
command is executed.
Steps to verify:
1. Build the libraries/libldap interactive test client with AddressSanitizer
or LeakSanitizer enabled.
2. Start the test client.
3. Execute the E command and enter a valid DN, for example:
cn=test,dc=example,dc=com
4. Repeat the E command several times.
5. Exit the client and inspect the sanitizer report or monitor its memory use.
Actual result:
Each E command allocates an exploded DN array that remains allocated until
the process terminates.
Expected result:
The exploded DN array should be released after all of its elements have been
printed.
Impact:
Repeated use of the E command increases the test client's memory consumption.
Validation status:
Confirmed by source review at the revision above. Runtime sanitizer validation
is pending.
--
You are receiving this mail because:
You are on the CC list for the issue.
https://bugs.openldap.org/show_bug.cgi?id=10517
Issue ID: 10517
Summary: mbedTLS doesn't verify certificate in reqcert try mode
Product: OpenLDAP
Version: unspecified
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: ---
When setting the authmode to OPTIONAL, mbedTLS documentation[0] says that it's
the user's responsibility to check the verification result if they care.
`reqcert try` does care, but mbedtls_ssl_get_verify_result is never called in
that case and so an invalid cert is not detected (easy to test by changing line
204 in test067).
[0].
https://os.mbed.com/teams/sandbox/code/mbedtls/docs/tip/ssl_8h.html#a569528…
--
You are receiving this mail because:
You are on the CC list for the issue.
https://bugs.openldap.org/show_bug.cgi?id=9640
Issue ID: 9640
Summary: ACL privilege for MOD_INCREMENT
Product: OpenLDAP
Version: unspecified
Hardware: All
OS: All
Status: UNCONFIRMED
Severity: normal
Priority: ---
Component: slapd
Assignee: bugs(a)openldap.org
Reporter: michael(a)stroeder.com
Target Milestone: ---
I'm using LDAP write operations with MOD_INCREMENT with pre-read-control for
uidNumber/gidNumber generation.
I'd like to limit write access to an Integer attribute "nextID" to
MOD_INCREMENT, ideally even restricting the de-/increment value.
(Uniqueness is achieved with slapo-unique anyway but still I'd like to avoid
users messing with this attribute).
IMHO the ideal solution would be a new privilege "i".
Example for limiting write access to increment by one and grant read access for
using read control:
access to
attrs=nextID
val=1
by group=... =ri
Example for decrementing by two without read:
access to
attrs=nextID
val=-2
by group=... =i
--
You are receiving this mail because:
You are on the CC list for the issue.
https://bugs.openldap.org/show_bug.cgi?id=7441
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=10437
Issue ID: 10437
Summary: Let service manager know when we're about to pause
Product: OpenLDAP
Version: unspecified
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: ---
Entering a pause and waiting for a task (back-ldap result, ...) to finish looks
exactly the same as slapd hanging, even cn=monitor will stop responding.
If the sysadmin has systemd or another manager that we can tell this is about
to happen, we might as well try.
--
You are receiving this mail because:
You are on the CC list for the issue.
https://bugs.openldap.org/show_bug.cgi?id=10436
Issue ID: 10436
Summary: Assorted fixes for 2.7
Product: OpenLDAP
Version: unspecified
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: ---
Minor cleanups for unitialised variables, typos and annoying compiler warnings.
--
You are receiving this mail because:
You are on the CC list for the issue.
https://bugs.openldap.org/show_bug.cgi?id=10500
Issue ID: 10500
Summary: back-ldif/ldif.c crc32() should be static — collides
with zlib's crc32 at link time
Product: OpenLDAP
Version: 2.6.13
Hardware: All
OS: Linux
Status: UNCONFIRMED
Keywords: needs_review
Severity: normal
Priority: ---
Component: build
Assignee: bugs(a)openldap.org
Reporter: ionutn(a)gmail.com
Target Milestone: ---
In servers/slapd/back-ldif/ldif.c, the helper crc32() (defined at line 393 in
2.6.13) is declared with external linkage:
unsigned int
crc32(const void *vbuf, int len)
{
...
}
It is only used inside that file (lines 470 and 511). It has the same name as
zlib's public crc32() but a completely different signature and semantics — it's
an internal helper for hashing LDIF entry filenames, unrelated to zlib's
CRC-32-IEEE-802.3.
This is a latent symbol collision that has been present for many years. Until
recently, ld silently resolved zlib's crc32 references (from inflate.c.o /
deflate.c.o, transitively pulled in by libcrypto) against back-ldif's crc32 —
because back-ldif is linked first — and libz.a's crc32.c.o was never pulled in.
NEWLY EXPOSED BY zlib 1.3.2: zlib 1.3.2's deflate.c added an internal call to
crc32_z, a symbol defined only in libz.a(crc32.c.o). When slapd is linked
against libssl/libcrypto + libz.a, ld now has to pull in crc32.c.o to satisfy
crc32_z, which brings the public crc32 along — colliding with back-ldif's:
libz.a(crc32.c.o): In function `crc32':
zlib-1.3.2/crc32.c:950: multiple definition of `crc32'
libbackends.a(ldifldif.o):.../back-ldif/ldif.c:399: first defined here
collect2: error: ld returned 1 exit status
Reproduction: any build that links slapd (or anything pulling in libbackends.a)
together with libssl + libcrypto + zlib 1.3.2's libz.a. Hit on Linux/aarch64
and Linux/x86_64 with GCC 8.5 + ld from binutils.
Suggested fix (one keyword)
---------------------------
Mark the function static:
-unsigned int
+static unsigned int
crc32(const void *vbuf, int len)
Tested locally on 2.6.13: slapd builds cleanly against zlib 1.3.2 with this
change. No behaviour change since the function is only ever called from
within ldif.c.
Versions
--------
- Confirmed: 2.6.13 (latest) and 2.6.10. The code path appears identical going
back many releases.
- ld behaviour change is triggered specifically by zlib 1.3.2 (released
Feb 2026).
--
You are receiving this mail because:
You are on the CC list for the issue.
https://bugs.openldap.org/show_bug.cgi?id=10508
Issue ID: 10508
Summary: syncrepl: be_modrdn / message_to_op called without
orm_no_opattrs=1, modifiersName clobbered by consumer
rootdn
Product: OpenLDAP
Version: 2.6.13
Hardware: i386
OS: Linux
Status: UNCONFIRMED
Keywords: needs_review
Severity: normal
Priority: ---
Component: slapd
Assignee: bugs(a)openldap.org
Reporter: apostnikov(a)gmail.com
Target Milestone: ---
Created attachment 1150
--> https://bugs.openldap.org/attachment.cgi?id=1150&action=edit
0001-syncrepl-set-orm_no_opattrs-for-backend-ops.patch
When syncrepl applies provider changes locally it invokes the backend
through be_add / be_modify / be_modrdn / be_delete, carrying the
provider's operational attributes in the modlist. Without
`op->orm_no_opattrs = 1` the backend (verified on back-mdb;
servers/slapd/back-mdb/modrdn.c:76 and modify.c:619) calls
slap_mods_opattrs(), which auto-injects a
`replace modifiersName=<op->o_dn>` mod whenever modifiersName is not
already present in the modlist. For syncrepl, `op->o_dn` is the
consumer's local rootdn (set at syncrepl.c:2165 — `op->o_dn =
op->o_bd->be_rootdn`), so the consumer ends up with
`modifiersName=cn=<consumer-rootdn>` instead of the provider's value.
ITS#4820 originally papered over this in syncrepl_diff_entry() with a
kludge that always forced modifiersName/modifyTimestamp into the diff
(`if (*mods && (modifiersName||modifyTimestamp)) attr_cmp(NULL,new)`).
ITS#10250 commit 87933f3e removed that kludge with the rationale "the
mod is being passed to the backend with orm_no_opattrs these days".
But two code paths in syncrepl.c never actually set orm_no_opattrs:
1. syncrepl_entry() refresh-mode rename-with-content path
(around line 4788 on master): the parallel be_modify branch sets
`orm_no_opattrs = 1/0` around its call; the be_modrdn branch
does not.
2. syncrepl_message_to_op() (delta-sync log replay; whole function
from line 3180 on master): none of the four backend dispatches
(be_add, be_modify, be_modrdn, be_delete) set the flag.
Once 87933f3e removed the kludge, the auto-inject surfaced whenever
provider and consumer happened to already agree on modifiersName so
syncrepl_diff_entry produced no mod for it. Whether that happens for a
given entry depends on attribute iteration order in the diff
(attribute hash bucket order), which is why x86_64/aarch64 mostly
mask the bug while x86/armv7/armhf/s390x (QEMU builders) trigger it
deterministically and ppc64le sees it as flake in test063.
Worst case the consumer ends up with the provider's entryCSN (the
modrdn bumped it) but the wrong modifiersName, and ITS#10358's
assert-retry then sees identical entryCSN, takes no further action,
and the corruption is permanent until something else updates the
entry.
Reproducer
----------------------------------------------------------------
Native: 32-bit or big-endian build of HEAD; run
cd tests
make BACKEND=mdb mdb-mod TESTS=test017-syncreplication-refresh
Cross-arch via Alpine's CI image (any host):
docker run --rm --platform linux/386 -v $PWD:/mnt \
registry.alpinelinux.org/alpine/infra/docker/alpine-gitlab-ci:latest-x86 \
sh -c 'cd /mnt && ./configure --enable-modules --enable-mdb=mod \
--enable-syncprov=mod && make && cd tests && make mdb-mod'
Expected on affected builders:
>>>>> Starting test017-syncreplication-refresh for mdb...
...
test failed - provider and consumer databases differ
>>>>> Failed test017-syncreplication-refresh for mdb
Capturing the diff (modify defines.sh's CMPOUT temporarily) shows:
348c348
< description: Example, Inc. ITS test domain
---
> description: Example, Inc. modify+modrdn test domain
350c350
< modifiersName: cn=Manager,dc=example,dc=com
---
> modifiersName: cn=consumer,dc=example,dc=com
(Both entries carry the SAME final entryCSN — that's the smoking gun:
the consumer adopted the provider's CSN via the modrdn, but the
follow-up modify hit `err=122 LDAP_ASSERTION_FAILED` because of the
CSN bump, and ITS#10358's retry then short-circuited.)
Proposed fix
----------------------------------------------------------------
Six added lines in servers/slapd/syncrepl.c — see attached patch
0001-syncrepl-set-orm_no_opattrs-for-backend-ops.patch. Tested
against OPENLDAP_REL_ENG_2_6_13: full make test suite passes on
x86 (linux/386 QEMU) including all six tests that ITS#10250 patched
plus test043-delta-syncrepl and test063-delta-multiprovider.
References / related ITS
----------------------------------------------------------------
ITS#4820 — the original modifiersName/modifyTimestamp kludge added in 2007
ITS#10250 — kludge removed (MR#817, commit 87933f3e), exposing this bug
ITS#10358 — assert-control retry (MR#781) — masks the divergence as silent
data loss
ITS#8852 — sorted_attr_cmp() prerequisite for ITS#10250 (commit 8986f99d)
--
You are receiving this mail because:
You are on the CC list for the issue.
https://bugs.openldap.org/show_bug.cgi?id=10554
Issue ID: 10554
Summary: More bugs in deprecated functions in getdn.c
Product: OpenLDAP
Version: unspecified
Hardware: All
OS: All
Status: UNCONFIRMED
Keywords: needs_review
Severity: trivial
Priority: ---
Component: libraries
Assignee: bugs(a)openldap.org
Reporter: hyc(a)openldap.org
Target Milestone: ---
Filing these for completeness, and to avoid repeated reports of them in the
future. The relevant functions have been deprecated since 2006 and nothing
currently uses them.
Reported by Yeo Jooho (여주호) <jh.yeo(a)piolink.com>
1. ldap_explode_rdn() allocates its values[] array without zeroing it. On
error,
freeing it can free garbage pointers left over from previous heap allocs.
2. strval2ADstrlen() terminates its loop on NUL but advances by UTF-8 char
length, so a truncated lead byte at the end of a value steps the pointer
past the terminator and reads out of bounds. Heap-buffer-overflow read,
confirmed it does not disclose adjacent memory into output. Trigger:
ldap_str2dn() on a value like "cn=ab\c9,dc=x" followed by ldap_dn2str()
with LDAP_DN_FORMAT_AD_CANONICAL. Will trigger an error in ASAN but is
benign in normal builds.
3. ldap_explode_dn() doesn't check ldap_rdn2str()'s return value. On failure
that leaves a NULL embedded mid-array, which stops the caller's
ldap_memvfree() walk early and leaks everything after it. Trigger: a DN
with a bad RDN anywhere but the last position.
--
You are receiving this mail because:
You are on the CC list for the issue.
https://bugs.openldap.org/show_bug.cgi?id=10491
Issue ID: 10491
Summary: constraint_attribute <attrs...> count doesn't work
with multiple attributes
Product: OpenLDAP
Version: unspecified
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: ---
constraint_check_count_violation (run on Modifies/Renames) only really checks
the last attribute in the list. 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=10492
Issue ID: 10492
Summary: slapd: send_ldap_result must massage return code
before handling abandon
Product: OpenLDAP
Version: 2.6.13
Hardware: All
OS: All
Status: UNCONFIRMED
Keywords: needs_review
Severity: normal
Priority: ---
Component: slapd
Assignee: bugs(a)openldap.org
Reporter: hyc(a)openldap.org
Target Milestone: ---
send_ldap_result checks if rs->sr_err == LDAP_REFERRAL and rs->sr_ref == NULL.
In that case, there's no actual referral to send so it changes rs->sr_err to
LDAP_NO_SUCH_OBJECT. Later send_ldap_response checks if rs->sr_ref == NULL and
asserts if rs->sr_err == LDAP_REFERRAL.
If an op was abandoned, the adjustment in send_ldap_result was being skipped,
allowing the assert in send_ldap_response to get triggered. The fix is to move
the check for abandon to after the error code adjustment has occurred.
--
You are receiving this mail because:
You are on the CC list for the issue.
https://bugs.openldap.org/show_bug.cgi?id=10485
Issue ID: 10485
Summary: potential division by zero in tier bestof
Product: OpenLDAP
Version: unspecified
Hardware: All
OS: All
Status: UNCONFIRMED
Keywords: needs_review
Severity: normal
Priority: ---
Component: lloadd
Assignee: bugs(a)openldap.org
Reporter: ondra(a)mistotebe.net
Target Milestone: ---
In rare cases gettimeofday could fall on tv_usec == 0 so we will just accept
it's been exactly 1 second and go with that.
--
You are receiving this mail because:
You are on the CC list for the issue.
https://bugs.openldap.org/show_bug.cgi?id=10482
Issue ID: 10482
Summary: Accesslog leaks UUID buffer when operation not logged
Product: OpenLDAP
Version: unspecified
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: ---
Fix incoming
--
You are receiving this mail because:
You are on the CC list for the issue.
https://bugs.openldap.org/show_bug.cgi?id=10483
Issue ID: 10483
Summary: slapo-chain leaks structures added online
Product: OpenLDAP
Version: unspecified
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: ---
chain_ldadd stores the DB in ce->ce_be, but there is no teardown of these on
shutdown so they get leaked and no way for ldap_chain_db_destroy to reach them
either.
--
You are receiving this mail because:
You are on the CC list for the issue.
https://bugs.openldap.org/show_bug.cgi?id=10481
Issue ID: 10481
Summary: cn=config leaks data in error path
Product: OpenLDAP
Version: unspecified
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: ---
Attempting an Add when objectclass is not known leaks in-progress structures.
--
You are receiving this mail because:
You are on the CC list for the issue.
https://bugs.openldap.org/show_bug.cgi?id=10493
Issue ID: 10493
Summary: change in 2.6.13 for ber_bvreplace_x introduces out of
bounds reads
Product: OpenLDAP
Version: 2.6.13
Hardware: All
OS: All
Status: UNCONFIRMED
Keywords: needs_review
Severity: normal
Priority: ---
Component: libraries
Assignee: bugs(a)openldap.org
Reporter: dirk(a)dmllr.de
Target Milestone: ---
2.6.13 includes the patch for "Fixed liblber ber_bvreplace_x potential NULL
dereference"
However, this now introduces OOB reads.
If ber_memrealloc_x fails to allocate memory, the function executes
AC_MEMCPY( dst->bv_val, src->bv_val, dst->bv_len + 1 );
Because `dst->bv_len` was not updated to `src->bv_len`, `AC_MEMCPY` copies
exactly `dst->bv_len + 1` bytes from `src`. Since `src` is strictly larger than
the old `dst->bv_len`, the byte copied into the final position
(`dst->bv_val[dst->bv_len]`) will be the corresponding character from `src`,
**not a null-terminator**.
This leaves the `dst->bv_val` buffer without a null-terminator. Any subsequent
string-based operations (like `strlen`, `printf`, or logging functions) acting
on the `berval`'s `bv_val` will read out-of-bounds into adjacent heap memory
until it randomly hits a null byte, leading to a heap buffer over-read (OOB
Read) or information leak.
Similarly, if the source buffer was not null-terminated and was precisely sized
to its length, it reads 1 byte past the end of src->bv_val, which is an
Out-of-Bounds Read that could lead to crashes or leaking adjacent memory.
suggest to use this instead:
if ( dst->bv_val != NULL ) {
AC_MEMCPY( dst->bv_val, src->bv_val, dst->bv_len );
dst->bv_val[dst->bv_len] = '\0';
}
--
You are receiving this mail because:
You are on the CC list for the issue.
https://bugs.openldap.org/show_bug.cgi?id=10498
Issue ID: 10498
Summary: libldap OpenSSL 4 compatibility
Product: OpenLDAP
Version: 2.6.13
Hardware: All
OS: All
Status: UNCONFIRMED
Keywords: needs_review
Severity: normal
Priority: ---
Component: libraries
Assignee: bugs(a)openldap.org
Reporter: rainer.jung(a)kippdata.de
Target Milestone: ---
Created attachment 1145
--> https://bugs.openldap.org/attachment.cgi?id=1145&action=edit
Patch for libldap OpenSSL 4 compatibility
libldap uses direct access to struct members made opaque in OpenSSL 4.
Alternative getter methods needed to access the struct members seem to be
available since OpenSSL 1.1.0, so very long ago. I suggest the attached
(trivial) patch to replace direct access with getter methods.
The patch is based on OpenLDAP 2.6.13. From visual code inspection it seems the
problem also exists in the master branch.
I did not try to compile the rest of OpenLDAP with OpenSSL 4, just libldap.
--
You are receiving this mail because:
You are on the CC list for the issue.
https://bugs.openldap.org/show_bug.cgi?id=10501
Issue ID: 10501
Summary: slapadd should ignore SLAP_AT_DYNAMIC attributes from
input
Product: OpenLDAP
Version: unspecified
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: ---
We say a backup should be done with slapcat (preferably) or `ldapsearch -MM '*'
+`, but the latter will include attributes that should never be stored. Admins
don't always know which those are and then they pollute the DB copy if present.
slapadd should just silently skip them on load.
--
You are receiving this mail because:
You are on the CC list for the issue.
https://bugs.openldap.org/show_bug.cgi?id=10505
Issue ID: 10505
Summary: lloadd doesn't fully validate incoming msgids
Product: OpenLDAP
Version: unspecified
Hardware: All
OS: All
Status: UNCONFIRMED
Keywords: needs_review
Severity: normal
Priority: ---
Component: lloadd
Assignee: bugs(a)openldap.org
Reporter: ondra(a)mistotebe.net
Target Milestone: ---
This can allow rogue clients reach an assert(0) with the right timing.
--
You are receiving this mail because:
You are on the CC list for the issue.
https://bugs.openldap.org/show_bug.cgi?id=10514
Issue ID: 10514
Summary: another mutex bug in back-monitor
Product: OpenLDAP
Version: unspecified
Hardware: All
OS: All
Status: UNCONFIRMED
Keywords: needs_review
Severity: normal
Priority: ---
Component: backends
Assignee: bugs(a)openldap.org
Reporter: hyc(a)openldap.org
Target Milestone: ---
Same as #1710 but introduced later in 127ac65c447b8d28ba3cd75e1b8cee28006d5472
Calling mutex_destroy on a locked mutex in cache.c:314 monitor_cache_remove().
--
You are receiving this mail because:
You are on the CC list for the issue.
https://bugs.openldap.org/show_bug.cgi?id=10516
Issue ID: 10516
Summary: dynamic backends don't get rebuilt
Product: OpenLDAP
Version: unspecified
Hardware: All
OS: All
Status: UNCONFIRMED
Keywords: needs_review
Severity: normal
Priority: ---
Component: build
Assignee: bugs(a)openldap.org
Reporter: hyc(a)openldap.org
Target Milestone: ---
The makefile rules need a small fix.
--
You are receiving this mail because:
You are on the CC list for the issue.
https://bugs.openldap.org/show_bug.cgi?id=10503
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=10526
Issue ID: 10526
Summary: back-mdb: mdb_idl_intersection is O(ID-gap), not O(n),
when a short candidate list is ANDed with a range
Product: OpenLDAP
Version: 2.5.20
Hardware: All
OS: Linux
Status: UNCONFIRMED
Keywords: needs_review
Severity: normal
Priority: ---
Component: backends
Assignee: bugs(a)openldap.org
Reporter: choeger(a)open-xchange.com
Target Milestone: ---
Created attachment 1157
--> https://bugs.openldap.org/attachment.cgi?id=1157&action=edit
the patch above
----------------------------------------------------------------------------
Note
The below report including the patch has been generated by Claude Opus 4.8.
I built packages based on the OpenLDAP-LTB project version v2.5.20 and verified
in our problematic production deployment.
This refers to the following thread:
https://lists.openldap.org/hyperkitty/list/openldap-technical@openldap.org/…
End Note
----------------------------------------------------------------------------
SUMMARY
-------
AND-filter searches can be orders of magnitude slower than the size of their
result set warrants. mdb_idl_intersection() falls back to a generic
element-at-a-time merge whose cost is proportional to the distance in ID space
between candidate IDs, not to the number of candidates. When one operand is a
range (any indexed term matching more than the IDL range threshold, e.g. a
common objectClass) and the other is a short list of IDs that are widely
separated, the merge walks the range one ID at a time across the entire gap.
This is common in practice: filters of the form
(&(objectClass=X)(someAttr=val)...) hit it whenever someAttr matches a small
number of entries whose IDs happen to be far apart and objectClass=X is stored
as a range that does not fully cover that span.
ENVIRONMENT
-----------
- OpenLDAP 2.5.19, slapd back-mdb.
- ~4.6M entries. An attribute is indexed eq and a given value normally matches
exactly one entry. A common objectClass value matches ~2,000,000 entries and
is therefore stored as an IDL range (it exceeds MDB_idl_db_max; see
servers/slapd/back-mdb/idl.c:467, "No room, convert to a range").
SYMPTOM
-------
Query: (&(objectClass=<common>)(<almost-unique-attr>=<value>)(<attr>=*))
The filter resolves to a single entry in all cases.
- When <almost-unique-attr>=<value> matches ONE entry: the search is very fast.
- When it matches TWO entries whose IDs are far apart (here 1759457 and
2731413, a gap of ~972,000), the same search becomes roughly 20x slower,
even though the AND still yields a single entry.
perf shows nearly all CPU in mdb_idl_next() and mdb_idl_intersection().
Enabling LDAP_DEBUG_FILTER and inspecting the per-term candidate counts
confirms the broad term is a range; gdb in mdb_idl_intersection shows
idmin/idmax equal to the two widely separated IDs.
Neither index_hash64 nor dropping the substring index changes the timing;
the cost is not in the index key lookups, it is in the candidate intersection.
ROOT CAUSE
----------
In servers/slapd/back-mdb/idl.c, mdb_idl_intersection() has fast paths for
both-ranges, for idmin==idmax, and for "range fully covers list". Anything
that misses those falls into a generic merge:
cursora = cursorb = idmin;
ida = mdb_idl_first( a, &cursora );
idb = mdb_idl_first( b, &cursorb );
cursorc = 0;
while( ida <= idmax || idb <= idmax ) {
if( ida == idb ) {
a[++cursorc] = ida;
ida = mdb_idl_next( a, &cursora );
idb = mdb_idl_next( b, &cursorb );
} else if ( ida < idb ) {
ida = mdb_idl_next( a, &cursora );
} else {
idb = mdb_idl_next( b, &cursorb );
}
}
For a range, mdb_idl_next() returns ++(*cursor), i.e. consecutive integers.
So when b is a range that does not fully cover list a (the "range fully covers
list" shortcut is missed because the non-matching list member lies outside the
range bounds), the loop advances idb one integer at a time from idmin to idmax.
With the two matches ~972,000 apart, that is ~972,000 iterations per search.
The single-match case is fast only because it lands on the idmin==idmax or
"range fully covers list" shortcut; it has nothing to do with the index.
The same loop is also O(gap) for the list-vs-list case (a populous list ANDed
with a short, widely-spread list): it walks the populous list across the gap.
mdb_idl_intersection() is reached for every non-first term of an AND filter via
list_candidates() in servers/slapd/back-mdb/filterindex.c.
REPRODUCTION (without specific data)
------------------------------------
1. Load a DB where some indexed attribute value V matches more than
MDB_idl_db_max entries (so V is stored as a range), and where the range's
[lo,hi] does not span the whole DB.
2. Pick another indexed attribute A and two entries that share a value W on A,
such that one of them is NOT in V's range and their IDs are far apart.
3. Time (&(A=W)) vs (&(V)(A=W)) repeatedly. The second is dramatically slower
despite returning fewer (or equal) entries.
A standalone harness that extracts the real 2.5.19 IDL routines, cross-checks a
fixed mdb_idl_intersection against brute-force set intersection, and benchmarks
the pathology is attached (idl_intersection_test.c).
PROPOSED FIX
------------
mdb_idl_intersection() never needs to iterate a range. After the existing
"range fully covers list" shortcut:
- If b is a range, the result is just the members of list a that fall within
[first,last]: iterate the (bounded) list, test each against the bounds.
O(|a|) instead of O(range width).
- If both a and b are lists, keep the sorted merge but, when the cursors
diverge, binary-search (mdb_idl_search) the lagging list forward to the
other's current value rather than stepping one element at a time. Each
catch-up becomes O(log n) instead of O(gap).
Result ordering (ascending) and the a[0] count convention are preserved, as is
the swap/copy-back behaviour. The in-place writes are safe: the write index
(cursorc, number of matches) never exceeds the read index.
Patch against OPENLDAP_REL_ENG_2_5_19 (applies cleanly to RE25 and master HEAD
as well); also attached as mdb-idl-intersection.patch:
--- a/servers/slapd/back-mdb/idl.c
+++ b/servers/slapd/back-mdb/idl.c
@@ -759,23 +759,49 @@
goto done;
}
- /* Fine, do the intersection one element at a time.
- * First advance to idmin in both IDLs.
+ /* If b is a range (and does not fully cover a, handled above), the
+ * intersection is just the elements of list a that fall within the
+ * range bounds. Walk the (bounded) list, never the (potentially huge)
+ * range: O(|a|), not O(range width).
*/
- cursora = cursorb = idmin;
- ida = mdb_idl_first( a, &cursora );
- idb = mdb_idl_first( b, &cursorb );
- cursorc = 0;
+ if ( MDB_IDL_IS_RANGE( b ) ) {
+ ID lo = MDB_IDL_RANGE_FIRST( b );
+ ID hi = MDB_IDL_RANGE_LAST( b );
+ cursorc = 0;
+ for ( ida = mdb_idl_first( a, &cursora );
+ ida != NOID;
+ ida = mdb_idl_next( a, &cursora ) ) {
+ if ( ida < lo )
+ continue;
+ if ( ida > hi )
+ break;
+ a[++cursorc] = ida;
+ }
+ a[0] = cursorc;
+ goto done;
+ }
- while( ida <= idmax || idb <= idmax ) {
- if( ida == idb ) {
+ /* Both a and b are lists. Sorted-merge intersection, but when the two
+ * cursors diverge, binary-search the lagging list forward to the
other's
+ * current value instead of stepping one element at a time. This keeps
a
+ * large gap in one list (e.g. two widely separated matches intersected
+ * against a populous list) from forcing a linear scan of the other
list
+ * across the whole gap: each catch-up is O(log n) rather than O(gap).
+ */
+ cursora = mdb_idl_search( a, idmin );
+ cursorb = mdb_idl_search( b, idmin );
+ cursorc = 0;
+ while ( cursora <= a[0] && cursorb <= b[0] ) {
+ ida = a[cursora];
+ idb = b[cursorb];
+ if ( ida == idb ) {
a[++cursorc] = ida;
- ida = mdb_idl_next( a, &cursora );
- idb = mdb_idl_next( b, &cursorb );
+ cursora++;
+ cursorb++;
} else if ( ida < idb ) {
- ida = mdb_idl_next( a, &cursora );
+ cursora = mdb_idl_search( a, idb );
} else {
- idb = mdb_idl_next( b, &cursorb );
+ cursorb = mdb_idl_search( b, ida );
}
}
a[0] = cursorc;
TESTING
-------
Using the attached harness (the patched function is byte-for-byte the shipped
code):
- Correctness: 400,000 randomized trials (two seeds) over zero/list/range
operands of varied sizes and gaps, in both argument orders, cross-checked
against brute-force set intersection. Zero mismatches.
- The reported case, list {1759457, 2731413} intersected with a 2,000,000-entry
range [1 .. 2731412] that does not cover the upper member, 1200 iterations:
original: 2.54 s, 1,166,348,400 mdb_idl_next calls, result count = 1
fixed: ~0 s, 1,200 mdb_idl_next calls, result count = 1
- Single-value baseline (original): ~0 s, 0 mdb_idl_next calls, confirming the
original is fast only for the single-match case.
--
You are receiving this mail because:
You are on the CC list for the issue.