Headline
CVE-2023-0359: Missing `ipv6` nullptr-check in `handle_ra_input
A missing nullptr-check in handle_ra_input can cause a nullptr-deref.
Summary
A missing nullptr-check in handle_ra_input can cause a nullptr-deref.
Description
The reachable_time update in handle_ra_input is missing a nullptr-check for net_pkt_iface(pkt)->config.ip.ipv6 before calling net_if_ipv6_set_reachable_time:
if (reachable_time && reachable_time <= MAX_REACHABLE_TIME && (net_if_ipv6_get_reachable_time(net_pkt_iface(pkt)) != reachable_time)) { net_if_ipv6_set_base_reachable_time(net_pkt_iface(pkt), reachable_time); net_if_ipv6_set_reachable_time(net_pkt_iface(pkt)->config.ip.ipv6); }
if (reachable_time && reachable_time <= MAX_REACHABLE_TIME &&
(net_if_ipv6_get_reachable_time(net_pkt_iface(pkt)) !=
reachable_time)) {
net_if_ipv6_set_base_reachable_time(net_pkt_iface(pkt),
reachable_time);
net_if_ipv6_set_reachable_time(
net_pkt_iface(pkt)->config.ip.ipv6);
}
The ipv6 interface pointer is passed into net_if_ipv6_calc_reachable_time in net_if_ipv6_set_reachable_time:
ipv6->reachable_time = net_if_ipv6_calc_reachable_time(ipv6);
/**
* @brief Set IPv6 reachable time for a given interface. This requires
* that base reachable time is set for the interface.
*
* @param ipv6 IPv6 address configuration
*/
static inline void net_if_ipv6_set_reachable_time(struct net_if_ipv6 *ipv6)
{
#if defined(CONFIG_NET_NATIVE_IPV6)
ipv6->reachable_time = net_if_ipv6_calc_reachable_time(ipv6);
#endif
}
The ipv6 interface pointer is then used without an additional check in net_if_ipv6_calc_reachable_time:
min_reachable = (MIN_RANDOM_NUMER * ipv6->base_reachable_time) / MIN_RANDOM_DENOM; max_reachable = (MAX_RANDOM_NUMER * ipv6->base_reachable_time) / MAX_RANDOM_DENOM;
uint32_t net_if_ipv6_calc_reachable_time(struct net_if_ipv6 *ipv6)
{
uint32_t min_reachable, max_reachable;
k_mutex_lock(&lock, K_FOREVER);
min_reachable = (MIN_RANDOM_NUMER * ipv6->base_reachable_time)
/ MIN_RANDOM_DENOM;
max_reachable = (MAX_RANDOM_NUMER * ipv6->base_reachable_time)
/ MAX_RANDOM_DENOM;
k_mutex_unlock(&lock);
NET_DBG("min_reachable:%u max_reachable:%u", min_reachable,
max_reachable);
return min_reachable +
sys_rand32_get() % (max_reachable - min_reachable);
}
However the other usages in handle_ra_input do check for a nullptr:
net_if_ipv6_get_reachable_time:
if (!iface->config.ip.ipv6) { return 0; }
/**
* @brief Get IPv6 reachable timeout specified for a given interface
*
* @param iface Network interface
*
* @return Reachable timeout
*/
static inline uint32_t net_if_ipv6_get_reachable_time(struct net_if *iface)
{
#if defined(CONFIG_NET_NATIVE_IPV6)
if (!iface->config.ip.ipv6) {
return 0;
}
return iface->config.ip.ipv6->reachable_time;
#else
return 0;
#endif
}
net_if_ipv6_set_base_reachable_time
if (!iface->config.ip.ipv6) { return; }
/**
* @brief Set IPv6 reachable time for a given interface
*
* @param iface Network interface
* @param reachable_time New reachable time
*/
static inline void net_if_ipv6_set_base_reachable_time(struct net_if *iface,
uint32_t reachable_time)
{
#if defined(CONFIG_NET_NATIVE_IPV6)
if (!iface->config.ip.ipv6) {
return;
}
iface->config.ip.ipv6->base_reachable_time = reachable_time;
#endif
}
Impact
- Potentially cause a Denial of Service.
- Note: We are able to cause a DoS in Zephyr 2.2.0 with fuzzing, but did not try to reproduce in current versions.
Proposed Fix
- Add a nullptr check in net_if_ipv6_set_reachable_time or add a wrapper function with a check similar to net_if_ipv6_get_reachable_time or net_if_ipv6_set_base_reachable_time.