You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
nDPI performs 16/32-bit field reads by casting a byte pointer directly and dereferencing it (*(u_int16_t*)&payload[*i] / ntohs(*((u_int16_t *)&buf[offset]))). When the byte offset is odd the pointer is not 2-byte aligned, which is undefined behavior (CWE-843). A build with -fsanitize=address,undefined -fno-sanitize-recover=all traps on it (UBSan reports Type mismatch in operation) and the process dies with SIGILL.
Two concrete sites are reachable from untrusted input and were confirmed to crash:
DNS dissector get16() (src/lib/protocols/dns.c) — an attacker-controlled DNS message whose query/answer/additional walk leaves an odd offset before a 16-bit field is read.
ndpi_serializer.c deserializer helpers — ndpi_init_serializer_ll reads *((u_int16_t*)&deserializer->buffer.data[offset]) on an unaligned offset (reached via get_ndpi_flow_info in example/reader_util.c).
The same pointer-cast pattern appears library-wide (dns.c, ndpi_serializer.c, capwap.c, hpvirtgrp.c, tls.c).
Expected behavior
nDPI should read multi-byte fields from a byte buffer without relying on host alignment: unaligned loads must be performed with memcpy or byte assembly, so the library is portable to strict-alignment CPUs and never triggers UBSan.
Obtained behavior
UBSan aborts the process (SIGILL / exit 134) on a single 45-byte DNS packet, both through the DNS dissector (fuzz_dns_parse) and through the full packet-processing path (fuzz_ndpi_reader). Under ASan/UBSan builds this is a remote DoS for any nDPI consumer that parses untrusted traffic.
nDPI Environment
OS: Ubuntu (OSS-Fuzz base-builder image, x86-64)
OS version: Debian 12 bookworm based
Architecture: x86-64 (the crash is the UBSan trap; on strict-alignment CPUs ARM/MIPS/RISC-V the same misaligned load faults natively)
nDPI version / commit: 252e2a5548a1ea4eb54d3089af836207b3ef32e6 (2026-07-28, "Add Nebula protocol dissector (Add Nebula protocol dissector #3210)"), version 5.1.0
Compilation flags: -fsanitize=address,undefined -fno-sanitize-recover=all (equivalent to ./configure --with-sanitizer)
How to reproduce the reported bug
Used ndpiReader options
# feed the attached pcap (or any of the 3 trigger packets wrapped in Ethernet/IP/UDP)
ndpiReader -i trigger1.pcap
Steps to reproduce
Build nDPI with UBSan: ./autogen.sh && CFLAGS="-fsanitize=address,undefined -fno-sanitize-recover=all" ./configure --with-only-libndpi && make
Generate the trigger packet (45-byte DNS payload, below) and wrap it as a UDP pcap.
Replay:
DNS dissector path: ./fuzz/fuzz_dns_parse < trigger1.bin
Full path: ./ndpiReader -i trigger1.pcap
Observe UBSan Type mismatch in operation trap → SIGILL / exit 134.
Trigger packets (45 bytes each, raw DNS payload, UDP)
Why: process_queries() loops QDCOUNT=12 but only one real query exists. On the 2nd iteration getNameLength(28) returns 1 (payload[28]=0x00), so x=29 (odd) and get16() performs *(u_int16_t*)&payload[29] — unaligned 16-bit load.
Trigger 2 — DNS response with EDNS OPT additional record:
Why: getNameLength returns 2 for the 0xC2 pointer and the following walk lands on an odd offset.
Trigger result
DNS dissector path (fuzz_dns_parse, all 3 triggers):
==806276==ERROR: AddressSanitizer: ILL on unknown address 0x5619571c2925 (pc 0x5619571c2925 ...)
#0 0x5619571c2925 in __clang_trap_msg$Undefined Behavior Sanitizer$Type mismatch in operation /src/ndpi/src/lib/./protocols/dns.c
#1 0x5619571c2925 in get16 /src/ndpi/src/lib/./protocols/dns.c
#2 0x5619571c2925 in process_queries /src/ndpi/src/lib/./protocols/dns.c:340:16
#3 0x5619571be13f in search_dns /src/ndpi/src/lib/./protocols/dns.c:1062:10
#4 0x5619571b812f in LLVMFuzzerTestOneInput /src/ndpi/fuzz/fuzz_dns_parse.c:94:3
...
SUMMARY: AddressSanitizer: ILL /src/ndpi/src/lib/./protocols/dns.c in __clang_trap_msg$Undefined Behavior Sanitizer$Type mismatch in operation
Full packet-processing path (fuzz_ndpi_reader, same pcap) — same root cause, second site:
==807234==ERROR: AddressSanitizer: ILL on unknown address ...
#0 ... in __clang_trap_msg$Undefined Behavior Sanitizer$Type mismatch in operation /src/ndpi/src/lib/ndpi_serializer.c
#1 ... in ndpi_init_serializer_ll /src/ndpi/src/lib/ndpi_serializer.c
#2 ... in get_ndpi_flow_info /src/ndpi/fuzz/../example/reader_util.c:944:11
SUMMARY: AddressSanitizer: ILL ... in __clang_trap_msg$Undefined Behavior Sanitizer$Type mismatch in operation
Replay exit code: 134 (UBSan trap aborts)
Deterministic: yes — all 3 triggers reproduce; nDPI's own dns_resp / dns_query_opt seeds crash too
Note: on x86-64 the misaligned load does not fault natively (the crash is the UBSan trap), but on strict-alignment architectures (ARM, MIPS, RISC-V) the same code faults in production.
Additional context
Affected code (DNS get16):
/* src/lib/protocols/dns.c:162-168 */staticu_int16_tget16(u_int*i, constu_int8_t*payload) {
u_int16_tv=*(u_int16_t*)&payload[*i]; /* <-- unaligned 16-bit load when *i is odd */
(*i) +=2;
return(ntohs(v));
}
In the DNS walkers (process_queries / process_answers / process_additionals), treat an odd walk offset as malformed (NDPI_MALFORMED_PACKET) and bail out before calling get16().
Audit the whole library for *(u_int16_t*)& / *(u_int32_t*)& pointer-cast dereferences (present in dns.c, ndpi_serializer.c, capwap.c, hpvirtgrp.c, tls.c) and replace with portable safe reads.
Describe the bug
nDPI performs 16/32-bit field reads by casting a byte pointer directly and dereferencing it (
*(u_int16_t*)&payload[*i]/ntohs(*((u_int16_t *)&buf[offset]))). When the byte offset is odd the pointer is not 2-byte aligned, which is undefined behavior (CWE-843). A build with-fsanitize=address,undefined -fno-sanitize-recover=alltraps on it (UBSan reportsType mismatch in operation) and the process dies with SIGILL.Two concrete sites are reachable from untrusted input and were confirmed to crash:
get16()(src/lib/protocols/dns.c) — an attacker-controlled DNS message whose query/answer/additional walk leaves an odd offset before a 16-bit field is read.ndpi_serializer.cdeserializer helpers —ndpi_init_serializer_llreads*((u_int16_t*)&deserializer->buffer.data[offset])on an unaligned offset (reached viaget_ndpi_flow_infoinexample/reader_util.c).The same pointer-cast pattern appears library-wide (
dns.c,ndpi_serializer.c,capwap.c,hpvirtgrp.c,tls.c).Expected behavior
nDPI should read multi-byte fields from a byte buffer without relying on host alignment: unaligned loads must be performed with
memcpyor byte assembly, so the library is portable to strict-alignment CPUs and never triggers UBSan.Obtained behavior
UBSan aborts the process (SIGILL / exit 134) on a single 45-byte DNS packet, both through the DNS dissector (
fuzz_dns_parse) and through the full packet-processing path (fuzz_ndpi_reader). Under ASan/UBSan builds this is a remote DoS for any nDPI consumer that parses untrusted traffic.nDPI Environment
base-builderimage, x86-64)252e2a5548a1ea4eb54d3089af836207b3ef32e6(2026-07-28, "Add Nebula protocol dissector (Add Nebula protocol dissector #3210)"), version 5.1.0-fsanitize=address,undefined -fno-sanitize-recover=all(equivalent to./configure --with-sanitizer)How to reproduce the reported bug
Used ndpiReader options
# feed the attached pcap (or any of the 3 trigger packets wrapped in Ethernet/IP/UDP) ndpiReader -i trigger1.pcapSteps to reproduce
./autogen.sh && CFLAGS="-fsanitize=address,undefined -fno-sanitize-recover=all" ./configure --with-only-libndpi && make./fuzz/fuzz_dns_parse < trigger1.bin./ndpiReader -i trigger1.pcapType mismatch in operationtrap → SIGILL / exit 134.Trigger packets (45 bytes each, raw DNS payload, UDP)
Trigger 1 — QDCOUNT=12 malformed query:
Structure:
Header: ID=0x4d15 Flags=0x0120 (query) QDCOUNT=0x000c (12!) ANCOUNT=0 NSCOUNT=0 ARCOUNT=1; QNAME: \x03www \x04ntop \x03org \x00; QTYPE=0x0001; QCLASS=0x0001; trailing EDNS OPT.Why:
process_queries()loops QDCOUNT=12 but only one real query exists. On the 2nd iterationgetNameLength(28)returns 1 (payload[28]=0x00), sox=29(odd) andget16()performs*(u_int16_t*)&payload[29]— unaligned 16-bit load.Trigger 2 — DNS response with EDNS OPT additional record:
Why:
process_additionalswalks the OPT record; at off=28 the name terminator makesgetNameLengthreturn 1, sox=29(odd).Trigger 3 — QNAME containing compression-pointer byte 0xC2:
Why:
getNameLengthreturns 2 for the 0xC2 pointer and the following walk lands on an odd offset.Trigger result
DNS dissector path (
fuzz_dns_parse, all 3 triggers):Full packet-processing path (
fuzz_ndpi_reader, same pcap) — same root cause, second site:134(UBSan trap aborts)dns_resp/dns_query_optseeds crash tooAdditional context
Affected code (DNS
get16):Affected code (serializer):
Suggested fix:
memcpy, e.g.:In the DNS walkers (
process_queries/process_answers/process_additionals), treat an odd walk offset as malformed (NDPI_MALFORMED_PACKET) and bail out before callingget16().Audit the whole library for
*(u_int16_t*)&/*(u_int32_t*)&pointer-cast dereferences (present indns.c,ndpi_serializer.c,capwap.c,hpvirtgrp.c,tls.c) and replace with portable safe reads.