All of lore.kernel.org
 help / color / mirror / Atom feed
* [Qemu-devel] [PATCH v2] nbd: Fix NBD unsupported options
@ 2016-04-06 16:59 Eric Blake
  2016-04-06 22:48 ` [Qemu-devel] [PATCH v2 2/1 for-2.6] nbd: Don't kill server when client requests unknown option Eric Blake
  0 siblings, 1 reply; 4+ messages in thread
From: Eric Blake @ 2016-04-06 16:59 UTC (permalink / raw)
  To: qemu-devel; +Cc: pbonzini, alex

From: Alex Bligh <alex@alex.org.uk>

nbd-client.c currently fails to handle unsupported options properly.
If during option haggling the server finds an option that is
unsupported, it returns an NBD_REP_ERR_UNSUP reply.

According to nbd's proto.md, the format for such a reply
should be:

  S: 64 bits, 0x3e889045565a9 (magic number for replies)
  S: 32 bits, the option as sent by the client to which this is a reply
  S: 32 bits, reply type (e.g., NBD_REP_ACK for successful completion,
     or NBD_REP_ERR_UNSUP to mark use of an option not known by this server
  S: 32 bits, length of the reply. This may be zero for some replies,
     in which case the next field is not sent
  S: any data as required by the reply (e.g., an export name in the case
     of NBD_REP_SERVER, or optional UTF-8 message for NBD_REP_ERR_*)

However, in nbd-client.c, the reply type was being read, and if it
contained an error, it was bailing out and issuing the next option
request without first reading the length. This meant that the
next option / handshake read had an extra 4 or more bytes of data in it.
In practice, this makes Qemu incompatible with servers that do not
support NBD_OPT_LIST.

To verify this isn't an error in the specification or my reading of
it, replies are sent by the reference implementation here:
 https://github.com/yoe/nbd/blob/66dfb35/nbd-server.c#L1232
and as is evident it always sends a 'datasize' (aka length) 32 bit
word. Unsupported elements are replied to here:
 https://github.com/yoe/nbd/blob/66dfb35/nbd-server.c#L1371

Signed-off-by: Alex Bligh <alex@alex.org.uk>
Message-Id: <1459882500-24316-1-git-send-email-alex@alex.org.uk>
[rework to ALWAYS consume an optional UTF-8 message from the server]
Signed-off-by: Eric Blake <eblake@redhat.com>
---

v2: patch grew in size, but now handles more servers

 nbd/client.c | 53 +++++++++++++++++++++++++++++++++++++++++++----------
 1 file changed, 43 insertions(+), 10 deletions(-)

diff --git a/nbd/client.c b/nbd/client.c
index d9b7a9b..67116b9 100644
--- a/nbd/client.c
+++ b/nbd/client.c
@@ -73,16 +73,44 @@ static QTAILQ_HEAD(, NBDExport) exports = QTAILQ_HEAD_INITIALIZER(exports);
 */


-static int nbd_handle_reply_err(uint32_t opt, uint32_t type, Error **errp)
+/* If type represents success, return 1 without further action.
+ * If type represents an error reply, consume the rest of the packet on ioc.
+ * Then return 0 for unsupported (so the client can fall back to
+ * other approaches), or -1 with errp set for other errors.
+ */
+static int nbd_handle_reply_err(QIOChannel *ioc, uint32_t opt, uint32_t type,
+                                Error **errp)
 {
+    uint32_t len;
+    char *msg = NULL;
+    int result = -1;
+
     if (!(type & (1 << 31))) {
-        return 0;
+        return 1;
+    }
+
+    if (read_sync(ioc, &len, sizeof(len)) != sizeof(len)) {
+        error_setg(errp, "failed to read option length");
+        return -1;
+    }
+    len = be32_to_cpu(len);
+    if (len) {
+        if (len > NBD_MAX_BUFFER_SIZE) {
+            error_setg(errp, "server's error message is too long");
+            goto cleanup;
+        }
+        msg = g_malloc(len + 1);
+        if (read_sync(ioc, msg, len) != len) {
+            error_setg(errp, "failed to read option error message");
+            goto cleanup;
+        }
+        msg[len] = '\0';
     }

     switch (type) {
     case NBD_REP_ERR_UNSUP:
-        error_setg(errp, "Unsupported option type %x", opt);
-        break;
+        result = 0;
+        goto cleanup;

     case NBD_REP_ERR_POLICY:
         error_setg(errp, "Denied by server for option %x", opt);
@@ -101,7 +129,13 @@ static int nbd_handle_reply_err(uint32_t opt, uint32_t type, Error **errp)
         break;
     }

-    return -1;
+    if (msg) {
+        error_append_hint(errp, "%s\n", msg);
+    }
+
+ cleanup:
+    g_free(msg);
+    return result;
 }

 static int nbd_receive_list(QIOChannel *ioc, char **name, Error **errp)
@@ -111,6 +145,7 @@ static int nbd_receive_list(QIOChannel *ioc, char **name, Error **errp)
     uint32_t type;
     uint32_t len;
     uint32_t namelen;
+    int error;

     *name = NULL;
     if (read_sync(ioc, &magic, sizeof(magic)) != sizeof(magic)) {
@@ -138,11 +173,9 @@ static int nbd_receive_list(QIOChannel *ioc, char **name, Error **errp)
         return -1;
     }
     type = be32_to_cpu(type);
-    if (type == NBD_REP_ERR_UNSUP) {
-        return 0;
-    }
-    if (nbd_handle_reply_err(opt, type, errp) < 0) {
-        return -1;
+    error = nbd_handle_reply_err(ioc, opt, type, errp);
+    if (error <= 0) {
+        return error;
     }

     if (read_sync(ioc, &len, sizeof(len)) != sizeof(len)) {
-- 
2.5.5

^ permalink raw reply related	[flat|nested] 4+ messages in thread

* [Qemu-devel] [PATCH v2 2/1 for-2.6] nbd: Don't kill server when client requests unknown option
  2016-04-06 16:59 [Qemu-devel] [PATCH v2] nbd: Fix NBD unsupported options Eric Blake
@ 2016-04-06 22:48 ` Eric Blake
  2016-04-07  8:50   ` Paolo Bonzini
  2016-04-09 22:41   ` Eric Blake
  0 siblings, 2 replies; 4+ messages in thread
