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

netfilter: use get_random_u32 instead of prandom

bh might occur while updating per-cpu rnd_state from user context,
ie. local_out path.

BUG: using smp_processor_id() in preemptible [00000000] code: nginx/2725
caller is nft_ng_random_eval+0x24/0x54 [nft_numgen]
Call Trace:
check_preemption_disabled+0xde/0xe0
nft_ng_random_eval+0x24/0x54 [nft_numgen]

Use the random driver instead, this also avoids need for local prandom
state. Moreover, prandom now uses the random driver since d4150779e60f
("random32: use real rng for non-deterministic randomness").

Based on earlier patch from Pablo Neira.

Fixes: 6b2faee0ca91 ("netfilter: nft_meta: place prandom handling in a helper")
Fixes: 978d8f9055c3 ("netfilter: nft_numgen: add map lookups for numgen random operations")
Signed-off-by: Florian Westphal <fw@strlen.de>
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>

authored by

Florian Westphal and committed by
Pablo Neira Ayuso
b1fd94e7 f5826c8c

+5 -20
+2 -11
net/netfilter/nft_meta.c
··· 14 14 #include <linux/in.h> 15 15 #include <linux/ip.h> 16 16 #include <linux/ipv6.h> 17 + #include <linux/random.h> 17 18 #include <linux/smp.h> 18 19 #include <linux/static_key.h> 19 20 #include <net/dst.h> ··· 32 31 #define NFT_META_SECS_PER_HOUR 3600 33 32 #define NFT_META_SECS_PER_DAY 86400 34 33 #define NFT_META_DAYS_PER_WEEK 7 35 - 36 - static DEFINE_PER_CPU(struct rnd_state, nft_prandom_state); 37 34 38 35 static u8 nft_meta_weekday(void) 39 36 { ··· 270 271 return true; 271 272 } 272 273 273 - static noinline u32 nft_prandom_u32(void) 274 - { 275 - struct rnd_state *state = this_cpu_ptr(&nft_prandom_state); 276 - 277 - return prandom_u32_state(state); 278 - } 279 - 280 274 #ifdef CONFIG_IP_ROUTE_CLASSID 281 275 static noinline bool 282 276 nft_meta_get_eval_rtclassid(const struct sk_buff *skb, u32 *dest) ··· 381 389 break; 382 390 #endif 383 391 case NFT_META_PRANDOM: 384 - *dest = nft_prandom_u32(); 392 + *dest = get_random_u32(); 385 393 break; 386 394 #ifdef CONFIG_XFRM 387 395 case NFT_META_SECPATH: ··· 510 518 len = IFNAMSIZ; 511 519 break; 512 520 case NFT_META_PRANDOM: 513 - prandom_init_once(&nft_prandom_state); 514 521 len = sizeof(u32); 515 522 break; 516 523 #ifdef CONFIG_XFRM
+3 -9
net/netfilter/nft_numgen.c
··· 9 9 #include <linux/netlink.h> 10 10 #include <linux/netfilter.h> 11 11 #include <linux/netfilter/nf_tables.h> 12 + #include <linux/random.h> 12 13 #include <linux/static_key.h> 13 14 #include <net/netfilter/nf_tables.h> 14 15 #include <net/netfilter/nf_tables_core.h> 15 - 16 - static DEFINE_PER_CPU(struct rnd_state, nft_numgen_prandom_state); 17 16 18 17 struct nft_ng_inc { 19 18 u8 dreg; ··· 134 135 u32 offset; 135 136 }; 136 137 137 - static u32 nft_ng_random_gen(struct nft_ng_random *priv) 138 + static u32 nft_ng_random_gen(const struct nft_ng_random *priv) 138 139 { 139 - struct rnd_state *state = this_cpu_ptr(&nft_numgen_prandom_state); 140 - 141 - return reciprocal_scale(prandom_u32_state(state), priv->modulus) + 142 - priv->offset; 140 + return reciprocal_scale(get_random_u32(), priv->modulus) + priv->offset; 143 141 } 144 142 145 143 static void nft_ng_random_eval(const struct nft_expr *expr, ··· 163 167 164 168 if (priv->offset + priv->modulus - 1 < priv->offset) 165 169 return -EOVERFLOW; 166 - 167 - prandom_init_once(&nft_numgen_prandom_state); 168 170 169 171 return nft_parse_register_store(ctx, tb[NFTA_NG_DREG], &priv->dreg, 170 172 NULL, NFT_DATA_VALUE, sizeof(u32));