From 703653d46f08be3e0d90915cad80f19129cf9416 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Thu, 3 Sep 2015 14:48:52 -0700 Subject: net: xt_qtaguid/xt_socket: fix refcount underflow and crash xt_socket_get[4|6]_sk() do not always increment sock refcount, which causes confusion in xt_qtaguid module which is not aware of this fact and drops the reference whether it should have or not. Fix it by changing xt_socket_get[4|6]_sk() to always increment recount of returned sock. This should fix the following crash: [ 111.319523] BUG: failure at /mnt/host/source/src/third_party/kernel/v3.18/net/ipv4/inet_timewait_sock.c:90/__inet_twsk_kill()! [ 111.331192] Kernel panic - not syncing: BUG! [ 111.335468] CPU: 0 PID: 0 Comm: swapper/0 Tainted: G U W 3.18.0-06867-g268df91 #1 [ 111.343810] Hardware name: Google Tegra210 Smaug Rev 1+ (DT) [ 111.349463] Call trace: [ 111.351917] [] dump_backtrace+0x0/0x10c [ 111.357314] [] show_stack+0x10/0x1c [ 111.362367] [] dump_stack+0x74/0x94 [ 111.367414] [] panic+0xec/0x238 [ 111.372116] [] __inet_twsk_kill+0xd0/0xf8 [ 111.377684] [] inet_twdr_do_twkill_work+0x64/0xd0 [ 111.383946] [] inet_twdr_hangman+0x2c/0xa4 [ 111.389602] [] call_timer_fn+0xac/0x160 [ 111.394995] [] run_timer_softirq+0x23c/0x274 [ 111.400824] [] __do_softirq+0x1a4/0x330 [ 111.406218] [] irq_exit+0x70/0xd0 [ 111.411093] [] __handle_domain_irq+0x84/0xa8 [ 111.416922] [] gic_handle_irq+0x4c/0x80 b/22476945 Originally reviewed at: https://chromium-review.googlesource.com/#/c/297414/ Change-Id: I51fa94a9d92a84a0bd3b58466d711e46a6892a79 Signed-off-by: Dmitry Torokhov [jstultz: Cherry-picked and added missing local var definition] Signed-off-by: John Stultz --- net/netfilter/xt_socket.c | 25 +++++++++++++++++++------ 1 file changed, 19 insertions(+), 6 deletions(-) (limited to 'net') diff --git a/net/netfilter/xt_socket.c b/net/netfilter/xt_socket.c index ae1a2c6d553..281f4d65b6e 100644 --- a/net/netfilter/xt_socket.c +++ b/net/netfilter/xt_socket.c @@ -147,6 +147,7 @@ struct sock *xt_socket_lookup_slow_v4(const struct sk_buff *skb, const struct net_device *indev) { const struct iphdr *iph = ip_hdr(skb); + struct sock *sk = skb->sk; __be32 uninitialized_var(daddr), uninitialized_var(saddr); __be16 uninitialized_var(dport), uninitialized_var(sport); u8 uninitialized_var(protocol); @@ -197,8 +198,14 @@ struct sock *xt_socket_lookup_slow_v4(const struct sk_buff *skb, } #endif - return xt_socket_get_sock_v4(dev_net(skb->dev), protocol, saddr, daddr, - sport, dport, indev); + if (sk) + atomic_inc(&sk->sk_refcnt); + else + sk = xt_socket_get_sock_v4(dev_net(skb->dev), protocol, + saddr, daddr, sport, dport, + indev); + + return sk; } EXPORT_SYMBOL(xt_socket_lookup_slow_v4); @@ -227,8 +234,7 @@ socket_match(const struct sk_buff *skb, struct xt_action_param *par, if (info->flags & XT_SOCKET_TRANSPARENT) transparent = xt_socket_sk_is_transparent(sk); - if (sk != skb->sk) - sock_gen_put(sk); + sock_gen_put(sk); if (wildcard || !transparent) sk = NULL; @@ -334,6 +340,7 @@ xt_socket_get_sock_v6(struct net *net, const u8 protocol, struct sock *xt_socket_lookup_slow_v6(const struct sk_buff *skb, const struct net_device *indev) { + struct sock *sk = skb->sk; __be16 uninitialized_var(dport), uninitialized_var(sport); const struct in6_addr *daddr = NULL, *saddr = NULL; struct ipv6hdr *iph = ipv6_hdr(skb); @@ -367,8 +374,14 @@ struct sock *xt_socket_lookup_slow_v6(const struct sk_buff *skb, return NULL; } - return xt_socket_get_sock_v6(dev_net(skb->dev), tproto, saddr, daddr, - sport, dport, indev); + if (sk) + atomic_inc(&sk->sk_refcnt); + else + sk = xt_socket_get_sock_v6(dev_net(skb->dev), tproto, + saddr, daddr, sport, dport, + indev); + + return sk; } EXPORT_SYMBOL(xt_socket_lookup_slow_v6); -- cgit v1.2.3