https://bugs.openldap.org/show_bug.cgi?id=10445
Issue ID: 10445 Summary: memroy leak in get_mra Product: OpenLDAP Version: 2.6.12 Hardware: All OS: All Status: UNCONFIRMED Keywords: needs_review Severity: normal Priority: --- Component: slapd Assignee: bugs@openldap.org Reporter: kangyang126@gmail.com Target Milestone: ---
# ASAN Leak Report: Extensible Matching Rule Assertion
## Component * **File:** `servers/slapd/mra.c` * **Function:** `get_mra`
## Issue Description **LeakSanitizer Error:** Memory leak detected when parsing a matching rule assertion (MRA).
```shell ==179518==ERROR: LeakSanitizer: detected memory leaks
Direct leak of 58 byte(s) in 1 object(s) allocated from: #0 0x555555963614 in malloc /src/llvm-project/compiler-rt/lib/asan/asan_malloc_linux.cpp:67:3 #1 0x555555f57804 in ber_memalloc_x /src/openldap/libraries/liblber/memory.c:228:9 #2 0x555555b3a3a3 in slap_sl_malloc /src/openldap/servers/slapd/sl_malloc.c:302:12 #3 0x555555b2b465 in slap_bv2tmp_ad /src/openldap/servers/slapd/ad.c:808:4 #4 0x555555a0a2a5 in get_mra /src/openldap/servers/slapd/mra.c:151:18 #5 0x5555559ea994 in get_filter0 /src/openldap/servers/slapd/filter.c:286:9 #6 0x5555559baea5 in str2filter_x /src/openldap/servers/slapd/str2filter.c:65:7 #7 0x5555559bb1b7 in str2filter /src/openldap/servers/slapd/str2filter.c:83:9 #8 0x555555b61174 in authzPrettyNormal /src/openldap/servers/slapd/saslauthz.c:804:7 #9 0x555555b617db in authzPretty /src/openldap/servers/slapd/saslauthz.c:905:7 #10 0x555555a86b16 in ordered_value_pretty /src/openldap/servers/slapd/value.c:511:7 #11 0x555555b40939 in slap_mods_check /src/openldap/servers/slapd/modify.c:577:11 #12 0x555555b3efa1 in do_modify /src/openldap/servers/slapd/modify.c:165:15 #13 0x5555559e055d in connection_operation /src/openldap/servers/slapd/connection.c:1137:7 #14 0x5555559df327 in connection_read_thread /src/openldap/servers/slapd/connection.c:1289:14 #15 0x5555559a769a in LLVMFuzzerTestOneInput /src/fuzz_slapd.c:88:5 ```
The function `get_mra` allocates memory for the `MatchingRuleAssertion` structure and its members (such as `ma_desc` or `ma_value`). If a logical error occurs *after* these allocations (e.g., "matching rule not recognized" or "inappropriate matching"), the function previously returned an error code immediately without freeing the allocated memory.
## Fix Implemented a `return_error` label with cleanup logic. * Replaced direct `return` statements in error paths with `goto return_error`. * The `return_error` block calls `mra_free()` to release all resources allocated for the MRA before returning the error code.
## Diff ```diff diff --git a/servers/slapd/mra.c b/servers/slapd/mra.c index 2295c13..55adfc3 100644 --- a/servers/slapd/mra.c +++ b/servers/slapd/mra.c @@ -33,6 +33,8 @@ mra_free( MatchingRuleAssertion *mra, int freeit ) { + if ( mra == NULL ) return; + #ifdef LDAP_COMP_MATCH /* free component assertion */ if ( mra->ma_rule->smr_usage & SLAP_MR_COMPONENT && mra->ma_cf ) { @@ -158,7 +160,8 @@ get_mra( ma.ma_rule = mr_bvfind( &rule_text ); if( ma.ma_rule == NULL ) { *text = "matching rule not recognized"; - return LDAP_INAPPROPRIATE_MATCHING; + rc = LDAP_INAPPROPRIATE_MATCHING; + goto return_error; } }
@@ -168,7 +171,8 @@ get_mra( */ if ( ma.ma_desc == NULL ) { *text = "no matching rule or type"; - return LDAP_INAPPROPRIATE_MATCHING; + rc = LDAP_INAPPROPRIATE_MATCHING; + goto return_error; }
if ( ma.ma_desc->ad_type->sat_equality != NULL && @@ -180,14 +184,16 @@ get_mra(
} else { *text = "no appropriate rule to use for type"; - return LDAP_INAPPROPRIATE_MATCHING; + rc = LDAP_INAPPROPRIATE_MATCHING; + goto return_error; } }
if ( ma.ma_desc != NULL ) { if( !mr_usable_with_at( ma.ma_rule, ma.ma_desc->ad_type ) ) { *text = "matching rule use with this attribute not appropriate"; - return LDAP_INAPPROPRIATE_MATCHING; + rc = LDAP_INAPPROPRIATE_MATCHING; + goto return_error; }
} @@ -200,18 +206,18 @@ get_mra( SLAP_MR_EXT|SLAP_MR_VALUE_OF_ASSERTION_SYNTAX, &value, &ma.ma_value, text, op->o_tmpmemctx );
- if( rc != LDAP_SUCCESS ) return rc; + if( rc != LDAP_SUCCESS ) goto return_error;
#ifdef LDAP_COMP_MATCH /* Check If this attribute is aliased */ if ( is_aliased_attribute && ma.ma_desc && ( aa = is_aliased_attribute ( ma.ma_desc ) ) ) { rc = get_aliased_filter ( op, &ma, aa, text ); - if ( rc != LDAP_SUCCESS ) return rc; + if ( rc != LDAP_SUCCESS ) goto return_error; } else if ( ma.ma_rule && ma.ma_rule->smr_usage & SLAP_MR_COMPONENT ) { /* Matching Rule for Component Matching */ rc = get_comp_filter( op, &ma.ma_value, &ma.ma_cf, text ); - if ( rc != LDAP_SUCCESS ) return rc; + if ( rc != LDAP_SUCCESS ) goto return_error; } #endif
@@ -228,4 +234,8 @@ get_mra( }
return LDAP_SUCCESS; + +return_error: + mra_free( op, &ma, 0 ); + return rc; } ```
## Harness ``` #define _GNU_SOURCE /* For memfd_create */ #include "portable.h" #include <stdio.h> #include <string.h> #include <unistd.h> #include <fcntl.h> #include <sys/mman.h> #include <sys/types.h> #include <signal.h>
#include "slap.h"
/* 1. Fix function declarations to match return types in OpenLDAP source code */ extern void slap_sl_mem_init( void ); extern int slapd_daemon_init( const char *urls ); extern int extops_init( void ); /* Returns int */ extern void lutil_passwd_init( void ); extern int slap_init( int mode, const char *name ); extern int read_config( const char *fname, const char *dname ); extern int connections_init( void ); /* Returns int */ extern int slap_startup( Backend *be ); void slapd_daemon_nothread( void ) {} extern int ldap_pvt_thread_initialize( void ); /* Returns int */ extern void* ldap_pvt_thread_pool_context( void ); extern void* connection_read_thread( void *ctx, void *arg ); extern void connection_closing( Connection *c, const char *why ); extern int connection_resched( Connection *c ); extern Connection* connection_init( int sfd, Listener *l, const char *authid, const char *dns, int flags, slap_ssf_t ssf, struct berval *authid_out ); extern ldap_pvt_thread_pool_t connection_pool;
static Listener *global_listener = NULL; static struct berval authid_bv = {0, ""};
int LLVMFuzzerInitialize(int *argc, char ***argv) { signal(SIGPIPE, SIG_IGN); slap_debug = 0; slap_sl_mem_init();
if ( 0 != slapd_daemon_init("ldapi://%2Ftmp%2Ffuzz.sock") ) return 1;
extops_init(); lutil_passwd_init();
if ( slap_init(SLAP_SERVER_MODE, "slapd-fuzz") ) return 2;
read_config( NULL, NULL );
connections_init(); slap_startup(NULL);
slapd_daemon_nothread(); ldap_pvt_thread_initialize();
global_listener = slapd_get_listeners()[0]; if (!global_listener) return 3;
return 0; }
#include <sys/socket.h>
int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) { if (Size < 2 || Size > 65536) return 0;
/* 2. Use socketpair to simulate socket read/write */ int sv[2]; if (socketpair(AF_UNIX, SOCK_STREAM, 0, sv) < 0) return 0;
if (write(sv[0], Data, Size) != (ssize_t)Size) { close(sv[0]); close(sv[1]); return 0; } /* Close write end so server receives EOF after reading data */ close(sv[0]);
/* 3. Create connection (server uses sv[1]) */ Connection *c = connection_init(sv[1], global_listener, "fuzz", "IP=127.0.0.1", 0, 0, &authid_bv); if (!c) { close(sv[1]); return 0; }
void* ctx = ldap_pvt_thread_pool_context();
/* Execute parsing logic */ connection_read_thread(ctx, (void*)(long)sv[1]);
/* Wait for thread pool to complete all tasks to prevent background threads from accessing freed connection */ int active = 0, pending = 0; do { ldap_pvt_thread_pool_query(&connection_pool, LDAP_PVT_THREAD_POOL_PARAM_PENDING, &pending); ldap_pvt_thread_pool_query(&connection_pool, LDAP_PVT_THREAD_POOL_PARAM_ACTIVE, &active); if (active == 0 && pending == 0) break; ldap_pvt_thread_yield(); } while (1);
/* 4. Thoroughly clean up manually, avoiding unstable macros * Directly manipulate queue pointers to ensure no 'error: use of undeclared identifier o_next' */ Operation *op; while ((op = LDAP_STAILQ_FIRST(&c->c_ops)) != NULL) { LDAP_STAILQ_REMOVE_HEAD(&c->c_ops, o_next); LDAP_STAILQ_NEXT(op, o_next) = NULL; slap_op_free(op, ctx); } LDAP_STAILQ_INIT(&c->c_ops);
/* Close and reclaim Connection memory */ connection_closing(c, "fuzz complete"); connection_resched(c);
/* ldap_pvt_thread_pool_resume(&connection_pool); */
return 0; } ```
https://bugs.openldap.org/show_bug.cgi?id=10445
Ondřej Kuzník ondra@mistotebe.net changed:
What |Removed |Added ---------------------------------------------------------------------------- Assignee|bugs@openldap.org |ondra@mistotebe.net
https://bugs.openldap.org/show_bug.cgi?id=10445
Ondřej Kuzník ondra@mistotebe.net changed:
What |Removed |Added ---------------------------------------------------------------------------- Ever confirmed|0 |1 Status|UNCONFIRMED |IN_PROGRESS
--- Comment #1 from Ondřej Kuzník ondra@mistotebe.net --- https://git.openldap.org/openldap/openldap/-/merge_requests/835
https://bugs.openldap.org/show_bug.cgi?id=10445
--- Comment #2 from KY kangyang126@gmail.com --- Confirmed with the commit, the memory leak is resolved.
https://bugs.openldap.org/show_bug.cgi?id=10445
Quanah Gibson-Mount quanah@openldap.org changed:
What |Removed |Added ---------------------------------------------------------------------------- Keywords|needs_review | Target Milestone|--- |2.6.13
https://bugs.openldap.org/show_bug.cgi?id=10445
Quanah Gibson-Mount quanah@openldap.org changed:
What |Removed |Added ---------------------------------------------------------------------------- Summary|memroy leak in get_mra |memory leak in get_mra
https://bugs.openldap.org/show_bug.cgi?id=10445
Quanah Gibson-Mount quanah@openldap.org changed:
What |Removed |Added ---------------------------------------------------------------------------- Resolution|--- |TEST Status|IN_PROGRESS |RESOLVED
--- Comment #3 from Quanah Gibson-Mount quanah@openldap.org --- head:
• c787dc9b by Ondřej Kuzník at 2026-02-10T23:39:55+00:00 ITS#10445 Free in-progress data if filter parsing fails
RE26:
• c3b1bb77 by Ondřej Kuzník at 2026-02-13T02:34:48+00:00 ITS#10445 Free in-progress data if filter parsing fails
https://bugs.openldap.org/show_bug.cgi?id=10445
Quanah Gibson-Mount quanah@openldap.org changed:
What |Removed |Added ---------------------------------------------------------------------------- Status|RESOLVED |VERIFIED Resolution|TEST |FIXED