diff --git a/block/nbd-client.c b/block/nbd-client.c index bfbaf7ebe9..8fe660b609 100644 --- a/block/nbd-client.c +++ b/block/nbd-client.c @@ -240,8 +240,8 @@ static int nbd_parse_offset_hole_payload(NBDStructuredReplyChunk *chunk, } /* nbd_parse_blockstatus_payload - * support only one extent in reply and only for - * base:allocation context + * Based on our request, we expect only one extent in reply, for the + * base:allocation context. */ static int nbd_parse_blockstatus_payload(NBDClientSession *client, NBDStructuredReplyChunk *chunk, @@ -250,7 +250,8 @@ static int nbd_parse_blockstatus_payload(NBDClientSession *client, { uint32_t context_id; - if (chunk->length != sizeof(context_id) + sizeof(*extent)) { + /* The server succeeded, so it must have sent [at least] one extent */ + if (chunk->length < sizeof(context_id) + sizeof(*extent)) { error_setg(errp, "Protocol error: invalid payload for " "NBD_REPLY_TYPE_BLOCK_STATUS"); return -EINVAL; @@ -276,10 +277,20 @@ static int nbd_parse_blockstatus_payload(NBDClientSession *client, return -EINVAL; } - /* The server is allowed to send us extra information on the final - * extent; just clamp it to the length we requested. */ + /* + * We used NBD_CMD_FLAG_REQ_ONE, so the server should not have + * sent us any more than one extent, nor should it have included + * status beyond our request in that extent. However, it's easy + * enough to ignore the server's noncompliance without killing the + * connection; just ignore trailing extents, and clamp things to + * the length of our request. + */ + if (chunk->length > sizeof(context_id) + sizeof(*extent)) { + trace_nbd_parse_blockstatus_compliance("more than one extent"); + } if (extent->length > orig_length) { extent->length = orig_length; + trace_nbd_parse_blockstatus_compliance("extent length too large"); } return 0; diff --git a/block/trace-events b/block/trace-events index e6bb5a8f05..debb25c0ac 100644 --- a/block/trace-events +++ b/block/trace-events @@ -157,6 +157,7 @@ nvme_cmd_map_qiov_iov(void *s, int i, void *page, int pages) "s %p iov[%d] %p pa iscsi_xcopy(void *src_lun, uint64_t src_off, void *dst_lun, uint64_t dst_off, uint64_t bytes, int ret) "src_lun %p offset %"PRIu64" dst_lun %p offset %"PRIu64" bytes %"PRIu64" ret %d" # nbd-client.c +nbd_parse_blockstatus_compliance(const char *err) "ignoring extra data from non-compliant server: %s" nbd_read_reply_entry_fail(int ret, const char *err) "ret = %d, err: %s" 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"