@@ -231,6 +231,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 {
struct nbd_structured_reply_error error;
char msg[NBD_MAX_STRING]; /* Common to all error types */
@@ -26,15 +26,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;
@@ -182,6 +183,25 @@ STATE_MACHINE {
SET_NEXT_STATE (%RECV_OFFSET_HOLE);
return 0;
}
+ else if (type == NBD_REPLY_TYPE_OFFSET_HOLE_EXT) {
+ if (cmd->type != NBD_CMD_READ) {
+ SET_NEXT_STATE (%.DEAD);
+ set_error (0, "invalid command for receiving offset-hole chunk, "
+ "cmd->type=%" PRIu16 ", "
+ "this is likely to be a bug in the server",
+ cmd->type);
+ return 0;
+ }
+ if (length != sizeof h->sbuf.sr.payload.offset_hole_ext) {
+ SET_NEXT_STATE (%.DEAD);
+ set_error (0, "invalid length in NBD_REPLY_TYPE_OFFSET_HOLE_EXT");
+ return 0;
+ }
+ h->rbuf = &h->sbuf.sr.payload.offset_hole_ext;
+ h->rlen = sizeof h->sbuf.sr.payload.offset_hole_ext;
+ SET_NEXT_STATE (%RECV_OFFSET_HOLE);
+ return 0;
+ }
else if (type == NBD_REPLY_TYPE_BLOCK_STATUS) {
if (cmd->type != NBD_CMD_BLOCK_STATUS) {
SET_NEXT_STATE (%.DEAD);
@@ -415,7 +435,8 @@ STATE_MACHINE {
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;
@@ -425,7 +446,14 @@ STATE_MACHINE {
return 0;
case 0:
offset = be64toh (h->sbuf.sr.payload.offset_hole.offset);
- length = be32toh (h->sbuf.sr.payload.offset_hole.length);
+ type = be16toh (h->sbuf.sr.hdr.structured_reply.type);
+
+ if (type == NBD_REPLY_TYPE_OFFSET_HOLE)
+ length = be32toh (h->sbuf.sr.payload.offset_hole.length);
+ else {
+ /* XXX Insist on h->extended_headers? */
+ length = be64toh (h->sbuf.sr.payload.offset_hole_ext.length);
+ }
assert (cmd); /* guaranteed by CHECK */
@@ -443,7 +471,10 @@ STATE_MACHINE {
/* 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);
memset (cmd->data + offset, 0, length);
if (CALLBACK_IS_NOT_NULL (cmd->cb.fn.chunk)) {
int error = cmd->error;