Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

inet: use bigger hash table for IP ID generation

In commit 73f156a6e8c1 ("inetpeer: get rid of ip_id_count")
I used a very small hash table that could be abused
by patient attackers to reveal sensitive information.

Switch to a dynamic sizing, depending on RAM size.

Typical big hosts will now use 128x more storage (2 MB)
to get a similar increase in security and reduction
of hash collisions.

As a bonus, use of alloc_large_system_hash() spreads
allocated memory among all NUMA nodes.

Fixes: 73f156a6e8c1 ("inetpeer: get rid of ip_id_count")
Reported-by: Amit Klein <aksecurity@gmail.com>
Signed-off-by: Eric Dumazet <edumazet@google.com>
Cc: Willy Tarreau <w@1wt.eu>
Signed-off-by: David S. Miller <davem@davemloft.net>

authored by

Eric Dumazet and committed by
David S. Miller
aa6dd211 eb9da2c1

+28 -14
+28 -14
net/ipv4/route.c
··· 66 66 #include <linux/types.h> 67 67 #include <linux/kernel.h> 68 68 #include <linux/mm.h> 69 + #include <linux/memblock.h> 69 70 #include <linux/string.h> 70 71 #include <linux/socket.h> 71 72 #include <linux/sockios.h> ··· 453 452 __ipv4_confirm_neigh(dev, *(__force u32 *)pkey); 454 453 } 455 454 456 - #define IP_IDENTS_SZ 2048u 457 - 455 + /* Hash tables of size 2048..262144 depending on RAM size. 456 + * Each bucket uses 8 bytes. 457 + */ 458 + static u32 ip_idents_mask __read_mostly; 458 459 static atomic_t *ip_idents __read_mostly; 459 460 static u32 *ip_tstamps __read_mostly; 460 461 ··· 466 463 */ 467 464 u32 ip_idents_reserve(u32 hash, int segs) 468 465 { 469 - u32 *p_tstamp = ip_tstamps + hash % IP_IDENTS_SZ; 470 - atomic_t *p_id = ip_idents + hash % IP_IDENTS_SZ; 471 - u32 old = READ_ONCE(*p_tstamp); 472 - u32 now = (u32)jiffies; 466 + u32 bucket, old, now = (u32)jiffies; 467 + atomic_t *p_id; 468 + u32 *p_tstamp; 473 469 u32 delta = 0; 470 + 471 + bucket = hash & ip_idents_mask; 472 + p_tstamp = ip_tstamps + bucket; 473 + p_id = ip_idents + bucket; 474 + old = READ_ONCE(*p_tstamp); 474 475 475 476 if (old != now && cmpxchg(p_tstamp, old, now) == old) 476 477 delta = prandom_u32_max(now - old); ··· 3564 3557 3565 3558 int __init ip_rt_init(void) 3566 3559 { 3560 + void *idents_hash; 3567 3561 int cpu; 3568 3562 3569 - ip_idents = kmalloc_array(IP_IDENTS_SZ, sizeof(*ip_idents), 3570 - GFP_KERNEL); 3571 - if (!ip_idents) 3572 - panic("IP: failed to allocate ip_idents\n"); 3563 + /* For modern hosts, this will use 2 MB of memory */ 3564 + idents_hash = alloc_large_system_hash("IP idents", 3565 + sizeof(*ip_idents) + sizeof(*ip_tstamps), 3566 + 0, 3567 + 16, /* one bucket per 64 KB */ 3568 + HASH_ZERO, 3569 + NULL, 3570 + &ip_idents_mask, 3571 + 2048, 3572 + 256*1024); 3573 3573 3574 - prandom_bytes(ip_idents, IP_IDENTS_SZ * sizeof(*ip_idents)); 3574 + ip_idents = idents_hash; 3575 3575 3576 - ip_tstamps = kcalloc(IP_IDENTS_SZ, sizeof(*ip_tstamps), GFP_KERNEL); 3577 - if (!ip_tstamps) 3578 - panic("IP: failed to allocate ip_tstamps\n"); 3576 + prandom_bytes(ip_idents, (ip_idents_mask + 1) * sizeof(*ip_idents)); 3577 + 3578 + ip_tstamps = idents_hash + (ip_idents_mask + 1) * sizeof(*ip_idents); 3579 3579 3580 3580 for_each_possible_cpu(cpu) { 3581 3581 struct uncached_list *ul = &per_cpu(rt_uncached_list, cpu);