From: Eric Dumazet edumazet@google.com
mainline inclusion from mainline-v5.14 commit 6457378fe796815c973f631a1904e147d6ee33b1 category: bugfix bugzilla: NA CVE: CVE-2021-20322
-------------------------------------------------
A group of security researchers brought to our attention the weakness of hash function used in fnhe_hashfun().
Lets use siphash instead of Jenkins Hash, to considerably reduce security risks.
Also remove the inline keyword, this really is distracting.
Fixes: d546c621542d ("ipv4: harden fnhe_hashfun()") Signed-off-by: Eric Dumazet edumazet@google.com Reported-by: Keyu Man kman001@ucr.edu Cc: Willy Tarreau w@1wt.eu Signed-off-by: David S. Miller davem@davemloft.net Signed-off-by: Xu Jia xujia39@huawei.com Reviewed-by: Yue Haibing yuehaibing@huawei.com Reviewed-by: Xiu Jianfeng xiujianfeng@huawei.com Signed-off-by: Yang Yingliang yangyingliang@huawei.com --- net/ipv4/route.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-)
diff --git a/net/ipv4/route.c b/net/ipv4/route.c index 958df3427c34f..dd43e798194ab 100644 --- a/net/ipv4/route.c +++ b/net/ipv4/route.c @@ -614,14 +614,14 @@ static struct fib_nh_exception *fnhe_oldest(struct fnhe_hash_bucket *hash) return oldest; }
-static inline u32 fnhe_hashfun(__be32 daddr) +static u32 fnhe_hashfun(__be32 daddr) { - static u32 fnhe_hashrnd __read_mostly; - u32 hval; + static siphash_key_t fnhe_hash_key __read_mostly; + u64 hval;
- net_get_random_once(&fnhe_hashrnd, sizeof(fnhe_hashrnd)); - hval = jhash_1word((__force u32) daddr, fnhe_hashrnd); - return hash_32(hval, FNHE_HASH_SHIFT); + net_get_random_once(&fnhe_hash_key, sizeof(fnhe_hash_key)); + hval = siphash_1u32((__force u32) daddr, &fnhe_hash_key); + return hash_64(hval, FNHE_HASH_SHIFT); }
static void fill_route_from_fnhe(struct rtable *rt, struct fib_nh_exception *fnhe)
From: Eric Dumazet edumazet@google.com
mainline inclusion from mainline-v5.15-rc1 commit 67d6d681e15b578c1725bad8ad079e05d1c48a8e category: bugfix bugzilla: NA CVE: CVE-2021-20322
-------------------------------------------------
Even after commit 6457378fe796 ("ipv4: use siphash instead of Jenkins in fnhe_hashfun()"), an attacker can still use brute force to learn some secrets from a victim linux host.
One way to defeat these attacks is to make the max depth of the hash table bucket a random value.
Before this patch, each bucket of the hash table used to store exceptions could contain 6 items under attack.
After the patch, each bucket would contains a random number of items, between 6 and 10. The attacker can no longer infer secrets.
This is slightly increasing memory size used by the hash table, by 50% in average, we do not expect this to be a problem.
This patch is more complex than the prior one (IPv6 equivalent), because IPv4 was reusing the oldest entry. Since we need to be able to evict more than one entry per update_or_create_fnhe() call, I had to replace fnhe_oldest() with fnhe_remove_oldest().
Also note that we will queue extra kfree_rcu() calls under stress, which hopefully wont be a too big issue.
Fixes: 4895c771c7f0 ("ipv4: Add FIB nexthop exceptions.") Signed-off-by: Eric Dumazet edumazet@google.com Reported-by: Keyu Man kman001@ucr.edu Cc: Willy Tarreau w@1wt.eu Signed-off-by: David S. Miller davem@davemloft.net Reviewed-by: David Ahern dsahern@kernel.org Tested-by: David Ahern dsahern@kernel.org Signed-off-by: David S. Miller davem@davemloft.net Signed-off-by: Xu Jia xujia39@huawei.com Reviewed-by: Yue Haibing yuehaibing@huawei.com Reviewed-by: Xiu Jianfeng xiujianfeng@huawei.com Signed-off-by: Yang Yingliang yangyingliang@huawei.com --- net/ipv4/route.c | 46 ++++++++++++++++++++++++++++++---------------- 1 file changed, 30 insertions(+), 16 deletions(-)
diff --git a/net/ipv4/route.c b/net/ipv4/route.c index dd43e798194ab..a7828f85afb2d 100644 --- a/net/ipv4/route.c +++ b/net/ipv4/route.c @@ -600,18 +600,25 @@ static void fnhe_flush_routes(struct fib_nh_exception *fnhe) } }
-static struct fib_nh_exception *fnhe_oldest(struct fnhe_hash_bucket *hash) +static void fnhe_remove_oldest(struct fnhe_hash_bucket *hash) { - struct fib_nh_exception *fnhe, *oldest; + struct fib_nh_exception __rcu **fnhe_p, **oldest_p; + struct fib_nh_exception *fnhe, *oldest = NULL;
- oldest = rcu_dereference(hash->chain); - for (fnhe = rcu_dereference(oldest->fnhe_next); fnhe; - fnhe = rcu_dereference(fnhe->fnhe_next)) { - if (time_before(fnhe->fnhe_stamp, oldest->fnhe_stamp)) + for (fnhe_p = &hash->chain; ; fnhe_p = &fnhe->fnhe_next) { + fnhe = rcu_dereference_protected(*fnhe_p, + lockdep_is_held(&fnhe_lock)); + if (!fnhe) + break; + if (!oldest || + time_before(fnhe->fnhe_stamp, oldest->fnhe_stamp)) { oldest = fnhe; + oldest_p = fnhe_p; + } } fnhe_flush_routes(oldest); - return oldest; + *oldest_p = oldest->fnhe_next; + kfree_rcu(oldest, rcu); }
static u32 fnhe_hashfun(__be32 daddr) @@ -688,16 +695,21 @@ static void update_or_create_fnhe(struct fib_nh *nh, __be32 daddr, __be32 gw, if (rt) fill_route_from_fnhe(rt, fnhe); } else { - if (depth > FNHE_RECLAIM_DEPTH) - fnhe = fnhe_oldest(hash); - else { - fnhe = kzalloc(sizeof(*fnhe), GFP_ATOMIC); - if (!fnhe) - goto out_unlock; - - fnhe->fnhe_next = hash->chain; - rcu_assign_pointer(hash->chain, fnhe); + /* Randomize max depth to avoid some side channels attacks. */ + int max_depth = FNHE_RECLAIM_DEPTH + + prandom_u32_max(FNHE_RECLAIM_DEPTH); + + while (depth > max_depth) { + fnhe_remove_oldest(hash); + depth--; } + + fnhe = kzalloc(sizeof(*fnhe), GFP_ATOMIC); + if (!fnhe) + goto out_unlock; + + fnhe->fnhe_next = hash->chain; + fnhe->fnhe_genid = genid; fnhe->fnhe_daddr = daddr; fnhe->fnhe_gw = gw; @@ -705,6 +717,8 @@ static void update_or_create_fnhe(struct fib_nh *nh, __be32 daddr, __be32 gw, fnhe->fnhe_mtu_locked = lock; fnhe->fnhe_expires = max(1UL, expires);
+ rcu_assign_pointer(hash->chain, fnhe); + /* Exception created; mark the cached routes for the nexthop * stale, so anyone caching it rechecks if this exception * applies to them.
From: Eric Dumazet edumazet@google.com
mainline inclusion from mainline-v5.14 commit 4785305c05b25a242e5314cc821f54ade4c18810 category: bugfix bugzilla: NA CVE: CVE-2021-20322
-------------------------------------------------
A group of security researchers brought to our attention the weakness of hash function used in rt6_exception_hash()
Lets use siphash instead of Jenkins Hash, to considerably reduce security risks.
Following patch deals with IPv4.
Fixes: 35732d01fe31 ("ipv6: introduce a hash table to store dst cache") Signed-off-by: Eric Dumazet edumazet@google.com Reported-by: Keyu Man kman001@ucr.edu Cc: Wei Wang weiwan@google.com Cc: Martin KaFai Lau kafai@fb.com Acked-by: Wei Wang weiwan@google.com Signed-off-by: David S. Miller davem@davemloft.net Signed-off-by: Xu Jia xujia39@huawei.com Reviewed-by: Yue Haibing yuehaibing@huawei.com Reviewed-by: Xiu Jianfeng xiujianfeng@huawei.com Signed-off-by: Yang Yingliang yangyingliang@huawei.com --- net/ipv6/route.c | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-)
diff --git a/net/ipv6/route.c b/net/ipv6/route.c index 1b37aea01a98f..369f4aaab1334 100644 --- a/net/ipv6/route.c +++ b/net/ipv6/route.c @@ -45,6 +45,7 @@ #include <linux/nsproxy.h> #include <linux/slab.h> #include <linux/jhash.h> +#include <linux/siphash.h> #include <net/net_namespace.h> #include <net/snmp.h> #include <net/ipv6.h> @@ -1337,17 +1338,24 @@ static void rt6_exception_remove_oldest(struct rt6_exception_bucket *bucket) static u32 rt6_exception_hash(const struct in6_addr *dst, const struct in6_addr *src) { - static u32 seed __read_mostly; - u32 val; + static siphash_key_t rt6_exception_key __read_mostly; + struct { + struct in6_addr dst; + struct in6_addr src; + } __aligned(SIPHASH_ALIGNMENT) combined = { + .dst = *dst, + }; + u64 val;
- net_get_random_once(&seed, sizeof(seed)); - val = jhash(dst, sizeof(*dst), seed); + net_get_random_once(&rt6_exception_key, sizeof(rt6_exception_key));
#ifdef CONFIG_IPV6_SUBTREES if (src) - val = jhash(src, sizeof(*src), val); + combined.src = *src; #endif - return hash_32(val, FIB6_EXCEPTION_BUCKET_SIZE_SHIFT); + val = siphash(&combined, sizeof(combined), &rt6_exception_key); + + return hash_64(val, FIB6_EXCEPTION_BUCKET_SIZE_SHIFT); }
/* Helper function to find the cached rt in the hash table
From: Eric Dumazet edumazet@google.com
mainline inclusion from mainline-v5.15-rc1 commit a00df2caffed3883c341d5685f830434312e4a43 category: bugfix bugzilla: NA CVE: CVE-2021-20322
-------------------------------------------------
Even after commit 4785305c05b2 ("ipv6: use siphash in rt6_exception_hash()"), an attacker can still use brute force to learn some secrets from a victim linux host.
One way to defeat these attacks is to make the max depth of the hash table bucket a random value.
Before this patch, each bucket of the hash table used to store exceptions could contain 6 items under attack.
After the patch, each bucket would contains a random number of items, between 6 and 10. The attacker can no longer infer secrets.
This is slightly increasing memory size used by the hash table, we do not expect this to be a problem.
Following patch is dealing with the same issue in IPv4.
Fixes: 35732d01fe31 ("ipv6: introduce a hash table to store dst cache") Signed-off-by: Eric Dumazet edumazet@google.com Reported-by: Keyu Man kman001@ucr.edu Cc: Wei Wang weiwan@google.com Cc: Martin KaFai Lau kafai@fb.com Reviewed-by: David Ahern dsahern@kernel.org Signed-off-by: David S. Miller davem@davemloft.net Signed-off-by: Xu Jia xujia39@huawei.com Reviewed-by: Yue Haibing yuehaibing@huawei.com Reviewed-by: Xiu Jianfeng xiujianfeng@huawei.com Signed-off-by: Yang Yingliang yangyingliang@huawei.com --- net/ipv6/route.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-)
diff --git a/net/ipv6/route.c b/net/ipv6/route.c index 369f4aaab1334..01f8e62302fab 100644 --- a/net/ipv6/route.c +++ b/net/ipv6/route.c @@ -1454,6 +1454,7 @@ static int rt6_insert_exception(struct rt6_info *nrt, struct rt6_exception_bucket *bucket; struct in6_addr *src_key = NULL; struct rt6_exception *rt6_ex; + int max_depth; int err = 0;
spin_lock_bh(&rt6_exception_lock); @@ -1515,7 +1516,9 @@ static int rt6_insert_exception(struct rt6_info *nrt, bucket->depth++; net->ipv6.rt6_stats->fib_rt_cache++;
- if (bucket->depth > FIB6_MAX_DEPTH) + /* Randomize max depth to avoid some side channels attacks. */ + max_depth = FIB6_MAX_DEPTH + prandom_u32_max(FIB6_MAX_DEPTH); + while (bucket->depth > max_depth) rt6_exception_remove_oldest(bucket);
out: