* [PATCH for-11.2 v3 0/6] Fix some low-severity NBD CVEs
@ 2026-11-10 15:30 Eric Blake
2026-11-10 15:53 ` [PATCH v3 1/6] docs: Mention security considerations for NBD Eric Blake
2026-11-10 16:27 ` [PATCH for-11.2 v3 0/6] Fix some low-severity NBD CVEs Stefan Hajnoczi
0 siblings, 2 replies; 12+ messages in thread
From: Eric Blake @ 2026-11-10 15:30 UTC (permalink / raw)
To: qemu-devel; +Cc: qemu-block, qemu-stable, berrange, stefanha
Since v2 (https://lists.nongnu.org/archive/html/qemu-devel/2026-11/msg01673.htm\
l):
- Tweak the documentation in patch 1 again [Stefan, Dan]
- Improve unit tests, and pass pylint iotest 297 (new patch 2, also patch 4, 6)
- Retain assertion at safer point for first bug (patch 3)
- Second CVE addressed as well (patch 5, 6)
Eric Blake (6):
docs: Mention security considerations for NBD
iotests: Refactor python NBD server setup
nbd/server: Reject rather than assert on non-compliant BLOCK_STATUS
iotests: Add new nbd-robustness test
nbd/server: Reject rather than assert on non-compliant
NBD_OPT_SET_META_CONTEXT
iotests: Add another nbd-robustness test
docs/system/security.rst | 23 +++
nbd/server.c | 10 +-
tests/qemu-iotests/iotests.py | 30 +++
tests/qemu-iotests/tests/nbd-multiconn | 44 +----
tests/qemu-iotests/tests/nbd-robustness | 205 ++++++++++++++++++++
tests/qemu-iotests/tests/nbd-robustness.out | 5 +
6 files changed, 279 insertions(+), 38 deletions(-)
create mode 100755 tests/qemu-iotests/tests/nbd-robustness
create mode 100644 tests/qemu-iotests/tests/nbd-robustness.out
--
2.56.0
^ permalink raw reply [flat|nested] 12+ messages in thread
* [PATCH v3 1/6] docs: Mention security considerations for NBD
2026-11-10 15:30 [PATCH for-11.2 v3 0/6] Fix some low-severity NBD CVEs Eric Blake
@ 2026-11-10 15:53 ` Eric Blake
2026-11-10 15:53 ` [PATCH v3 2/6] iotests: Refactor python NBD server setup Eric Blake
` (5 more replies)
2026-11-10 16:27 ` [PATCH for-11.2 v3 0/6] Fix some low-severity NBD CVEs Stefan Hajnoczi
1 sibling, 6 replies; 12+ messages in thread
From: Eric Blake @ 2026-11-10 15:53 UTC (permalink / raw)
To: qemu-devel; +Cc: qemu-block, qemu-stable, berrange, stefanha, Pierrick Bouvier
Document a policy that NBD connections should strongly consider using
TLS and x509 certificates to filter out unwanted clients. If a
malicious server or client can trigger a QEMU assertion failure on a
plaintext connection, but can trivially have risk mitigated by
enabling TLS to weed out unknown partners (so that a full-on attack of
QEMU would first require chaining some other attack to convince a
"trusted" client to break protocol in the first place), then that
reduces the CVSS-BE score of the QEMU bug. In turn, our policy already
states that low severity scores can be fixed without an embargo.
Signed-off-by: Eric Blake <eblake@redhat.com>
---
v3: more wording tweaks, since even a TLS-authorized partner cannot
be considered 100% trusted, but is reduced risk.
---
docs/system/security.rst | 23 +++++++++++++++++++++++
1 file changed, 23 insertions(+)
diff --git a/docs/system/security.rst b/docs/system/security.rst
index e211fdd2344..e30f771731a 100644
--- a/docs/system/security.rst
+++ b/docs/system/security.rst
@@ -320,3 +320,26 @@ encryption and authorization control policy on client connections.
In summary, the monitor console is considered a privileged control interface to
QEMU and as such should only be made accessible to a trusted management
application or user.
+
+Block device network protocols (NBD, NFS, etc.)
+'''''''''''''''''''''''''''''''''''''''''''''''
+
+When using QEMU to access remote storage over a network (such as QEMU
+acting as an NBD or NFS client) or to provide remote storage access
+(such as QEMU acting as an NBD server), it is important to realize
+that an unencrypted connection can allow any external process a
+potential attack vector via the network port. When possible, QEMU
+should be configured to require TLS to prevent a man-in-the-middle
+attack, as well as use x509 client certificates with the
+tls-creds-x509 object set with 'verify-peer=on' to check that the peer
+is allowed to connect. It may also be worth using 'tls-authz' to
+filter which certificates are allowed if the CA issues certs more
+broadly.
+
+Proper hardening of network connections cannot prevent a client from
+violating protocol, but does increase the complexity of an attack,
+because a malicious actor must first find a way to convince an
+authenticated client to send the bytes that will trigger any QEMU
+misbehavior. Use of a hardened connection is therefore an
+environmental factor used in determining the risk and severity of a
+network-triggered bug.
--
2.56.0
^ permalink raw reply related [flat|nested] 12+ messages in thread
* [PATCH v3 2/6] iotests: Refactor python NBD server setup
2026-11-10 15:53 ` [PATCH v3 1/6] docs: Mention security considerations for NBD Eric Blake
@ 2026-11-10 15:53 ` Eric Blake
2026-11-10 16:11 ` Daniel P. Berrangé
2026-11-10 15:53 ` [PATCH v3 3/6] nbd/server: Reject rather than assert on non-compliant BLOCK_STATUS Eric Blake
` (4 subsequent siblings)
5 siblings, 1 reply; 12+ messages in thread
From: Eric Blake @ 2026-11-10 15:53 UTC (permalink / raw)
To: qemu-devel
Cc: qemu-block, qemu-stable, berrange, stefanha, Kevin Wolf,
Hanna Reitz, Vladimir Sementsov-Ogievskiy
Extract code from nbd-multiconn that I plan to use in another upcoming
unit test.
Signed-off-by: Eric Blake <eblake@redhat.com>
---
tests/qemu-iotests/iotests.py | 30 ++++++++++++++++++
tests/qemu-iotests/tests/nbd-multiconn | 44 +++++---------------------
2 files changed, 38 insertions(+), 36 deletions(-)
diff --git a/tests/qemu-iotests/iotests.py b/tests/qemu-iotests/iotests.py
index 642a160bb28..7017d2016cf 100644
--- a/tests/qemu-iotests/iotests.py
+++ b/tests/qemu-iotests/iotests.py
@@ -1385,6 +1385,36 @@ def case_skip(self, reason):
case_notrun(reason)
self.skipTest(reason)
+ @contextmanager
+ def run_nbd_server(self, nbd_sock: str,
+ max_conn: Optional[int] = None) -> Iterator[None]:
+ args: Dict[str, Any] = {
+ 'addr': {
+ 'type': 'unix',
+ 'data': {'path': nbd_sock}
+ }
+ }
+ if max_conn is not None:
+ args['max-connections'] = max_conn
+
+ self.vm.cmd('nbd-server-start', args)
+ try:
+ yield
+ finally:
+ self.vm.cmd('nbd-server-stop')
+
+ def add_nbd_export(self, node_name: str, name: str,
+ writable: Optional[bool] = None) -> None:
+ args: Dict[str, Any] = {
+ 'type': 'nbd',
+ 'id': name,
+ 'node-name': node_name,
+ 'name': name,
+ }
+ if writable is not None:
+ args['writable'] = writable
+
+ self.vm.cmd('block-export-add', args)
def notrun(reason):
'''Skip this test suite'''
diff --git a/tests/qemu-iotests/tests/nbd-multiconn b/tests/qemu-iotests/tests/nbd-multiconn
index 479e872f2a8..96efe01e583 100755
--- a/tests/qemu-iotests/tests/nbd-multiconn
+++ b/tests/qemu-iotests/tests/nbd-multiconn
@@ -62,55 +62,27 @@ class TestNbdMulticonn(iotests.QMPTestCase):
except OSError:
pass
- @contextmanager
- def run_server(self, max_connections=None):
- args = {
- 'addr': {
- 'type': 'unix',
- 'data': {'path': nbd_sock}
- }
- }
- if max_connections is not None:
- args['max-connections'] = max_connections
-
- self.vm.cmd('nbd-server-start', args)
- yield
-
- self.vm.cmd('nbd-server-stop')
-
- def add_export(self, name, writable=None):
- args = {
- 'type': 'nbd',
- 'id': name,
- 'node-name': 'n',
- 'name': name,
- }
- if writable is not None:
- args['writable'] = writable
-
- self.vm.cmd('block-export-add', args)
-
def test_default_settings(self):
- with self.run_server():
- self.add_export('r')
- self.add_export('w', writable=True)
+ with self.run_nbd_server(nbd_sock):
+ self.add_nbd_export('n', 'r')
+ self.add_nbd_export('n', 'w', writable=True)
with open_nbd('r') as h:
self.assertTrue(h.can_multi_conn())
with open_nbd('w') as h:
self.assertTrue(h.can_multi_conn())
def test_limited_connections(self):
- with self.run_server(max_connections=1):
- self.add_export('r')
- self.add_export('w', writable=True)
+ with self.run_nbd_server(nbd_sock, max_conn=1):
+ self.add_nbd_export('n', 'r')
+ self.add_nbd_export('n', 'w', writable=True)
with open_nbd('r') as h:
self.assertFalse(h.can_multi_conn())
with open_nbd('w') as h:
self.assertFalse(h.can_multi_conn())
def test_parallel_writes(self):
- with self.run_server():
- self.add_export('w', writable=True)
+ with self.run_nbd_server(nbd_sock):
+ self.add_nbd_export('n', 'w', writable=True)
clients = [nbd.NBD() for _ in range(3)]
for c in clients:
--
2.56.0
^ permalink raw reply related [flat|nested] 12+ messages in thread
* [PATCH v3 3/6] nbd/server: Reject rather than assert on non-compliant BLOCK_STATUS
2026-11-10 15:53 ` [PATCH v3 1/6] docs: Mention security considerations for NBD Eric Blake
2026-11-10 15:53 ` [PATCH v3 2/6] iotests: Refactor python NBD server setup Eric Blake
@ 2026-11-10 15:53 ` Eric Blake
2026-11-10 16:12 ` Daniel P. Berrangé
2026-11-10 15:54 ` [PATCH v3 4/6] iotests: Add new nbd-robustness test Eric Blake
` (3 subsequent siblings)
5 siblings, 1 reply; 12+ messages in thread
From: Eric Blake @ 2026-11-10 15:53 UTC (permalink / raw)
To: qemu-devel
Cc: qemu-block, qemu-stable, berrange, stefanha, Feifan Qian,
Haotian Jiang, Vladimir Sementsov-Ogievskiy
The NBD spec is clear that a client must not send NBD_BLOCK_STATUS
requests to a server if it did not negotiate any metacontexts with
NBD_OPT_SET_META_CONTEXT for the current export. However, if the
non-compliant client uses NBD_OPT_EXTENDED_HEADERS and then passes in
the relatively new NBD_CMD_FLAG_PAYLOAD_LEN flag alongside an
inappropriate block status request, this was enough to trigger an
assertion in qemu. The assertion can also be tripped by initially
requesting the context for one export, then using NBD_OPT_GO on a
different export (nbd_check_meta_export() sets contexts.count to 0,
but did not wipe contexts.exp). Forcing the client command to be
rejected if contexts.count is 0 solves both triggers, and allows the
assertion to still operate cleanly at a later location.
If you do not use recommended practice to prevent unwanted clients
from connecting to QEMU's NBD server, then any client can cause a
denial of service with minimal effort. However, with TLS and x509
certs in place, an attacker must first chain in an attack to convince
the client to send unwanted traffic, which is a factor in declaring
this a lower-risk denial of service.
Fixes: CVE-2026-124559
Fixes: 2dcbb11b ("nbd/server: Add FLAG_PAYLOAD support to CMD_BLOCK_STATUS", v8.2.0)
Reported-by: Feifan Qian <bea1e@proton.me>
Reported-by: https://gitlab.com/qemu-project/qemu/-/work_items/3796
CC: Haotian Jiang <jianghaotian.sunday@gmail.com>
CC: qemu-stable@nongnu.org
Signed-off-by: Eric Blake <eblake@redhat.com>
---
v3: Move assert later rather than dropping it; improve commit message.
R-b dropped.
---
nbd/server.c | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/nbd/server.c b/nbd/server.c
index e6c47f8c2e3..8a01c27de07 100644
--- a/nbd/server.c
+++ b/nbd/server.c
@@ -2584,17 +2584,18 @@ nbd_co_block_status_payload_read(NBDClient *client, NBDRequest *request,
return -EINVAL;
}
- assert(client->contexts.exp == client->exp);
nr_bitmaps = client->exp->nr_export_bitmaps;
request->contexts = g_new0(NBDMetaContexts, 1);
request->contexts->exp = client->exp;
- if (payload_len % sizeof(uint32_t) ||
+ if (!client->contexts.count ||
+ payload_len % sizeof(uint32_t) ||
payload_len < sizeof(NBDBlockStatusPayload) ||
payload_len > (sizeof(NBDBlockStatusPayload) +
sizeof(id) * client->contexts.count)) {
goto skip;
}
+ assert(client->contexts.exp == client->exp);
buf = g_malloc(payload_len);
if (nbd_read(client->ioc, buf, payload_len,
--
2.56.0
^ permalink raw reply related [flat|nested] 12+ messages in thread
* [PATCH v3 4/6] iotests: Add new nbd-robustness test
2026-11-10 15:53 ` [PATCH v3 1/6] docs: Mention security considerations for NBD Eric Blake
2026-11-10 15:53 ` [PATCH v3 2/6] iotests: Refactor python NBD server setup Eric Blake
2026-11-10 15:53 ` [PATCH v3 3/6] nbd/server: Reject rather than assert on non-compliant BLOCK_STATUS Eric Blake
@ 2026-11-10 15:54 ` Eric Blake
2026-11-10 15:54 ` [PATCH v3 5/6] nbd/server: Reject rather than assert on non-compliant NBD_OPT_SET_META_CONTEXT Eric Blake
` (2 subsequent siblings)
5 siblings, 0 replies; 12+ messages in thread
From: Eric Blake @ 2026-11-10 15:54 UTC (permalink / raw)
To: qemu-devel
Cc: qemu-block, qemu-stable, berrange, stefanha,
Vladimir Sementsov-Ogievskiy, Kevin Wolf, Hanna Reitz
Convert the proof of concept test by Sunday Jiang into an iotest. I
tried to get libnbd to trigger the situation, but forcing a block
status command when no status has been initiated is not something that
libnbd is willing to do. So this results in a hard-coded sequence of
bytes sent over a raw socket. Swapping the order with the previous
patch shows the assertion failure that was previously reachable.
Based-on: https://gitlab.com/qemu-project/qemu/-/work_items/4091
Signed-off-by: Eric Blake <eblake@redhat.com>
---
v3: fix copy-paste error, enhance test further, fix iotest 297 lints
---
tests/qemu-iotests/tests/nbd-robustness | 132 ++++++++++++++++++++
tests/qemu-iotests/tests/nbd-robustness.out | 5 +
2 files changed, 137 insertions(+)
create mode 100755 tests/qemu-iotests/tests/nbd-robustness
create mode 100644 tests/qemu-iotests/tests/nbd-robustness.out
diff --git a/tests/qemu-iotests/tests/nbd-robustness b/tests/qemu-iotests/tests/nbd-robustness
new file mode 100755
index 00000000000..fe61a839250
--- /dev/null
+++ b/tests/qemu-iotests/tests/nbd-robustness
@@ -0,0 +1,132 @@
+#!/usr/bin/env python3
+# group: rw auto quick
+#
+# Test cases for NBD robustness against malicious clients
+#
+# Copyright (C) 2026 Red Hat, Inc.
+#
+# SPDX-License-Identifier: GPL-2.0-or-later
+
+import os
+import socket
+import struct
+
+import iotests
+from iotests import qemu_img_create, qemu_io
+
+# Selected NBD Protocol Constants
+NBD_MAGIC_INIT = 0x4e42444d41474943 # "NBDMAGIC"
+NBD_MAGIC_OPTS = 0x49484156454f5054 # "IHAVEOPT"
+NBD_MAGIC_REPLY = 0x0003e889045565a9 # Reply magic
+NBD_MAGIC_EXT_REPLY = 0x6e8a278c # Structured reply magic
+NBD_FLAG_C_FIXED_NEWSTYLE = 0x1
+NBD_OPT_GO = 7
+NBD_OPT_SET_META_CONTEXT = 10
+NBD_OPT_EXTENDED_HEADERS = 11
+NBD_REP_ACK = 1
+NBD_REP_INFO = 3
+NBD_CMD_BLOCK_STATUS = 7
+
+disk = os.path.join(iotests.test_dir, 'disk')
+size = '4M'
+nbd_sock = os.path.join(iotests.sock_dir, 'nbd_sock')
+
+
+class TestNbdRobustness(iotests.QMPTestCase):
+ def setUp(self):
+ qemu_img_create('-f', iotests.imgfmt, disk, size)
+ qemu_io('-c', 'w -P 1 0 2M', '-c', 'w -P 2 2M 2M', disk)
+
+ self.vm = iotests.VM()
+ self.vm.launch()
+ self.vm.cmd('blockdev-add', {
+ 'driver': iotests.imgfmt,
+ 'node-name': 'n',
+ 'file': {'driver': 'file', 'filename': disk}
+ })
+
+ def tearDown(self):
+ self.vm.shutdown()
+ os.remove(disk)
+ try:
+ os.remove(nbd_sock)
+ except OSError:
+ pass
+
+ def recv_exact(self, s: socket.socket, n: int) -> bytes:
+ buf = bytearray()
+ while len(buf) < n:
+ chunk = s.recv(n - len(buf))
+ if not chunk:
+ raise EOFError(f"Unexpected EOF while reading {n} bytes "
+ f"(got {len(buf)})")
+ buf.extend(chunk)
+ return bytes(buf)
+
+ def test_invalid_block_status(self):
+ with self.run_nbd_server(nbd_sock):
+ self.add_nbd_export('n', 'r')
+
+ # Use raw socket traffic to intentionally violate NBD protocol
+ with socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) as s:
+ s.connect(nbd_sock)
+ # Server sends NBDMAGIC(8) + IHAVEOPT(8) + flags(2)
+ data = self.recv_exact(s, 18)
+ magic, opt, flags = struct.unpack(">QQH", data)
+ self.assertEqual(magic, NBD_MAGIC_INIT)
+ self.assertEqual(opt, NBD_MAGIC_OPTS)
+ self.assertEqual(flags, 0x3)
+
+ # Reply with flags(4) - just fixed newstyle flag
+ s.sendall(struct.pack(">I", NBD_FLAG_C_FIXED_NEWSTYLE))
+
+ # Request NBD_OPT_EXTENDED_HEADERS, no extra payload
+ s.sendall(struct.pack(">QII", NBD_MAGIC_OPTS,
+ NBD_OPT_EXTENDED_HEADERS, 0))
+
+ # Expect successful reply:
+ # magic(8) + option(4) + reply(4) + length(4)
+ data = self.recv_exact(s, 20)
+ magic, opt, reply, length = struct.unpack(">QIII", data)
+ self.assertEqual(magic, NBD_MAGIC_REPLY)
+ self.assertEqual(opt, NBD_OPT_EXTENDED_HEADERS)
+ self.assertEqual(reply, NBD_REP_ACK)
+ self.assertEqual(length, 0)
+
+ # Request NBD_OPT_GO, name but no info as payload
+ payload = struct.pack(">I", 1) + b'r' + struct.pack(">H", 0)
+ s.sendall(struct.pack(">QII", NBD_MAGIC_OPTS, NBD_OPT_GO,
+ len(payload)) + payload)
+
+ # Expect (and ignore) zero or more info packets before ack
+ while True:
+ data = self.recv_exact(s, 20)
+ magic, opt, reply, length = struct.unpack(">QIII", data)
+ self.assertEqual(magic, NBD_MAGIC_REPLY)
+ self.assertEqual(opt, NBD_OPT_GO)
+ payload = self.recv_exact(s, length)
+ if reply != NBD_REP_INFO:
+ break
+ self.assertEqual(reply, NBD_REP_ACK)
+
+ # Send invalid NBD_CMD_BLOCK_STATUS with payload flag. This
+ # violates the NBD protocol (since no context was negotiated),
+ # but should not crash the server.
+ payload = struct.pack(">QI", 0x1000, 0)
+ s.sendall(struct.pack(">IHHQQQ", 0x21e41c71, 0x20,
+ NBD_CMD_BLOCK_STATUS, 1, 0,
+ len(payload)) + payload)
+
+ # Expect a rejection.
+ data = self.recv_exact(s, 32)
+ magic, flags, rtype, cookie, _, length = \
+ struct.unpack(">IHHQQQ", data)
+ self.assertEqual(magic, NBD_MAGIC_EXT_REPLY)
+ self.assertEqual(rtype & 0x8000, 0x8000)
+ self.assertEqual(cookie, 1)
+ self.recv_exact(s, length)
+
+ self.assertTrue(self.vm.is_running())
+
+if __name__ == '__main__':
+ iotests.main(supported_fmts=['qcow2'])
diff --git a/tests/qemu-iotests/tests/nbd-robustness.out b/tests/qemu-iotests/tests/nbd-robustness.out
new file mode 100644
index 00000000000..ae1213e6f86
--- /dev/null
+++ b/tests/qemu-iotests/tests/nbd-robustness.out
@@ -0,0 +1,5 @@
+.
+----------------------------------------------------------------------
+Ran 1 tests
+
+OK
--
2.56.0
^ permalink raw reply related [flat|nested] 12+ messages in thread
* [PATCH v3 5/6] nbd/server: Reject rather than assert on non-compliant NBD_OPT_SET_META_CONTEXT
2026-11-10 15:53 ` [PATCH v3 1/6] docs: Mention security considerations for NBD Eric Blake
` (2 preceding siblings ...)
2026-11-10 15:54 ` [PATCH v3 4/6] iotests: Add new nbd-robustness test Eric Blake
@ 2026-11-10 15:54 ` Eric Blake
2026-11-10 16:19 ` Daniel P. Berrangé
2026-11-10 15:54 ` [PATCH v3 6/6] iotests: Add another nbd-robustness test Eric Blake
2026-11-10 16:10 ` [PATCH v3 1/6] docs: Mention security considerations for NBD Daniel P. Berrangé
5 siblings, 1 reply; 12+ messages in thread
From: Eric Blake @ 2026-11-10 15:54 UTC (permalink / raw)
To: qemu-devel
Cc: qemu-block, qemu-stable, berrange, stefanha,
Vladimir Sementsov-Ogievskiy
The NBD protocol says that NBD_OPT_{LIST,SET}_META_CONTEXT sends
several lengths that must be consistent: an overall option reply
length, then a series of smaller lengths/data pairs representing the
export name and zero or more context names. If the smaller lengths
within the payload do not add up to the larger value, the client has
sent garbage bytes that will trigger the server's parse of the
client's next option to be out of sync. However, QEMU was dying with
an assertion failure rather than diagnosing the issue, which a
malicious client can then exploit to mount a denial-of-service attack
against QEMU as the NBD server.
As with several other recent NBD protocol violations, the likelihood
of this attack can be reduced by using TLS and x509 certs to reject
unwanted clients (a "trusted" client can still trigger the problem,
but you first have to compromise the trusted client into sending
arbitrary traffic).
Fixes: CVE-2026-125683
Fixes: e7b1948d51 ("nbd: BLOCK_STATUS for standard get_block_status function: server part", v2.12.0)
Reported-by: TonySD via https://gitlab.com/qemu-project/qemu/-/work_items/4843
CC: qemu-stable@nongnu.org
Signed-off-by: Eric Blake <eblake@redhat.com>
---
nbd/server.c | 5 +++++
1 file changed, 5 insertions(+)
diff --git a/nbd/server.c b/nbd/server.c
index 8a01c27de07..8c45f9fc43f 100644
--- a/nbd/server.c
+++ b/nbd/server.c
@@ -1095,6 +1095,11 @@ nbd_negotiate_meta_queries(NBDClient *client, Error **errp)
}
}
}
+ if (client->optlen > 0) {
+ return nbd_opt_invalid(client, errp,
+ "Inconsistent lengths in option %s",
+ nbd_opt_lookup(client->opt));
+ }
if (meta->base_allocation) {
ret = nbd_negotiate_send_meta_context(client, "base:allocation",
--
2.56.0
^ permalink raw reply related [flat|nested] 12+ messages in thread
* [PATCH v3 6/6] iotests: Add another nbd-robustness test
2026-11-10 15:53 ` [PATCH v3 1/6] docs: Mention security considerations for NBD Eric Blake
` (3 preceding siblings ...)
2026-11-10 15:54 ` [PATCH v3 5/6] nbd/server: Reject rather than assert on non-compliant NBD_OPT_SET_META_CONTEXT Eric Blake
@ 2026-11-10 15:54 ` Eric Blake
2026-11-10 16:10 ` [PATCH v3 1/6] docs: Mention security considerations for NBD Daniel P. Berrangé
5 siblings, 0 replies; 12+ messages in thread
From: Eric Blake @ 2026-11-10 15:54 UTC (permalink / raw)
To: qemu-devel
Cc: qemu-block, qemu-stable, berrange, stefanha,
Vladimir Sementsov-Ogievskiy, Kevin Wolf, Hanna Reitz
Convert the proof of concept test by TonySD into another case of the
iotest. This is yet another instance where a hard-coded exchange is
easier than forcing libnbd into producing an exchange that violates
the protocol. Swapping the order with the previous patch shows the
assertion failure that was previously reachable.
Based-on: https://gitlab.com/qemu-project/qemu/-/work_items/4843
Signed-off-by: Eric Blake <eblake@redhat.com>
---
v3: new
---
tests/qemu-iotests/tests/nbd-robustness | 73 +++++++++++++++++++++
tests/qemu-iotests/tests/nbd-robustness.out | 4 +-
2 files changed, 75 insertions(+), 2 deletions(-)
diff --git a/tests/qemu-iotests/tests/nbd-robustness b/tests/qemu-iotests/tests/nbd-robustness
index fe61a839250..410dfca04e3 100755
--- a/tests/qemu-iotests/tests/nbd-robustness
+++ b/tests/qemu-iotests/tests/nbd-robustness
@@ -21,6 +21,7 @@ NBD_MAGIC_REPLY = 0x0003e889045565a9 # Reply magic
NBD_MAGIC_EXT_REPLY = 0x6e8a278c # Structured reply magic
NBD_FLAG_C_FIXED_NEWSTYLE = 0x1
NBD_OPT_GO = 7
+NBD_OPT_STRUCTURED_REPLY = 8
NBD_OPT_SET_META_CONTEXT = 10
NBD_OPT_EXTENDED_HEADERS = 11
NBD_REP_ACK = 1
@@ -128,5 +129,77 @@ class TestNbdRobustness(iotests.QMPTestCase):
self.assertTrue(self.vm.is_running())
+ def test_invalid_set_meta_context(self):
+ with self.run_nbd_server(nbd_sock):
+ self.add_nbd_export('n', 'r')
+
+ # Use raw socket traffic to intentionally violate NBD protocol
+ with socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) as s:
+ s.connect(nbd_sock)
+ # Server sends NBDMAGIC(8) + IHAVEOPT(8) + flags(2)
+ data = self.recv_exact(s, 18)
+ magic, opt, flags = struct.unpack(">QQH", data)
+ self.assertEqual(magic, NBD_MAGIC_INIT)
+ self.assertEqual(opt, NBD_MAGIC_OPTS)
+ self.assertEqual(flags, 0x3)
+
+ # Reply with flags(4) - just fixed newstyle flag
+ s.sendall(struct.pack(">I", NBD_FLAG_C_FIXED_NEWSTYLE))
+
+ # Request NBD_OPT_STRUCTURED_REPLY, no extra payload
+ s.sendall(struct.pack(">QII", NBD_MAGIC_OPTS,
+ NBD_OPT_STRUCTURED_REPLY, 0))
+
+ # Expect successful reply:
+ # magic(8) + option(4) + reply(4) + length(4)
+ data = self.recv_exact(s, 20)
+ magic, opt, reply, length = struct.unpack(">QIII", data)
+ self.assertEqual(magic, NBD_MAGIC_REPLY)
+ self.assertEqual(opt, NBD_OPT_STRUCTURED_REPLY)
+ self.assertEqual(reply, NBD_REP_ACK)
+ self.assertEqual(length, 0)
+
+ # Request NBD_OPT_SET_META_CONTEXT with invalid length
+ # namelen(4) + str + queries(4) + {querylen(4) + str)
+ payload = struct.pack(">I", 1) + b'r'
+ payload += struct.pack(">II", 1, 1) + b'X'
+ s.sendall(struct.pack(">QII", NBD_MAGIC_OPTS,
+ NBD_OPT_SET_META_CONTEXT,
+ len(payload) + 1) + payload)
+
+ # Request NBD_OPT_GO, name but no info as payload
+ # (request is early, but does not hurt here)
+ payload = struct.pack(">I", 1) + b'r' + struct.pack(">H", 0)
+ s.sendall(struct.pack(">QII", NBD_MAGIC_OPTS, NBD_OPT_GO,
+ len(payload)) + payload)
+
+ # Expect error reply to SET_META_CONTEXT:
+ # magic(8) + option(4) + reply(4) + length(4)
+ data = self.recv_exact(s, 20)
+ magic, opt, reply, length = struct.unpack(">QIII", data)
+ self.assertEqual(magic, NBD_MAGIC_REPLY)
+ self.assertEqual(opt, NBD_OPT_SET_META_CONTEXT)
+ self.assertEqual(reply & 0x80000000, 0x80000000)
+ self.recv_exact(s, length)
+
+ # Expect early disconnect for unaligned GO. Could show up as
+ # either b'' or ConnectionResetError
+ try:
+ data = s.recv(1)
+ self.assertEqual(data, b'')
+ except ConnectionResetError:
+ pass
+
+ # Further writes must trigger an error (EPIPE / ECONNRESET)
+ with self.assertRaises((BrokenPipeError, ConnectionResetError,
+ OSError)):
+ s.sendall(b'X')
+
+ # QEMU should still be up
+ self.assertIsNone(self.vm.exitcode())
+ self.assertTrue(self.vm.is_running())
+ res = self.vm.cmd('query-status')
+ self.assertEqual(res['status'], 'running')
+
if __name__ == '__main__':
iotests.main(supported_fmts=['qcow2'])
diff --git a/tests/qemu-iotests/tests/nbd-robustness.out b/tests/qemu-iotests/tests/nbd-robustness.out
index ae1213e6f86..fbc63e62f88 100644
--- a/tests/qemu-iotests/tests/nbd-robustness.out
+++ b/tests/qemu-iotests/tests/nbd-robustness.out
@@ -1,5 +1,5 @@
-.
+..
----------------------------------------------------------------------
-Ran 1 tests
+Ran 2 tests
OK
--
2.56.0
^ permalink raw reply related [flat|nested] 12+ messages in thread
* Re: [PATCH v3 1/6] docs: Mention security considerations for NBD
2026-11-10 15:53 ` [PATCH v3 1/6] docs: Mention security considerations for NBD Eric Blake
` (4 preceding siblings ...)
2026-11-10 15:54 ` [PATCH v3 6/6] iotests: Add another nbd-robustness test Eric Blake
@ 2026-11-10 16:10 ` Daniel P. Berrangé
5 siblings, 0 replies; 12+ messages in thread
From: Daniel P. Berrangé @ 2026-11-10 16:10 UTC (permalink / raw)
To: Eric Blake
Cc: qemu-devel, qemu-block, qemu-stable, stefanha, Pierrick Bouvier
On Tue, Nov 10, 2026 at 09:53:57AM -0600, Eric Blake wrote:
> Document a policy that NBD connections should strongly consider using
> TLS and x509 certificates to filter out unwanted clients. If a
> malicious server or client can trigger a QEMU assertion failure on a
> plaintext connection, but can trivially have risk mitigated by
> enabling TLS to weed out unknown partners (so that a full-on attack of
> QEMU would first require chaining some other attack to convince a
> "trusted" client to break protocol in the first place), then that
> reduces the CVSS-BE score of the QEMU bug. In turn, our policy already
> states that low severity scores can be fixed without an embargo.
>
> Signed-off-by: Eric Blake <eblake@redhat.com>
>
> ---
> v3: more wording tweaks, since even a TLS-authorized partner cannot
> be considered 100% trusted, but is reduced risk.
> ---
> docs/system/security.rst | 23 +++++++++++++++++++++++
> 1 file changed, 23 insertions(+)
Reviewed-by: Daniel P. Berrangé <berrange@redhat.com>
With regards,
Daniel
--
|: https://berrange.com ~~ https://hachyderm.io/@berrange :|
|: https://libvirt.org ~~ https://entangle-photo.org :|
|: https://pixelfed.art/berrange ~~ https://fstop138.berrange.com :|
^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [PATCH v3 2/6] iotests: Refactor python NBD server setup
2026-11-10 15:53 ` [PATCH v3 2/6] iotests: Refactor python NBD server setup Eric Blake
@ 2026-11-10 16:11 ` Daniel P. Berrangé
0 siblings, 0 replies; 12+ messages in thread
From: Daniel P. Berrangé @ 2026-11-10 16:11 UTC (permalink / raw)
To: Eric Blake
Cc: qemu-devel, qemu-block, qemu-stable, stefanha, Kevin Wolf,
Hanna Reitz, Vladimir Sementsov-Ogievskiy
On Tue, Nov 10, 2026 at 09:53:58AM -0600, Eric Blake wrote:
> Extract code from nbd-multiconn that I plan to use in another upcoming
> unit test.
>
> Signed-off-by: Eric Blake <eblake@redhat.com>
> ---
> tests/qemu-iotests/iotests.py | 30 ++++++++++++++++++
> tests/qemu-iotests/tests/nbd-multiconn | 44 +++++---------------------
> 2 files changed, 38 insertions(+), 36 deletions(-)
Reviewed-by: Daniel P. Berrangé <berrange@redhat.com>
With regards,
Daniel
--
|: https://berrange.com ~~ https://hachyderm.io/@berrange :|
|: https://libvirt.org ~~ https://entangle-photo.org :|
|: https://pixelfed.art/berrange ~~ https://fstop138.berrange.com :|
^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [PATCH v3 3/6] nbd/server: Reject rather than assert on non-compliant BLOCK_STATUS
2026-11-10 15:53 ` [PATCH v3 3/6] nbd/server: Reject rather than assert on non-compliant BLOCK_STATUS Eric Blake
@ 2026-11-10 16:12 ` Daniel P. Berrangé
0 siblings, 0 replies; 12+ messages in thread
From: Daniel P. Berrangé @ 2026-11-10 16:12 UTC (permalink / raw)
To: Eric Blake
Cc: qemu-devel, qemu-block, qemu-stable, stefanha, Feifan Qian,
Haotian Jiang, Vladimir Sementsov-Ogievskiy
On Tue, Nov 10, 2026 at 09:53:59AM -0600, Eric Blake wrote:
> The NBD spec is clear that a client must not send NBD_BLOCK_STATUS
> requests to a server if it did not negotiate any metacontexts with
> NBD_OPT_SET_META_CONTEXT for the current export. However, if the
> non-compliant client uses NBD_OPT_EXTENDED_HEADERS and then passes in
> the relatively new NBD_CMD_FLAG_PAYLOAD_LEN flag alongside an
> inappropriate block status request, this was enough to trigger an
> assertion in qemu. The assertion can also be tripped by initially
> requesting the context for one export, then using NBD_OPT_GO on a
> different export (nbd_check_meta_export() sets contexts.count to 0,
> but did not wipe contexts.exp). Forcing the client command to be
> rejected if contexts.count is 0 solves both triggers, and allows the
> assertion to still operate cleanly at a later location.
>
> If you do not use recommended practice to prevent unwanted clients
> from connecting to QEMU's NBD server, then any client can cause a
> denial of service with minimal effort. However, with TLS and x509
> certs in place, an attacker must first chain in an attack to convince
> the client to send unwanted traffic, which is a factor in declaring
> this a lower-risk denial of service.
>
> Fixes: CVE-2026-124559
> Fixes: 2dcbb11b ("nbd/server: Add FLAG_PAYLOAD support to CMD_BLOCK_STATUS", v8.2.0)
> Reported-by: Feifan Qian <bea1e@proton.me>
> Reported-by: https://gitlab.com/qemu-project/qemu/-/work_items/3796
> CC: Haotian Jiang <jianghaotian.sunday@gmail.com>
> CC: qemu-stable@nongnu.org
> Signed-off-by: Eric Blake <eblake@redhat.com>
>
> ---
> v3: Move assert later rather than dropping it; improve commit message.
> R-b dropped.
> ---
> nbd/server.c | 5 +++--
> 1 file changed, 3 insertions(+), 2 deletions(-)
Reviewed-by: Daniel P. Berrangé <berrange@redhat.com>
With regards,
Daniel
--
|: https://berrange.com ~~ https://hachyderm.io/@berrange :|
|: https://libvirt.org ~~ https://entangle-photo.org :|
|: https://pixelfed.art/berrange ~~ https://fstop138.berrange.com :|
^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [PATCH v3 5/6] nbd/server: Reject rather than assert on non-compliant NBD_OPT_SET_META_CONTEXT
2026-11-10 15:54 ` [PATCH v3 5/6] nbd/server: Reject rather than assert on non-compliant NBD_OPT_SET_META_CONTEXT Eric Blake
@ 2026-11-10 16:19 ` Daniel P. Berrangé
0 siblings, 0 replies; 12+ messages in thread
From: Daniel P. Berrangé @ 2026-11-10 16:19 UTC (permalink / raw)
To: Eric Blake
Cc: qemu-devel, qemu-block, qemu-stable, stefanha,
Vladimir Sementsov-Ogievskiy
On Tue, Nov 10, 2026 at 09:54:01AM -0600, Eric Blake wrote:
> The NBD protocol says that NBD_OPT_{LIST,SET}_META_CONTEXT sends
> several lengths that must be consistent: an overall option reply
> length, then a series of smaller lengths/data pairs representing the
> export name and zero or more context names. If the smaller lengths
> within the payload do not add up to the larger value, the client has
> sent garbage bytes that will trigger the server's parse of the
> client's next option to be out of sync. However, QEMU was dying with
> an assertion failure rather than diagnosing the issue, which a
> malicious client can then exploit to mount a denial-of-service attack
> against QEMU as the NBD server.
>
> As with several other recent NBD protocol violations, the likelihood
> of this attack can be reduced by using TLS and x509 certs to reject
> unwanted clients (a "trusted" client can still trigger the problem,
> but you first have to compromise the trusted client into sending
> arbitrary traffic).
>
> Fixes: CVE-2026-125683
> Fixes: e7b1948d51 ("nbd: BLOCK_STATUS for standard get_block_status function: server part", v2.12.0)
> Reported-by: TonySD via https://gitlab.com/qemu-project/qemu/-/work_items/4843
> CC: qemu-stable@nongnu.org
> Signed-off-by: Eric Blake <eblake@redhat.com>
> ---
> nbd/server.c | 5 +++++
> 1 file changed, 5 insertions(+)
Reviewed-by: Daniel P. Berrangé <berrange@redhat.com>
With regards,
Daniel
--
|: https://berrange.com ~~ https://hachyderm.io/@berrange :|
|: https://libvirt.org ~~ https://entangle-photo.org :|
|: https://pixelfed.art/berrange ~~ https://fstop138.berrange.com :|
^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [PATCH for-11.2 v3 0/6] Fix some low-severity NBD CVEs
2026-11-10 15:30 [PATCH for-11.2 v3 0/6] Fix some low-severity NBD CVEs Eric Blake
2026-11-10 15:53 ` [PATCH v3 1/6] docs: Mention security considerations for NBD Eric Blake
@ 2026-11-10 16:27 ` Stefan Hajnoczi
1 sibling, 0 replies; 12+ messages in thread
From: Stefan Hajnoczi @ 2026-11-10 16:27 UTC (permalink / raw)
To: Eric Blake; +Cc: qemu-devel, qemu-block, qemu-stable, berrange
On Tue, Nov 10, 2026 at 09:30:00AM -0600, Eric Blake wrote:
> Since v2 (https://lists.nongnu.org/archive/html/qemu-devel/2026-11/msg01673.htm\
> l):
> - Tweak the documentation in patch 1 again [Stefan, Dan]
> - Improve unit tests, and pass pylint iotest 297 (new patch 2, also patch 4, 6)
> - Retain assertion at safer point for first bug (patch 3)
> - Second CVE addressed as well (patch 5, 6)
>
> Eric Blake (6):
> docs: Mention security considerations for NBD
> iotests: Refactor python NBD server setup
> nbd/server: Reject rather than assert on non-compliant BLOCK_STATUS
> iotests: Add new nbd-robustness test
> nbd/server: Reject rather than assert on non-compliant
> NBD_OPT_SET_META_CONTEXT
> iotests: Add another nbd-robustness test
>
> docs/system/security.rst | 23 +++
> nbd/server.c | 10 +-
> tests/qemu-iotests/iotests.py | 30 +++
> tests/qemu-iotests/tests/nbd-multiconn | 44 +----
> tests/qemu-iotests/tests/nbd-robustness | 205 ++++++++++++++++++++
> tests/qemu-iotests/tests/nbd-robustness.out | 5 +
> 6 files changed, 279 insertions(+), 38 deletions(-)
> create mode 100755 tests/qemu-iotests/tests/nbd-robustness
> create mode 100644 tests/qemu-iotests/tests/nbd-robustness.out
>
> --
> 2.56.0
>
>
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
^ permalink raw reply [flat|nested] 12+ messages in thread
end of thread, other threads:[~2026-11-10 16:28 UTC | newest]
Thread overview: 12+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-11-10 15:30 [PATCH for-11.2 v3 0/6] Fix some low-severity NBD CVEs Eric Blake
2026-11-10 15:53 ` [PATCH v3 1/6] docs: Mention security considerations for NBD Eric Blake
2026-11-10 15:53 ` [PATCH v3 2/6] iotests: Refactor python NBD server setup Eric Blake
2026-11-10 16:11 ` Daniel P. Berrangé
2026-11-10 15:53 ` [PATCH v3 3/6] nbd/server: Reject rather than assert on non-compliant BLOCK_STATUS Eric Blake
2026-11-10 16:12 ` Daniel P. Berrangé
2026-11-10 15:54 ` [PATCH v3 4/6] iotests: Add new nbd-robustness test Eric Blake
2026-11-10 15:54 ` [PATCH v3 5/6] nbd/server: Reject rather than assert on non-compliant NBD_OPT_SET_META_CONTEXT Eric Blake
2026-11-10 16:19 ` Daniel P. Berrangé
2026-11-10 15:54 ` [PATCH v3 6/6] iotests: Add another nbd-robustness test Eric Blake
2026-11-10 16:10 ` [PATCH v3 1/6] docs: Mention security considerations for NBD Daniel P. Berrangé
2026-11-10 16:27 ` [PATCH for-11.2 v3 0/6] Fix some low-severity NBD CVEs Stefan Hajnoczi
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.