2023-09-18 20:27:20

by Tejun Heo

[permalink] [raw]
Subject: Re: [PATCH] workqueue: Fix UAF report by KASAN in pwq_release_workfn()

On Thu, Sep 07, 2023 at 10:13:23AM +0800, Z qiang wrote:
> >
> > On Wed, Sep 06, 2023 at 10:12:34AM +0800, Z qiang wrote:
> > > Flush the pwq_release_worker is insufficient, the call_rcu() is
> > > invoked to release wq
> > > in pwq_release_workfn(), this is also asynchronous.
> >
> > But rcu_free_pwq() doesn't access wq or anything. The last access is from
> > the work function.
>
> The rcu_free_wq() will access wq->cpu_pwq or unbound_attrs,
> but at this time, the kfree(wq) may have been called in alloc_workqueue().

I'm not following. The only way alloc_and_link fails is if
apply_wqattrs_prepare() fails and if prepare fails, none of the pwq's are
installed and pwq_unbound_release_workfn() won't try to free the wq as the
pwq's don't have any reference on it. So, if you flush the pwq release work
items, there can be no rcu_free_wq() in flight. Can you please try to see
whether the problem is reproducible with flushing?

Thanks.

--
tejun


2023-09-19 05:27:25

by Zqiang

[permalink] [raw]
Subject: Re: [PATCH] workqueue: Fix UAF report by KASAN in pwq_release_workfn()

>
> On Thu, Sep 07, 2023 at 10:13:23AM +0800, Z qiang wrote:
> > >
> > > On Wed, Sep 06, 2023 at 10:12:34AM +0800, Z qiang wrote:
> > > > Flush the pwq_release_worker is insufficient, the call_rcu() is
> > > > invoked to release wq
> > > > in pwq_release_workfn(), this is also asynchronous.
> > >
> > > But rcu_free_pwq() doesn't access wq or anything. The last access is from
> > > the work function.
> >
> > The rcu_free_wq() will access wq->cpu_pwq or unbound_attrs,
> > but at this time, the kfree(wq) may have been called in alloc_workqueue().
>
> I'm not following. The only way alloc_and_link fails is if
> apply_wqattrs_prepare() fails and if prepare fails, none of the pwq's are
> installed and pwq_unbound_release_workfn() won't try to free the wq as the
> pwq's don't have any reference on it. So, if you flush the pwq release work
> items, there can be no rcu_free_wq() in flight. Can you please try to see
> whether the problem is reproducible with flushing?
>

you are right . sorry, I ignore if apply_wqattrs_prepare() fails,
none of the pwq is installed,
the install_unbound_pwq() is not invoked. I will resend v2 and test.

Thanks
Zqiang

>
> Thanks.
>
> --
> tejun

2023-12-08 07:32:49

by Xuewen Yan

[permalink] [raw]
Subject: Re: [PATCH] workqueue: Fix UAF report by KASAN in pwq_release_workfn()

Hi qiang && Tejun

On Tue, Sep 19, 2023 at 7:30 PM Z qiang <[email protected]> wrote:
>
> >
> > On Thu, Sep 07, 2023 at 10:13:23AM +0800, Z qiang wrote:
> > > >
> > > > On Wed, Sep 06, 2023 at 10:12:34AM +0800, Z qiang wrote:
> > > > > Flush the pwq_release_worker is insufficient, the call_rcu() is
> > > > > invoked to release wq
> > > > > in pwq_release_workfn(), this is also asynchronous.
> > > >
> > > > But rcu_free_pwq() doesn't access wq or anything. The last access is from
> > > > the work function.
> > >
> > > The rcu_free_wq() will access wq->cpu_pwq or unbound_attrs,
> > > but at this time, the kfree(wq) may have been called in alloc_workqueue().
> >
> > I'm not following. The only way alloc_and_link fails is if
> > apply_wqattrs_prepare() fails and if prepare fails, none of the pwq's are
> > installed and pwq_unbound_release_workfn() won't try to free the wq as the
> > pwq's don't have any reference on it. So, if you flush the pwq release work
> > items, there can be no rcu_free_wq() in flight. Can you please try to see
> > whether the problem is reproducible with flushing?
> >
>
> you are right . sorry, I ignore if apply_wqattrs_prepare() fails,
> none of the pwq is installed,
> the install_unbound_pwq() is not invoked. I will resend v2 and test.

I want to ask a question, why delete the v1?
Although add the flush_work, the release work would release the lockdep key,
and at the last of alloc_workqueue, it would also unregister_lockdep_key twice.

And other question, why not free lockdep and just unregister_lockdep?

4136 if (is_last) {
4137 wq_unregister_lockdep(wq);
4138 call_rcu(&wq->rcu, rcu_free_wq);
4139 }

I would really appreciate it if you could help explain it.

Thanks!
BR



>
> Thanks
> Zqiang
>
> >
> > Thanks.
> >
> > --
> > tejun

2023-12-08 08:18:31

by Zqiang

[permalink] [raw]
Subject: Re: [PATCH] workqueue: Fix UAF report by KASAN in pwq_release_workfn()

>
> Hi qiang && Tejun
>
> On Tue, Sep 19, 2023 at 7:30 PM Z qiang <[email protected]> wrote:
> >
> > >
> > > On Thu, Sep 07, 2023 at 10:13:23AM +0800, Z qiang wrote:
> > > > >
> > > > > On Wed, Sep 06, 2023 at 10:12:34AM +0800, Z qiang wrote:
> > > > > > Flush the pwq_release_worker is insufficient, the call_rcu() is
> > > > > > invoked to release wq
> > > > > > in pwq_release_workfn(), this is also asynchronous.
> > > > >
> > > > > But rcu_free_pwq() doesn't access wq or anything. The last access is from
> > > > > the work function.
> > > >
> > > > The rcu_free_wq() will access wq->cpu_pwq or unbound_attrs,
> > > > but at this time, the kfree(wq) may have been called in alloc_workqueue().
> > >
> > > I'm not following. The only way alloc_and_link fails is if
> > > apply_wqattrs_prepare() fails and if prepare fails, none of the pwq's are
> > > installed and pwq_unbound_release_workfn() won't try to free the wq as the
> > > pwq's don't have any reference on it. So, if you flush the pwq release work
> > > items, there can be no rcu_free_wq() in flight. Can you please try to see
> > > whether the problem is reproducible with flushing?
> > >
> >
> > you are right . sorry, I ignore if apply_wqattrs_prepare() fails,
> > none of the pwq is installed,
> > the install_unbound_pwq() is not invoked. I will resend v2 and test.
>
> I want to ask a question, why delete the v1?
> Although add the flush_work, the release work would release the lockdep key,
> and at the last of alloc_workqueue, it would also unregister_lockdep_key twice.
>

For unbound wq, if apply_workqueue_attrs() return error, the link_pwq() will
not be invoked, that means will not insert pwq->pwqs_node to wq->pwqs list,
that also means the is_last is false in pwq_release_workfn().



> And other question, why not free lockdep and just unregister_lockdep?
>
> 4136 if (is_last) {
> 4137 wq_unregister_lockdep(wq);
> 4138 call_rcu(&wq->rcu, rcu_free_wq);
> 4139 }

free lockdep in rcu_free_wq();

Thanks
Zqiang

>
> I would really appreciate it if you could help explain it.
>
> Thanks!
> BR
>
>
>
> >
> > Thanks
> > Zqiang
> >
> > >
> > > Thanks.
> > >
> > > --
> > > tejun