[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: NULL pointer dereference in xenbus_thread->...



On Wed, Apr 30, 2025 at 11:44 AM Jürgen Groß <jgross@xxxxxxxx> wrote:
> I have looked at this issue multiple times now.
>
> Just some remarks what IMO could go wrong (I didn't find any proof that
> this really happened, though), in case someone wants to double check:
>
> The most probably candidate for something going wrong is a use-after-free
> of a struct xb_req_data element (normally named "req" in the related code).
>
> Some words about the not really obvious locking scheme used for those
> elements: A "req" is owned by a thread as long as it isn't in any of the
> lists it can live in (xs_reply_list or xb_write_list). Putting it into one
> of the lists or removing it again requires to hold the xb_write_mutex.

While I see how the ownership is transferred between lists and the
static variables in process_writes(), xs_wait_for_reply()/read_reply()
is looking at req, too.

> A "req" needs to be in a certain state when either in one of the lists or
> when being owned by a worker thread.
>
> I'm wondering whether it could happen that a thread waiting for a "req"
> could be woken up and the "req" is being freed before the waiting thread
> can react. Normally this shouldn't be possible, but "never say never".
> What catched my eye today is the test of req->state == xb_req_state_wait_reply
> in process_msg() just after dropping the xb_write_mutex. This looks a little
> bit fishy, but OTOH the request has been just removed from the xs_reply_list,
> so no mutex should be needed for that test.
>
> Possible candidates for such an "impossible" scenario include a wrap of
> xs_request_id (not very probable, though, as having 4 billion Xenstore
> requests "in flight" is rather unlikely IMHO).

If read_reply() exits because req->err is set, and req->state is not
queued or wait_reply, the req will be freed in xs_wait_for_reply(),
but not removed from the list.  If it is on the xs_reply_list,
req->cb(req) could be called, but that would need req->state ==
wait_reply.  I don't see how this could happen, but it came to mind.

Put another way, kfree() in xs_wait_for_reply() doesn't know if the
req is on the list or not.  I wonder if it would be better to just
have
if (req->state == xb_req_state_got_reply)
    kfree(req);
else
    req->state = xb_req_state_aborted;

Maybe also some sort of assertion that req is not on a list, too.
This would at least make it clear exactly when xs_wait_for_reply() is
supposed to free req.

Given these states:
enum xb_req_state {
xb_req_state_queued,
xb_req_state_wait_reply,
xb_req_state_got_reply,
xb_req_state_aborted
};

Being explicit on who frees each state would be good.

Or if all list manipulations have to be protected by xb_write_mutex,
then just let the "caller" xs_wait_for_reply() always unlink and
kfree()?

Yet it's unclear how req->cb(req) leads to a use-after-free.
req->cb() was valid to make the call, but then the waitqueue was
(probably) zeroed?  That seems like a race.  ... or is wake_up()
faulting because xs_wait_for_reply() woke up and freed the req before
wake_up() finished with it?

__wake_up_common (kernel/sched/wait.c:85) is:

list_for_each_entry_safe_from(curr, next, &wq_head->head, entry) {

I'l need to look more into the wake_up() implementation...

Regards,
Jason



 


Rackspace

Lists.xenproject.org is hosted with RackSpace, monitoring our
servers 24x7x365 and backed by RackSpace's Fanatical Support®.