From 315077b9e655ac60b0aa9fbd3e7eec343e2c47da Mon Sep 17 00:00:00 2001 From: "W.C.A. Wijngaards" Date: Tue, 24 Mar 2026 08:45:52 +0100 Subject: [PATCH] - Fix to check for invalid http content length and chunk size, and to check the RR rdata field lengths when decompressing and inserting RRs from an authority zone transfer. This stops large memory use and heap buffer-overflow read errors. Thanks to Haruto Kimura (Stella) for the report. --- doc/Changelog | 7 +++++++ services/authzone.c | 5 +++++ util/netevent.c | 23 +++++++++++++++++++---- 3 files changed, 31 insertions(+), 4 deletions(-) diff --git a/doc/Changelog b/doc/Changelog index f92de7fd0..5981ac2fa 100644 --- a/doc/Changelog +++ b/doc/Changelog @@ -1,3 +1,10 @@ +24 March 2026: Wouter + - Fix to check for invalid http content length and chunk size, + and to check the RR rdata field lengths when decompressing and + inserting RRs from an authority zone transfer. This stops + large memory use and heap buffer-overflow read errors. Thanks + to Haruto Kimura (Stella) for the report. + 20 March 2026: Wouter - Fix for testcode pktview to check buffer size and log errors. diff --git a/services/authzone.c b/services/authzone.c index 3965b5e17..9778cf1b7 100644 --- a/services/authzone.c +++ b/services/authzone.c @@ -1384,6 +1384,9 @@ decompress_rr_into_buffer(struct sldns_buffer* buf, uint8_t* pkt, len = 0; break; case LDNS_RDF_TYPE_STR: + /* Check rdlen for resilience, because it is + * checked above, that rdlen > 0 */ + if(rdlen < 1) return 0; /* malformed */ len = rd[0] + 1; break; default: @@ -1391,6 +1394,8 @@ decompress_rr_into_buffer(struct sldns_buffer* buf, uint8_t* pkt, break; } if(len) { + if(len > rdlen) + return 0; /* malformed */ if(!sldns_buffer_available(buf, len)) return 0; /* too long for buffer */ sldns_buffer_write(buf, rd, len); diff --git a/util/netevent.c b/util/netevent.c index 01ef54dfe..a86e22518 100644 --- a/util/netevent.c +++ b/util/netevent.c @@ -4870,8 +4870,17 @@ http_process_initial_header(struct comm_point* c) return 0; } } else if(strncasecmp(line, "Content-Length: ", 16) == 0) { - if(!c->http_is_chunked) - c->tcp_byte_count = (size_t)atoi(line+16); + if(!c->http_is_chunked) { + char* end = NULL; + long long cl; + errno = 0; + cl = strtoll(line+16, &end, 10); + if(end == line+16 || errno != 0 || cl < 0) { + verbose(VERB_ALGO, "http invalid Content-Length: " ARG_LL "d", cl); + return 0; /* reject */ + } + c->tcp_byte_count = (size_t)cl; + } } else if(strncasecmp(line, "Transfer-Encoding: chunked", 19+7) == 0) { c->tcp_byte_count = 0; c->http_is_chunked = 1; @@ -4927,9 +4936,15 @@ http_process_chunk_header(struct comm_point* c) if(c->http_in_chunk_headers == 1) { /* read chunked start line */ char* end = NULL; - c->tcp_byte_count = (size_t)strtol(line, &end, 16); - if(end == line) + long chunk_sz; + errno = 0; + chunk_sz = strtol(line, &end, 16); + if(end == line || errno != 0 || chunk_sz < 0) { + verbose(VERB_ALGO, "http invalid chunk size: %ld", + chunk_sz); return 0; + } + c->tcp_byte_count = (size_t)chunk_sz; c->http_in_chunk_headers = 0; /* remove header text from front of buffer */ http_moveover_buffer(c->buffer);