| Message ID | 20260926175129.2612644-1-nicoyip.dev@gmail.com |
|---|---|
| State | New |
| Headers | show |
| Series | [net] ipvs: Defer application parent freeing until after RCU readers | expand |
Hello, On Sun, 27 Sep 2026, Chengfeng Ye wrote: > unregister_ip_vs_app() removes each incarnation from its protocol list and > uses call_rcu() to defer freeing it, but frees the parent application > immediately. An RCU reader that already found an incarnation can still > access the parent through inc->app in ip_vs_app_inc_get(). > > During FTP helper module unload, the following interleaving is possible: > > CPU 0 (RCU reader) CPU 1 (module unload) > tcp_app_conn_bind() > find inc in protocol list > unregister_ip_vs_app() > ip_vs_app_inc_release(inc) > list_del_rcu(&inc->p_list) > call_rcu(&inc->rcu_head, ...) > kfree(a) > ip_vs_app_inc_get(inc) > try_module_get(inc->app->module) > > The helper module is already going away, so try_module_get() would fail, > but evaluating its argument first reads the freed parent. The subsequent > rcu_barrier() in pernet unregistration cannot protect this earlier free. > KASAN reported: > > BUG: KASAN: slab-use-after-free in ip_vs_app_inc_get+0x7c/0x90 > Call Trace: > ip_vs_app_inc_get+0x7c/0x90 > tcp_app_conn_bind+0x1bc/0x290 > ip_vs_conn_new+0x1915/0x20d0 > ip_vs_schedule+0x697/0xea0 > tcp_conn_schedule+0x489/0x820 > ip_vs_in_hook+0x7bf/0x1f40 > Allocated by task 88: > kmemdup_noprof+0x20/0x50 > register_ip_vs_app+0x12d/0x2c0 > __ip_vs_ftp_init+0x56/0x160 [ip_vs_ftp] > Freed by task 104: > kfree+0x131/0x3c0 > unregister_ip_vs_app+0x2f4/0x5b0 > unregister_pernet_operations+0x232/0x490 > unregister_pernet_subsys+0x1c/0x30 > __do_sys_delete_module+0x346/0x510 > > Use kfree_rcu() with the existing rcu_head to keep the parent alive until > these readers finish. Successful helper references already prevent normal > module unload, and incarnation RCU callbacks do not access the parent. > > Fixes: 363c97d7435e ("ipvs: convert app locks") > Cc: stable@vger.kernel.org > Signed-off-by: Chengfeng Ye <nicoyip.dev@gmail.com> Looks good to me for the nf tree, thanks! Acked-by: Julian Anastasov <ja@ssi.bg> > --- > net/netfilter/ipvs/ip_vs_app.c | 2 +- > 1 file changed, 1 insertion(+), 1 deletion(-) > > diff --git a/net/netfilter/ipvs/ip_vs_app.c b/net/netfilter/ipvs/ip_vs_app.c > index 11cbdbaf561d..2c1b47702da2 100644 > --- a/net/netfilter/ipvs/ip_vs_app.c > +++ b/net/netfilter/ipvs/ip_vs_app.c > @@ -242,7 +242,7 @@ void unregister_ip_vs_app(struct netns_ipvs *ipvs, struct ip_vs_app *app) > } > > list_del(&a->a_list); > - kfree(a); > + kfree_rcu(a, rcu_head); > > /* decrease the module use count */ > ip_vs_use_count_dec(); > -- > 2.43.0 Regards -- Julian Anastasov <ja@ssi.bg>
diff --git a/net/netfilter/ipvs/ip_vs_app.c b/net/netfilter/ipvs/ip_vs_app.c index 11cbdbaf561d..2c1b47702da2 100644 --- a/net/netfilter/ipvs/ip_vs_app.c +++ b/net/netfilter/ipvs/ip_vs_app.c @@ -242,7 +242,7 @@ void unregister_ip_vs_app(struct netns_ipvs *ipvs, struct ip_vs_app *app) } list_del(&a->a_list); - kfree(a); + kfree_rcu(a, rcu_head); /* decrease the module use count */ ip_vs_use_count_dec();
unregister_ip_vs_app() removes each incarnation from its protocol list and uses call_rcu() to defer freeing it, but frees the parent application immediately. An RCU reader that already found an incarnation can still access the parent through inc->app in ip_vs_app_inc_get(). During FTP helper module unload, the following interleaving is possible: CPU 0 (RCU reader) CPU 1 (module unload) tcp_app_conn_bind() find inc in protocol list unregister_ip_vs_app() ip_vs_app_inc_release(inc) list_del_rcu(&inc->p_list) call_rcu(&inc->rcu_head, ...) kfree(a) ip_vs_app_inc_get(inc) try_module_get(inc->app->module) The helper module is already going away, so try_module_get() would fail, but evaluating its argument first reads the freed parent. The subsequent rcu_barrier() in pernet unregistration cannot protect this earlier free. KASAN reported: BUG: KASAN: slab-use-after-free in ip_vs_app_inc_get+0x7c/0x90 Call Trace: ip_vs_app_inc_get+0x7c/0x90 tcp_app_conn_bind+0x1bc/0x290 ip_vs_conn_new+0x1915/0x20d0 ip_vs_schedule+0x697/0xea0 tcp_conn_schedule+0x489/0x820 ip_vs_in_hook+0x7bf/0x1f40 Allocated by task 88: kmemdup_noprof+0x20/0x50 register_ip_vs_app+0x12d/0x2c0 __ip_vs_ftp_init+0x56/0x160 [ip_vs_ftp] Freed by task 104: kfree+0x131/0x3c0 unregister_ip_vs_app+0x2f4/0x5b0 unregister_pernet_operations+0x232/0x490 unregister_pernet_subsys+0x1c/0x30 __do_sys_delete_module+0x346/0x510 Use kfree_rcu() with the existing rcu_head to keep the parent alive until these readers finish. Successful helper references already prevent normal module unload, and incarnation RCU callbacks do not access the parent. Fixes: 363c97d7435e ("ipvs: convert app locks") Cc: stable@vger.kernel.org Signed-off-by: Chengfeng Ye <nicoyip.dev@gmail.com> --- net/netfilter/ipvs/ip_vs_app.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)