https://bugs.openldap.org/show_bug.cgi?id=10430
Issue ID: 10430 Summary: Heap Buffer Overflow in parse_whsp causes Out-of-Bounds Read Product: OpenLDAP Version: unspecified Hardware: x86_64 OS: Linux Status: UNCONFIRMED Keywords: needs_review Severity: normal Priority: --- Component: libraries Assignee: bugs@openldap.org Reporter: zesheng@tamu.edu Target Milestone: ---
## Summary
A heap buffer overflow vulnerability in OpenLDAP's `parse_whsp()` function allows an attacker to trigger out-of-bounds memory reads when parsing malformed LDAP schema definitions, potentially leading to information disclosure or crashes.
## Root Cause
The vulnerability exists in `libraries/libldap/schema.c` at line 1090. The `parse_whsp()` function iterates through a string checking for whitespace characters using `LDAP_SPACE()` macro, but does not verify that the pointer stays within the allocated buffer bounds. When parsing malformed schema strings that end unexpectedly, the function reads beyond the heap buffer.
### Vulnerable Code (schema.c:1086-1092) ```c /* Gobble optional whitespace */ static void parse_whsp(const char **sp) { while (LDAP_SPACE(**sp)) // BUG: no bounds check, can read past buffer (*sp)++; } ```
### LDAP_SPACE Macro (ldap_pvt.h:255) ```c #define LDAP_SPACE(c) ((c) == ' ' || (c) == '\t' || (c) == '\n') ```
The macro checks for space/tab/newline but does NOT check for null terminator. If the heap memory immediately after the allocated buffer happens to contain whitespace bytes (0x20, 0x09, 0x0a), the function continues reading out of bounds.
## PoC
### Trigger file
A crafted schema string (`poc`, 49 bytes) that triggers the overflow:
``` (13.5 NAME 'caseExactMatch' SYNTAX 'nooideithe1r. ```
``` hexdump -C poc: 00000000 28 31 33 2e 35 20 4e 41 4d 45 20 27 63 61 73 65 |(13.5 NAME 'case| 00000010 45 78 61 63 74 4d 61 74 63 68 27 20 53 59 4e 54 |ExactMatch' SYNT| 00000020 41 58 20 27 6e 6f 6f 69 64 65 69 74 68 65 31 72 |AX 'nooideithe1r| 00000030 2e |.| ```
### How to generate poc
```python # generate_poc.py data = b"(13.5 NAME 'caseExactMatch' SYNTAX 'nooideithe1r." with open("poc", "wb") as f: f.write(data) ```
---
## Trigger Method 1: Direct API Call
```c // test_schema.c #include <stdio.h> #include <stdlib.h> #include <string.h> #include <ldap.h> #include <ldap_schema.h>
int main(int argc, char **argv) { FILE *f = fopen("poc", "rb"); fseek(f, 0, SEEK_END); long size = ftell(f); fseek(f, 0, SEEK_SET);
char *input = malloc(size + 1); fread(input, 1, size, f); input[size] = '\0'; fclose(f);
int code = 0; const char *errp = NULL;
// This triggers the vulnerability LDAPAttributeType *at = ldap_str2attributetype(input, &code, &errp, LDAP_SCHEMA_ALLOW_ALL); if (at) { ldap_attributetype_free(at); }
free(input); return 0; } ```
**Build and run:** ```bash # Build OpenLDAP with AddressSanitizer cd openldap ./configure CC=clang CFLAGS="-fsanitize=address -g" --without-tls make
# Compile and run test clang -fsanitize=address -g -I include \ -L libraries/libldap/.libs -L libraries/liblber/.libs \ test_schema.c -o test_schema -lldap -llber \ -Wl,-rpath,libraries/libldap/.libs:libraries/liblber/.libs
./test_schema ```
**Output:** ``` ==PID==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x506000000052 at pc 0x... bp 0x... sp 0x... READ of size 1 at 0x506000000052 thread T0 #0 0x... in parse_whsp libraries/libldap/schema.c:1090:9 #1 0x... in ldap_str2attributetype libraries/libldap/schema.c:2313:5
0x506000000052 is located 0 bytes after 50-byte region [0x506000000020,0x506000000052) allocated by thread T0 here: #0 0x... in malloc #1 0x... in main test_schema.c:...
SUMMARY: AddressSanitizer: heap-buffer-overflow schema.c:1090:9 in parse_whsp ```
---
## Trigger Method 2: Fuzzer
```c // fuzz_schema.c #include <stdint.h> #include <stddef.h> #include <stdlib.h> #include <string.h> #include <ldap.h> #include <ldap_schema.h>
static const unsigned int flags[] = { LDAP_SCHEMA_ALLOW_NONE, LDAP_SCHEMA_ALLOW_NO_OID, LDAP_SCHEMA_ALLOW_QUOTED, LDAP_SCHEMA_ALLOW_DESCR, LDAP_SCHEMA_ALLOW_ALL, };
#define NUM_FLAGS (sizeof(flags) / sizeof(flags[0]))
int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { if (size == 0 || size > 16384) return 0;
char *input = (char *)malloc(size + 1); if (!input) return 0; memcpy(input, data, size); input[size] = '\0';
int code = 0; const char *errp = NULL;
// Test ldap_str2attributetype with different flags for (size_t i = 0; i < NUM_FLAGS; i++) { code = 0; errp = NULL; LDAPAttributeType *at = ldap_str2attributetype(input, &code, &errp, flags[i]); if (at) { ldap_attributetype_free(at); } }
// Test ldap_str2objectclass for (size_t i = 0; i < NUM_FLAGS; i++) { code = 0; errp = NULL; LDAPObjectClass *oc = ldap_str2objectclass(input, &code, &errp, flags[i]); if (oc) { ldap_objectclass_free(oc); } }
free(input); return 0; } ```
**Build and run:** ```bash clang -fsanitize=fuzzer,address -g -I include \ -L libraries/libldap/.libs -L libraries/liblber/.libs \ fuzz_schema.c -o fuzz_schema -lldap -llber \ -Wl,-rpath,libraries/libldap/.libs:libraries/liblber/.libs
mkdir corpus && cp poc corpus/ ./fuzz_schema corpus/ ```
---
## Impact
| Aspect | Details | |--------|---------| | **Type** | Out-of-Bounds Read / Information Disclosure | | **Severity** | High | | **Attack Vector** | Malicious schema definition string | | **Affected Components** | `parse_whsp()`, `ldap_str2attributetype()`, `ldap_str2objectclass()`, libldap | | **Affected Applications** | Any application using libldap to parse LDAP schema definitions | | **CWE** | CWE-125 (Out-of-bounds Read) |
---
## Suggested Fix
Add bounds checking in `parse_whsp()` to ensure it doesn't read past the null terminator:
```c static void parse_whsp(const char **sp) { while (**sp && LDAP_SPACE(**sp)) // Add null check (*sp)++; } ```
Note: `LDAP_SPACE('\0')` is false (0x00 is not space/tab/newline), so the issue only occurs when adjacent heap memory contains whitespace bytes. However, adding an explicit null check makes the code more robust and clearly documents the termination condition.
https://bugs.openldap.org/show_bug.cgi?id=10430
Quanah Gibson-Mount quanah@openldap.org changed:
What |Removed |Added ---------------------------------------------------------------------------- Keywords|needs_review | Target Milestone|--- |2.6.11
https://bugs.openldap.org/show_bug.cgi?id=10430
Quanah Gibson-Mount quanah@openldap.org changed:
What |Removed |Added ---------------------------------------------------------------------------- Target Milestone|2.6.11 |2.6.12
https://bugs.openldap.org/show_bug.cgi?id=10430
Ondřej Kuzník ondra@mistotebe.net changed:
What |Removed |Added ---------------------------------------------------------------------------- Ever confirmed|0 |1 Status|UNCONFIRMED |IN_PROGRESS Assignee|bugs@openldap.org |ondra@mistotebe.net
--- Comment #1 from Ondřej Kuzník ondra@mistotebe.net --- Please test with https://git.openldap.org/openldap/openldap/-/merge_requests/826
https://bugs.openldap.org/show_bug.cgi?id=10430
Quanah Gibson-Mount quanah@openldap.org changed:
What |Removed |Added ---------------------------------------------------------------------------- Status|IN_PROGRESS |RESOLVED Resolution|--- |FIXED
--- Comment #2 from Quanah Gibson-Mount quanah@openldap.org --- head:
• 2bf4c168 by Ondřej Kuzník at 2026-01-29T22:15:52+00:00 ITS#10429 Empty types are not valid LDIF, reject sooner
RE26:
• d13c4ab4 by Ondřej Kuzník at 2026-01-29T22:16:41+00:00 ITS#10430 Do not scroll past nul bytes
https://bugs.openldap.org/show_bug.cgi?id=10430
Quanah Gibson-Mount quanah@openldap.org changed:
What |Removed |Added ---------------------------------------------------------------------------- Status|RESOLVED |VERIFIED
https://bugs.openldap.org/show_bug.cgi?id=10430
--- Comment #3 from zesheng@tamu.edu --- Hi,
Thank you for the fix. We tested commit 2c5388b on master with our original PoC, but the issue still reproduces.
### Test Code
```c #include <stdio.h> #include <stdlib.h> #include <string.h> #include <ldap.h> #include <ldap_schema.h>
int main(int argc, char **argv) { FILE *f = fopen("poc", "rb"); fseek(f, 0, SEEK_END); long size = ftell(f); fseek(f, 0, SEEK_SET);
char *input = malloc(size + 1); fread(input, 1, size, f); input[size] = '\0'; fclose(f);
int code = 0; const char *errp = NULL; LDAPAttributeType *at = ldap_str2attributetype(input, &code, &errp, LDAP_SCHEMA_ALLOW_ALL); if (at) ldap_attributetype_free(at);
free(input); return 0; } ```
PoC (49 bytes): ``` (13.5 NAME 'caseExactMatch' SYNTAX 'nooideithe1r. ```
Build: ```bash clang -fsanitize=address -g -O1 -I include \ -L libraries/libldap/.libs -L libraries/liblber/.libs \ test.c -o test -lldap -llber \ -Wl,-rpath,libraries/libldap/.libs:libraries/liblber/.libs ```
### Crash Trace
``` ==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x5060000000b2 at pc 0x7fb2de6cc7b2 bp 0x7ffd07918e30 sp 0x7ffd07918e28 READ of size 1 at 0x5060000000b2 thread T0 #0 in parse_whsp schema.c:1090:10 #1 in ldap_str2attributetype schema.c:2313:5 #2 in main test.c:18:29
0x5060000000b2 is located 0 bytes after 50-byte region [0x506000000080,0x5060000000b2) allocated by thread T0 here: #0 in malloc #1 in main test.c:11:19
SUMMARY: AddressSanitizer: heap-buffer-overflow schema.c:1090:10 in parse_whsp ```
We are happy to test any updated patches.
https://bugs.openldap.org/show_bug.cgi?id=10430
--- Comment #4 from Ondřej Kuzník ondra@mistotebe.net --- On Fri, Jan 30, 2026 at 01:13:41PM +0000, openldap-its@openldap.org wrote:
Hi,
Thank you for the fix. We tested commit 2c5388b on master with our original PoC, but the issue still reproduces.
Hi, you're right, looking at it, there are probably more of these in the parser as it doesn't check for end of text in important places. Are you interested in providing patches and a corpus that you've accumulated as part of this work? With help and support from someone in the project of course.
Thanks,
https://bugs.openldap.org/show_bug.cgi?id=10430
--- Comment #5 from zesheng@tamu.edu --- Sure! Happy to do so!
May I paste my patch directly in this thread?
https://bugs.openldap.org/show_bug.cgi?id=10430
--- Comment #6 from Ondřej Kuzník ondra@mistotebe.net --- On Sat, Jan 31, 2026 at 10:12:50AM +0000, openldap-its@openldap.org wrote:
Sure! Happy to do so!
May I paste my patch directly in this thread?
For patches, first check out: https://openldap.org/devel/contributing.html
Afterwards, if you prefer posting the patches as attachments to the bug, do that. If you prefer tracking the work in a merge request, follow the sign-up instructions outlined here: https://git.openldap.org/users/sign_up
Either approach is fine with us.
For the corpus that you accumulate as part of it, uploading it here for posterity will probably do as a start. That's unless you find a decent way to integrate it within the existing codebase which would be great but probably a daunting task especially since you're not familiar with the project yet.
Thanks,
https://bugs.openldap.org/show_bug.cgi?id=10430
--- Comment #7 from dwijimehta@gmail.com --- Created attachment 1113 --> https://bugs.openldap.org/attachment.cgi?id=1113&action=edit [PATCH] ITS#10430 - do not advance past NUL in get_token()
This patch does not crash on the PoC and passes all other tests.
https://bugs.openldap.org/show_bug.cgi?id=10430
Quanah Gibson-Mount quanah@openldap.org changed:
What |Removed |Added ---------------------------------------------------------------------------- Status|VERIFIED |CONFIRMED Resolution|FIXED |---
https://bugs.openldap.org/show_bug.cgi?id=10430
Quanah Gibson-Mount quanah@openldap.org changed:
What |Removed |Added ---------------------------------------------------------------------------- Target Milestone|2.6.12 |2.6.13
https://bugs.openldap.org/show_bug.cgi?id=10430
--- Comment #8 from Ondřej Kuzník ondra@mistotebe.net --- On Thu, Feb 05, 2026 at 12:08:28AM +0000, openldap-its@openldap.org wrote:
This patch does not crash on the PoC and passes all other tests.
Thanks, applied to master as 1ac9e3806396111020548d743948a1673be14210
https://bugs.openldap.org/show_bug.cgi?id=10430
Quanah Gibson-Mount quanah@openldap.org changed:
What |Removed |Added ---------------------------------------------------------------------------- Resolution|--- |TEST Status|CONFIRMED |RESOLVED
--- Comment #9 from Quanah Gibson-Mount quanah@openldap.org --- head:
• 1ac9e380 by Dwij Mehta at 2026-02-10T18:11:31+00:00 ITS#10430 - do not advance past NUL in get_token()
RE26:
• afda4781 by Dwij Mehta at 2026-02-13T02:29:06+00:00 ITS#10430 - do not advance past NUL in get_token()
https://bugs.openldap.org/show_bug.cgi?id=10430
Quanah Gibson-Mount quanah@openldap.org changed:
What |Removed |Added ---------------------------------------------------------------------------- Status|RESOLVED |VERIFIED Resolution|TEST |FIXED