On 12.08.26 13:29, Denis V. Lunev wrote:
nbd_receive_replies() reads a reply header into s->reply and, when the
header turns out to be unusable, reports a channel error and returns
without touching it. The cookie stays there until the request which
owns the reply clears it, and until then the waiters are explicitly
allowed to look at a cookie which is not theirs:

     if (s->reply.cookie != 0) {
         ind2 = COOKIE_TO_INDEX(s->reply.cookie);
         assert(!s->requests[ind2].receiving);

Two of the error paths leave a value chosen by the server behind: one
returns before the cookie is validated at all, the other returns
because that validation has failed. A waiter which picks such a cookie
up turns it into an index which is not in requests[] and accesses the
array out of bounds, at an offset the server controls.

The reply is of no use to anybody at this point, so clear the cookie
before the mutex is released and keep the invariant that a non-zero
s->reply.cookie is always an index of a live request.

Observing the stale cookie takes a second thread, which a multiqueue
configuration provides. Within one AioContext there is no yield point
between the failed read and the clearing done by the owner in
nbd_co_receive_one_chunk(), so nothing else of this node runs in
between. The parked waiters cannot see it either, as they are woken
only after the cookie has been cleared. What can get in is a request
entering nbd_receive_replies() afresh, one just sent or one back for
its next reply chunk, because that path takes the mutex without
looking at the state.

Signed-off-by: Denis V. Lunev<[email protected]>
Cc: Eric Blake<[email protected]>
Cc: Vladimir Sementsov-Ogievskiy<[email protected]>

Reviewed-by: Vladimir Sementsov-Ogievskiy <[email protected]>

--
Best regards,
Vladimir

Reply via email to