[Libguestfs] [libnbd PATCH 3/8] pread: Reject server SR read response with no data chunks

Eric Blake eblake at redhat.com
Thu Jun 20 13:02:28 UTC 2019


On 6/20/19 12:49 AM, Richard W.M. Jones wrote:
> On Mon, Jun 17, 2019 at 07:07:53PM -0500, Eric Blake wrote:
>> The NBD spec requires that a server doing structured reads must not
>> succeed unless it covers the entire buffer with reply chunks. In the
>> general case, this requires a lot of bookkeeping to check whether
>> offsets were non-overlapping and sufficient, and we'd rather defer
>> such checking to an optional callback function. But a callback
>> function will only be reached per chunk, while we still want to fail
>> the overall read if the callback function was never called because the
>> server erroneously replied with NBD_REPLY_TYPE_NONE with no other
>> chunks instead of an expected NBD_REPLY_TYPE_ERROR*. For this specific
>> error case, the bookkeeping is much simpler - we merely track if we've
>> seen at least one data chunk.
> 
> I guess the implicit assumption here is that count > 0, which IIRC the
> NBD protocol demands.  It seems like we don't actually check this in
> nbd_internal_command_common so would it be worth adding a check there?

The protocol says a 0-length read request is unspecified behavior. I
don't know if nbd_internal_command_common should reject it - a
particular server may have reliable behavior, and thus libnbd should not
prevent a client from doing it. On the other hand, the spec says a
0-length structured read chunk is broken. In qemu-nbd, a 0-length read
is not forbidden, but it results in a mere NBD_REPLY_TYPE_DONE rather
than an NBD_REPLY_TYPE_OFFSET_DATA.  Which means this patch should
probably special case a 0 length request to NOT be an error even when
data_seen is false.

We may someday want to add a strict mode (where we do as much as we can
to prevent clients from sending 0-length or out-of-bounds requests, and
do additional sanity checking that server replies are compliant), vs. a
loose mode (where clients can send malformed requests, in order to test
server responses). This is yet one more place to consider gating under
such an option setting.

> 
> In any case this patch on its own looks good, so ACK.

Here's the tweak I'm adding before pushing:

diff --git i/generator/states-reply-structured.c
w/generator/states-reply-structured.c
index 6740400..9bde46f 100644
--- i/generator/states-reply-structured.c
+++ w/generator/states-reply-structured.c
@@ -95,6 +95,10 @@
     return 0;
   }
   else if (type == NBD_REPLY_TYPE_OFFSET_DATA) {
+    /* 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, so we use < instead of <=.
+     */
     if (length < sizeof h->sbuf.sr.payload.offset_data) {
       SET_NEXT_STATE (%.DEAD);
       set_error (0, "too short length in NBD_REPLY_TYPE_OFFSET_DATA");
@@ -110,9 +110,9 @@
     return 0;
   }
   else if (type == NBD_REPLY_TYPE_OFFSET_HOLE) {
-    if (length != 12) {
+    if (length != sizeof h->sbuf.sr.payload.offset_hole) {
       SET_NEXT_STATE (%.DEAD);
-      set_error (0, "invalid length in NBD_REPLY_TYPE_NONE");
+      set_error (0, "invalid length in NBD_REPLY_TYPE_OFFSET_HOLE");
       return -1;
     }
     h->rbuf = &h->sbuf.sr.payload.offset_hole;
@@ -356,6 +360,10 @@
       return -1;
     }

+    /* 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.
+     */
     memset (cmd->data + offset, 0, length);

     SET_NEXT_STATE(%FINISH);
diff --git i/lib/aio.c w/lib/aio.c
index 7fb0fdf..2dcce68 100644
--- i/lib/aio.c
+++ w/lib/aio.c
@@ -73,7 +73,10 @@ nbd_unlocked_aio_command_completed (struct nbd_handle *h,

   type = cmd->type;
   error = cmd->error;
-  if (type == NBD_CMD_READ && !cmd->data_seen && !error)
+  /* The spec states that a 0-length read request is unspecified; but
+   * it is easy enough to treat it as successful as an extension.
+   */
+  if (type == NBD_CMD_READ && !cmd->data_seen && cmd->count && !error)
     error = EIO;

   /* Retire it from the list and free it. */

-- 
Eric Blake, Principal Software Engineer
Red Hat, Inc.           +1-919-301-3226
Virtualization:  qemu.org | libvirt.org

-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 488 bytes
Desc: OpenPGP digital signature
URL: <http://listman.redhat.com/archives/libguestfs/attachments/20190620/7e411854/attachment.sig>


More information about the Libguestfs mailing list