From a9bfcdb86b2da91077c21c169e767975c029eabe Mon Sep 17 00:00:00 2001 From: Denys Fedoryshchenko Date: Mon, 10 Aug 2026 09:41:38 +0300 Subject: dhcpv6: bail out of the relay loop when it makes no progress The relay decapsulation loop only advances pkt->hdr when it finds a Relay-Message option inside the current relay header. A Relay-Forward packet that carries no Relay-Message option leaves pkt->hdr pointing at the same header, so the outer loop runs again on the same input and allocates another struct dhcpv6_relay on every pass. A 34 byte packet, a Relay-Forward header with no options at all, is enough to allocate without limit. Instrumented with ASan it reaches 11.8 million allocations and over a gigabyte in a few seconds: ERROR: libFuzzer: out-of-memory (used: 1249Mb; limit: 512Mb) Live Heap Allocations: 781874575 bytes in 11834644 chunks #1 in dhcpv6_packet_parse dhcpv6_packet.c:142 The socket is per session, so this needs an established session, but the packet is parsed before any DHCPv6 level validation and one client can exhaust memory for the whole daemon. Remember the header at the top of each pass and treat a pass that did not move it as a malformed packet. --- accel-pppd/ipv6/dhcpv6_packet.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/accel-pppd/ipv6/dhcpv6_packet.c b/accel-pppd/ipv6/dhcpv6_packet.c index e56d7b1d..c1a57668 100644 --- a/accel-pppd/ipv6/dhcpv6_packet.c +++ b/accel-pppd/ipv6/dhcpv6_packet.c @@ -133,6 +133,8 @@ struct dhcpv6_packet *dhcpv6_packet_parse(const void *buf, size_t size) endptr = ((void *)pkt->hdr) + size; while (pkt->hdr->type == D6_RELAY_FORW) { + struct dhcpv6_msg_hdr *prev_hdr = pkt->hdr; + rhdr = (struct dhcpv6_relay_hdr *)pkt->hdr; if (((void *)rhdr) + sizeof(*rhdr) > endptr) { log_warn("dhcpv6: invalid packet received\n"); @@ -167,6 +169,11 @@ struct dhcpv6_packet *dhcpv6_packet_parse(const void *buf, size_t size) ptr += sizeof(*opth) + ntohs(opth->len); } + + if (pkt->hdr == prev_hdr) { + log_warn("dhcpv6: invalid packet received\n"); + goto error; + } } ptr = pkt->hdr->data; -- cgit v1.2.3