Blame SOURCES/kvm-nbd-Tolerate-some-server-non-compliance-in-NBD_CMD_B.patch

7711c0
From 557b85bcd4aedf4550b272abb57f817f7dc8eba1 Mon Sep 17 00:00:00 2001
7711c0
From: John Snow <jsnow@redhat.com>
7711c0
Date: Mon, 6 May 2019 17:56:12 +0200
7711c0
Subject: [PATCH 02/53] nbd: Tolerate some server non-compliance in
7711c0
 NBD_CMD_BLOCK_STATUS
7711c0
7711c0
RH-Author: John Snow <jsnow@redhat.com>
7711c0
Message-id: <20190506175629.11079-3-jsnow@redhat.com>
7711c0
Patchwork-id: 87193
7711c0
O-Subject: [RHEL-7.7 qemu-kvm-rhev PATCH 02/19] nbd: Tolerate some server non-compliance in NBD_CMD_BLOCK_STATUS
7711c0
Bugzilla: 1692018
7711c0
RH-Acked-by: Max Reitz <mreitz@redhat.com>
7711c0
RH-Acked-by: Stefano Garzarella <sgarzare@redhat.com>
7711c0
RH-Acked-by: Thomas Huth <thuth@redhat.com>
7711c0
7711c0
From: Eric Blake <eblake@redhat.com>
7711c0
7711c0
The NBD spec states that NBD_CMD_FLAG_REQ_ONE (which we currently
7711c0
always use) should not reply with an extent larger than our request,
7711c0
and that the server's response should be exactly one extent. Right
7711c0
now, that means that if a server sends more than one extent, we treat
7711c0
the server as broken, fail the block status request, and disconnect,
7711c0
which prevents all further use of the block device. But while good
7711c0
software should be strict in what it sends, it should be tolerant in
7711c0
what it receives.
7711c0
7711c0
While trying to implement NBD_CMD_BLOCK_STATUS in nbdkit, we
7711c0
temporarily had a non-compliant server sending too many extents in
7711c0
spite of REQ_ONE. Oddly enough, 'qemu-img convert' with qemu 3.1
7711c0
failed with a somewhat useful message:
7711c0
  qemu-img: Protocol error: invalid payload for NBD_REPLY_TYPE_BLOCK_STATUS
7711c0
7711c0
which then disappeared with commit d8b4bad8, on the grounds that an
7711c0
error message flagged only at the time of coroutine teardown is
7711c0
pointless, and instead we should rely on the actual failed API to
7711c0
report an error - in other words, the 3.1 behavior was masking the
7711c0
fact that qemu-img was not reporting an error. That has since been
7711c0
fixed in the previous patch, where qemu-img convert now fails with:
7711c0
  qemu-img: error while reading block status of sector 0: Invalid argument
7711c0
7711c0
But even that is harsh.  Since we already partially relaxed things in
7711c0
commit acfd8f7a to tolerate a server that exceeds the cap (although
7711c0
that change was made prior to the NBD spec actually putting a cap on
7711c0
the extent length during REQ_ONE - in fact, the NBD spec change was
7711c0
BECAUSE of the qemu behavior prior to that commit), it's not that much
7711c0
harder to argue that we should also tolerate a server that sends too
7711c0
many extents.  But at the same time, it's nice to trace when we are
7711c0
being tolerant of server non-compliance, in order to help server
7711c0
writers fix their implementations to be more portable (if they refer
7711c0
to our traces, rather than just stderr).
7711c0
7711c0
Reported-by: Richard W.M. Jones <rjones@redhat.com>
7711c0
Signed-off-by: Eric Blake <eblake@redhat.com>
7711c0
Message-Id: <20190323212639.579-3-eblake@redhat.com>
7711c0
Reviewed-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
7711c0
(cherry picked from commit a39286dd61e455014694cb6aa44cfa9e5c86d101)
7711c0
Signed-off-by: John Snow <jsnow@redhat.com>
7711c0
Signed-off-by: Miroslav Rezanina <mrezanin@redhat.com>
7711c0
---
7711c0
 block/nbd-client.c | 21 ++++++++++++++++-----
