diff --git a/fuzz/corpus/fuzz_dns_parse/issue3213_trigger1 b/fuzz/corpus/fuzz_dns_parse/issue3213_trigger1 new file mode 100644 index 00000000000..822189b6234 Binary files /dev/null and b/fuzz/corpus/fuzz_dns_parse/issue3213_trigger1 differ diff --git a/fuzz/corpus/fuzz_dns_parse/issue3213_trigger2 b/fuzz/corpus/fuzz_dns_parse/issue3213_trigger2 new file mode 100644 index 00000000000..184eede3b20 Binary files /dev/null and b/fuzz/corpus/fuzz_dns_parse/issue3213_trigger2 differ diff --git a/fuzz/corpus/fuzz_dns_parse/issue3213_trigger3 b/fuzz/corpus/fuzz_dns_parse/issue3213_trigger3 new file mode 100644 index 00000000000..c16ff3a4bd0 Binary files /dev/null and b/fuzz/corpus/fuzz_dns_parse/issue3213_trigger3 differ diff --git a/src/include/ndpi_define.h.in b/src/include/ndpi_define.h.in index c05c33fefed..a6243c3598e 100644 --- a/src/include/ndpi_define.h.in +++ b/src/include/ndpi_define.h.in @@ -131,22 +131,38 @@ #define NDPI_ARRAY_LENGTH(array) (sizeof(array) / sizeof((array)[0])) -/* the get_uXX will return raw network packet bytes !! */ -#define get_u_int8_t(X,O) (*(u_int8_t *)((&(((u_int8_t *)X)[O])))) -#define get_u_int16_t(X,O) (*(u_int16_t *)((&(((u_int8_t *)X)[O])))) -#define get_u_int32_t(X,O) (*(u_int32_t *)((&(((u_int8_t *)X)[O])))) -#if defined(__arm__) +/* The get_uXX helpers return raw network packet bytes. */ +#ifndef NDPI_CFFI_PREPROCESSING #include #include -static inline uint64_t get_u_int64_t(const uint8_t* X, int O) + +#define get_u_int8_t(X,O) (((const uint8_t *)(X))[O]) + +static inline uint16_t ndpi_get_u_int16_t(const void *X, int O) { - uint64_t tmp; - memcpy(&tmp, X + O, sizeof(tmp)); - return tmp; + uint16_t value; + memcpy(&value, ((const uint8_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 uint32_t ndpi_get_u_int32_t(const void *X, int O) +{ + uint32_t value; + memcpy(&value, ((const uint8_t *)X) + O, sizeof(value)); + return value; +} + +static inline uint64_t ndpi_get_u_int64_t(const void *X, int O) +{ + 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) diff --git a/src/include/ndpi_typedefs.h b/src/include/ndpi_typedefs.h index d4829c1c714..bfb21b3deaf 100644 --- a/src/include/ndpi_typedefs.h +++ b/src/include/ndpi_typedefs.h @@ -1543,10 +1543,23 @@ 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. The union member + * is the alignment guarantee; the assertion below protects the storage size. + */ #ifdef NDPI_CFFI_PREPROCESSING -typedef struct { char c[72]; } ndpi_serializer; +typedef union { ndpi_private_serializer _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 + +#if !defined(NDPI_CFFI_PREPROCESSING) \ + && defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201112L +_Static_assert(sizeof(ndpi_serializer) == sizeof(ndpi_private_serializer), + "ndpi_serializer storage size must match ndpi_private_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 &&