From 4600e779e801b51405ed51d421ecfe8e33e5059d Mon Sep 17 00:00:00 2001 From: Denys Fedoryshchenko Date: Mon, 10 Aug 2026 08:44:13 +0300 Subject: ipoe: bounds check classless routes and avoid unaligned reads The destination was always read as a 32 bit word regardless of how many significant octets the prefix length implies, and the gateway was read without checking that four bytes remain in the option. Neither read was bounded by the end of the option, so a client could make the decoder run past it. dhcpv4_check_options() only enforces a minimum length of 5 for option 121, which is short of the 9 bytes a /32 route needs. Reading the destination as a word was also wrong for any prefix shorter than /32, as it pulled in the first octets of the gateway: 10.0.1.0/24 via 1.1.1.1 printed as 10.0.0.1/24. Read only the significant octets, check the remaining length before both reads, and copy the gateway rather than dereferencing a possibly unaligned pointer. --- accel-pppd/ctrl/ipoe/dhcpv4_options.c | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/accel-pppd/ctrl/ipoe/dhcpv4_options.c b/accel-pppd/ctrl/ipoe/dhcpv4_options.c index 57999995..042a7406 100644 --- a/accel-pppd/ctrl/ipoe/dhcpv4_options.c +++ b/accel-pppd/ctrl/ipoe/dhcpv4_options.c @@ -265,6 +265,7 @@ static void print_classless_route(const struct dhcpv4_option *opt, int elem_size { const uint8_t *ptr = opt->data; const uint8_t *endptr = ptr + opt->len; + unsigned int prefix_len, i; int mask; uint32_t mask1; uint32_t ip; @@ -278,19 +279,20 @@ static void print_classless_route(const struct dhcpv4_option *opt, int elem_size if (mask > 32) return; - ip = ntohl(*(uint32_t *)ptr); + prefix_len = (mask + 7) / 8; + if ((size_t)(endptr - ptr) < prefix_len + sizeof(gw)) + return; + + ip = 0; + for (i = 0; i < prefix_len; i++) + ip |= (uint32_t)ptr[i] << (24 - i * 8); mask1 = mask ? UINT32_MAX << (32 - mask) : 0; ip &= mask1; - if (mask <= 8) - ptr++; - else if (mask <= 16) - ptr += 2; - else if (mask <= 24) - ptr += 3; - else - ptr += 4; - gw = ntohl(*(uint32_t *)ptr); - ptr += 4; + ptr += prefix_len; + + memcpy(&gw, ptr, sizeof(gw)); + gw = ntohl(gw); + ptr += sizeof(gw); print("%i.%i.%i.%i/%i via %i.%i.%i.%i", (ip >> 24) & 0xff, -- cgit v1.2.3