7711c0
 block/trace-events |  1 +
7711c0
 2 files changed, 17 insertions(+), 5 deletions(-)
7711c0
7711c0
diff --git a/block/nbd-client.c b/block/nbd-client.c
7711c0
index 1230850..f3c31d1 100644
7711c0
--- a/block/nbd-client.c
7711c0
+++ b/block/nbd-client.c
7711c0
@@ -227,8 +227,8 @@ static int nbd_parse_offset_hole_payload(NBDStructuredReplyChunk *chunk,
7711c0
 }
7711c0
 
7711c0
 /* nbd_parse_blockstatus_payload
7711c0
- * support only one extent in reply and only for
7711c0
- * base:allocation context
7711c0
+ * Based on our request, we expect only one extent in reply, for the
7711c0
+ * base:allocation context.
7711c0
  */
7711c0
 static int nbd_parse_blockstatus_payload(NBDClientSession *client,
7711c0
                                          NBDStructuredReplyChunk *chunk,
7711c0
@@ -237,7 +237,8 @@ static int nbd_parse_blockstatus_payload(NBDClientSession *client,
7711c0
 {
7711c0
     uint32_t context_id;
7711c0
 
7711c0
-    if (chunk->length != sizeof(context_id) + sizeof(*extent)) {
7711c0
+    /* The server succeeded, so it must have sent [at least] one extent */
7711c0
+    if (chunk->length < sizeof(context_id) + sizeof(*extent)) {
7711c0
         error_setg(errp, "Protocol error: invalid payload for "
7711c0
                          "NBD_REPLY_TYPE_BLOCK_STATUS");
7711c0
         return -EINVAL;
7711c0
@@ -263,10 +264,20 @@ static int nbd_parse_blockstatus_payload(NBDClientSession *client,
7711c0
         return -EINVAL;
7711c0
     }
7711c0
 
7711c0
-    /* The server is allowed to send us extra information on the final
7711c0
-     * extent; just clamp it to the length we requested. */
7711c0
+    /*
7711c0
+     * We used NBD_CMD_FLAG_REQ_ONE, so the server should not have
7711c0
+     * sent us any more than one extent, nor should it have included
7711c0
+     * status beyond our request in that extent. However, it's easy
7711c0
+     * enough to ignore the server's noncompliance without killing the
7711c0
+     * connection; just ignore trailing extents, and clamp things to
7711c0
+     * the length of our request.
7711c0
+     */
7711c0
+    if (chunk->length > sizeof(context_id) + sizeof(*extent)) {
7711c0
+        trace_nbd_parse_blockstatus_compliance("more than one extent");
7711c0
+    }
7711c0
     if (extent->length > orig_length) {
7711c0
         extent->length = orig_length;
7711c0
+        trace_nbd_parse_blockstatus_compliance("extent length too large");
7711c0
     }
7711c0
 
7711c0
     return 0;
7711c0
diff --git a/block/trace-events b/block/trace-events
7711c0
index 6d4d399..59c6f54 100644
7711c0
--- a/block/trace-events
7711c0
+++ b/block/trace-events
7711c0
@@ -152,5 +152,6 @@ nvme_cmd_map_qiov_pages(void *s, int i, uint64_t page) "s %p page[%d] 0x%"PRIx64
7711c0
 nvme_cmd_map_qiov_iov(void *s, int i, void *page, int pages) "s %p iov[%d] %p pages %d"
7711c0
 
7711c0
 # block/nbd-client.c
7711c0
+nbd_parse_blockstatus_compliance(const char *err) "ignoring extra data from non-compliant server: %s"
7711c0
 nbd_read_reply_entry_fail(int ret, const char *err) "ret = %d, err: %s"
7711c0
 nbd_co_request_fail(uint64_t from, uint32_t len, uint64_t handle, uint16_t flags, uint16_t type, const char *name, int ret, const char *err) "Request failed { .from = %" PRIu64", .len = %" PRIu32 ", .handle = %" PRIu64 ", .flags = 0x%" PRIx16 ", .type = %" PRIu16 " (%s) } ret = %d, err: %s"
7711c0
-- 
7711c0
1.8.3.1
7711c0