On 7/21/23 18:08, Eric Blake wrote:
> While working on later patches, I noticed that I was frequently
> referring to the wire contents of h->sbuf.reply.hdr.structured.length,
> byte-swapping it, then repeating open-coded math for how many bytes
> were consumed in earlier states.  It's easier to normalize the
> remaining bytes to be parsed into a variable that persists across the
> life of the reply, and also makes it possible to track that by the end
> of any structured reply, we have dealt with all of the bytes that the
> header advertised.  Some of the worst implicit assumptions in block
> status code will be fixed in the next patch with the help of this new
> variable.
> 
> Signed-off-by: Eric Blake <ebl...@redhat.com>
> ---
> 
> v4: new patch, replacing part of v3's 5/22
> ---
>  lib/internal.h                 |  3 ++
>  generator/states-reply-chunk.c | 59 ++++++++++++++++------------------
>  2 files changed, 31 insertions(+), 31 deletions(-)
> 
> diff --git a/lib/internal.h b/lib/internal.h
> index 4b0043b3..b38ae524 100644
> --- a/lib/internal.h
> +++ b/lib/internal.h
> @@ -304,6 +304,9 @@ struct nbd_handle {
>    string_vector querylist;
>    size_t querynum;
> 
> +  /* Chunk payload length remaining to be parsed */
> +  size_t payload_left;
> +
>    /* When receiving block status, this is used. */
>    uint32_t *bs_entries;
> 
> diff --git a/generator/states-reply-chunk.c b/generator/states-reply-chunk.c
> index 26b8a6b0..da5fc426 100644
> --- a/generator/states-reply-chunk.c
> +++ b/generator/states-reply-chunk.c
> @@ -69,6 +69,7 @@  REPLY.CHUNK_REPLY.START:
>    /* Skip an unexpected structured reply, including to an unknown cookie. */
>    if (cmd == NULL || !h->structured_replies)
>      goto resync;
> +  h->payload_left = length;
> 
>    switch (type) {
>    case NBD_REPLY_TYPE_NONE:
> @@ -87,6 +88,7 @@  REPLY.CHUNK_REPLY.START:
>        goto resync;
>      h->rbuf = &h->sbuf.reply.payload.offset_data;
>      h->rlen = sizeof h->sbuf.reply.payload.offset_data;
> +    h->payload_left -= h->rlen;
>      SET_NEXT_STATE (%RECV_OFFSET_DATA);
>      break;
> 
> @@ -96,6 +98,7 @@  REPLY.CHUNK_REPLY.START:
>        goto resync;
>      h->rbuf = &h->sbuf.reply.payload.offset_hole;
>      h->rlen = sizeof h->sbuf.reply.payload.offset_hole;
> +    h->payload_left -= h->rlen;
>      SET_NEXT_STATE (%RECV_OFFSET_HOLE);
>      break;
> 
> @@ -141,7 +144,8 @@  REPLY.CHUNK_REPLY.START:
> 
>   resync:
>    h->rbuf = NULL;
> -  h->rlen = length;
> +  h->rlen = h->payload_left;
> +  h->payload_left = 0;
>    SET_NEXT_STATE (%RESYNC);
>    return 0;
> 
> @@ -156,7 +160,8 @@  REPLY.CHUNK_REPLY.RECV_ERROR:
>      SET_NEXT_STATE (%.READY);
>      return 0;
>    case 0:
> -    length = be32toh (h->sbuf.reply.hdr.structured.length);
> +    length = h->payload_left;
> +    h->payload_left -= MIN (length, sizeof 
> h->sbuf.reply.payload.error.error);
>      assert (length >= sizeof h->sbuf.reply.payload.error.error.error);
>      assert (cmd);
> 
> @@ -164,12 +169,13 @@  REPLY.CHUNK_REPLY.RECV_ERROR:
>        goto resync;
> 
>      msglen = be16toh (h->sbuf.reply.payload.error.error.len);
> -    if (msglen > length - sizeof h->sbuf.reply.payload.error.error ||
> +    if (msglen > h->payload_left ||
>          msglen > sizeof h->sbuf.reply.payload.error.msg)
>        goto resync;
> 
>      h->rbuf = h->sbuf.reply.payload.error.msg;
>      h->rlen = msglen;
> +    h->payload_left -= h->rlen;
>      SET_NEXT_STATE (%RECV_ERROR_MESSAGE);
>    }
>    return 0;
> @@ -180,12 +186,13 @@  REPLY.CHUNK_REPLY.RECV_ERROR:
>    if (cmd->error == 0)
>      cmd->error = nbd_internal_errno_of_nbd_error (error);
>    h->rbuf = NULL;
> -  h->rlen = length - MIN (length, sizeof h->sbuf.reply.payload.error.error);
> +  h->rlen = h->payload_left;
> +  h->payload_left = 0;
>    SET_NEXT_STATE (%RESYNC);
>    return 0;
> 
>   REPLY.CHUNK_REPLY.RECV_ERROR_MESSAGE:
> -  uint32_t length, msglen;
> +  uint32_t msglen;
>    uint16_t type;
> 
>    switch (recv_into_rbuf (h)) {
> @@ -195,33 +202,31 @@  REPLY.CHUNK_REPLY.RECV_ERROR_MESSAGE:
>      SET_NEXT_STATE (%.READY);
>      return 0;
>    case 0:
> -    length = be32toh (h->sbuf.reply.hdr.structured.length);
>      msglen = be16toh (h->sbuf.reply.payload.error.error.len);
>      type = be16toh (h->sbuf.reply.hdr.structured.type);
> 
> -    length -= sizeof h->sbuf.reply.payload.error.error + msglen;
> -
>      if (msglen)
>        debug (h, "structured error server message: %.*s", (int)msglen,
>               h->sbuf.reply.payload.error.msg);
> 
>      /* Special case two specific errors; silently ignore tail for all others 
> */
>      h->rbuf = NULL;
> -    h->rlen = length;
> +    h->rlen = h->payload_left;
>      switch (type) {
>      case NBD_REPLY_TYPE_ERROR:
> -      if (length != 0)
> +      if (h->payload_left != 0)
>          debug (h, "ignoring unexpected slop after error message, "
>                 "the server may have a bug");
>        break;
>      case NBD_REPLY_TYPE_ERROR_OFFSET:
> -      if (length != sizeof h->sbuf.reply.payload.error.offset)
> +      if (h->payload_left != sizeof h->sbuf.reply.payload.error.offset)
>          debug (h, "unable to safely extract error offset, "
>                 "the server may have a bug");
>        else
>          h->rbuf = &h->sbuf.reply.payload.error.offset;
>        break;
>      }
> +    h->payload_left = 0;
>      SET_NEXT_STATE (%RECV_ERROR_TAIL);
>    }
>    return 0;
> @@ -286,7 +291,6 @@  REPLY.CHUNK_REPLY.RECV_ERROR_TAIL:
>   REPLY.CHUNK_REPLY.RECV_OFFSET_DATA:
>    struct command *cmd = h->reply_cmd;
>    uint64_t offset;
> -  uint32_t length;
> 
>    switch (recv_into_rbuf (h)) {
>    case -1: SET_NEXT_STATE (%.DEAD); return 0;
> @@ -295,29 +299,24 @@  REPLY.CHUNK_REPLY.RECV_OFFSET_DATA:
>      SET_NEXT_STATE (%.READY);
>      return 0;
>    case 0:
> -    length = be32toh (h->sbuf.reply.hdr.structured.length);
>      offset = be64toh (h->sbuf.reply.payload.offset_data.offset);
> 
>      assert (cmd); /* guaranteed by CHECK */
> -
>      assert (cmd->data && cmd->type == NBD_CMD_READ);
> 
> -    /* Length of the data following. */
> -    length -= 8;
> -
>      /* Is the data within bounds? */
> -    if (! structured_reply_in_bounds (offset, length, cmd)) {
> +    if (! structured_reply_in_bounds (offset, h->payload_left, cmd)) {
>        SET_NEXT_STATE (%.DEAD);
>        return 0;
>      }
>      if (cmd->data_seen <= cmd->count)
> -      cmd->data_seen += length;
> +      cmd->data_seen += h->payload_left;
>      /* Now this is the byte offset in the read buffer. */
>      offset -= cmd->offset;
> 
>      /* Set up to receive the data directly to the user buffer. */
>      h->rbuf = (char *)cmd->data + offset;
> -    h->rlen = length;
> +    h->rlen = h->payload_left;
>      SET_NEXT_STATE (%RECV_OFFSET_DATA_DATA);
>    }
>    return 0;
> @@ -325,7 +324,6 @@  REPLY.CHUNK_REPLY.RECV_OFFSET_DATA:
>   REPLY.CHUNK_REPLY.RECV_OFFSET_DATA_DATA:
>    struct command *cmd = h->reply_cmd;
>    uint64_t offset;
> -  uint32_t length;
> 
>    switch (recv_into_rbuf (h)) {
>    case -1: SET_NEXT_STATE (%.DEAD); return 0;
> @@ -334,7 +332,6 @@  REPLY.CHUNK_REPLY.RECV_OFFSET_DATA_DATA:
>      SET_NEXT_STATE (%.READY);
>      return 0;
>    case 0:
> -    length = be32toh (h->sbuf.reply.hdr.structured.length);
>      offset = be64toh (h->sbuf.reply.payload.offset_data.offset);
> 
>      assert (cmd); /* guaranteed by CHECK */
> @@ -343,11 +340,12 @@  REPLY.CHUNK_REPLY.RECV_OFFSET_DATA_DATA:
> 
>        if (CALL_CALLBACK (cmd->cb.fn.chunk,
>                           (char *)cmd->data + (offset - cmd->offset),
> -                         length - sizeof offset, offset,
> +                         h->payload_left, offset,
>                           LIBNBD_READ_DATA, &error) == -1)
>          if (cmd->error == 0)
>            cmd->error = error ? error : EPROTO;
>      }
> +    h->payload_left = 0;
> 
>      SET_NEXT_STATE (%FINISH);
>    }
> @@ -369,7 +367,6 @@  REPLY.CHUNK_REPLY.RECV_OFFSET_HOLE:
>      length = be32toh (h->sbuf.reply.payload.offset_hole.length);
> 
>      assert (cmd); /* guaranteed by CHECK */
> -
>      assert (cmd->data && cmd->type == NBD_CMD_READ);
> 
>      /* Is the data within bounds? */
> @@ -405,8 +402,8 @@  REPLY.CHUNK_REPLY.RECV_OFFSET_HOLE:
> 
>   REPLY.CHUNK_REPLY.RECV_BS_ENTRIES:
>    struct command *cmd = h->reply_cmd;
> -  uint32_t length;
>    size_t i;
> +  size_t count;
>    uint32_t context_id;
> 
>    switch (recv_into_rbuf (h)) {
> @@ -416,20 +413,20 @@  REPLY.CHUNK_REPLY.RECV_BS_ENTRIES:
>      SET_NEXT_STATE (%.READY);
>      return 0;
>    case 0:
> -    length = be32toh (h->sbuf.reply.hdr.structured.length);
> -
>      assert (cmd); /* guaranteed by CHECK */
>      assert (cmd->type == NBD_CMD_BLOCK_STATUS);
>      assert (CALLBACK_IS_NOT_NULL (cmd->cb.fn.extent));
>      assert (h->bs_entries);
> -    assert (length >= 12);
> +    assert (h->payload_left >= 12);
>      assert (h->meta_valid);
> 
>      /* Need to byte-swap the entries returned, but apart from that we
>       * don't validate them.
>       */
> -    for (i = 0; i < length/4; ++i)
> +    for (i = 0; i < h->payload_left / sizeof *h->bs_entries; ++i)
>        h->bs_entries[i] = be32toh (h->bs_entries[i]);
> +    count = (h->payload_left / sizeof *h->bs_entries) - 1;
> +    h->payload_left = 0;
> 
>      /* Look up the context ID. */
>      context_id = h->bs_entries[0];
> @@ -443,8 +440,7 @@  REPLY.CHUNK_REPLY.RECV_BS_ENTRIES:
> 
>        if (CALL_CALLBACK (cmd->cb.fn.extent,
>                           h->meta_contexts.ptr[i].name, cmd->offset,
> -                         &h->bs_entries[1], (length-4) / 4,
> -                         &error) == -1)
> +                         &h->bs_entries[1], count, &error) == -1)
>          if (cmd->error == 0)
>            cmd->error = error ? error : EPROTO;
>      }
> @@ -494,6 +490,7 @@  REPLY.CHUNK_REPLY.RESYNC:
>   REPLY.CHUNK_REPLY.FINISH:
>    uint16_t flags;
> 
> +  assert (h->payload_left == 0);
>    flags = be16toh (h->sbuf.reply.hdr.structured.flags);
>    if (flags & NBD_REPLY_FLAG_DONE) {
>      SET_NEXT_STATE (%^FINISH_COMMAND);

Acked-by: Laszlo Ersek <ler...@redhat.com>

_______________________________________________
Libguestfs mailing list
Libguestfs@redhat.com
https://listman.redhat.com/mailman/listinfo/libguestfs

Reply via email to