netfilter: ipt_CLUSTERIP: fix use-after-free of proc entry
authorSabrina Dubroca <sd@queasysnail.net>
Tue, 18 Jul 2017 12:56:17 +0000 (14:56 +0200)
committerPablo Neira Ayuso <pablo@netfilter.org>
Wed, 19 Jul 2017 09:53:25 +0000 (11:53 +0200)
When we delete a netns with a CLUSTERIP rule, clusterip_net_exit() is
called first, removing /proc/net/ipt_CLUSTERIP.
Then clusterip_config_entry_put() is called from clusterip_tg_destroy(),
and tries to remove its entry under /proc/net/ipt_CLUSTERIP/.

Fix this by checking that the parent directory of the entry to remove
hasn't already been deleted.

The following triggers a KASAN splat (stealing the reproducer from
202f59afd441, thanks to Jianlin Shi and Xin Long):

    ip netns add test
    ip link add veth0_in type veth peer name veth0_out
    ip link set veth0_in netns test
    ip netns exec test ip link set lo up
    ip netns exec test ip link set veth0_in up
    ip netns exec test iptables -I INPUT -d 1.2.3.4 -i veth0_in -j     \
        CLUSTERIP --new --clustermac 89:d4:47:eb:9a:fa --total-nodes 3 \
        --local-node 1 --hashmode sourceip-sourceport
    ip netns del test

Fixes: ce4ff76c15a8 ("netfilter: ipt_CLUSTERIP: make proc directory per net namespace")
Signed-off-by: Sabrina Dubroca <sd@queasysnail.net>
Reviewed-by: Xin Long <lucien.xin@gmail.com>
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
net/ipv4/netfilter/ipt_CLUSTERIP.c

index 7d72decb80f9f9c4150bd2a42c4b802ba1fd7f17..efaa04dcc80e3a66ef34fa6bd13be66b601925b0 100644 (file)
@@ -117,7 +117,8 @@ clusterip_config_entry_put(struct net *net, struct clusterip_config *c)
                 * functions are also incrementing the refcount on their own,
                 * so it's safe to remove the entry even if it's in use. */
 #ifdef CONFIG_PROC_FS
-               proc_remove(c->pde);
+               if (cn->procdir)
+                       proc_remove(c->pde);
 #endif
                return;
        }
@@ -815,6 +816,7 @@ static void clusterip_net_exit(struct net *net)
 #ifdef CONFIG_PROC_FS
        struct clusterip_net *cn = net_generic(net, clusterip_net_id);
        proc_remove(cn->procdir);
+       cn->procdir = NULL;
 #endif
        nf_unregister_net_hook(net, &cip_arp_ops);
 }