OSDN Git Service

ANDROID: net: xt_qtaguid/xt_socket: fix refcount underflow and crash
authorDmitry Torokhov <dtor@chromium.org>
Thu, 3 Sep 2015 21:48:52 +0000 (14:48 -0700)
committerDmitry Shmidt <dimitrysh@google.com>
Thu, 19 Jan 2017 21:32:31 +0000 (13:32 -0800)
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] [<ffffffc000207288>] dump_backtrace+0x0/0x10c
[  111.357314] [<ffffffc0002073a4>] show_stack+0x10/0x1c
[  111.362367] [<ffffffc000a82d1c>] dump_stack+0x74/0x94
[  111.367414] [<ffffffc000a81824>] panic+0xec/0x238
[  111.372116] [<ffffffc000981648>] __inet_twsk_kill+0xd0/0xf8
[  111.377684] [<ffffffc0009817b0>] inet_twdr_do_twkill_work+0x64/0xd0
[  111.383946] [<ffffffc000981a5c>] inet_twdr_hangman+0x2c/0xa4
[  111.389602] [<ffffffc000271cf0>] call_timer_fn+0xac/0x160
[  111.394995] [<ffffffc00027250c>] run_timer_softirq+0x23c/0x274
[  111.400824] [<ffffffc000220a68>] __do_softirq+0x1a4/0x330
[  111.406218] [<ffffffc000220e94>] irq_exit+0x70/0xd0
[  111.411093] [<ffffffc000264e00>] __handle_domain_irq+0x84/0xa8
[  111.416922] [<ffffffc0002003ec>] 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 <dtor@google.com>
[jstultz: Cherry-picked and added missing local var definition]
Signed-off-by: John Stultz <john.stultz@linaro.org>
net/netfilter/xt_socket.c

index 69775b4..a52fbaf 100644 (file)
@@ -151,6 +151,7 @@ struct sock *xt_socket_lookup_slow_v4(struct net *net,
        const struct iphdr *iph = ip_hdr(skb);
        struct sk_buff *data_skb = NULL;
        int doff = 0;
+       struct sock *sk = skb->sk;
        __be32 uninitialized_var(daddr), uninitialized_var(saddr);
        __be16 uninitialized_var(dport), uninitialized_var(sport);
        u8 uninitialized_var(protocol);
@@ -205,8 +206,14 @@ struct sock *xt_socket_lookup_slow_v4(struct net *net,
        }
 #endif
 
-       return xt_socket_get_sock_v4(net, data_skb, doff, protocol, saddr,
-                                    daddr, sport, dport, indev);
+       if (sk)
+               atomic_inc(&sk->sk_refcnt);
+       else
+               sk = xt_socket_get_sock_v4(dev_net(skb->dev), data_skb, doff,
+                                          protocol, saddr, daddr, sport,
+                                          dport, indev);
+
+       return sk;
 }
 EXPORT_SYMBOL(xt_socket_lookup_slow_v4);
 
@@ -240,8 +247,7 @@ socket_match(const struct sk_buff *skb, struct xt_action_param *par,
                    transparent)
                        pskb->mark = sk->sk_mark;
 
-               if (sk != skb->sk)
-                       sock_gen_put(sk);
+               sock_gen_put(sk);
 
                if (wildcard || !transparent)
                        sk = NULL;
@@ -349,6 +355,7 @@ struct sock *xt_socket_lookup_slow_v6(struct net *net,
                                             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);
@@ -388,8 +395,14 @@ struct sock *xt_socket_lookup_slow_v6(struct net *net,
                return NULL;
        }
 
-       return xt_socket_get_sock_v6(net, data_skb, doff, tproto, saddr, daddr,
-                                    sport, dport, indev);
+       if (sk)
+               atomic_inc(&sk->sk_refcnt);
+       else
+               sk = xt_socket_get_sock_v6(dev_net(skb->dev), data_skb, doff,
+                                          tproto, saddr, daddr, sport, dport,
+                                          indev);
+
+       return sk;
 }
 EXPORT_SYMBOL(xt_socket_lookup_slow_v6);