From 7866d35199624b9129cd2bdca0976a155ae1e581 Mon Sep 17 00:00:00 2001 From: RajaMuhammadAwais <1.19938988e+08+RajaMuhammadAwais@users.noreply.github.com> Date: Sun, 23 Aug 2026 08:44:59 +0000 Subject: [PATCH 1/5] fix: avoid unaligned reads in DNS and serializers --- fuzz/corpus/fuzz_dns_parse/issue3213_trigger1 | Bin 0 -> 45 bytes fuzz/corpus/fuzz_dns_parse/issue3213_trigger2 | Bin 0 -> 45 bytes fuzz/corpus/fuzz_dns_parse/issue3213_trigger3 | Bin 0 -> 45 bytes src/include/ndpi_define.h.in | 39 ++++++++++++------ src/include/ndpi_typedefs.h | 4 +- src/lib/ndpi_serializer.c | 12 +++--- src/lib/protocols/dns.c | 12 +++--- 7 files changed, 40 insertions(+), 27 deletions(-) create mode 100644 fuzz/corpus/fuzz_dns_parse/issue3213_trigger1 create mode 100644 fuzz/corpus/fuzz_dns_parse/issue3213_trigger2 create mode 100644 fuzz/corpus/fuzz_dns_parse/issue3213_trigger3 diff --git a/fuzz/corpus/fuzz_dns_parse/issue3213_trigger1 b/fuzz/corpus/fuzz_dns_parse/issue3213_trigger1 new file mode 100644 index 0000000000000000000000000000000000000000..822189b6234fc1f46529f8bc3ad9b6a7237ecedc GIT binary patch literal 45 rcmeYjWmI6`VE_R}=JN7#mb{Yu0_OapbRZuH88ijJattgC%nS?wlHUbO literal 0 HcmV?d00001 diff --git a/fuzz/corpus/fuzz_dns_parse/issue3213_trigger2 b/fuzz/corpus/fuzz_dns_parse/issue3213_trigger2 new file mode 100644 index 0000000000000000000000000000000000000000..184eede3b202a8b2146d63553bf2f110b67a574b GIT binary patch literal 45 rcmeYjU9G^t$N&P2%;n|fEO{mQ1t1=JN7#mb{Yuf +/* The get_uXX helpers return raw network packet bytes. */ #include -static inline uint64_t get_u_int64_t(const uint8_t* X, int O) + +#define get_u_int8_t(X,O) (((const u_int8_t *)(X))[O]) + +static inline u_int16_t ndpi_get_u_int16_t(const void *X, int O) { - uint64_t tmp; - memcpy(&tmp, X + O, sizeof(tmp)); - return tmp; + u_int16_t value; + memcpy(&value, ((const u_int8_t *)X) + O, sizeof(value)); + return value; } -#else -#define get_u_int64_t(X,O) (*(u_int64_t *)((&(((u_int8_t *)X)[O])))) -#endif // __arm__ + +static inline u_int32_t ndpi_get_u_int32_t(const void *X, int O) +{ + u_int32_t value; + memcpy(&value, ((const u_int8_t *)X) + O, sizeof(value)); + return value; +} + +static inline u_int64_t ndpi_get_u_int64_t(const void *X, int O) +{ + u_int64_t value; + memcpy(&value, ((const u_int8_t *)X) + O, sizeof(value)); + return value; +} + +#define get_u_int16_t(X,O) ndpi_get_u_int16_t((X), (O)) +#define get_u_int32_t(X,O) ndpi_get_u_int32_t((X), (O)) +#define get_u_int64_t(X,O) ndpi_get_u_int64_t((X), (O)) /* new definitions to get little endian from network bytes */ #define get_ul8(X,O) get_u_int8_t(X,O) diff --git a/src/include/ndpi_typedefs.h b/src/include/ndpi_typedefs.h index d4829c1c714..644d203c6ad 100644 --- a/src/include/ndpi_typedefs.h +++ b/src/include/ndpi_typedefs.h @@ -1544,9 +1544,9 @@ typedef struct { #define ndpi_private_deserializer ndpi_private_serializer #ifdef NDPI_CFFI_PREPROCESSING -typedef struct { char c[72]; } ndpi_serializer; +typedef union { u_int64_t _alignment; char c[72]; } ndpi_serializer; #else -typedef struct { char c[sizeof(ndpi_private_serializer)]; } ndpi_serializer; +typedef union { ndpi_private_serializer _alignment; char c[sizeof(ndpi_private_serializer)]; } ndpi_serializer; #endif #define ndpi_deserializer ndpi_serializer diff --git a/src/lib/ndpi_serializer.c b/src/lib/ndpi_serializer.c index 4824f464815..e52324b3983 100644 --- a/src/lib/ndpi_serializer.c +++ b/src/lib/ndpi_serializer.c @@ -545,14 +545,14 @@ static inline void ndpi_deserialize_single_uint8(ndpi_private_deserializer *dese static inline void ndpi_deserialize_single_uint16(ndpi_private_deserializer *deserializer, u_int32_t offset, u_int16_t *s) { - *s = ntohs(*((u_int16_t *) &deserializer->buffer.data[offset])); + *s = ntohs(get_u_int16_t(deserializer->buffer.data, offset)); } /* ********************************** */ static inline void ndpi_deserialize_single_uint32(ndpi_private_deserializer *deserializer, u_int32_t offset, u_int32_t *s) { - *s = ntohl(*((u_int32_t *) &deserializer->buffer.data[offset])); + *s = ntohl(get_u_int32_t(deserializer->buffer.data, offset)); } /* ********************************** */ @@ -566,28 +566,28 @@ static inline void ndpi_deserialize_single_int8(ndpi_private_deserializer *deser static inline void ndpi_deserialize_single_int16(ndpi_private_deserializer *deserializer, u_int32_t offset, int16_t *s) { - *s = ntohs(*((int16_t *) &deserializer->buffer.data[offset])); + *s = ntohs(get_u_int16_t(deserializer->buffer.data, offset)); } /* ********************************** */ static inline void ndpi_deserialize_single_int32(ndpi_private_deserializer *deserializer, u_int32_t offset, int32_t *s) { - *s = ntohl(*((int32_t *) &deserializer->buffer.data[offset])); + *s = ntohl(get_u_int32_t(deserializer->buffer.data, offset)); } /* ********************************** */ static inline void ndpi_deserialize_single_uint64(ndpi_private_deserializer *deserializer, u_int32_t offset, u_int64_t *s) { - *s = ndpi_ntohll(*(u_int64_t*)&deserializer->buffer.data[offset]); + *s = ndpi_ntohll(get_u_int64_t(deserializer->buffer.data, offset)); } /* ********************************** */ static inline void ndpi_deserialize_single_int64(ndpi_private_deserializer *deserializer, u_int32_t offset, int64_t *s) { - *s = ndpi_ntohll(*(int64_t*)&deserializer->buffer.data[offset]); + *s = ndpi_ntohll(get_u_int64_t(deserializer->buffer.data, offset)); } /* ********************************** */ diff --git a/src/lib/protocols/dns.c b/src/lib/protocols/dns.c index 38be95b7fd4..3095abe4bdf 100644 --- a/src/lib/protocols/dns.c +++ b/src/lib/protocols/dns.c @@ -148,7 +148,7 @@ static u_int16_t checkDNSSubprotocol(u_int16_t sport, u_int16_t dport) { /* *********************************************** */ static u_int16_t get16(u_int *i, const u_int8_t *payload) { - u_int16_t v = *(u_int16_t*)&payload[*i]; + u_int16_t v = get_u_int16_t(payload, *i); (*i) += 2; @@ -230,7 +230,7 @@ static u_int64_t fpc_dns_cache_key_from_packet(const unsigned char *ip, int ip_l if(ip_len == 16) key = ndpi_quick_hash64((const char *)ip, 16); else - key = (u_int64_t)(*(u_int32_t *)ip); + key = (u_int64_t)get_u_int32_t(ip, 0); return key; } @@ -581,21 +581,21 @@ static int process_additionals(struct ndpi_detection_module_struct *ndpi_struct, if(rsp_type == 41 /* OPT */) { /* https://en.wikipedia.org/wiki/Extension_Mechanisms_for_DNS */ - flow->protos.dns.edns0_udp_payload_size = ntohs(*((u_int16_t*)&packet->payload[x])); /* EDNS(0) */ + flow->protos.dns.edns0_udp_payload_size = ntohs(get_u_int16_t(packet->payload, x)); /* EDNS(0) */ #ifdef DNS_DEBUG printf("[DNS] [response] edns0_udp_payload_size: %u\n", flow->protos.dns.edns0_udp_payload_size); #endif x += 6; - rdata_len = ntohs(*((u_int16_t *)&packet->payload[x])); + rdata_len = ntohs(get_u_int16_t(packet->payload, x)); #ifdef DNS_DEBUG printf("[DNS] [response] rdata len: %u\n", rdata_len); #endif if(rdata_len > 0 && x + 6 <= packet->payload_packet_len) { - opt_code = ntohs(*((u_int16_t *)&packet->payload[x + 2])); - opt_len = ntohs(*((u_int16_t *)&packet->payload[x + 4])); + opt_code = ntohs(get_u_int16_t(packet->payload, x + 2)); + opt_len = ntohs(get_u_int16_t(packet->payload, x + 4)); opt = &packet->payload[x + 6]; /* TODO: parse the TLV list */ if(opt_code == 0x03 && From 969afcc6fc716e294dca144906c28d26afb724c3 Mon Sep 17 00:00:00 2001 From: RajaMuhammadAwais <1.19938988e+08+RajaMuhammadAwais@users.noreply.github.com> Date: Mon, 24 Aug 2026 16:00:33 +0000 Subject: [PATCH 2/5] fix: make alignment helpers portable for CI --- src/include/ndpi_define.h.in | 23 +++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/src/include/ndpi_define.h.in b/src/include/ndpi_define.h.in index df6df5f7afd..a6243c3598e 100644 --- a/src/include/ndpi_define.h.in +++ b/src/include/ndpi_define.h.in @@ -132,34 +132,37 @@ #define NDPI_ARRAY_LENGTH(array) (sizeof(array) / sizeof((array)[0])) /* The get_uXX helpers return raw network packet bytes. */ +#ifndef NDPI_CFFI_PREPROCESSING +#include #include -#define get_u_int8_t(X,O) (((const u_int8_t *)(X))[O]) +#define get_u_int8_t(X,O) (((const uint8_t *)(X))[O]) -static inline u_int16_t ndpi_get_u_int16_t(const void *X, int O) +static inline uint16_t ndpi_get_u_int16_t(const void *X, int O) { - u_int16_t value; - memcpy(&value, ((const u_int8_t *)X) + O, sizeof(value)); + uint16_t value; + memcpy(&value, ((const uint8_t *)X) + O, sizeof(value)); return value; } -static inline u_int32_t ndpi_get_u_int32_t(const void *X, int O) +static inline uint32_t ndpi_get_u_int32_t(const void *X, int O) { - u_int32_t value; - memcpy(&value, ((const u_int8_t *)X) + O, sizeof(value)); + uint32_t value; + memcpy(&value, ((const uint8_t *)X) + O, sizeof(value)); return value; } -static inline u_int64_t ndpi_get_u_int64_t(const void *X, int O) +static inline uint64_t ndpi_get_u_int64_t(const void *X, int O) { - u_int64_t value; - memcpy(&value, ((const u_int8_t *)X) + O, sizeof(value)); + uint64_t value; + memcpy(&value, ((const uint8_t *)X) + O, sizeof(value)); return value; } #define get_u_int16_t(X,O) ndpi_get_u_int16_t((X), (O)) #define get_u_int32_t(X,O) ndpi_get_u_int32_t((X), (O)) #define get_u_int64_t(X,O) ndpi_get_u_int64_t((X), (O)) +#endif /* NDPI_CFFI_PREPROCESSING */ /* new definitions to get little endian from network bytes */ #define get_ul8(X,O) get_u_int8_t(X,O) From 2c2e0430583ec5dc176c8e708bdf5b6b2570d395 Mon Sep 17 00:00:00 2001 From: RajaMuhammadAwais <1.19938988e+08+RajaMuhammadAwais@users.noreply.github.com> Date: Mon, 24 Aug 2026 16:08:03 +0000 Subject: [PATCH 3/5] fix: keep serializer alignment type visible to cffi --- src/include/ndpi_typedefs.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/include/ndpi_typedefs.h b/src/include/ndpi_typedefs.h index 644d203c6ad..85211ee03d7 100644 --- a/src/include/ndpi_typedefs.h +++ b/src/include/ndpi_typedefs.h @@ -1544,7 +1544,7 @@ typedef struct { #define ndpi_private_deserializer ndpi_private_serializer #ifdef NDPI_CFFI_PREPROCESSING -typedef union { u_int64_t _alignment; char c[72]; } ndpi_serializer; +typedef union { ndpi_private_serializer _alignment; char c[72]; } ndpi_serializer; #else typedef union { ndpi_private_serializer _alignment; char c[sizeof(ndpi_private_serializer)]; } ndpi_serializer; #endif From 0f9b793217ca9528ac0a2a8f4744efb46466200c Mon Sep 17 00:00:00 2001 From: RajaMuhammadAwais <1.19938988e+08+RajaMuhammadAwais@users.noreply.github.com> Date: Wed, 26 Aug 2026 10:10:08 +0000 Subject: [PATCH 4/5] Document serializer storage alignment contract --- src/include/ndpi_typedefs.h | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/include/ndpi_typedefs.h b/src/include/ndpi_typedefs.h index 85211ee03d7..bb62d4a6ada 100644 --- a/src/include/ndpi_typedefs.h +++ b/src/include/ndpi_typedefs.h @@ -1543,12 +1543,26 @@ typedef struct { #define ndpi_private_deserializer ndpi_private_serializer +/* + * The public serializer is opaque, but its storage is reinterpreted as + * ndpi_private_serializer by the implementation. Keep the private type as a + * union member so the public object carries the private type's alignment while + * retaining the existing character-buffer storage contract. + */ #ifdef NDPI_CFFI_PREPROCESSING typedef union { ndpi_private_serializer _alignment; char c[72]; } ndpi_serializer; #else typedef union { ndpi_private_serializer _alignment; char c[sizeof(ndpi_private_serializer)]; } ndpi_serializer; #endif +#if !defined(NDPI_CFFI_PREPROCESSING) \ + && defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201112L +_Static_assert(_Alignof(ndpi_serializer) == _Alignof(ndpi_private_serializer), + "ndpi_serializer must preserve ndpi_private_serializer alignment"); +_Static_assert(sizeof(ndpi_serializer) == sizeof(ndpi_private_serializer), + "ndpi_serializer storage size must match ndpi_private_serializer"); +#endif + #define ndpi_deserializer ndpi_serializer /* **************************************** */ From 2d4f051a2c9e9a70d9b2d0bed7511a28c89f6642 Mon Sep 17 00:00:00 2001 From: RajaMuhammadAwais <1.19938988e+08+RajaMuhammadAwais@users.noreply.github.com> Date: Wed, 26 Aug 2026 10:30:26 +0000 Subject: [PATCH 5/5] Simplify serializer alignment contract assertion --- src/include/ndpi_typedefs.h | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/include/ndpi_typedefs.h b/src/include/ndpi_typedefs.h index bb62d4a6ada..bfb21b3deaf 100644 --- a/src/include/ndpi_typedefs.h +++ b/src/include/ndpi_typedefs.h @@ -1547,7 +1547,8 @@ typedef struct { * The public serializer is opaque, but its storage is reinterpreted as * ndpi_private_serializer by the implementation. Keep the private type as a * union member so the public object carries the private type's alignment while - * retaining the existing character-buffer storage contract. + * retaining the existing character-buffer storage contract. The union member + * is the alignment guarantee; the assertion below protects the storage size. */ #ifdef NDPI_CFFI_PREPROCESSING typedef union { ndpi_private_serializer _alignment; char c[72]; } ndpi_serializer; @@ -1557,8 +1558,6 @@ typedef union { ndpi_private_serializer _alignment; char c[sizeof(ndpi_private_s #if !defined(NDPI_CFFI_PREPROCESSING) \ && defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201112L -_Static_assert(_Alignof(ndpi_serializer) == _Alignof(ndpi_private_serializer), - "ndpi_serializer must preserve ndpi_private_serializer alignment"); _Static_assert(sizeof(ndpi_serializer) == sizeof(ndpi_private_serializer), "ndpi_serializer storage size must match ndpi_private_serializer"); #endif