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 | } |
