@@ -250,6 +250,7 @@ struct nbd_handle {
union {
struct nbd_structured_reply_offset_data offset_data;
struct nbd_structured_reply_offset_hole offset_hole;
+ struct nbd_structured_reply_offset_hole_ext offset_hole_ext;
struct nbd_structured_reply_block_status_hdr bs_hdr;
struct nbd_structured_reply_block_status_ext_hdr bs_ext_hdr;
struct {
@@ -247,6 +247,11 @@ struct nbd_structured_reply_offset_hole {
uint32_t length; /* Length of hole. */
} NBD_ATTRIBUTE_PACKED;
+struct nbd_structured_reply_offset_hole_ext {
+ uint64_t offset;
+ uint64_t length; /* Length of hole. */
+} NBD_ATTRIBUTE_PACKED;
+
/* NBD_REPLY_TYPE_BLOCK_STATUS block descriptor. */
struct nbd_block_descriptor {
uint32_t length; /* length of block */
@@ -292,6 +297,7 @@ struct nbd_structured_reply_error {
#define NBD_REPLY_TYPE_NONE 0
#define NBD_REPLY_TYPE_OFFSET_DATA 1
#define NBD_REPLY_TYPE_OFFSET_HOLE 2
+#define NBD_REPLY_TYPE_OFFSET_HOLE_EXT 3
#define NBD_REPLY_TYPE_BLOCK_STATUS 5
#define NBD_REPLY_TYPE_BLOCK_STATUS_EXT 6
#define NBD_REPLY_TYPE_ERROR NBD_REPLY_TYPE_ERR (1)
@@ -28,15 +28,16 @@
* requesting command.
*/
static bool
-structured_reply_in_bounds (uint64_t offset, uint32_t length,
+structured_reply_in_bounds (uint64_t offset, uint64_t length,
const struct command *cmd)
{
if (offset < cmd->offset ||
offset >= cmd->offset + cmd->count ||
- offset + length > cmd->offset + cmd->count) {
+ length > cmd->offset + cmd->count ||
+ offset > cmd->offset + cmd->count - length) {
set_error (0, "range of structured reply is out of bounds, "
"offset=%" PRIu64 ", cmd->offset=%" PRIu64 ", "
- "length=%" PRIu32 ", cmd->count=%" PRIu64 ": "
+ "length=%" PRIu64 ", cmd->count=%" PRIu64 ": "
"this is likely to be a bug in the NBD server",
offset, cmd->offset, length, cmd->count);
return false;
@@ -141,6 +142,21 @@ REPLY.STRUCTURED_REPLY.CHECK:
SET_NEXT_STATE (%RECV_OFFSET_HOLE);
break;
+ case NBD_REPLY_TYPE_OFFSET_HOLE_EXT:
+ if (cmd->type != NBD_CMD_READ ||
+ length != sizeof h->sbuf.reply.payload.offset_hole_ext)
+ goto resync;
+ if (!h->extended_headers) {
+ debug (h, "unexpected 64-bit hole without extended headers, "
+ "this is probably a server bug");
+ if (cmd->error == 0)
+ cmd->error = EPROTO;
+ }
+ h->rbuf = &h->sbuf.reply.payload.offset_hole_ext;
+ h->rlen = sizeof h->sbuf.reply.payload.offset_hole_ext;
+ SET_NEXT_STATE (%RECV_OFFSET_HOLE);
+ break;
+
case NBD_REPLY_TYPE_BLOCK_STATUS:
if (cmd->type != NBD_CMD_BLOCK_STATUS ||
length < 12 || ((length-4) & 7) != 0)
@@ -406,7 +422,8 @@ REPLY.STRUCTURED_REPLY.RECV_OFFSET_DATA_DATA:
REPLY.STRUCTURED_REPLY.RECV_OFFSET_HOLE:
struct command *cmd = h->reply_cmd;
uint64_t offset;
- uint32_t length;
+ uint64_t length;
+ uint16_t type;
switch (recv_into_rbuf (h)) {
case -1: SET_NEXT_STATE (%.DEAD); return 0;
@@ -416,10 +433,14 @@ REPLY.STRUCTURED_REPLY.RECV_OFFSET_HOLE:
return 0;
case 0:
offset = be64toh (h->sbuf.reply.payload.offset_hole.offset);
- length = be32toh (h->sbuf.reply.payload.offset_hole.length);
+ type = be16toh (h->sbuf.reply.hdr.structured.type);
+
+ if (type == NBD_REPLY_TYPE_OFFSET_HOLE)
+ length = be32toh (h->sbuf.reply.payload.offset_hole.length);
+ else
+ length = be64toh (h->sbuf.reply.payload.offset_hole_ext.length);
assert (cmd); /* guaranteed by CHECK */
-
assert (cmd->data && cmd->type == NBD_CMD_READ);
/* Is the data within bounds? */
@@ -435,7 +456,10 @@ REPLY.STRUCTURED_REPLY.RECV_OFFSET_HOLE:
/* The spec states that 0-length requests are unspecified, but
* 0-length replies are broken. Still, it's easy enough to support
* them as an extension, and this works even when length == 0.
+ * Although length is 64 bits, the bounds check above ensures that
+ * it is no larger than the 64M cap we put on NBD_CMD_READ.
*/
+ assert (length <= SIZE_MAX);
if (!cmd->initialized)
memset ((char *) cmd->data + offset, 0, length);
if (CALLBACK_IS_NOT_NULL (cmd->cb.fn.chunk)) {