* [PULL 1/2] linux-user: Validate guest-passed dm_ioctl data_size
2026-07-13 14:35 [PULL 0/2] Linux user for v11.1 patches Helge Deller
@ 2026-07-13 14:35 ` Helge Deller
2026-07-13 14:35 ` [PULL 2/2] linux-user/alpha: populate AT_HWCAP from env->amask Helge Deller
1 sibling, 0 replies; 3+ messages in thread
From: Helge Deller @ 2026-07-13 14:35 UTC (permalink / raw)
To: Stefan Hajnoczi, qemu-devel
Cc: Laurent Vivier, Pierrick Bouvier, Helge Deller, Peter Maydell
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
^ permalink raw reply related [flat|nested] 3+ messages in thread* [PULL 2/2] linux-user/alpha: populate AT_HWCAP from env->amask
2026-07-13 14:35 [PULL 0/2] Linux user for v11.1 patches Helge Deller
2026-07-13 14:35 ` [PULL 1/2] linux-user: Validate guest-passed dm_ioctl data_size Helge Deller
@ 2026-07-13 14:35 ` Helge Deller
1 sibling, 0 replies; 3+ messages in thread
From: Helge Deller @ 2026-07-13 14:35 UTC (permalink / raw)
To: Stefan Hajnoczi, qemu-devel
Cc: Laurent Vivier, Pierrick Bouvier, Helge Deller, Matt Turner
From: Matt Turner <mattst88@gmail.com>
Alpha has never set AT_HWCAP in linux-user emulation, so getauxval(AT_HWCAP)
always returned 0 regardless of the emulated CPU model.
The Linux kernel computes ELF_HWCAP as ~amask(-1), i.e. the set of ISA
extension bits that the amask instruction reports as supported (cleared in
its output). env->amask stores exactly those bits with the same layout
(BWX=0x1, FIX=0x2, CIX=0x4, MVI=0x100, TRAP=0x200, PREFETCH=0x1000), so
returning it directly from get_elf_hwcap matches the kernel convention.
Add HAVE_ELF_HWCAP to target_elf.h and implement get_elf_hwcap() in
elfload.c to expose the emulated CPU's capability mask to user-space
programs via the auxiliary vector.
Without this fix, programs using getauxval(AT_HWCAP) to detect BWX/FIX/CIX
(such as glibc's memcpy or JIT compilers targeting Alpha) incorrectly
concluded that no extensions were available even when emulating ev56+.
Signed-off-by: Matt Turner <mattst88@gmail.com>
Cc: qemu-stable@nongnu.org
Reviewed-by: Helge Deller <deller@gmx.de>
Signed-off-by: Helge Deller <deller@gmx.de>
---
linux-user/alpha/elfload.c | 11 +++++++++++
linux-user/alpha/target_elf.h | 1 +
2 files changed, 12 insertions(+)
diff --git a/linux-user/alpha/elfload.c b/linux-user/alpha/elfload.c
index 1969f620a5..7be9e466b6 100644
--- a/linux-user/alpha/elfload.c
+++ b/linux-user/alpha/elfload.c
@@ -6,6 +6,17 @@
#include "target_elf.h"
+abi_ulong get_elf_hwcap(CPUState *cs)
+{
+ /*
+ * The Linux kernel computes ELF_HWCAP as ~amask(-1), which clears a bit
+ * for each supported ISA extension. env->amask stores exactly those bits
+ * set for the extensions supported by the emulated CPU model, matching
+ * the kernel's convention: bit set in AT_HWCAP ↔ extension present.
+ */
+ return cpu_env(cs)->amask;
+}
+
void elf_core_copy_regs(target_elf_gregset_t *r, const CPUAlphaState *env)
{
int i;
diff --git a/linux-user/alpha/target_elf.h b/linux-user/alpha/target_elf.h
index 4987ae3944..dd90c6f783 100644
--- a/linux-user/alpha/target_elf.h
+++ b/linux-user/alpha/target_elf.h
@@ -12,6 +12,7 @@
#define ELF_MACHINE EM_ALPHA
#define HAVE_ELF_CORE_DUMP 1
+#define HAVE_ELF_HWCAP 1
/*
* Matches the kernel's elf_gregset_t (ELF_NGREG = 33):
--
2.54.0
^ permalink raw reply related [flat|nested] 3+ messages in thread