diff options
author | Eric Dumazet <edumazet@google.com> | 2013-01-03 17:18:39 -0500 |
---|---|---|
committer | Pablo Neira Ayuso <pablo@netfilter.org> | 2013-01-04 14:14:42 -0500 |
commit | 2727de76041b2064c0b74f00a2a89678fb3efafc (patch) | |
tree | 57b15dc3cb1e39d7079c60bcf4d76ce34cdb0084 | |
parent | 757ae316fb35811cfd8c67de0e0b8680ec4c1f37 (diff) |
netfilter: xt_recent: avoid high order page allocations
xt_recent can try high order page allocations and this can fail.
iptables: page allocation failure: order:9, mode:0xc0d0
It also wastes about half the allocated space because of kmalloc()
power-of-two roundups and struct recent_table layout.
Use vmalloc() instead to save space and be less prone to allocation
errors when memory is fragmented.
Reported-by: Miroslav Kratochvil <exa.exa@gmail.com>
Reported-by: Dave Jones <davej@redhat.com>
Reported-by: Harald Reindl <h.reindl@thelounge.net>
Signed-off-by: Eric Dumazet <edumazet@google.com>
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
-rw-r--r-- | net/netfilter/xt_recent.c | 23 |
1 files changed, 18 insertions, 5 deletions
diff --git a/net/netfilter/xt_recent.c b/net/netfilter/xt_recent.c index dab053e2a1a2..978efc9b555a 100644 --- a/net/netfilter/xt_recent.c +++ b/net/netfilter/xt_recent.c | |||
@@ -29,6 +29,7 @@ | |||
29 | #include <linux/skbuff.h> | 29 | #include <linux/skbuff.h> |
30 | #include <linux/inet.h> | 30 | #include <linux/inet.h> |
31 | #include <linux/slab.h> | 31 | #include <linux/slab.h> |
32 | #include <linux/vmalloc.h> | ||
32 | #include <net/net_namespace.h> | 33 | #include <net/net_namespace.h> |
33 | #include <net/netns/generic.h> | 34 | #include <net/netns/generic.h> |
34 | 35 | ||
@@ -310,6 +311,14 @@ out: | |||
310 | return ret; | 311 | return ret; |
311 | } | 312 | } |
312 | 313 | ||
314 | static void recent_table_free(void *addr) | ||
315 | { | ||
316 | if (is_vmalloc_addr(addr)) | ||
317 | vfree(addr); | ||
318 | else | ||
319 | kfree(addr); | ||
320 | } | ||
321 | |||
313 | static int recent_mt_check(const struct xt_mtchk_param *par, | 322 | static int recent_mt_check(const struct xt_mtchk_param *par, |
314 | const struct xt_recent_mtinfo_v1 *info) | 323 | const struct xt_recent_mtinfo_v1 *info) |
315 | { | 324 | { |
@@ -322,6 +331,7 @@ static int recent_mt_check(const struct xt_mtchk_param *par, | |||
322 | #endif | 331 | #endif |
323 | unsigned int i; | 332 | unsigned int i; |
324 | int ret = -EINVAL; | 333 | int ret = -EINVAL; |
334 | size_t sz; | ||
325 | 335 | ||
326 | if (unlikely(!hash_rnd_inited)) { | 336 | if (unlikely(!hash_rnd_inited)) { |
327 | get_random_bytes(&hash_rnd, sizeof(hash_rnd)); | 337 | get_random_bytes(&hash_rnd, sizeof(hash_rnd)); |
@@ -360,8 +370,11 @@ static int recent_mt_check(const struct xt_mtchk_param *par, | |||
360 | goto out; | 370 | goto out; |
361 | } | 371 | } |
362 | 372 | ||
363 | t = kzalloc(sizeof(*t) + sizeof(t->iphash[0]) * ip_list_hash_size, | 373 | sz = sizeof(*t) + sizeof(t->iphash[0]) * ip_list_hash_size; |
364 | GFP_KERNEL); | 374 | if (sz <= PAGE_SIZE) |
375 | t = kzalloc(sz, GFP_KERNEL); | ||
376 | else | ||
377 | t = vzalloc(sz); | ||
365 | if (t == NULL) { | 378 | if (t == NULL) { |
366 | ret = -ENOMEM; | 379 | ret = -ENOMEM; |
367 | goto out; | 380 | goto out; |
@@ -377,14 +390,14 @@ static int recent_mt_check(const struct xt_mtchk_param *par, | |||
377 | uid = make_kuid(&init_user_ns, ip_list_uid); | 390 | uid = make_kuid(&init_user_ns, ip_list_uid); |
378 | gid = make_kgid(&init_user_ns, ip_list_gid); | 391 | gid = make_kgid(&init_user_ns, ip_list_gid); |
379 | if (!uid_valid(uid) || !gid_valid(gid)) { | 392 | if (!uid_valid(uid) || !gid_valid(gid)) { |
380 | kfree(t); | 393 | recent_table_free(t); |
381 | ret = -EINVAL; | 394 | ret = -EINVAL; |
382 | goto out; | 395 | goto out; |
383 | } | 396 | } |
384 | pde = proc_create_data(t->name, ip_list_perms, recent_net->xt_recent, | 397 | pde = proc_create_data(t->name, ip_list_perms, recent_net->xt_recent, |
385 | &recent_mt_fops, t); | 398 | &recent_mt_fops, t); |
386 | if (pde == NULL) { | 399 | if (pde == NULL) { |
387 | kfree(t); | 400 | recent_table_free(t); |
388 | ret = -ENOMEM; | 401 | ret = -ENOMEM; |
389 | goto out; | 402 | goto out; |
390 | } | 403 | } |
@@ -435,7 +448,7 @@ static void recent_mt_destroy(const struct xt_mtdtor_param *par) | |||
435 | remove_proc_entry(t->name, recent_net->xt_recent); | 448 | remove_proc_entry(t->name, recent_net->xt_recent); |
436 | #endif | 449 | #endif |
437 | recent_table_flush(t); | 450 | recent_table_flush(t); |
438 | kfree(t); | 451 | recent_table_free(t); |
439 | } | 452 | } |
440 | mutex_unlock(&recent_mutex); | 453 | mutex_unlock(&recent_mutex); |
441 | } | 454 | } |