From: Eric Blake @ 2016-04-06 22:48 UTC (permalink / raw)
  To: qemu-devel; +Cc: pbonzini, alex

nbd-server.c currently fails to handle unsupported options properly.
If during option haggling the client sends an unknown request, the
server kills the connection instead of letting the client try to
fall back to something older.  This is precisely what advertising
NBD_FLAG_FIXED_NEWSTYLE was supposed to fix.

Signed-off-by: Eric Blake <eblake@redhat.com>
---

Turns out our server has a very similar bug to the client.
If desired, I can spin a v3 that moves the hunk in nbd/client.c
to the previous patch.

 nbd/client.c | 2 ++
 nbd/server.c | 5 ++++-
 2 files changed, 6 insertions(+), 1 deletion(-)

diff --git a/nbd/client.c b/nbd/client.c
index 67116b9..fc382c5 100644
--- a/nbd/client.c
+++ b/nbd/client.c
@@ -109,6 +109,8 @@ static int nbd_handle_reply_err(QIOChannel *ioc, uint32_t opt, uint32_t type,

     switch (type) {
     case NBD_REP_ERR_UNSUP:
+        TRACE("server doesn't understand request %d, attempting fallback",
+              opt);
         result = 0;
         goto cleanup;

diff --git a/nbd/server.c b/nbd/server.c
index b95571b..7843584 100644
--- a/nbd/server.c
+++ b/nbd/server.c
@@ -482,9 +482,12 @@ static int nbd_negotiate_options(NBDClient *client)
                 return -EINVAL;
             default:
                 TRACE("Unsupported option 0x%x", clientflags);
+                if (nbd_negotiate_drop_sync(client->ioc, length) != length) {
+                    return -EIO;
+                }
                 nbd_negotiate_send_rep(client->ioc, NBD_REP_ERR_UNSUP,
                                        clientflags);
-                return -EINVAL;
+                break;
             }
         } else {
             /*
-- 
2.5.5

^ permalink raw reply related	[flat|nested] 4+ messages in thread

* Re: [Qemu-devel] [PATCH v2 2/1 for-2.6] nbd: Don't kill server when client requests unknown option
  2016-04-06 22:48 ` [Qemu-devel] [PATCH v2 2/1 for-2.6] nbd: Don't kill server when client requests unknown option Eric Blake
@ 2016-04-07  8:50   ` Paolo Bonzini
  2016-04-09 22:41   ` Eric Blake
  1 sibling, 0 replies; 4+ messages in thread
From: Paolo Bonzini @ 2016-04-07  8:50 UTC (permalink / raw)
  To: Eric Blake, qemu-devel; +Cc: alex



On 07/04/2016 00:48, Eric Blake wrote:
> nbd-server.c currently fails to handle unsupported options properly.
> If during option haggling the client sends an unknown request, the
> server kills the connection instead of letting the client try to
> fall back to something older.  This is precisely what advertising
> NBD_FLAG_FIXED_NEWSTYLE was supposed to fix.
> 
> Signed-off-by: Eric Blake <eblake@redhat.com>
> ---
> 
> Turns out our server has a very similar bug to the client.
> If desired, I can spin a v3 that moves the hunk in nbd/client.c
> to the previous patch.

I'll do that myself.  Thanks for the patches!

Paolo

^ permalink raw reply	[flat|nested] 4+ messages in thread

* Re: [Qemu-devel] [PATCH v2 2/1 for-2.6] nbd: Don't kill server when client requests unknown option
  2016-04-06 22:48 ` [Qemu-devel] [PATCH v2 2/1 for-2.6] nbd: Don't kill server when client requests unknown option Eric Blake
  2016-04-07  8:50   ` Paolo Bonzini
@ 2016-04-09 22:41   ` Eric Blake
  1 sibling, 0 replies; 4+ messages in thread
From: Eric Blake @ 2016-04-09 22:41 UTC (permalink / raw)
  To: qemu-devel; +Cc: pbonzini, alex, qemu-stable

[-- Attachment #1: Type: text/plain, Size: 2160 bytes --]

Adding qemu-stable; this fix needs to be backported to 2.5.x stable
series (in the file ./nbd.c at the time), if we want to be able to allow
a 2.7 client to connect to a 2.5 server.

On 04/06/2016 04:48 PM, Eric Blake wrote:
> nbd-server.c currently fails to handle unsupported options properly.
> If during option haggling the client sends an unknown request, the
> server kills the connection instead of letting the client try to
> fall back to something older.  This is precisely what advertising
> NBD_FLAG_FIXED_NEWSTYLE was supposed to fix.
> 
> Signed-off-by: Eric Blake <eblake@redhat.com>
> ---
> 
> Turns out our server has a very similar bug to the client.
> If desired, I can spin a v3 that moves the hunk in nbd/client.c
> to the previous patch.
> 
>  nbd/client.c | 2 ++
>  nbd/server.c | 5 ++++-
>  2 files changed, 6 insertions(+), 1 deletion(-)
> 
> diff --git a/nbd/client.c b/nbd/client.c
> index 67116b9..fc382c5 100644
> --- a/nbd/client.c
> +++ b/nbd/client.c
> @@ -109,6 +109,8 @@ static int nbd_handle_reply_err(QIOChannel *ioc, uint32_t opt, uint32_t type,
> 
>      switch (type) {
>      case NBD_REP_ERR_UNSUP:
> +        TRACE("server doesn't understand request %d, attempting fallback",
> +              opt);
>          result = 0;
>          goto cleanup;
> 
> diff --git a/nbd/server.c b/nbd/server.c
> index b95571b..7843584 100644
> --- a/nbd/server.c
> +++ b/nbd/server.c
> @@ -482,9 +482,12 @@ static int nbd_negotiate_options(NBDClient *client)
>                  return -EINVAL;
>              default:
>                  TRACE("Unsupported option 0x%x", clientflags);
> +                if (nbd_negotiate_drop_sync(client->ioc, length) != length) {
> +                    return -EIO;
> +                }
>                  nbd_negotiate_send_rep(client->ioc, NBD_REP_ERR_UNSUP,
>                                         clientflags);
> -                return -EINVAL;
> +                break;
>              }
>          } else {
>              /*
> 

-- 
Eric Blake   eblake redhat com    +1-919-301-3266
Libvirt virtualization library http://libvirt.org


[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 604 bytes --]

^ permalink raw reply	[flat|nested] 4+ messages in thread

end of thread, other threads:[~2016-04-09 22:41 UTC | newest]

Thread overview: 4+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2016-04-06 16:59 [Qemu-devel] [PATCH v2] nbd: Fix NBD unsupported options Eric Blake
2016-04-06 22:48 ` [Qemu-devel] [PATCH v2 2/1 for-2.6] nbd: Don't kill server when client requests unknown option Eric Blake
2016-04-07  8:50   ` Paolo Bonzini
2016-04-09 22:41   ` Eric Blake

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.