Skip to content

Commit e54f69c

Browse files
committed
net: fix __dst_negative_advice() race
jira VULN-5442 cve CVE-2024-36971 commit-author Eric Dumazet <[email protected]> commit 92f1655 upstream-diff - The patch departs significantly from upstream Linux. Using tag resf_kernel-rt-4.18.0-477.27.1.rt7.290.el8_8 as the source of truth for this patch. __dst_negative_advice() does not enforce proper RCU rules when sk->dst_cache must be cleared, leading to possible UAF. RCU rules are that we must first clear sk->sk_dst_cache, then call dst_release(old_dst). Note that sk_dst_reset(sk) is implementing this protocol correctly, while __dst_negative_advice() uses the wrong order. Given that ip6_negative_advice() has special logic against RTF_CACHE, this means each of the three ->negative_advice() existing methods must perform the sk_dst_reset() themselves. Note the check against NULL dst is centralized in __dst_negative_advice(), there is no need to duplicate it in various callbacks. Many thanks to Clement Lecigne for tracking this issue. This old bug became visible after the blamed commit, using UDP sockets. Fixes: a87cb3e ("net: Facility to report route quality of connected sockets") Reported-by: Clement Lecigne <[email protected]> Diagnosed-by: Clement Lecigne <[email protected]> Signed-off-by: Eric Dumazet <[email protected]> Cc: Tom Herbert <[email protected]> Reviewed-by: David Ahern <[email protected]> Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Jakub Kicinski <[email protected]> (cherry picked from commit 92f1655) Signed-off-by: Greg Rose <[email protected]>
1 parent 724c34d commit e54f69c

File tree

4 files changed

+23
-34
lines changed

4 files changed

+23
-34
lines changed

include/net/sock.h

+1-1
Original file line numberDiff line numberDiff line change
@@ -2008,7 +2008,7 @@ sk_dst_get(struct sock *sk)
20082008

20092009
static inline void __dst_negative_advice(struct sock *sk)
20102010
{
2011-
struct dst_entry *ndst, *dst = __sk_dst_get(sk);
2011+
struct dst_entry *dst = __sk_dst_get(sk);
20122012

20132013
if (dst && dst->ops->negative_advice) {
20142014
ndst = dst->ops->negative_advice(dst);

net/ipv4/route.c

+6-12
Original file line numberDiff line numberDiff line change
@@ -857,22 +857,16 @@ static void ip_do_redirect(struct dst_entry *dst, struct sock *sk, struct sk_buf
857857
__ip_do_redirect(rt, skb, &fl4, true);
858858
}
859859

860-
static struct dst_entry *ipv4_negative_advice(struct dst_entry *dst)
860+
static void ipv4_negative_advice(struct sock *sk,
861+
struct dst_entry *dst)
861862
{
862863
struct rtable *rt = (struct rtable *)dst;
863864
struct dst_entry *ret = dst;
864865

865-
if (rt) {
866-
if (dst->obsolete > 0) {
867-
ip_rt_put(rt);
868-
ret = NULL;
869-
} else if ((rt->rt_flags & RTCF_REDIRECTED) ||
870-
rt->dst.expires) {
871-
ip_rt_put(rt);
872-
ret = NULL;
873-
}
874-
}
875-
return ret;
866+
if ((dst->obsolete > 0) ||
867+
(rt->rt_flags & RTCF_REDIRECTED) ||
868+
rt->dst.expires)
869+
sk_dst_reset(sk);
876870
}
877871

878872
/*

net/ipv6/route.c

+13-13
Original file line numberDiff line numberDiff line change
@@ -2399,24 +2399,24 @@ static struct dst_entry *ip6_dst_check(struct dst_entry *dst, u32 cookie)
23992399
return dst_ret;
24002400
}
24012401

2402-
static struct dst_entry *ip6_negative_advice(struct dst_entry *dst)
2402+
static void ip6_negative_advice(struct sock *sk,
2403+
struct dst_entry *dst)
24032404
{
24042405
struct rt6_info *rt = (struct rt6_info *) dst;
24052406

2406-
if (rt) {
2407-
if (rt->rt6i_flags & RTF_CACHE) {
2408-
rcu_read_lock();
2409-
if (rt6_check_expired(rt)) {
2410-
rt6_remove_exception_rt(rt);
2411-
dst = NULL;
2412-
}
2413-
rcu_read_unlock();
2414-
} else {
2415-
dst_release(dst);
2416-
dst = NULL;
2407+
if (rt->rt6i_flags & RTF_CACHE) {
2408+
rcu_read_lock();
2409+
if (rt6_check_expired(rt)) {
2410+
/* counteract the dst_release() in sk_dst_reset() */
2411+
dst_hold(dst);
2412+
sk_dst_reset(sk);
2413+
2414+
rt6_remove_exception_rt(rt);
24172415
}
2416+
rcu_read_unlock();
2417+
return;
24182418
}
2419-
return dst;
2419+
sk_dst_reset(sk);
24202420
}
24212421

24222422
static void ip6_link_failure(struct sk_buff *skb)

net/xfrm/xfrm_policy.c

+3-8
Original file line numberDiff line numberDiff line change
@@ -3561,15 +3561,10 @@ static void xfrm_link_failure(struct sk_buff *skb)
35613561
/* Impossible. Such dst must be popped before reaches point of failure. */
35623562
}
35633563

3564-
static struct dst_entry *xfrm_negative_advice(struct dst_entry *dst)
3564+
static void xfrm_negative_advice(struct sock *sk, struct dst_entry *dst)
35653565
{
3566-
if (dst) {
3567-
if (dst->obsolete) {
3568-
dst_release(dst);
3569-
dst = NULL;
3570-
}
3571-
}
3572-
return dst;
3566+
if (dst->obsolete)
3567+
sk_dst_reset(sk);
35733568
}
35743569

35753570
static void xfrm_init_pmtu(struct xfrm_dst **bundle, int nr)

0 commit comments

Comments
 (0)