From: Helge Deller <deller@kernel.org>
To: Stefan Hajnoczi <stefanha@gmail.com>, qemu-devel@nongnu.org
Cc: Laurent Vivier <laurent@vivier.eu>,
Pierrick Bouvier <pierrick.bouvier@oss.qualcomm.com>,
Helge Deller <deller@gmx.de>,
Peter Maydell <peter.maydell@linaro.org>
Subject: [PULL 1/2] linux-user: Validate guest-passed dm_ioctl data_size
Date: Mon, 13 Jul 2026 16:35:30 +0200 [thread overview]
Message-ID: <20260713143533.4641-2-deller@kernel.org> (raw)
In-Reply-To: <20260713143533.4641-1-deller@kernel.org>
From: Peter Maydell <peter.maydell@linaro.org>
In do_ioctl_dm() we work with a struct dm_ioctl from the guest. This
has a fixed initial part, and then a variable data part; the guest
tells us how long that part is by setting the data_size field. The
data_size is supposed to include the length of the fixed parts of the
struct dm_ioctl. Currently we don't validate anything about the
guest-provided data_size, and we use it to allocate a buffer which we
then copy the fixed part of the dm_ioctl struct into. This means
that if the guest passes a very small data_size the copy of the fixed
part will overrun the buffer.
Perform the same sanitizing of the minimum and maximum limits of the
data_size that the kernel does in drivers/md/dm-ioctl.c in the
copy_params() function.
Cc: qemu-stable@nongnu.org
Fixes: 56e904ecb2018 ("linux-user: implement device mapper ioctls")
Resolves: https://gitlab.com/qemu-project/qemu/-/work_items/3736
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
Reviewed-by: Helge Deller <deller@gmx.de>
Signed-off-by: Helge Deller <deller@gmx.de>
---
linux-user/syscall.c | 30 ++++++++++++++++++++++++++----
1 file changed, 26 insertions(+), 4 deletions(-)
diff --git a/linux-user/syscall.c b/linux-user/syscall.c
index d257fb9ca9..3f060ee8b6 100644
--- a/linux-user/syscall.c
+++ b/linux-user/syscall.c
@@ -5088,6 +5088,9 @@ do_ioctl_usbdevfs_submiturb(const IOCTLEntry *ie, uint8_t *buf_temp,
}
#endif /* CONFIG_USBFS */
+#define DM_MAX_TARGETS 1048576
+#define DM_MAX_TARGET_PARAMS 1024
+
static abi_long do_ioctl_dm(const IOCTLEntry *ie, uint8_t *buf_temp, int fd,
int cmd, abi_long arg)
{
@@ -5100,6 +5103,7 @@ static abi_long do_ioctl_dm(const IOCTLEntry *ie, uint8_t *buf_temp, int fd,
abi_long ret;
void *big_buf = NULL;
char *host_data;
+ const size_t minimum_data_size = offsetof(struct dm_ioctl, data);
arg_type++;
target_size = thunk_type_size(arg_type, 0);
@@ -5111,9 +5115,26 @@ static abi_long do_ioctl_dm(const IOCTLEntry *ie, uint8_t *buf_temp, int fd,
thunk_convert(buf_temp, argptr, arg_type, THUNK_HOST);
unlock_user(argptr, arg, 0);
- /* buf_temp is too small, so fetch things into a bigger buffer */
- big_buf = g_malloc0(((struct dm_ioctl*)buf_temp)->data_size * 2);
- memcpy(big_buf, buf_temp, target_size);
+ /* At this point this includes the size of the fixed dm_ioctl parts */
+ guest_data_size = ((struct dm_ioctl *)buf_temp)->data_size;
+
+ if (guest_data_size < minimum_data_size ||
+ guest_data_size > DM_MAX_TARGETS * DM_MAX_TARGET_PARAMS) {
+ ret = -TARGET_EINVAL;
+ goto out;
+ }
+
+ /*
+ * buf_temp is too small, so fetch things into a bigger buffer. Here
+ * we copy all of the fixed parts of struct dm_ioctl but not the
+ * data at the end (which in the struct is "char data[7]" but in
+ * reality is command-specific and might be nothing or might be
+ * much larger, as defined by data_size). We know struct dm_ioctl's
+ * size is not target specific so we don't need to distinguish between
+ * its minimum size for the host vs the target.
+ */
+ big_buf = g_malloc0(guest_data_size * 2);
+ memcpy(big_buf, buf_temp, minimum_data_size);
buf_temp = big_buf;
host_dm = big_buf;
@@ -5122,7 +5143,8 @@ static abi_long do_ioctl_dm(const IOCTLEntry *ie, uint8_t *buf_temp, int fd,
ret = -TARGET_EINVAL;
goto out;
}
- guest_data_size = host_dm->data_size - host_dm->data_start;
+ /* Adjust down to only the size of the payload */
+ guest_data_size -= host_dm->data_start;
host_data = (char*)host_dm + host_dm->data_start;
argptr = lock_user(VERIFY_READ, guest_data, guest_data_size, 1);
--
2.54.0
next prev parent reply other threads:[~2026-07-13 14:36 UTC|newest]
Thread overview: 3+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-07-13 14:35 [PULL 0/2] Linux user for v11.1 patches Helge Deller
2026-07-13 14:35 ` Helge Deller [this message]
2026-07-13 14:35 ` [PULL 2/2] linux-user/alpha: populate AT_HWCAP from env->amask Helge Deller
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20260713143533.4641-2-deller@kernel.org \
--to=deller@kernel.org \
--cc=deller@gmx.de \
--cc=laurent@vivier.eu \
--cc=peter.maydell@linaro.org \
--cc=pierrick.bouvier@oss.qualcomm.com \
--cc=qemu-devel@nongnu.org \
--cc=stefanha@gmail.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
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.