linux-user: fix name_to_handle_at when AT_HANDLE_MNT_ID_UNIQUE flag is set

Linux 6.12 added AT_HANDLE_MNT_ID_UNIQUE, which indicates that mount_id
is 64-bits. If name_to_handle_at is called with this flag set then qemu
passes a 4 byte int to the kernel, which then tries to store 8 bytes in
a 4 byte variable, causing a SIGSEGV[1][2].

This stores mount_id in a 64-bit var if the flag is set.

1. https://gitlab.postmarketos.org/postmarketOS/pmaports/-/work_items/4431
2. https://github.com/systemd/systemd/issues/41279

Signed-off-by: Clayton Craft <craftyguy@postmarketos.org>
Reviewed-by: Helge Deller <deller@gmx.de>
Message-id: 20260325-fix-name-to-handle-at-v1-1-49fb922e6fd3@craftyguy.net
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
Clayton Craft
2026-03-25 22:59:36 -07:00
committed by Peter Maydell
parent 8330da591e
commit 22966937f4

View File

@@ -8166,6 +8166,9 @@ static int do_futex(CPUState *cpu, bool time64, target_ulong uaddr,
#endif
#if defined(TARGET_NR_name_to_handle_at) && defined(CONFIG_OPEN_BY_HANDLE)
#ifndef AT_HANDLE_MNT_ID_UNIQUE
#define AT_HANDLE_MNT_ID_UNIQUE 0x001
#endif
static abi_long do_name_to_handle_at(abi_long dirfd, abi_long pathname,
abi_long handle, abi_long mount_id,
abi_long flags)
@@ -8173,6 +8176,7 @@ static abi_long do_name_to_handle_at(abi_long dirfd, abi_long pathname,
struct file_handle *target_fh;
struct file_handle *fh;
int mid = 0;
uint64_t mid64 = 0;
abi_long ret;
char *name;
unsigned int size, total_size;
@@ -8196,7 +8200,12 @@ static abi_long do_name_to_handle_at(abi_long dirfd, abi_long pathname,
fh = g_malloc0(total_size);
fh->handle_bytes = size;
ret = get_errno(name_to_handle_at(dirfd, path(name), fh, &mid, flags));
if (flags & AT_HANDLE_MNT_ID_UNIQUE) {
ret = get_errno(name_to_handle_at(dirfd, path(name), fh,
(int *)&mid64, flags));
} else {
ret = get_errno(name_to_handle_at(dirfd, path(name), fh, &mid, flags));
}
unlock_user(name, pathname, 0);
/* man name_to_handle_at(2):
@@ -8210,8 +8219,14 @@ static abi_long do_name_to_handle_at(abi_long dirfd, abi_long pathname,
g_free(fh);
unlock_user(target_fh, handle, total_size);
if (put_user_s32(mid, mount_id)) {
return -TARGET_EFAULT;
if (flags & AT_HANDLE_MNT_ID_UNIQUE) {
if (put_user_u64(mid64, mount_id)) {
return -TARGET_EFAULT;
}
} else {
if (put_user_s32(mid, mount_id)) {
return -TARGET_EFAULT;
}
}
return ret;