| From dcd440dae67976b1599fc5cc1b7263027f769311 Mon Sep 17 00:00:00 2001 |
| From: Andrey Ryabinin <aryabinin@virtuozzo.com> |
| Date: Mon, 22 Oct 2018 23:30:40 +0200 |
| Subject: netfilter: ipset: fix ip_set_list allocation failure |
| |
| [ Upstream commit ed956f3947a01ff9875cd908d7c1ef1fe7f47bf0 ] |
| |
| ip_set_create() and ip_set_net_init() attempt to allocate physically |
| contiguous memory for ip_set_list. If memory is fragmented, the |
| allocations could easily fail: |
| |
| vzctl: page allocation failure: order:7, mode:0xc0d0 |
| |
| Call Trace: |
| dump_stack+0x19/0x1b |
| warn_alloc_failed+0x110/0x180 |
| __alloc_pages_nodemask+0x7bf/0xc60 |
| alloc_pages_current+0x98/0x110 |
| kmalloc_order+0x18/0x40 |
| kmalloc_order_trace+0x26/0xa0 |
| __kmalloc+0x279/0x290 |
| ip_set_net_init+0x4b/0x90 [ip_set] |
| ops_init+0x3b/0xb0 |
| setup_net+0xbb/0x170 |
| copy_net_ns+0xf1/0x1c0 |
| create_new_namespaces+0xf9/0x180 |
| copy_namespaces+0x8e/0xd0 |
| copy_process+0xb61/0x1a00 |
| do_fork+0x91/0x320 |
| |
| Use kvcalloc() to fallback to 0-order allocations if high order |
| page isn't available. |
| |
| Signed-off-by: Andrey Ryabinin <aryabinin@virtuozzo.com> |
| Signed-off-by: Jozsef Kadlecsik <kadlec@blackhole.kfki.hu> |
| Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org> |
| Signed-off-by: Sasha Levin <sashal@kernel.org> |
| --- |
| net/netfilter/ipset/ip_set_core.c | 8 ++++---- |
| 1 file changed, 4 insertions(+), 4 deletions(-) |
| |
| diff --git a/net/netfilter/ipset/ip_set_core.c b/net/netfilter/ipset/ip_set_core.c |
| index fa15a831aeee..68db946df151 100644 |
| --- a/net/netfilter/ipset/ip_set_core.c |
| +++ b/net/netfilter/ipset/ip_set_core.c |
| @@ -960,7 +960,7 @@ static int ip_set_create(struct net *net, struct sock *ctnl, |
| /* Wraparound */ |
| goto cleanup; |
| |
| - list = kcalloc(i, sizeof(struct ip_set *), GFP_KERNEL); |
| + list = kvcalloc(i, sizeof(struct ip_set *), GFP_KERNEL); |
| if (!list) |
| goto cleanup; |
| /* nfnl mutex is held, both lists are valid */ |
| @@ -972,7 +972,7 @@ static int ip_set_create(struct net *net, struct sock *ctnl, |
| /* Use new list */ |
| index = inst->ip_set_max; |
| inst->ip_set_max = i; |
| - kfree(tmp); |
| + kvfree(tmp); |
| ret = 0; |
| } else if (ret) { |
| goto cleanup; |
| @@ -2058,7 +2058,7 @@ ip_set_net_init(struct net *net) |
| if (inst->ip_set_max >= IPSET_INVALID_ID) |
| inst->ip_set_max = IPSET_INVALID_ID - 1; |
| |
| - list = kcalloc(inst->ip_set_max, sizeof(struct ip_set *), GFP_KERNEL); |
| + list = kvcalloc(inst->ip_set_max, sizeof(struct ip_set *), GFP_KERNEL); |
| if (!list) |
| return -ENOMEM; |
| inst->is_deleted = false; |
| @@ -2086,7 +2086,7 @@ ip_set_net_exit(struct net *net) |
| } |
| } |
| nfnl_unlock(NFNL_SUBSYS_IPSET); |
| - kfree(rcu_dereference_protected(inst->ip_set_list, 1)); |
| + kvfree(rcu_dereference_protected(inst->ip_set_list, 1)); |
| } |
| |
| static struct pernet_operations ip_set_net_ops = { |
| -- |
| 2.17.1 |
| |