From: Jun Yang xfrm_state_migrate_install() transfers the cloned state's allocation reference to the state table. xfrm_state_migrate() returns the state without a caller reference, and xfrm_migrate() saves it in x_new[]. If a later migration step fails, xfrm_migrate() deletes those states during rollback. A concurrent deletion, for example from the expiry timer, can drop the table reference and free a state before rollback calls xfrm_state_delete(). Take a caller reference before installing each clone. Drop it if installation fails, after a successful migration, or after deleting the new states on rollback. Fixes: 80c9abaabf42 ("[XFRM]: Extension for dynamic update of endpoint address(es)") Cc: stable@kernel.org Reported-by: TencentOS Corvus AI Signed-off-by: Jun Yang --- A KASAN reproducer for this issue is available if requested. net/xfrm/xfrm_policy.c | 7 ++++++- net/xfrm/xfrm_state.c | 6 +++++- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/net/xfrm/xfrm_policy.c b/net/xfrm/xfrm_policy.c index 9ea60c7717ff..06aa036701c3 100644 --- a/net/xfrm/xfrm_policy.c +++ b/net/xfrm/xfrm_policy.c @@ -4726,6 +4726,9 @@ int xfrm_migrate(const struct xfrm_selector *sel, u8 dir, u8 type, xfrm_states_delete(x_cur, nx_cur); } + if (nx_new) + xfrm_states_put(x_new, nx_new); + /* Stage 5 - announce */ km_migrate(sel, dir, type, m, num_migrate, k, net, encap); @@ -4740,8 +4743,10 @@ restore_state: xfrm_pol_put(pol); if (nx_cur) xfrm_states_put(x_cur, nx_cur); - if (nx_new) + if (nx_new) { xfrm_states_delete(x_new, nx_new); + xfrm_states_put(x_new, nx_new); + } return err; } diff --git a/net/xfrm/xfrm_state.c b/net/xfrm/xfrm_state.c index e45aa1ed5b96..aa97acaacd64 100644 --- a/net/xfrm/xfrm_state.c +++ b/net/xfrm/xfrm_state.c @@ -2220,8 +2220,12 @@ struct xfrm_state *xfrm_state_migrate(struct xfrm_state *x, xfrm_migrate_sync(xc, x); - if (xfrm_state_migrate_install(x, xc, m, extack) < 0) + /* Keep a caller reference after installation transfers ownership. */ + xfrm_state_hold(xc); + if (xfrm_state_migrate_install(x, xc, m, extack) < 0) { + xfrm_state_put(xc); return NULL; + } return xc; } -- 2.43.7