From 6c38823e8971fabd4d2e4d447c65b39f57b953e3 Mon Sep 17 00:00:00 2001
From: Roger Pau Monne <roger.pau@citrix.com>
Date: Tue, 14 Jul 2026 13:13:12 +0200
Subject: xen/dmop: check number of input buffers
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

The hypercall requires at least one input buffer, as both arch-specific
implementations of dm_op() unconditionally assume ->buf[0] to be valid (and
not stack rubble).

Additionally, XEN_DMOP_modified_memory requires two input buffers, yet the
code was assuming the second buffer to always be provided by the user when
checking for the number of extents.  In case the caller sets nr_bufs to 1,
the code in modified_memory() will read stack garbage as the size of the
buffer, thus allowing the caller some degree of insight on the contents of
the stack by probing whether the hypercall returns -EINVAL or -EFAULT as a
result of such bogus call.

This is XSA-506 / CVE-2026-62433.

Fixes: e3b93b3c5954 ("dmop: add xendevicemodel_modified_memory_bulk()")
Fixes: 85cb15dfe4d1 ("x86/hvm/dmop: only copy what is needed to/from the guest")
Signed-off-by: Roger Pau Monné <roger.pau@citrix.com>
Reviewed-by: Andrew Cooper <andrew.cooper3@citrix.com>
Reviewed-by: Jan Beulich <jbeulich@suse.com>
---
 xen/arch/x86/hvm/dm.c | 9 +++++++++
 xen/common/dm.c       | 3 +++
 2 files changed, 12 insertions(+)

diff --git a/xen/arch/x86/hvm/dm.c b/xen/arch/x86/hvm/dm.c
index 066498e07ea1..1f44fff12a21 100644
--- a/xen/arch/x86/hvm/dm.c
+++ b/xen/arch/x86/hvm/dm.c
@@ -494,6 +494,12 @@ int dm_op(const struct dmop_args *op_args)
         struct xen_dm_op_modified_memory *data =
             &op.u.modified_memory;
 
+        if ( op_args->nr_bufs != 2 )
+        {
+            rc = -EINVAL;
+            break;
+        }
+
         rc = modified_memory(d, op_args, data);
         const_op = !rc;
         break;
@@ -655,6 +661,9 @@ int compat_dm_op(
     unsigned int i;
     int rc;
 
+    if ( !nr_bufs )
+        return -ENODATA;
+
     if ( nr_bufs > ARRAY_SIZE(args.buf) )
         return -E2BIG;
 
diff --git a/xen/common/dm.c b/xen/common/dm.c
index 201b652deb7e..8689728ab7a3 100644
--- a/xen/common/dm.c
+++ b/xen/common/dm.c
@@ -26,6 +26,9 @@ long do_dm_op(
     struct dmop_args args;
     int rc;
 
+    if ( !nr_bufs )
+        return -ENODATA;
+
     if ( nr_bufs > ARRAY_SIZE(args.buf) )
         return -E2BIG;
 
-- 
2.53.0

