summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDenys Fedoryshchenko <denys.f@collabora.com>2026-08-10 08:44:13 +0300
committerDenys Fedoryshchenko <denys.f@collabora.com>2026-08-10 08:44:13 +0300
commit4600e779e801b51405ed51d421ecfe8e33e5059d (patch)
treede5cc6c77b4075c8d4633abeff80a89a2abc8da7
parent0dd152a83012764a77ef2a7ae7bf7c20c3d43c35 (diff)
downloadaccel-ppp-4600e779e801b51405ed51d421ecfe8e33e5059d.tar.gz
accel-ppp-4600e779e801b51405ed51d421ecfe8e33e5059d.zip
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.
-rw-r--r--accel-pppd/ctrl/ipoe/dhcpv4_options.c24
1 files 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,