Messages in this thread | | | Subject | Re: [PATCH] once: Fix panic when module unload | From | Kefeng Wang <> | Date | Fri, 16 Jul 2021 13:03:23 +0800 |
| |
Hi all, kindly ping...
On 2021/6/22 10:21, Kefeng Wang wrote: > DO_ONCE > DEFINE_STATIC_KEY_TRUE(___once_key); > __do_once_done > once_disable_jump(once_key); > INIT_WORK(&w->work, once_deferred); > struct once_work *w; > w->key = key; > schedule_work(&w->work); module unload > //*the key is destroy* > process_one_work > once_deferred > BUG_ON(!static_key_enabled(work->key)); > static_key_count((struct static_key *)x) //*access key, crash* > > When module uses DO_ONCE mechanism, it could crash due to the above > concurrency problem, we could reproduce it with link[1]. > > Fix it by add/put module refcount in the once work process. > > [1] > https://lore.kernel.org/netdev/eaa6c371-465e-57eb-6be9-f4b16b9d7cbf@huawei.com/ > > Cc: Hannes Frederic Sowa <hannes@stressinduktion.org> > Cc: Daniel Borkmann <daniel@iogearbox.net> > Cc: David S. Miller <davem@davemloft.net> > Cc: Eric Dumazet <edumazet@google.com> > Reported-by: Minmin chen <chenmingmin@huawei.com> > Signed-off-by: Kefeng Wang <wangkefeng.wang@huawei.com> > --- > lib/once.c | 17 +++++++++++++++++ > 1 file changed, 17 insertions(+) > > diff --git a/lib/once.c b/lib/once.c > index 8b7d6235217e..959f8db41ccf 100644 > --- a/lib/once.c > +++ b/lib/once.c > @@ -3,10 +3,12 @@ > #include <linux/spinlock.h> > #include <linux/once.h> > #include <linux/random.h> > +#include <linux/module.h> > > struct once_work { > struct work_struct work; > struct static_key_true *key; > + struct module *module; > }; > > static void once_deferred(struct work_struct *w) > @@ -16,11 +18,24 @@ static void once_deferred(struct work_struct *w) > work = container_of(w, struct once_work, work); > BUG_ON(!static_key_enabled(work->key)); > static_branch_disable(work->key); > + module_put(work->module); > kfree(work); > } > > +static struct module *find_module_by_key(struct static_key_true *key) > +{ > + struct module *mod; > + > + preempt_disable(); > + mod = __module_address((unsigned long)key); > + preempt_enable(); > + > + return mod; > +} > + > static void once_disable_jump(struct static_key_true *key) > { > + struct module *mod = find_module_by_key(key); > struct once_work *w; > > w = kmalloc(sizeof(*w), GFP_ATOMIC); > @@ -29,6 +44,8 @@ static void once_disable_jump(struct static_key_true *key) > > INIT_WORK(&w->work, once_deferred); > w->key = key; > + w->module = mod; > + __module_get(mod); > schedule_work(&w->work); > } >
| |