]> git.hungrycats.org Git - linux/commitdiff
netfilter: nf_qeueue: Drop queue entries on nf_unregister_hook
authorEric W. Biederman <ebiederm@xmission.com>
Fri, 19 Jun 2015 19:03:39 +0000 (14:03 -0500)
committerSasha Levin <sasha.levin@oracle.com>
Sun, 5 Jul 2015 14:12:48 +0000 (10:12 -0400)
[ Upstream commit 8405a8fff3f8545c888a872d6e3c0c8eecd4d348 ]

Add code to nf_unregister_hook to flush the nf_queue when a hook is
unregistered.  This guarantees that the pointer that the nf_queue code
retains into the nf_hook list will remain valid while a packet is
queued.

I tested what would happen if we do not flush queued packets and was
trivially able to obtain the oops below.  All that was required was
to stop the nf_queue listening process, to delete all of the nf_tables,
and to awaken the nf_queue listening process.

> BUG: unable to handle kernel paging request at 0000000100000001
> IP: [<0000000100000001>] 0x100000001
> PGD b9c35067 PUD 0
> Oops: 0010 [#1] SMP
> Modules linked in:
> CPU: 0 PID: 519 Comm: lt-nfqnl_test Not tainted
> task: ffff8800b9c8c050 ti: ffff8800ba9d8000 task.ti: ffff8800ba9d8000
> RIP: 0010:[<0000000100000001>]  [<0000000100000001>] 0x100000001
> RSP: 0018:ffff8800ba9dba40  EFLAGS: 00010a16
> RAX: ffff8800bab48a00 RBX: ffff8800ba9dba90 RCX: ffff8800ba9dba90
> RDX: ffff8800b9c10128 RSI: ffff8800ba940900 RDI: ffff8800bab48a00
> RBP: ffff8800b9c10128 R08: ffffffff82976660 R09: ffff8800ba9dbb28
> R10: dead000000100100 R11: dead000000200200 R12: ffff8800ba940900
> R13: ffffffff8313fd50 R14: ffff8800b9c95200 R15: 0000000000000000
> FS:  00007fb91fc34700(0000) GS:ffff8800bfa00000(0000) knlGS:0000000000000000
> CS:  0010 DS: 0000 ES: 0000 CR0: 0000000080050033
> CR2: 0000000100000001 CR3: 00000000babfb000 CR4: 00000000000007f0
> Stack:
>  ffffffff8206ab0f ffffffff82982240 ffff8800bab48a00 ffff8800b9c100a8
>  ffff8800b9c10100 0000000000000001 ffff8800ba940900 ffff8800b9c10128
>  ffffffff8206bd65 ffff8800bfb0d5e0 ffff8800bab48a00 0000000000014dc0
> Call Trace:
>  [<ffffffff8206ab0f>] ? nf_iterate+0x4f/0xa0
>  [<ffffffff8206bd65>] ? nf_reinject+0x125/0x190
>  [<ffffffff8206dee5>] ? nfqnl_recv_verdict+0x255/0x360
>  [<ffffffff81386290>] ? nla_parse+0x80/0xf0
>  [<ffffffff8206c42c>] ? nfnetlink_rcv_msg+0x13c/0x240
>  [<ffffffff811b2fec>] ? __memcg_kmem_get_cache+0x4c/0x150
>  [<ffffffff8206c2f0>] ? nfnl_lock+0x20/0x20
>  [<ffffffff82068159>] ? netlink_rcv_skb+0xa9/0xc0
>  [<ffffffff820677bf>] ? netlink_unicast+0x12f/0x1c0
>  [<ffffffff82067ade>] ? netlink_sendmsg+0x28e/0x650
>  [<ffffffff81fdd814>] ? sock_sendmsg+0x44/0x50
>  [<ffffffff81fde07b>] ? ___sys_sendmsg+0x2ab/0x2c0
>  [<ffffffff810e8f73>] ? __wake_up+0x43/0x70
>  [<ffffffff8141a134>] ? tty_write+0x1c4/0x2a0
>  [<ffffffff81fde9f4>] ? __sys_sendmsg+0x44/0x80
>  [<ffffffff823ff8d7>] ? system_call_fastpath+0x12/0x6a
> Code:  Bad RIP value.
> RIP  [<0000000100000001>] 0x100000001
>  RSP <ffff8800ba9dba40>
> CR2: 0000000100000001
> ---[ end trace 08eb65d42362793f ]---

Cc: stable@vger.kernel.org
Signed-off-by: "Eric W. Biederman" <ebiederm@xmission.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
Signed-off-by: Sasha Levin <sasha.levin@oracle.com>
include/net/netfilter/nf_queue.h
net/netfilter/core.c
net/netfilter/nf_internals.h
net/netfilter/nf_queue.c
net/netfilter/nfnetlink_queue_core.c

index 84a53d7803069dd15bb5bb8575dffa1a900d8ea5..1a91f979160e4a88bbd138d90e0ae0b66a6dcf96 100644 (file)
@@ -28,6 +28,8 @@ struct nf_queue_entry {
 struct nf_queue_handler {
        int                     (*outfn)(struct nf_queue_entry *entry,
                                         unsigned int queuenum);
+       void                    (*nf_hook_drop)(struct net *net,
+                                               struct nf_hook_ops *ops);
 };
 
 void nf_register_queue_handler(const struct nf_queue_handler *qh);
index 024a2e25c8a49448afbeed4f780912b24a6318a3..d047a6679ff2c81ba0b58034f13d87e80e58d103 100644 (file)
@@ -88,6 +88,7 @@ void nf_unregister_hook(struct nf_hook_ops *reg)
        static_key_slow_dec(&nf_hooks_needed[reg->pf][reg->hooknum]);
 #endif
        synchronize_net();
+       nf_queue_nf_hook_drop(reg);
 }
 EXPORT_SYMBOL(nf_unregister_hook);
 
index 61a3c927e63cf1c9f0f9b596155e5c8a74bcd71a..aba1d7dac17c33ac50d7cde14a55ef9203ddeb6c 100644 (file)
@@ -24,6 +24,7 @@ int nf_queue(struct sk_buff *skb, struct nf_hook_ops *elem, u_int8_t pf,
             unsigned int hook, struct net_device *indev,
             struct net_device *outdev, int (*okfn)(struct sk_buff *),
             unsigned int queuenum);
+void nf_queue_nf_hook_drop(struct nf_hook_ops *ops);
 int __init netfilter_queue_init(void);
 
 /* nf_log.c */
index 4c8b68e5fa164fd71b6f613b66b36d52e6717bf4..77ee2d4d5046e4ea914a12cf9ce6a19ad05cf0e1 100644 (file)
@@ -95,6 +95,23 @@ bool nf_queue_entry_get_refs(struct nf_queue_entry *entry)
 }
 EXPORT_SYMBOL_GPL(nf_queue_entry_get_refs);
 
