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=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=10591
Issue ID: 10591
Summary: slapo-syncprov frees modtarget and sessionlog nodes
without checking that they were removed from their AVL
tree, causing a use-after-free and SIGSEGV in the
comparison callback
Product: OpenLDAP
Version: unspecified
Hardware: All
OS: All
Status: UNCONFIRMED
Keywords: needs_review
Severity: normal
Priority: ---
Component: overlays
Assignee: bugs(a)openldap.org
Reporter: bohdan.kmit(a)kiteworks.com
Target Milestone: ---
## Symptom
slapd terminates with SIGSEGV. Across 89 coredumps collected from several
instances, 64 abort inside `malloc()` and 18 inside `free()` at unrelated
allocation sites — glibc detecting an already-corrupt heap at whatever the
process allocated next, reported variously as:
```
double free or corruption (out)
malloc(): unaligned tcache chunk detected
malloc(): unaligned fastbin chunk detected
```
Two cores caught the fault itself rather than a later detection. Both have no
allocator frame and are structurally identical:
```
#0 sp_avl_cmp (c1=0x7f5778104590, c2=0x7f5f4954e13a) at syncprov.c:433
#1 ldap_avl_delete (root=..., data=c1, fcmp=sp_avl_cmp) at avl.c:199
#2 syncprov_op_cleanup (op, rs) at syncprov.c:1589
#3 slap_cleanup_play at result.c:607
#4 send_ldap_response at result.c:797
#5 slap_send_ldap_result at result.c:926
#6 mdb_add at add.c:389
#7 overlay_op_walk (which=op_add) at backover.c:706
#9 accesslog_response at accesslog.c:1966
#10 slap_response_play at result.c:573
#13 mdb_modify at modify.c:803
#16 syncrepl_message_to_op at syncrepl.c:3271
#17 do_syncrep2 at syncrepl.c:1555
#18 do_syncrepl at syncrepl.c:2197
```
`si_addr` is `c2 + 0x10` in both. `modtarget` is
```c
typedef struct modtarget {
struct modinst *mt_mods; /* +0 */
struct modinst *mt_tail; /* +8 */
struct berval mt_dn; /* +16 -> bv_len */
ldap_pvt_thread_mutex_t mt_mutex;
} modtarget;
```
so offset 16 is `mt_dn.bv_len`, exactly the field `sp_avl_cmp` reads at
syncprov.c:433. Both `c2` values are not 8-byte aligned, so `si_mods` is
holding
freed and reused memory rather than a live `modtarget`.
The path is a replicated MODIFY applied by syncrepl, whose response triggers
the
accesslog overlay to perform a nested internal ADD into the log database; that
nested operation's cleanup walks `si_mods` and dereferences the stale node.
## Analysis
`syncprov_op_cleanup()`, syncprov.c:
```c
ldap_avl_delete( &si->si_mods, mt, sp_avl_cmp ); /* return value ignored */
ldap_pvt_thread_mutex_unlock( &si->si_mods_mutex );
ldap_pvt_thread_mutex_destroy( &mt->mt_mutex );
ch_free( mt->mt_dn.bv_val );
ch_free( mt );
```
`ldap_avl_delete()` returns the node it removed, or NULL when the comparison
does not locate it. Since `sp_avl_cmp` orders by `mt_dn`, a search can fail to
find a target that is physically still in the tree, and can also match a
different target that shares a DN. In either case `mt` is freed while still
linked, and the next traversal dereferences it.
Access to the tree is correctly serialised at all three sites (`ldap_avl_find`
at :2787, `ldap_avl_insert` at :2861, `ldap_avl_delete` at :1589 all under
`si_mods_mutex`), so this is a lifetime defect rather than a data race.
Three related weaknesses in the same overlay:
1. `ldap_avl_insert()` at syncprov.c:2861 also ignores its return. A failed
insert leaves `mt` unreferenced by the tree while `opc->smt` still points at
it, so the later cleanup finds nothing to remove.
2. The `mt_mods` walk at syncprov.c:1574 and the `mt_mods` and `o_callback`
walks in the abandon path at syncprov.c:2848 and :2856 have no termination
condition:
```c
for (m2 = &mt->mt_mods; ; m2 = &(*m2)->mi_next) {
```
If the entry is not on the list the loop runs off the end. This is the same
pattern ITS#10408 corrected for `si_ops`, which was released in 2.6.15; the
equivalent code two functions away was not changed.
3. The sessionlog trim at syncprov.c:1799 has the identical delete-then-free
shape on a different tree:
```c
ldap_tavl_delete( &sl->sl_entries, se, syncprov_sessionlog_cmp );
ch_free( se );
```
One of the collected cores aborts in `syncprov_add_slog()` ->
`ldap_tavl_insert()` at syncprov.c:1754, consistent with that tree also
holding a freed node. The sessionlog is written on every logged operation,
so
on a busy provider it is exercised harder than `si_mods`.
## Same defect in two other overlays
Found while auditing for the pattern; neither was loaded when the crash was
observed, so these are reported from code inspection only.
`slapo-pcache`, `remove_from_template()`: `ldap_avl_delete()` on
`template->qbase` is unchecked and followed immediately by
`ch_free( qc->qbase )`. The `ldap_tavl_delete()` on the scope tree above it is
also unchecked, and both callers free `qc` only later, so a failed removal
leaves either tree pointing at freed memory.
`slapo-seqmod`, `seqmod_op_cleanup()`: the lookup result is validated with
`assert( av != NULL )` and then dereferenced as `av->avl_data`. Under NDEBUG
the
assert is compiled out and the dereference faults instead. Its
`ldap_avl_delete()` is also unchecked, though nothing is freed on that path.
An audit of `servers/slapd` found no other unguarded `for (p = &head; ; p =
&(*p)->next)` list walks, and `back-ldap/chain.c:1506` already checks its
`ldap_tavl_delete()` return and logs on failure.
## Affected versions
Verified byte-identical in `OPENLDAP_REL_ENG_2_6_15`, `OPENLDAP_REL_ENG_2_7_0`,
`OPENLDAP_REL_ENG_2_7_1` and current `master`. Diffing `syncprov.c` between
2.6.15
and master produces no hunk touching `si_mods`, `modtarget`, `sp_avl_cmp`,
`mt_mods`, `mt_tail` or `opc->smt`, and `avl.c` is unchanged. So there is no
release to upgrade to.
Crash observed on 2.6.15, x86-64, glibc, back-mdb, overlays `syncprov` and
`accesslog` loaded, three-way multi-provider mesh with delta-syncrepl consumers
reading the accesslog. Sustained write load of roughly 25 to 30 operations per
second.
## Proposed fix
Three patches, one per overlay:
1. **slapo-syncprov** — capture the return of `ldap_avl_delete()` and
`ldap_tavl_delete()` and free only on an identity match, logging otherwise;
bound the three list walks; report a failed `ldap_avl_insert()`. This
converts a use-after-free into a bounded leak: a leaked `modtarget` is a few
dozen bytes plus a DN, a dangling one takes the process down.
2. **slapo-pcache** — same guard on both removals in
`remove_from_template()`.
3. **slapo-seqmod** — replace the assert with a real check that releases the
mutex, completes the callback teardown and returns; report a failed
`ldap_avl_delete()`.
--
You are receiving this mail because:
You are on the CC list for the issue.
https://bugs.openldap.org/show_bug.cgi?id=10597
Issue ID: 10597
Summary: slapo-accesslog frees a shared `entryUUID` value from
operations that do not hold the mutex guarding it,
causing a double free and heap corruption. Introduced
in 2.6.14 by the fix for ITS#10482.
Product: OpenLDAP
Version: 2.6.14
Hardware: All
OS: All
Status: UNCONFIRMED
Keywords: needs_review
Severity: normal
Priority: ---
Component: overlays
Assignee: bugs(a)openldap.org
Reporter: bohdan.kmit(a)kiteworks.com
Target Milestone: ---
Created attachment 1212
--> https://bugs.openldap.org/attachment.cgi?id=1212&action=edit
Patch
# Symptom
`slapd` terminates with SIGSEGV under concurrent write and search load. glibc
reports the corruption at whatever the process allocates next, so the message
varies:
```
double free or corruption (out)
malloc(): unaligned fastbin chunk detected
malloc(): unaligned tcache chunk detected
free(): invalid pointer
```
Of ten cores collected from one instance, nine abort inside `malloc()` or
`calloc()` at unrelated allocation sites and carry no information about the
origin. One caught the faulting free itself:
```
#4 free ()
#5 accesslog_response (op=<optimized out>, rs=...) at accesslog.c:2034
#6 ...
#8 slap_send_ldap_result ()
```
(line 2034 in 2.6.15; the equivalent is 2176 in current master.)
# Analysis
`accesslog_response()` ends with:
```c
skip:
if ( !BER_BVISNULL( &li->li_uuid ) ) {
ber_memfree( li->li_uuid.bv_val );
BER_BVZERO( &li->li_uuid );
}
if ( lo->mask & LOG_OP_WRITES ) {
/* We haven't transitioned to li_log_mutex yet */
ldap_pvt_thread_mutex_unlock( &li->li_op_rmutex );
}
return SLAP_CB_CONTINUE;
```
`li_uuid` is a field of the shared `log_info` instance, not of the operation.
Access to it is asymmetric:
* Write operations (`bi_op_add`, `bi_op_delete`, `bi_op_modify`, `bi_op_modrdn`
→ `accesslog_op_mod()`) acquire `li_op_rmutex` and populate `li_uuid` with
`ber_dupbv()` while holding it. They still hold it on entry to
`accesslog_response()`, free under it at `skip:`, and release it immediately
after. This is correct.
* Non-write operations (`bi_op_bind`, `bi_op_compare`, `bi_op_search`,
`bi_extended` → `accesslog_op_misc()`) take no lock at all.
`accesslog_response()` acquires `li_op_rmutex` for them only part-way through
the function, and **all four `goto skip` sites precede that acquisition**
(master: 1656, 1661, 1671, 1683 versus the lock at 1693). Such an operation
therefore reaches `skip:` holding no lock, and frees a pointer it never set.
The `goto skip` conditions reachable this way are:
| site | condition |
|------|-----------------------------------------------------|
| 1656 | log database absent or not open |
| 1661 | `op->o_dont_replicate` |
| 1671 | `li_success` configured and the operation failed |
| 1683 | operation not in `li_ops` and no matching `logbase` |
Two consequences follow:
1. Two non-write operations completing concurrently both observe a non-NULL
`li_uuid` and both call `ber_memfree()` on it — a double free.
2. A non-write operation can free it in the window between a write operation
storing the value and that write operation taking ownership of it (master
1716–1718, under both mutexes). The write path then works with, and frees, a
dangling pointer.
Either way the allocator's free lists are corrupted, and the failure surfaces
later at an unrelated allocation, which is why almost every core points
somewhere innocent.
The exposure depends strongly on configuration. With `olcAccessLogOps: writes`,
searches are not in `li_ops`, so **every search** fails the test at 1673 and
takes `goto skip` at 1683. A read-heavy workload therefore executes the
unlocked free at close to search rate. Note that 1683 is unreachable for write
operations, since `accesslog_op_mod()` applies the same test before registering
the callback.
Access to the tree of related state is otherwise correct: every use of
`li_mincsn`, `li_sids` and `li_numcsns` in `log_old_lookup()`,
`accesslog_purge()`, `accesslog_response()` and `accesslog_db_root()` is
serialised by `li_log_mutex`, and no locked region in the file contains a
`return` or `goto` that bypasses its unlock. `li_uuid` is the only unserialised
mutation.
# Regression
The unconditional free was added by:
```
5c4e7f2f1a ITS#10482 slapo-accesslog: do not leak entryUUID (2026-03-31)
ab4e53e54b same, backported to the 2.6 branch
```
That commit consists solely of those four lines. It plugs a genuine leak but
places the release on a path reachable without the mutex.
# Affected versions
| release | affected |
|--------------|----------|
| 2.6.13 | no |
| 2.6.14 | yes |
| 2.6.15 | yes |
| 2.7.0, 2.7.1 | yes |
| master | yes |
Observed on 2.6.15, x86-64, glibc, back-mdb, with `syncprov` and `accesslog` on
the same database and delta-syncrepl consumers reading the accesslog.
# Reproduction
Configuration: `accesslog` over back-mdb with `olcAccessLogOps: writes` and
`olcAccessLogSuccess: TRUE`.
Load: six concurrent MODIFY streams cycling over four DNs (to maximise
same-target concurrency), two more spread over twenty DNs, one stream of ADDs
of existing entries (returning `entryAlreadyExists`), and twelve concurrent
search streams over the same subtree.
Result: SIGSEGV within **13–24 seconds**, repeatably — effectively one crash
per load application.
Two observations that isolate the mechanism:
* The **same write load with no search streams ran 300 seconds without a
fault.** Searches are required, because only non-write operations take the
unlocked path.
* Instances receiving the identical replicated write stream but carrying **no
local search traffic** ran 68 minutes without a fault, while an instance with
local searches plus the same replicated writes crashed.
`MALLOC_CHECK_` does not help: by the time of the second free the chunk has
typically been handed to another allocation, so it is a valid live chunk and
nothing is flagged at the free.
# Proposed fix
Free the value only on the path that owns it and already holds the mutex:
```diff
skip:
- if ( !BER_BVISNULL( &li->li_uuid ) ) {
- ber_memfree( li->li_uuid.bv_val );
- BER_BVZERO( &li->li_uuid );
- }
if ( lo->mask & LOG_OP_WRITES ) {
+ /* Only this path holds li_op_rmutex, which guards li_uuid */
+ if ( !BER_BVISNULL( &li->li_uuid ) ) {
+ ber_memfree( li->li_uuid.bv_val );
+ BER_BVZERO( &li->li_uuid );
+ }
/* We haven't transitioned to li_log_mutex yet */
ldap_pvt_thread_mutex_unlock( &li->li_op_rmutex );
}
```
Non-write operations never populate `li_uuid`, so they have nothing to release;
the leak ITS#10482 addressed remains fixed for the write path, which is the
only producer. Because every `goto skip` precedes the mutex acquisition, no
path can now reach the label holding the lock without releasing it.
# Related observation, not addressed by this patch
`li_old` has the same missing release. `accesslog_op_mod()` assigns `li->li_old
= entry_dup( e )` without freeing any previous value, `accesslog_response()`
moves it out only on the non-skip path, and neither `skip:` nor
`accesslog_db_destroy()` releases it. A write operation whose response takes
`goto skip` therefore strands an `Entry`, which the next write overwrites and
leaks. This requires `olcAccessLogOld` to be configured and is a leak only — no
unserialised free, so no corruption. Reported for completeness rather than
fixed here.
--
You are receiving this mail because:
You are on the CC list for the issue.
https://bugs.openldap.org/show_bug.cgi?id=10598
Issue ID: 10598
Summary: Version 2.7 is both released in the past and a future
release
Product: website
Version: unspecified
Hardware: All
OS: All
Status: UNCONFIRMED
Keywords: needs_review
Severity: normal
Priority: ---
Component: website
Assignee: bugs(a)openldap.org
Reporter: dpa-openldap(a)aegee.org
Target Milestone: ---
The roadmap at https://www.openldap.org/software/roadmap.html contains:
Future Minor Releases
OpenLDAP 2.7 (Released August 2026)
2.7 cannot be at the same time a Future Release and Released in August 2026.
--
You are receiving this mail because:
You are on the CC list for the issue.
https://bugs.openldap.org/show_bug.cgi?id=10596
Issue ID: 10596
Summary: back-ldap: tainting of a cached connection doesn't
remove it from cache
Product: OpenLDAP
Version: 2.6.14
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: ---
The patch for ITS#10550 was incomplete and missed the removal when only
tainting. This eventually leaves a freed connection in the tree.
--
You are receiving this mail because:
You are on the CC list for the issue.
https://bugs.openldap.org/show_bug.cgi?id=10550
Issue ID: 10550
Summary: back ldap idletimeout broken
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: ---
When an operation takes longer than idletimeout/conttl, back-ldap decides to
terminate the connection. But if that connection is private, it doesn't do it
right and asserts in ldap_back_conn_delete
Two issues:
- the TAINTED flag should not be set without the real state being set to match
- a connection with an outstanding operation shouldn't be considered idle
--
You are receiving this mail because:
You are on the CC list for the issue.
https://bugs.openldap.org/show_bug.cgi?id=10528
Issue ID: 10528
Summary: core.ldif fails to load via "include:" in cn=config on
Symas OpenLDAP 2.6.13-3 (RHEL 9)
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: lucas.vicente(a)ebz.tec.br
Target Milestone: ---
Hello,
We are experiencing an issue while trying to load the core.ldif schema default
using the dynamic configuration (cn=config).
When using the following in my LDIF:
include: file:///opt/symas/etc/openldap/schema/core.ldif
I get this error:
olcAttributeTypes: value #48 olcAttributeTypes: Unexpected token before MUST c
MAY ( searchGuide $ description ) )
slapadd: could not add entry dn="cn={0}core,cn=schema,cn=config" (line=14):
olcAttributeTypes: Unexpected token before MUST c MAY ( searchGuide $
description ) )
Obs:
If I create a slapd.conf that includes the core schema and run:
slaptest -f slapd.conf -F /tmp/slapd.d
It works without errors. However, when trying to load the same schema using
slapadd + include: in cn=config, it fails.
For reference, I tested the same steps on version 2.6.10 and it worked without
errors.
Steps to reproduce:
Install Symas OpenLDAP 2.6.13 on RHEL 9
Try to load the core schema using:
dn: cn=schema,cn=config
objectClass: olcSchemaConfig
cn: schema
include: file:///opt/symas/etc/openldap/schema/core.ldif
Would you be able to verify this behavior?
Thanks!
--
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=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.