From patchwork Wed Apr 6 16:59:22 2016 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Eric Blake X-Patchwork-Id: 8763841 Return-Path: X-Original-To: patchwork-qemu-devel@patchwork.kernel.org Delivered-To: patchwork-parsemail@patchwork1.web.kernel.org Received: from mail.kernel.org (mail.kernel.org [198.145.29.136]) by patchwork1.web.kernel.org (Postfix) with ESMTP id CB0A09F36E for ; Wed, 6 Apr 2016 17:00:49 +0000 (UTC) Received: from mail.kernel.org (localhost [127.0.0.1]) by mail.kernel.org (Postfix) with ESMTP id BD758201BB for ; Wed, 6 Apr 2016 17:00:48 +0000 (UTC) Received: from lists.gnu.org (lists.gnu.org [208.118.235.17]) (using TLSv1 with cipher AES256-SHA (256/256 bits)) (No client certificate requested) by mail.kernel.org (Postfix) with ESMTPS id 993C5201B9 for ; Wed, 6 Apr 2016 17:00:47 +0000 (UTC) Received: from localhost ([::1]:44497 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1anqow-0007C3-Kp for patchwork-qemu-devel@patchwork.kernel.org; Wed, 06 Apr 2016 13:00:46 -0400 Received: from eggs.gnu.org ([2001:4830:134:3::10]:34004) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1anqol-00079q-AW for qemu-devel@nongnu.org; Wed, 06 Apr 2016 13:00:36 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1anqoi-0006Cr-3X for qemu-devel@nongnu.org; Wed, 06 Apr 2016 13:00:35 -0400 Received: from resqmta-po-05v.sys.comcast.net ([96.114.154.164]:46187 helo=CMGW) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1anqoh-0006Cc-J6 for qemu-devel@nongnu.org; Wed, 06 Apr 2016 13:00:32 -0400 Received: from resomta-po-06v.sys.comcast.net ([96.114.154.230]) by comcast with SMTP id nqnFaxpkPmXdbnqnia6pUp; Wed, 06 Apr 2016 16:59:30 +0000 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=comcast.net; s=q20140121; t=1459961970; bh=7OZ+mge2JRBJAS4b8vnjTx1JexZnJlMGTQ1VOaOiUtY=; h=Received:Received:From:To:Subject:Date:Message-Id; b=iKYj4kUgNMWsW4t2yzTTQSugqjvzzCPMRM0JvMXlYop4TYRc0jqFeGP+CEFgVCSie pxeoiDvH7wbYF9QauEkT8rHt6QuMydlUecgOEOB5RannGeW63W+0dK9L2hD/QTVVjt CuCUtl1m7bkrZfvtDuKU6yjk4D1BLZHMMw3mwcW1sUGrAcfMnPsMuoK1NRgrtk3Wlj pYGkumqybZ5bVyl7nDcSz2Iexu+blT/Ls0vShUXVYW7f7ZBIfP+5qTRtDPt0ZhX7Nw Oyj3qN2+LQ3xFye5O0qfAgb9Mx16l6Dheu+VlcWusFwLz8JTpD3/FBJTsY/yIPxePU RuyYNXglY01SQ== Received: from red.redhat.com ([24.10.254.122]) by resomta-po-06v.sys.comcast.net with comcast id f4zR1s00L2fD5rL014zVKG; Wed, 06 Apr 2016 16:59:30 +0000 From: Eric Blake To: qemu-devel@nongnu.org Date: Wed, 6 Apr 2016 10:59:22 -0600 Message-Id: <1459961962-18771-1-git-send-email-eblake@redhat.com> X-Mailer: git-send-email 2.5.5 X-detected-operating-system: by eggs.gnu.org: Genre and OS details not recognized. X-Received-From: 96.114.154.164 Cc: pbonzini@redhat.com, alex@alex.org.uk Subject: [Qemu-devel] [PATCH v2] nbd: Fix NBD unsupported options X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.14 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+patchwork-qemu-devel=patchwork.kernel.org@nongnu.org Sender: qemu-devel-bounces+patchwork-qemu-devel=patchwork.kernel.org@nongnu.org X-Spam-Status: No, score=-6.8 required=5.0 tests=BAYES_00,DKIM_SIGNED, RCVD_IN_DNSWL_HI, T_DKIM_INVALID, UNPARSEABLE_RELAY autolearn=ham version=3.3.1 X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on mail.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP From: Alex Bligh 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 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 --- 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)) {