Commit 2727de76041b2064c0b74f00a2a89678fb3efafc
Committed by
Pablo Neira Ayuso
1 parent
757ae316fb
Exists in
smarc-l5.0.0_1.0.0-ga
and in
5 other branches
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>
Showing 1 changed file with 18 additions and 5 deletions Side-by-side Diff
net/netfilter/xt_recent.c
... | ... | @@ -29,6 +29,7 @@ |
29 | 29 | #include <linux/skbuff.h> |
30 | 30 | #include <linux/inet.h> |
31 | 31 | #include <linux/slab.h> |
32 | +#include <linux/vmalloc.h> | |
32 | 33 | #include <net/net_namespace.h> |
33 | 34 | #include <net/netns/generic.h> |
34 | 35 | |
... | ... | @@ -310,6 +311,14 @@ |
310 | 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 | 322 | static int recent_mt_check(const struct xt_mtchk_param *par, |
314 | 323 | const struct xt_recent_mtinfo_v1 *info) |
315 | 324 | { |
... | ... | @@ -322,6 +331,7 @@ |
322 | 331 | #endif |
323 | 332 | unsigned int i; |
324 | 333 | int ret = -EINVAL; |
334 | + size_t sz; | |
325 | 335 | |
326 | 336 | if (unlikely(!hash_rnd_inited)) { |
327 | 337 | get_random_bytes(&hash_rnd, sizeof(hash_rnd)); |
... | ... | @@ -360,8 +370,11 @@ |
360 | 370 | goto out; |
361 | 371 | } |
362 | 372 | |
363 | - t = kzalloc(sizeof(*t) + sizeof(t->iphash[0]) * ip_list_hash_size, | |
364 | - GFP_KERNEL); | |
373 | + sz = sizeof(*t) + sizeof(t->iphash[0]) * ip_list_hash_size; | |
374 | + if (sz <= PAGE_SIZE) | |
375 | + t = kzalloc(sz, GFP_KERNEL); | |
376 | + else | |
377 | + t = vzalloc(sz); | |
365 | 378 | if (t == NULL) { |
366 | 379 | ret = -ENOMEM; |
367 | 380 | goto out; |
368 | 381 | |
... | ... | @@ -377,14 +390,14 @@ |
377 | 390 | uid = make_kuid(&init_user_ns, ip_list_uid); |
378 | 391 | gid = make_kgid(&init_user_ns, ip_list_gid); |
379 | 392 | if (!uid_valid(uid) || !gid_valid(gid)) { |
380 | - kfree(t); | |
393 | + recent_table_free(t); | |
381 | 394 | ret = -EINVAL; |
382 | 395 | goto out; |
383 | 396 | } |
384 | 397 | pde = proc_create_data(t->name, ip_list_perms, recent_net->xt_recent, |
385 | 398 | &recent_mt_fops, t); |
386 | 399 | if (pde == NULL) { |
387 | - kfree(t); | |
400 | + recent_table_free(t); | |
388 | 401 | ret = -ENOMEM; |
389 | 402 | goto out; |
390 | 403 | } |
... | ... | @@ -435,7 +448,7 @@ |
435 | 448 | remove_proc_entry(t->name, recent_net->xt_recent); |
436 | 449 | #endif |
437 | 450 | recent_table_flush(t); |
438 | - kfree(t); | |
451 | + recent_table_free(t); | |
439 | 452 | } |
440 | 453 | mutex_unlock(&recent_mutex); |
441 | 454 | } |