+void nf_queue_nf_hook_drop(struct nf_hook_ops *ops)
+{
+       const struct nf_queue_handler *qh;
+       struct net *net;
+
+       rtnl_lock();
+       rcu_read_lock();
+       qh = rcu_dereference(queue_handler);
+       if (qh) {
+               for_each_net(net) {
+                       qh->nf_hook_drop(net, ops);
+               }
+       }
+       rcu_read_unlock();
+       rtnl_unlock();
+}
+
 /*
  * Any packet that leaves via this function must come back
  * through nf_reinject().
index 7c60ccd61a3e1685875adc9f2d2391e4957399c8..8add272654f6695b821b2be3b4df607abed3e378 100644 (file)
@@ -815,6 +815,27 @@ static struct notifier_block nfqnl_dev_notifier = {
        .notifier_call  = nfqnl_rcv_dev_event,
 };
 
+static int nf_hook_cmp(struct nf_queue_entry *entry, unsigned long ops_ptr)
+{
+       return entry->elem == (struct nf_hook_ops *)ops_ptr;
+}
+
+static void nfqnl_nf_hook_drop(struct net *net, struct nf_hook_ops *hook)
+{
+       struct nfnl_queue_net *q = nfnl_queue_pernet(net);
+       int i;
+
+       rcu_read_lock();
+       for (i = 0; i < INSTANCE_BUCKETS; i++) {
+               struct nfqnl_instance *inst;
+               struct hlist_head *head = &q->instance_table[i];
+
+               hlist_for_each_entry_rcu(inst, head, hlist)
+                       nfqnl_flush(inst, nf_hook_cmp, (unsigned long)hook);
+       }
+       rcu_read_unlock();
+}
+
 static int
 nfqnl_rcv_nl_event(struct notifier_block *this,
                   unsigned long event, void *ptr)
@@ -1022,7 +1043,8 @@ static const struct nla_policy nfqa_cfg_policy[NFQA_CFG_MAX+1] = {
 };
 
 static const struct nf_queue_handler nfqh = {
-       .outfn  = &nfqnl_enqueue_packet,
+       .outfn          = &nfqnl_enqueue_packet,
+       .nf_hook_drop   = &nfqnl_nf_hook_drop,
 };
 
 static int