2021-06-07 07:58:32

by Greg Kroah-Hartman

[permalink] [raw]
Subject: Re: KASAN: use-after-free Read in hci_chan_del

On Mon, Jun 07, 2021 at 03:48:28PM +0800, Hillf Danton wrote:
> On Sun, 6 Jun 2021 11:54:22 +0200 Greg KH wrote:
> >On Sun, Jun 06, 2021 at 04:50:04PM +0800, Hillf Danton wrote:
> >>
> >> To fix the uaf reported, add reference count to hci channel to track users.
> >> Then only channels with zero users will be released.
> >>
> >> It is now only for thoughts.
> >>
> >> +++ x/include/net/bluetooth/hci_core.h
> >> @@ -704,6 +704,7 @@ struct hci_chan {
> >> struct sk_buff_head data_q;
> >> unsigned int sent;
> >> __u8 state;
> >> + atomic_t ref;
> >
> >Please no, never use "raw" atomic variables. Especially for something
> >like this, use a kref.
>
> Fair, thanks for taking a look at it.
>
> Spin with care for the race the added ref fails to cut.

I do not understand what you mean here.

> To ease review the full syzreport is also attached.
>
> To fix uaf, add user track to hci channel and we will only release channel if
> its user hits zero. And a dryrun mechanism is also added to take care of the
> race user track fails to cut.
>
> CPU0 CPU1
> ---- ----
> hci_chan_del l2cap_conn_del
> chan->user = 0;
>
> if (chan->user != 0)
> return;
> synchronize_rcu();
> kfree(chan);
>
> hci_chan_del();
>
> It is now only for thoughts.
>
> +++ x/include/net/bluetooth/hci_core.h
> @@ -704,6 +704,10 @@ struct hci_chan {
> struct sk_buff_head data_q;
> unsigned int sent;
> __u8 state;
> + __u8 user;

No.

> + __u8 release;

No please no.

> +
> +#define HCHAN_RELEASE_DRYRUN 1
> };
>
> struct hci_conn_params {
> +++ x/net/bluetooth/l2cap_core.c
> @@ -1903,6 +1903,12 @@ static void l2cap_conn_del(struct hci_co
>
> mutex_unlock(&conn->chan_lock);
>
> + /* see comment in hci_chan_del() */
> + conn->hchan->release = HCHAN_RELEASE_DRYRUN;
> + smp_wmb();
> + conn->hchan->user--;

And the reason you are open-coding a kref is why???

Please again no.

> + hci_chan_del(conn->hchan);
> + conn->hchan->release = 0;
> hci_chan_del(conn->hchan);
>
> if (conn->info_state & L2CAP_INFO_FEAT_MASK_REQ_SENT)
> @@ -7716,6 +7722,8 @@ static struct l2cap_conn *l2cap_conn_add
> kref_init(&conn->ref);
> hcon->l2cap_data = conn;
> conn->hcon = hci_conn_get(hcon);
> + /* dec in l2cap_conn_del() */
> + hchan->user++;

{sigh}

No, there is a reason we wrote kref many _decades_ ago. Please use it,
your original attempt with an atomic was just fine, just use the proper
data structures the kernel provides you as this is obviously a reference
counted object.

thanks,

greg k-h