linux-user/riscv: Fix handling of cpu mask in riscv_hwprobe syscall

The third argument of the syscall contains the size of the
cpu mask in bytes, not bits.  Nor is the size rounded up to
a multiple of sizeof(abi_ulong).

Cc: qemu-stable@nongnu.org
Reported-by: Andreas Schwab <schwab@suse.de>
Fixes: 9e1c7d982d ("linux-user/riscv: Add syscall riscv_hwprobe")
Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
Reviewed-by: Alistair Francis <alistair.francis@wdc.com>
Message-ID: <20250308225902.1208237-3-richard.henderson@linaro.org>
Signed-off-by: Alistair Francis <alistair.francis@wdc.com>
This commit is contained in:
Richard Henderson 2025-03-08 14:58:40 -08:00 committed by Alistair Francis
parent 4e9e2478df
commit 1a010d22b7

View file

@ -9119,37 +9119,40 @@ static void risc_hwprobe_fill_pairs(CPURISCVState *env,
} }
} }
static int cpu_set_valid(abi_long arg3, abi_long arg4)
{
int ret, i, tmp;
size_t host_mask_size, target_mask_size;
unsigned long *host_mask;
/* /*
* cpu_set_t represent CPU masks as bit masks of type unsigned long *. * If the cpumask_t of (target_cpus, cpusetsize) cannot be read: -EFAULT.
* arg3 contains the cpu count. * If the cpumast_t has no bits set: -EINVAL.
* Otherwise the cpumask_t contains some bit set: 0.
* Unlike the kernel, we do not mask cpumask_t by the set of online cpus,
* nor bound the search by cpumask_size().
*/ */
tmp = (8 * sizeof(abi_ulong)); static int nonempty_cpu_set(abi_ulong cpusetsize, abi_ptr target_cpus)
target_mask_size = ((arg3 + tmp - 1) / tmp) * sizeof(abi_ulong); {
host_mask_size = (target_mask_size + (sizeof(*host_mask) - 1)) & unsigned char *p = lock_user(VERIFY_READ, target_cpus, cpusetsize, 1);
~(sizeof(*host_mask) - 1); int ret = -TARGET_EFAULT;
host_mask = alloca(host_mask_size); if (p) {
ret = -TARGET_EINVAL;
ret = target_to_host_cpu_mask(host_mask, host_mask_size, /*
arg4, target_mask_size); * Since we only care about the empty/non-empty state of the cpumask_t
if (ret != 0) { * not the individual bits, we do not need to repartition the bits
* from target abi_ulong to host unsigned long.
*
* Note that the kernel does not round up cpusetsize to a multiple of
* sizeof(abi_ulong). After bounding cpusetsize by cpumask_size(),
* it copies exactly cpusetsize bytes into a zeroed buffer.
*/
for (abi_ulong i = 0; i < cpusetsize; ++i) {
if (p[i]) {
ret = 0;
break;
}
}
unlock_user(p, target_cpus, 0);
}
return ret; return ret;
} }
for (i = 0 ; i < host_mask_size / sizeof(*host_mask); i++) {
if (host_mask[i] != 0) {
return 0;
}
}
return -TARGET_EINVAL;
}
static abi_long do_riscv_hwprobe(CPUArchState *cpu_env, abi_long arg1, static abi_long do_riscv_hwprobe(CPUArchState *cpu_env, abi_long arg1,
abi_long arg2, abi_long arg3, abi_long arg2, abi_long arg3,
abi_long arg4, abi_long arg5) abi_long arg4, abi_long arg5)
@ -9164,7 +9167,7 @@ static abi_long do_riscv_hwprobe(CPUArchState *cpu_env, abi_long arg1,
/* check cpu_set */ /* check cpu_set */
if (arg3 != 0) { if (arg3 != 0) {
ret = cpu_set_valid(arg3, arg4); ret = nonempty_cpu_set(arg3, arg4);
if (ret != 0) { if (ret != 0) {
return ret; return ret;
} }