summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorHauke Mehrtens2026-05-23 12:18:34 +0000
committerHauke Mehrtens2026-06-18 00:35:03 +0000
commit41b476dffce6eda24e0cb0909c8b3688e56810f5 (patch)
tree28eb8c09546f458a3fd2078d29e107471946bebe
parent6644b4623bc82c8380a9487eaa9eb19dfdf1c4dc (diff)
downloadodhcpd-41b476dffce6eda24e0cb0909c8b3688e56810f5.tar.gz
netlink: avoid 32-bit-wide shift when computing /0 IPv4 netmask
cb_addr_valid() computes the netmask as ~((1U << (32 - prefixlen)) - 1). For a /0 IPv4 prefix the shift count is 32 — equal to the width of the shifted type — which is undefined behaviour in C. The kernel does deliver /0 addresses (e.g. on default-route-only interfaces), so this is reachable. Do the shift in uint64_t and truncate so /0 cleanly produces a zero netmask, matching what the rest of the code already expects. 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 a212daeace2733858fcd42d785aa719ecd0cd3cd)
-rw-r--r--src/netlink.c10
1 files changed, 8 insertions, 2 deletions
diff --git a/src/netlink.c b/src/netlink.c
index 2473476..8fd3872 100644
--- a/src/netlink.c
+++ b/src/netlink.c
@@ -587,8 +587,14 @@ static int cb_addr_valid(struct nl_msg *msg, void *arg)
memset(&oaddrs[ctxt->ret], 0, sizeof(oaddrs[ctxt->ret]));
oaddrs[ctxt->ret].prefix_len = ifa->ifa_prefixlen;
- if (ifa->ifa_family == AF_INET)
- oaddrs[ctxt->ret].netmask = htonl(~((1U << (32 - ifa->ifa_prefixlen)) - 1));
+ if (ifa->ifa_family == AF_INET) {
+ /* ifa_prefixlen is 0..32. A /0 prefix turns "32 - prefix" into a
+ * 32-bit shift on a 32-bit type, which is undefined behaviour;
+ * compute the host mask in 64-bit and truncate so /0 yields a
+ * zero netmask as expected. */
+ uint32_t host_bits = (uint32_t)((1ULL << (32 - ifa->ifa_prefixlen)) - 1);
+ oaddrs[ctxt->ret].netmask = htonl(~host_bits);
+ }
nla_memcpy(&oaddrs[ctxt->ret].addr, nla_addr, sizeof(oaddrs[ctxt->ret].addr));