From: "Denis V. Lunev" <den@openvz.org>
To: qemu-devel@nongnu.org
Cc: qemu-block@nongnu.org, "Denis V. Lunev" <den@openvz.org>,
Eric Blake <eblake@redhat.com>,
Vladimir Sementsov-Ogievskiy <vsementsov@yandex-team.ru>
Subject: [PATCH 2/3] block/nbd: never index requests[] with an unchecked cookie
Date: Wed, 12 Aug 2026 12:29:05 +0200 [thread overview]
Message-ID: <20260812102906.894063-3-den@openvz.org> (raw)
In-Reply-To: <20260812102906.894063-1-den@openvz.org>
Cookies are converted into indices of s->requests[] in several places
and the result is used right away, without any check:
int i = COOKIE_TO_INDEX(cookie);
...
return nbd_co_receive_offset_data_payload(s, s->requests[i].offset,
COOKIE_TO_INDEX() subtracts one, so a zero cookie becomes an index of
-1 and the access lands in front of the array. This is undefined and,
depending on the type of the index and on what the compiler has put
there, it can as well pass silently: at the site above i is signed, so
even a plain i < MAX_NBD_REQUESTS check would happily let -1 through.
The only cookie which is checked today is the one taken from the wire,
in nbd_receive_replies(), where an invalid value is a protocol error
rather than an internal inconsistency and thus has to stay a channel
error. Route every other conversion through a helper which asserts the
range before it returns the slot, so that the check can not be
forgotten again.
The cookie of the reply in flight goes through the helper as well. It
is not an unchecked value: it has either passed the check above, or it
has been cleared by the previous commit, so a stale index there is an
internal inconsistency too.
Signed-off-by: Denis V. Lunev <den@openvz.org>
Cc: Eric Blake <eblake@redhat.com>
Cc: Vladimir Sementsov-Ogievskiy <vsementsov@yandex-team.ru>
---
block/nbd.c | 27 ++++++++++++++++++---------
1 file changed, 18 insertions(+), 9 deletions(-)
diff --git a/block/nbd.c b/block/nbd.c
index d9b776283f..21f0f9d40d 100644
--- a/block/nbd.c
+++ b/block/nbd.c
@@ -136,6 +136,16 @@ static void nbd_clear_bdrvstate(BlockDriverState *bs)
s->x_dirty_bitmap = NULL;
}
+/* Not for cookies coming from the wire, those are checked separately. */
+static NBDClientRequest *nbd_request_by_cookie(BDRVNBDState *s, uint64_t cookie)
+{
+ uint64_t ind = COOKIE_TO_INDEX(cookie);
+
+ assert(ind < MAX_NBD_REQUESTS);
+
+ return &s->requests[ind];
+}
+
/* Called with s->receive_mutex taken. */
static bool coroutine_fn nbd_recv_coroutine_wake_one(NBDClientRequest *req)
{
@@ -422,7 +432,8 @@ static coroutine_fn int nbd_receive_replies(BDRVNBDState *s, uint64_t cookie,
Error **errp)
{
int ret;
- uint64_t ind = COOKIE_TO_INDEX(cookie), ind2;
+ NBDClientRequest *req = nbd_request_by_cookie(s, cookie);
+ uint64_t ind2;
QEMU_LOCK_GUARD(&s->receive_mutex);
while (true) {
@@ -437,10 +448,9 @@ static coroutine_fn int nbd_receive_replies(BDRVNBDState *s, uint64_t cookie,
* woken by whoever set s->reply.cookie (or never wait in this
* yield). So, we should not wake it here.
*/
- ind2 = COOKIE_TO_INDEX(s->reply.cookie);
- assert(!s->requests[ind2].receiving);
+ assert(!nbd_request_by_cookie(s, s->reply.cookie)->receiving);
- s->requests[ind].receiving = true;
+ req->receiving = true;
qemu_co_mutex_unlock(&s->receive_mutex);
qemu_coroutine_yield();
@@ -454,7 +464,7 @@ static coroutine_fn int nbd_receive_replies(BDRVNBDState *s, uint64_t cookie,
*/
qemu_co_mutex_lock(&s->receive_mutex);
- assert(!s->requests[ind].receiving);
+ assert(!req->receiving);
continue;
}
@@ -861,7 +871,6 @@ static coroutine_fn int nbd_co_do_receive_one_chunk(
{
ERRP_GUARD();
int ret;
- int i = COOKIE_TO_INDEX(cookie);
void *local_payload = NULL;
NBDStructuredReplyChunk *chunk;
@@ -919,8 +928,8 @@ static coroutine_fn int nbd_co_do_receive_one_chunk(
return -EINVAL;
}
- return nbd_co_receive_offset_data_payload(s, s->requests[i].offset,
- qiov, errp);
+ return nbd_co_receive_offset_data_payload(
+ s, nbd_request_by_cookie(s, cookie)->offset, qiov, errp);
}
if (nbd_reply_type_is_error(chunk->type)) {
@@ -1067,7 +1076,7 @@ static bool coroutine_fn nbd_reply_chunk_iter_receive(BDRVNBDState *s,
break_loop:
qemu_mutex_lock(&s->requests_lock);
- s->requests[COOKIE_TO_INDEX(cookie)].coroutine = NULL;
+ nbd_request_by_cookie(s, cookie)->coroutine = NULL;
s->in_flight--;
qemu_co_queue_next(&s->free_sema);
qemu_mutex_unlock(&s->requests_lock);
--
2.53.0
next prev parent reply other threads:[~2026-08-12 10:29 UTC|newest]
Thread overview: 8+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-08-12 10:29 [PATCH 0/3] block/nbd: fix a race in reply processing Denis V. Lunev
2026-08-12 10:29 ` [PATCH 1/3] block/nbd: clear reply.cookie when the reply is rejected Denis V. Lunev
2026-08-19 11:51 ` Vladimir Sementsov-Ogievskiy
2026-08-12 10:29 ` Denis V. Lunev [this message]
2026-08-19 12:09 ` [PATCH 2/3] block/nbd: never index requests[] with an unchecked cookie Vladimir Sementsov-Ogievskiy
2026-08-12 10:29 ` [PATCH 3/3] block/nbd: clear reply.cookie under receive_mutex Denis V. Lunev
2026-08-19 12:17 ` Vladimir Sementsov-Ogievskiy
2026-08-19 9:34 ` [PATCH 0/3] block/nbd: fix a race in reply processing Denis V. Lunev
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20260812102906.894063-3-den@openvz.org \
--to=den@openvz.org \
--cc=eblake@redhat.com \
--cc=qemu-block@nongnu.org \
--cc=qemu-devel@nongnu.org \
--cc=vsementsov@yandex-team.ru \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.