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: [email protected]
          Reporter: [email protected]
  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;
}
```

-- 
You are receiving this mail because:
You are on the CC list for the issue.

Reply via email to