gtp_encap_disable() releases the kernel-created sockets while sk_created is still true and without waiting for in-flight readers. This allows two concurrent use-after-free scenarios: 1. A softirq packet handler that already observed sk_created == true via smp_load_acquire() can dereference sk0/sk1u after they have been freed. 2. gtp_genl_send_echo_req() runs in process context without RTNL. synchronize_net() waits for RCU-protected softirq handlers but does not cover this non-RCU generic netlink reader, which can dereference freed sk0/sk1u during concurrent teardown: RIP: 0010:ip4_route_output_gtp (drivers/net/gtp.c) gtp_genl_send_echo_req Kernel panic - not syncing: Fatal exception Reorder gtp_encap_disable() to clear sk_created first, then call synchronize_net() to wait for in-flight softirq handlers before releasing the sockets. Hold RTNL in gtp_genl_send_echo_req() to serialize with teardown for the process-context path. Under RTNL, the smp_load_acquire() from patch 1/2 becomes redundant and is replaced with a plain read. Fixes: b20dc3c68458 ("gtp: Allow to create GTP device without FDs") Fixes: d33bd757d362 ("gtp: Implement GTP echo request") Reported-by: AutonomousCodeSecurity@microsoft.com Reported-by: Cen Zhang (Microsoft) Signed-off-by: Cen Zhang (Microsoft) --- v2: New patch. Fix teardown race with synchronize_net() for softirq paths and rtnl_lock() for the process-context genl echo path. v1: https://lore.kernel.org/netdev/20260816035205.57966-1-blbllhy@gmail.com/ --- drivers/net/gtp.c | 61 ++++++++++++++++++++++++++++++----------------- 1 file changed, 39 insertions(+), 22 deletions(-) diff --git a/drivers/net/gtp.c b/drivers/net/gtp.c index ead519ee18d1..7ac9764696f2 100644 --- a/drivers/net/gtp.c +++ b/drivers/net/gtp.c @@ -896,12 +896,14 @@ static void gtp_encap_disable_sock(struct sock *sk) static void gtp_encap_disable(struct gtp_dev *gtp) { if (gtp->sk_created) { - udp_tunnel_sock_release(gtp->sk0); - udp_tunnel_sock_release(gtp->sk1u); - /* Pairs with smp_load_acquire() in the RX and - * genl echo paths. + /* Prevent new readers from entering echo handlers, + * then wait for in-flight softirq readers to complete + * before releasing the sockets. */ smp_store_release(>p->sk_created, false); + synchronize_net(); + udp_tunnel_sock_release(gtp->sk0); + udp_tunnel_sock_release(gtp->sk1u); gtp->sk0 = NULL; gtp->sk1u = NULL; } else { @@ -1473,8 +1475,7 @@ static int gtp_create_sockets(struct gtp_dev *gtp, const struct nlattr *nla, gtp->sk1u = sk1u; /* Ensure sk0/sk1u are visible before sk_created is set. - * Pairs with smp_load_acquire() in the RX and genl - * echo paths. + * Pairs with smp_load_acquire() in the RX echo paths. */ smp_store_release(>p->sk_created, true); @@ -2362,6 +2363,7 @@ static int gtp_genl_send_echo_req(struct sk_buff *skb, struct genl_info *info) struct sock *sk; __be16 port; int len; + int ret; if (!info->attrs[GTPA_VERSION] || !info->attrs[GTPA_LINK] || @@ -2373,17 +2375,22 @@ static int gtp_genl_send_echo_req(struct sk_buff *skb, struct genl_info *info) dst_ip = nla_get_be32(info->attrs[GTPA_PEER_ADDRESS]); src_ip = nla_get_be32(info->attrs[GTPA_MS_ADDRESS]); + rtnl_lock(); + gtp = gtp_find_dev(sock_net(skb->sk), info->attrs); - if (!gtp) - return -ENODEV; + if (!gtp) { + ret = -ENODEV; + goto out_unlock; + } - /* Pairs with smp_store_release() in gtp_create_sockets() - * and gtp_encap_disable(). - */ - if (!smp_load_acquire(>p->sk_created)) - return -EOPNOTSUPP; - if (!(gtp->dev->flags & IFF_UP)) - return -ENETDOWN; + if (!gtp->sk_created) { + ret = -EOPNOTSUPP; + goto out_unlock; + } + if (!(gtp->dev->flags & IFF_UP)) { + ret = -ENETDOWN; + goto out_unlock; + } if (version == GTP_V0) { struct gtp0_header *gtp0_h; @@ -2392,8 +2399,10 @@ static int gtp_genl_send_echo_req(struct sk_buff *skb, struct genl_info *info) sizeof(struct iphdr) + sizeof(struct udphdr); skb_to_send = netdev_alloc_skb_ip_align(gtp->dev, len); - if (!skb_to_send) - return -ENOMEM; + if (!skb_to_send) { + ret = -ENOMEM; + goto out_unlock; + } sk = gtp->sk0; port = htons(GTP0_PORT); @@ -2409,8 +2418,10 @@ static int gtp_genl_send_echo_req(struct sk_buff *skb, struct genl_info *info) sizeof(struct iphdr) + sizeof(struct udphdr); skb_to_send = netdev_alloc_skb_ip_align(gtp->dev, len); - if (!skb_to_send) - return -ENOMEM; + if (!skb_to_send) { + ret = -ENOMEM; + goto out_unlock; + } sk = gtp->sk1u; port = htons(GTP1U_PORT); @@ -2420,7 +2431,8 @@ static int gtp_genl_send_echo_req(struct sk_buff *skb, struct genl_info *info) memset(gtp1u_h, 0, sizeof(struct gtp1_header_long)); gtp1u_build_echo_msg(gtp1u_h, GTP_ECHO_REQ); } else { - return -ENODEV; + ret = -ENODEV; + goto out_unlock; } rt = ip4_route_output_gtp(&fl4, sk, dst_ip, src_ip); @@ -2428,7 +2440,8 @@ static int gtp_genl_send_echo_req(struct sk_buff *skb, struct genl_info *info) netdev_dbg(gtp->dev, "no route for echo request to %pI4\n", &dst_ip); kfree_skb(skb_to_send); - return -ENODEV; + ret = -ENODEV; + goto out_unlock; } local_bh_disable(); @@ -2442,7 +2455,11 @@ static int gtp_genl_send_echo_req(struct sk_buff *skb, struct genl_info *info) dev_net(gtp->dev)), false, 0); local_bh_enable(); - return 0; + ret = 0; + +out_unlock: + rtnl_unlock(); + return ret; } static const struct nla_policy gtp_genl_policy[GTPA_MAX + 1] = { -- 2.55.0