netfilter: ipt_CLUSTERIP: fix out-of-bounds accesses in clusterip_tg_check()
authorDmitry Vyukov <dvyukov@google.com>
Tue, 30 Jan 2018 14:21:34 +0000 (15:21 +0100)
committerPablo Neira Ayuso <pablo@netfilter.org>
Wed, 31 Jan 2018 14:00:33 +0000 (15:00 +0100)
Commit 136e92bbec0a switched local_nodes from an array to a bitmask
but did not add proper bounds checks. As the result
clusterip_config_init_nodelist() can both over-read
ipt_clusterip_tgt_info.local_nodes and over-write
clusterip_config.local_nodes.

Add bounds checks for both.

Fixes: 136e92bbec0a ("[NETFILTER] CLUSTERIP: use a bitmap to store node responsibility data")
Signed-off-by: Dmitry Vyukov <dvyukov@google.com>
Reported-by: syzbot <syzkaller@googlegroups.com>
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
net/ipv4/netfilter/ipt_CLUSTERIP.c

index 69060e3..1e4a720 100644 (file)
@@ -431,7 +431,7 @@ static int clusterip_tg_check(const struct xt_tgchk_param *par)
        struct ipt_clusterip_tgt_info *cipinfo = par->targinfo;
        const struct ipt_entry *e = par->entryinfo;
        struct clusterip_config *config;
-       int ret;
+       int ret, i;
 
        if (par->nft_compat) {
                pr_err("cannot use CLUSTERIP target from nftables compat\n");
@@ -450,8 +450,18 @@ static int clusterip_tg_check(const struct xt_tgchk_param *par)
                pr_info("Please specify destination IP\n");
                return -EINVAL;
        }
-
-       /* FIXME: further sanity checks */
+       if (cipinfo->num_local_nodes > ARRAY_SIZE(cipinfo->local_nodes)) {
+               pr_info("bad num_local_nodes %u\n", cipinfo->num_local_nodes);
+               return -EINVAL;
+       }
+       for (i = 0; i < cipinfo->num_local_nodes; i++) {
+               if (cipinfo->local_nodes[i] - 1 >=
+                   sizeof(config->local_nodes) * 8) {
+                       pr_info("bad local_nodes[%d] %u\n",
+                               i, cipinfo->local_nodes[i]);
+                       return -EINVAL;
+               }
+       }
 
        config = clusterip_config_find_get(par->net, e->ip.dst.s_addr, 1);
        if (!config) {