diff options
| author | Denys Fedoryshchenko <denys.f@collabora.com> | 2026-08-10 09:41:38 +0300 |
|---|---|---|
| committer | Denys Fedoryshchenko <denys.f@collabora.com> | 2026-08-10 09:41:38 +0300 |
| commit | a9bfcdb86b2da91077c21c169e767975c029eabe (patch) | |
| tree | 983446de1ad7049734b1853a41a2ef73b13c0fa6 | |
| parent | 3aa2a84122a48e28aef252b8777d5e57f28fafe5 (diff) | |
| download | accel-ppp-a9bfcdb86b2da91077c21c169e767975c029eabe.tar.gz accel-ppp-a9bfcdb86b2da91077c21c169e767975c029eabe.zip | |
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.
| -rw-r--r-- | accel-pppd/ipv6/dhcpv6_packet.c | 7 |
1 files changed, 7 insertions, 0 deletions
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; |
