summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorHauke Mehrtens2026-05-23 12:17:55 +0000
committerHauke Mehrtens2026-06-18 00:35:02 +0000
commit6644b4623bc82c8380a9487eaa9eb19dfdf1c4dc (patch)
treebff5e3d233b7ce8fb517fb612b95008adc7718bd
parent1c461b0c1dc5edccca42efbf76fc633143ff3bba (diff)
downloadodhcpd-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.c14
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;