diff options
| author | Denys Fedoryshchenko <denys.f@collabora.com> | 2026-08-10 08:44:13 +0300 |
|---|---|---|
| committer | Denys Fedoryshchenko <denys.f@collabora.com> | 2026-08-10 08:44:13 +0300 |
| commit | 4600e779e801b51405ed51d421ecfe8e33e5059d (patch) | |
| tree | de5cc6c77b4075c8d4633abeff80a89a2abc8da7 /accel-pppd/ctrl/ipoe | |
| parent | 0dd152a83012764a77ef2a7ae7bf7c20c3d43c35 (diff) | |
| download | accel-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.
Diffstat (limited to 'accel-pppd/ctrl/ipoe')
| -rw-r--r-- | accel-pppd/ctrl/ipoe/dhcpv4_options.c | 24 |
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, |
