* [PATCH ipsec 0/2] xfrm: fixes for xfrm_state_find under preemption
@ 2025-05-23 15:11 Sabrina Dubroca
2025-05-23 15:11 ` [PATCH ipsec 1/2] xfrm: state: initialize state_ptrs earlier in xfrm_state_find Sabrina Dubroca
` (2 more replies)
0 siblings, 3 replies; 9+ messages in thread
From: Sabrina Dubroca @ 2025-05-23 15:11 UTC (permalink / raw)
To: netdev
Cc: Sabrina Dubroca, Steffen Klassert, Antony Antony, Tobias Brunner,
Florian Westphal
While looking at the pcpu_id changes, I found two issues that can
happen if we get preempted and the cpu_id changes. The second patch
takes care of both problems. The first patch also makes sure we don't
use state_ptrs uninitialized, which could currently happen. syzbot
seems to have hit this issue [1].
[1] https://syzkaller.appspot.com/bug?extid=7ed9d47e15e88581dc5b
Sabrina Dubroca (2):
xfrm: state: initialize state_ptrs earlier in xfrm_state_find
xfrm: state: use a consistent pcpu_id for xfrm_state_find
net/xfrm/xfrm_state.c | 23 ++++++++---------------
1 file changed, 8 insertions(+), 15 deletions(-)
--
2.49.0
^ permalink raw reply [flat|nested] 9+ messages in thread* [PATCH ipsec 1/2] xfrm: state: initialize state_ptrs earlier in xfrm_state_find 2025-05-23 15:11 [PATCH ipsec 0/2] xfrm: fixes for xfrm_state_find under preemption Sabrina Dubroca @ 2025-05-23 15:11 ` Sabrina Dubroca 2025-05-23 18:05 ` Florian Westphal 2025-05-23 15:11 ` [PATCH ipsec 2/2] xfrm: state: use a consistent pcpu_id " Sabrina Dubroca 2025-06-10 7:55 ` [PATCH ipsec 0/2] xfrm: fixes for xfrm_state_find under preemption Steffen Klassert 2 siblings, 1 reply; 9+ messages in thread From: Sabrina Dubroca @ 2025-05-23 15:11 UTC (permalink / raw) To: netdev Cc: Sabrina Dubroca, Steffen Klassert, Florian Westphal, syzbot+7ed9d47e15e88581dc5b In case of preemption, xfrm_state_look_at will find a different pcpu_id and look up states for that other CPU. If we matched a state for CPU2 in the state_cache while the lookup started on CPU1, we will jump to "found", but the "best" state that we got will be ignored and we will enter the "acquire" block. This block uses state_ptrs, which isn't initialized at this point. Let's initialize state_ptrs just after taking rcu_read_lock. This will also prevent a possible misuse in the future, if someone adjusts this function. Reported-by: syzbot+7ed9d47e15e88581dc5b@syzkaller.appspotmail.com Fixes: e952837f3ddb ("xfrm: state: fix out-of-bounds read during lookup") Signed-off-by: Sabrina Dubroca <sd@queasysnail.net> --- net/xfrm/xfrm_state.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/net/xfrm/xfrm_state.c b/net/xfrm/xfrm_state.c index 07fe8e5daa32..ff6813ecc6df 100644 --- a/net/xfrm/xfrm_state.c +++ b/net/xfrm/xfrm_state.c @@ -1389,6 +1389,8 @@ xfrm_state_find(const xfrm_address_t *daddr, const xfrm_address_t *saddr, sequence = read_seqcount_begin(&net->xfrm.xfrm_state_hash_generation); rcu_read_lock(); + xfrm_hash_ptrs_get(net, &state_ptrs); + hlist_for_each_entry_rcu(x, &pol->state_cache_list, state_cache) { if (x->props.family == encap_family && x->props.reqid == tmpl->reqid && @@ -1429,8 +1431,6 @@ xfrm_state_find(const xfrm_address_t *daddr, const xfrm_address_t *saddr, else if (acquire_in_progress) /* XXX: acquire_in_progress should not happen */ WARN_ON(1); - xfrm_hash_ptrs_get(net, &state_ptrs); - h = __xfrm_dst_hash(daddr, saddr, tmpl->reqid, encap_family, state_ptrs.hmask); hlist_for_each_entry_rcu(x, state_ptrs.bydst + h, bydst) { #ifdef CONFIG_XFRM_OFFLOAD -- 2.49.0 ^ permalink raw reply related [flat|nested] 9+ messages in thread
* Re: [PATCH ipsec 1/2] xfrm: state: initialize state_ptrs earlier in xfrm_state_find 2025-05-23 15:11 ` [PATCH ipsec 1/2] xfrm: state: initialize state_ptrs earlier in xfrm_state_find Sabrina Dubroca @ 2025-05-23 18:05 ` Florian Westphal 0 siblings, 0 replies; 9+ messages in thread From: Florian Westphal @ 2025-05-23 18:05 UTC (permalink / raw) To: Sabrina Dubroca; +Cc: netdev, Steffen Klassert, syzbot+7ed9d47e15e88581dc5b Sabrina Dubroca <sd@queasysnail.net> wrote: > In case of preemption, xfrm_state_look_at will find a different > pcpu_id and look up states for that other CPU. If we matched a state > for CPU2 in the state_cache while the lookup started on CPU1, we will > jump to "found", but the "best" state that we got will be ignored and > we will enter the "acquire" block. This block uses state_ptrs, which > isn't initialized at this point. Yep, I missed the "goto" and cc doesn't complain either. > Let's initialize state_ptrs just after taking rcu_read_lock. This will > also prevent a possible misuse in the future, if someone adjusts this > function. Thanks for fixing this bug. Reviewed-by: Florian Westphal <fw@strlen.de> ^ permalink raw reply [flat|nested] 9+ messages in thread
* [PATCH ipsec 2/2] xfrm: state: use a consistent pcpu_id in xfrm_state_find 2025-05-23 15:11 [PATCH ipsec 0/2] xfrm: fixes for xfrm_state_find under preemption Sabrina Dubroca 2025-05-23 15:11 ` [PATCH ipsec 1/2] xfrm: state: initialize state_ptrs earlier in xfrm_state_find Sabrina Dubroca @ 2025-05-23 15:11 ` Sabrina Dubroca 2025-05-23 18:12 ` Florian Westphal 2025-05-26 6:28 ` Steffen Klassert 2025-06-10 7:55 ` [PATCH ipsec 0/2] xfrm: fixes for xfrm_state_find under preemption Steffen Klassert 2 siblings, 2 replies; 9+ messages in thread From: Sabrina Dubroca @ 2025-05-23 15:11 UTC (permalink / raw) To: netdev Cc: Sabrina Dubroca, Steffen Klassert, Antony Antony, Tobias Brunner, Florian Westphal If we get preempted during xfrm_state_find, we could run xfrm_state_look_at using a different pcpu_id than the one xfrm_state_find saw. This could lead to ignoring states that should have matched, and triggering acquires on a CPU that already has a pcpu state. xfrm_state_find starts on CPU1 pcpu_id = 1 lookup starts <preemption, we're now on CPU2> xfrm_state_look_at pcpu_id = 2 finds a state found: best->pcpu_num != pcpu_id (2 != 1) if (!x && !error && !acquire_in_progress) { ... xfrm_state_alloc xfrm_init_tempstate ... This can be avoided by passing the original pcpu_id down to all xfrm_state_look_at() calls. Also switch to raw_smp_processor_id, disabling preempting just to re-enable it immediately doesn't really make sense. Fixes: 1ddf9916ac09 ("xfrm: Add support for per cpu xfrm state handling.") Signed-off-by: Sabrina Dubroca <sd@queasysnail.net> --- net/xfrm/xfrm_state.c | 19 ++++++------------- 1 file changed, 6 insertions(+), 13 deletions(-) diff --git a/net/xfrm/xfrm_state.c b/net/xfrm/xfrm_state.c index ff6813ecc6df..3dc78ef2bf7d 100644 --- a/net/xfrm/xfrm_state.c +++ b/net/xfrm/xfrm_state.c @@ -1307,14 +1307,8 @@ static void xfrm_hash_grow_check(struct net *net, int have_hash_collision) static void xfrm_state_look_at(struct xfrm_policy *pol, struct xfrm_state *x, const struct flowi *fl, unsigned short family, struct xfrm_state **best, int *acq_in_progress, - int *error) + int *error, unsigned int pcpu_id) { - /* We need the cpu id just as a lookup key, - * we don't require it to be stable. - */ - unsigned int pcpu_id = get_cpu(); - put_cpu(); - /* Resolution logic: * 1. There is a valid state with matching selector. Done. * 2. Valid state with inappropriate selector. Skip. @@ -1381,8 +1375,7 @@ xfrm_state_find(const xfrm_address_t *daddr, const xfrm_address_t *saddr, /* We need the cpu id just as a lookup key, * we don't require it to be stable. */ - pcpu_id = get_cpu(); - put_cpu(); + pcpu_id = raw_smp_processor_id(); to_put = NULL; @@ -1402,7 +1395,7 @@ xfrm_state_find(const xfrm_address_t *daddr, const xfrm_address_t *saddr, tmpl->id.proto == x->id.proto && (tmpl->id.spi == x->id.spi || !tmpl->id.spi)) xfrm_state_look_at(pol, x, fl, encap_family, - &best, &acquire_in_progress, &error); + &best, &acquire_in_progress, &error, pcpu_id); } if (best) @@ -1419,7 +1412,7 @@ xfrm_state_find(const xfrm_address_t *daddr, const xfrm_address_t *saddr, tmpl->id.proto == x->id.proto && (tmpl->id.spi == x->id.spi || !tmpl->id.spi)) xfrm_state_look_at(pol, x, fl, family, - &best, &acquire_in_progress, &error); + &best, &acquire_in_progress, &error, pcpu_id); } cached: @@ -1460,7 +1453,7 @@ xfrm_state_find(const xfrm_address_t *daddr, const xfrm_address_t *saddr, tmpl->id.proto == x->id.proto && (tmpl->id.spi == x->id.spi || !tmpl->id.spi)) xfrm_state_look_at(pol, x, fl, family, - &best, &acquire_in_progress, &error); + &best, &acquire_in_progress, &error, pcpu_id); } if (best || acquire_in_progress) goto found; @@ -1495,7 +1488,7 @@ xfrm_state_find(const xfrm_address_t *daddr, const xfrm_address_t *saddr, tmpl->id.proto == x->id.proto && (tmpl->id.spi == x->id.spi || !tmpl->id.spi)) xfrm_state_look_at(pol, x, fl, family, - &best, &acquire_in_progress, &error); + &best, &acquire_in_progress, &error, pcpu_id); } found: -- 2.49.0 ^ permalink raw reply related [flat|nested] 9+ messages in thread
* Re: [PATCH ipsec 2/2] xfrm: state: use a consistent pcpu_id in xfrm_state_find 2025-05-23 15:11 ` [PATCH ipsec 2/2] xfrm: state: use a consistent pcpu_id " Sabrina Dubroca @ 2025-05-23 18:12 ` Florian Westphal 2025-05-26 6:28 ` Steffen Klassert 1 sibling, 0 replies; 9+ messages in thread From: Florian Westphal @ 2025-05-23 18:12 UTC (permalink / raw) To: Sabrina Dubroca; +Cc: netdev, Steffen Klassert, Antony Antony, Tobias Brunner Sabrina Dubroca <sd@queasysnail.net> wrote: > If we get preempted during xfrm_state_find, we could run > xfrm_state_look_at using a different pcpu_id than the one > xfrm_state_find saw. [..] > This can be avoided by passing the original pcpu_id down to all > xfrm_state_look_at() calls. FWIW I found this get/put pair slightly confusing as well, so: Reviewed-by: Florian Westphal <fw@strlen.de> ^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [PATCH ipsec 2/2] xfrm: state: use a consistent pcpu_id in xfrm_state_find 2025-05-23 15:11 ` [PATCH ipsec 2/2] xfrm: state: use a consistent pcpu_id " Sabrina Dubroca 2025-05-23 18:12 ` Florian Westphal @ 2025-05-26 6:28 ` Steffen Klassert 2025-05-26 13:57 ` Florian Westphal 1 sibling, 1 reply; 9+ messages in thread From: Steffen Klassert @ 2025-05-26 6:28 UTC (permalink / raw) To: Sabrina Dubroca; +Cc: netdev, Antony Antony, Tobias Brunner, Florian Westphal On Fri, May 23, 2025 at 05:11:18PM +0200, Sabrina Dubroca wrote: > If we get preempted during xfrm_state_find, we could run > xfrm_state_look_at using a different pcpu_id than the one > xfrm_state_find saw. This could lead to ignoring states that should > have matched, and triggering acquires on a CPU that already has a pcpu > state. > > xfrm_state_find starts on CPU1 > pcpu_id = 1 > lookup starts > <preemption, we're now on CPU2> > xfrm_state_look_at pcpu_id = 2 > finds a state > found: > best->pcpu_num != pcpu_id (2 != 1) > if (!x && !error && !acquire_in_progress) { > ... > xfrm_state_alloc > xfrm_init_tempstate > ... > > This can be avoided by passing the original pcpu_id down to all > xfrm_state_look_at() calls. > > Also switch to raw_smp_processor_id, disabling preempting just to > re-enable it immediately doesn't really make sense. > > Fixes: 1ddf9916ac09 ("xfrm: Add support for per cpu xfrm state handling.") > Signed-off-by: Sabrina Dubroca <sd@queasysnail.net> > --- > net/xfrm/xfrm_state.c | 19 ++++++------------- > 1 file changed, 6 insertions(+), 13 deletions(-) > > diff --git a/net/xfrm/xfrm_state.c b/net/xfrm/xfrm_state.c > index ff6813ecc6df..3dc78ef2bf7d 100644 > --- a/net/xfrm/xfrm_state.c > +++ b/net/xfrm/xfrm_state.c > @@ -1307,14 +1307,8 @@ static void xfrm_hash_grow_check(struct net *net, int have_hash_collision) > static void xfrm_state_look_at(struct xfrm_policy *pol, struct xfrm_state *x, > const struct flowi *fl, unsigned short family, > struct xfrm_state **best, int *acq_in_progress, > - int *error) > + int *error, unsigned int pcpu_id) > { > - /* We need the cpu id just as a lookup key, > - * we don't require it to be stable. > - */ > - unsigned int pcpu_id = get_cpu(); > - put_cpu(); > - > /* Resolution logic: > * 1. There is a valid state with matching selector. Done. > * 2. Valid state with inappropriate selector. Skip. > @@ -1381,8 +1375,7 @@ xfrm_state_find(const xfrm_address_t *daddr, const xfrm_address_t *saddr, > /* We need the cpu id just as a lookup key, > * we don't require it to be stable. > */ > - pcpu_id = get_cpu(); > - put_cpu(); > + pcpu_id = raw_smp_processor_id(); This codepath can be taken from the forwarding path with preemtion disabled. raw_smp_processor_id will trigger a warning in that case, maybe better to use raw_cpu_ptr? Thanks! ^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [PATCH ipsec 2/2] xfrm: state: use a consistent pcpu_id in xfrm_state_find 2025-05-26 6:28 ` Steffen Klassert @ 2025-05-26 13:57 ` Florian Westphal 2025-05-26 12:17 ` Steffen Klassert 0 siblings, 1 reply; 9+ messages in thread From: Florian Westphal @ 2025-05-26 13:57 UTC (permalink / raw) To: Steffen Klassert; +Cc: Sabrina Dubroca, netdev, Antony Antony, Tobias Brunner Steffen Klassert <steffen.klassert@secunet.com> wrote: > > - pcpu_id = get_cpu(); > > - put_cpu(); > > + pcpu_id = raw_smp_processor_id(); > > This codepath can be taken from the forwarding path with preemtion > disabled. raw_smp_processor_id will trigger a warning in that case, Are you sure? smp_processor_id() emits a warning when called from preemptible context, raw_smp_processor_id() should not do that. We use raw_smp_processor_id from various netfilter modules as well and I never saw preemption warnings. ^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [PATCH ipsec 2/2] xfrm: state: use a consistent pcpu_id in xfrm_state_find 2025-05-26 13:57 ` Florian Westphal @ 2025-05-26 12:17 ` Steffen Klassert 0 siblings, 0 replies; 9+ messages in thread From: Steffen Klassert @ 2025-05-26 12:17 UTC (permalink / raw) To: Florian Westphal; +Cc: Sabrina Dubroca, netdev, Antony Antony, Tobias Brunner On Mon, May 26, 2025 at 03:57:04PM +0200, Florian Westphal wrote: > Steffen Klassert <steffen.klassert@secunet.com> wrote: > > > - pcpu_id = get_cpu(); > > > - put_cpu(); > > > + pcpu_id = raw_smp_processor_id(); > > > > This codepath can be taken from the forwarding path with preemtion > > disabled. raw_smp_processor_id will trigger a warning in that case, > > Are you sure? smp_processor_id() emits a warning when called from > preemptible context, raw_smp_processor_id() should not do that. > > We use raw_smp_processor_id from various netfilter modules as well > and I never saw preemption warnings. You are right, I just saw smp_processor_id. Sorry for the noise! ^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [PATCH ipsec 0/2] xfrm: fixes for xfrm_state_find under preemption 2025-05-23 15:11 [PATCH ipsec 0/2] xfrm: fixes for xfrm_state_find under preemption Sabrina Dubroca 2025-05-23 15:11 ` [PATCH ipsec 1/2] xfrm: state: initialize state_ptrs earlier in xfrm_state_find Sabrina Dubroca 2025-05-23 15:11 ` [PATCH ipsec 2/2] xfrm: state: use a consistent pcpu_id " Sabrina Dubroca @ 2025-06-10 7:55 ` Steffen Klassert 2 siblings, 0 replies; 9+ messages in thread From: Steffen Klassert @ 2025-06-10 7:55 UTC (permalink / raw) To: Sabrina Dubroca; +Cc: netdev, Antony Antony, Tobias Brunner, Florian Westphal On Fri, May 23, 2025 at 05:11:16PM +0200, Sabrina Dubroca wrote: > While looking at the pcpu_id changes, I found two issues that can > happen if we get preempted and the cpu_id changes. The second patch > takes care of both problems. The first patch also makes sure we don't > use state_ptrs uninitialized, which could currently happen. syzbot > seems to have hit this issue [1]. > > [1] https://syzkaller.appspot.com/bug?extid=7ed9d47e15e88581dc5b > > Sabrina Dubroca (2): > xfrm: state: initialize state_ptrs earlier in xfrm_state_find > xfrm: state: use a consistent pcpu_id for xfrm_state_find Applied, thanks a lot! ^ permalink raw reply [flat|nested] 9+ messages in thread
end of thread, other threads:[~2025-06-10 8:01 UTC | newest] Thread overview: 9+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2025-05-23 15:11 [PATCH ipsec 0/2] xfrm: fixes for xfrm_state_find under preemption Sabrina Dubroca 2025-05-23 15:11 ` [PATCH ipsec 1/2] xfrm: state: initialize state_ptrs earlier in xfrm_state_find Sabrina Dubroca 2025-05-23 18:05 ` Florian Westphal 2025-05-23 15:11 ` [PATCH ipsec 2/2] xfrm: state: use a consistent pcpu_id " Sabrina Dubroca 2025-05-23 18:12 ` Florian Westphal 2025-05-26 6:28 ` Steffen Klassert 2025-05-26 13:57 ` Florian Westphal 2025-05-26 12:17 ` Steffen Klassert 2025-06-10 7:55 ` [PATCH ipsec 0/2] xfrm: fixes for xfrm_state_find under preemption Steffen Klassert
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox; as well as URLs for NNTP newsgroup(s).