diff options
| author | Hauke Mehrtens | 2026-05-23 12:17:55 +0000 |
|---|---|---|
| committer | Hauke Mehrtens | 2026-06-18 00:35:02 +0000 |
| commit | 6644b4623bc82c8380a9487eaa9eb19dfdf1c4dc (patch) | |
| tree | bff5e3d233b7ce8fb517fb612b95008adc7718bd | |
| parent | 1c461b0c1dc5edccca42efbf76fc633143ff3bba (diff) | |
| download | odhcpd-6644b4623bc82c8380a9487eaa9eb19dfdf1c4dc.tar.gz | |
dhcpv6-ia: avoid undefined shifts in assign_pd()
assign_pd() computes (1 << (64 - length)) where the literal is a plain
int. When the prefix length is <= 32 the shift count is >= 32 bits,
which is undefined behaviour, and even at length == 33 the result
(1 << 31) is signed-overflow UB.
In normal operation the delegated prefix length is bounded by
dhcpv6_pd_min_len whose default keeps the shift in safe territory, but
that knob is user-configurable down to 1 in UCI, so a misconfigured
interface trips UB on every PD assignment. Compute the shift in
uint64_t and truncate explicitly to uint32_t so the arithmetic is
well-defined; for sane prefix lengths (>=33) the value is unchanged.
Assisted-by: Claude:claude-opus-4-7
Link: https://github.com/openwrt/odhcpd/pull/401
Signed-off-by: Hauke Mehrtens <hauke@hauke-m.de>
(cherry picked from commit 19bf3b73580b50030490c04e44adb6a14fe8c31c)
| -rw-r--r-- | src/dhcpv6-ia.c | 14 |
1 files changed, 10 insertions, 4 deletions
diff --git a/src/dhcpv6-ia.c b/src/dhcpv6-ia.c index 2ad628d..60793fa 100644 --- a/src/dhcpv6-ia.c +++ b/src/dhcpv6-ia.c @@ -346,8 +346,14 @@ static bool assign_pd(struct interface *iface, struct dhcpv6_lease *assign) if (iface->addr6_len < 1) return false; - /* Try honoring the hint first */ - uint32_t current = 1, asize = (1 << (64 - assign->length)) - 1; + /* Try honoring the hint first. + * + * Subnet-id slots per delegated prefix = 2^(64 - length); the literal + * 1 is plain int, so shifting it by 32-63 bits is undefined behaviour + * for short prefix lengths (the user can drive this via + * dhcpv6_pd_min_len). Compute the shift in uint64_t and truncate. + */ + uint32_t current = 1, asize = (uint32_t)((1ULL << (64 - assign->length)) - 1); if (assign->assigned_subnet_id) { list_for_each_entry(c, &iface->ia_assignments, head) { if (c->flags & OAF_DHCPV6_NA) @@ -362,7 +368,7 @@ static bool assign_pd(struct interface *iface, struct dhcpv6_lease *assign) return true; } - current = (c->assigned_subnet_id + (1 << (64 - c->length))); + current = (uint32_t)(c->assigned_subnet_id + (1ULL << (64 - c->length))); } } @@ -384,7 +390,7 @@ static bool assign_pd(struct interface *iface, struct dhcpv6_lease *assign) return true; } - current = (c->assigned_subnet_id + (1 << (64 - c->length))); + current = (uint32_t)(c->assigned_subnet_id + (1ULL << (64 - c->length))); } return false; |