Skip to content

Commit 1a010d2

Browse files
rth7680alistair23
authored andcommitted
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: [email protected] Reported-by: Andreas Schwab <[email protected]> Fixes: 9e1c7d9 ("linux-user/riscv: Add syscall riscv_hwprobe") Signed-off-by: Richard Henderson <[email protected]> Reviewed-by: Alistair Francis <[email protected]> Message-ID: <[email protected]> Signed-off-by: Alistair Francis <[email protected]>
1 parent 4e9e247 commit 1a010d2

File tree

1 file changed

+29
-26
lines changed

1 file changed

+29
-26
lines changed

linux-user/syscall.c

Lines changed: 29 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -9119,35 +9119,38 @@ static void risc_hwprobe_fill_pairs(CPURISCVState *env,
91199119
}
91209120
}
91219121

9122-
static int cpu_set_valid(abi_long arg3, abi_long arg4)
9122+
/*
9123+
* If the cpumask_t of (target_cpus, cpusetsize) cannot be read: -EFAULT.
9124+
* If the cpumast_t has no bits set: -EINVAL.
9125+
* Otherwise the cpumask_t contains some bit set: 0.
9126+
* Unlike the kernel, we do not mask cpumask_t by the set of online cpus,
9127+
* nor bound the search by cpumask_size().
9128+
*/
9129+
static int nonempty_cpu_set(abi_ulong cpusetsize, abi_ptr target_cpus)
91239130
{
9124-
int ret, i, tmp;
9125-
size_t host_mask_size, target_mask_size;
9126-
unsigned long *host_mask;
9127-
9128-
/*
9129-
* cpu_set_t represent CPU masks as bit masks of type unsigned long *.
9130-
* arg3 contains the cpu count.
9131-
*/
9132-
tmp = (8 * sizeof(abi_ulong));
9133-
target_mask_size = ((arg3 + tmp - 1) / tmp) * sizeof(abi_ulong);
9134-
host_mask_size = (target_mask_size + (sizeof(*host_mask) - 1)) &
9135-
~(sizeof(*host_mask) - 1);
9136-
9137-
host_mask = alloca(host_mask_size);
9138-
9139-
ret = target_to_host_cpu_mask(host_mask, host_mask_size,
9140-
arg4, target_mask_size);
9141-
if (ret != 0) {
9142-
return ret;
9143-
}
9131+
unsigned char *p = lock_user(VERIFY_READ, target_cpus, cpusetsize, 1);
9132+
int ret = -TARGET_EFAULT;
91449133

9145-
for (i = 0 ; i < host_mask_size / sizeof(*host_mask); i++) {
9146-
if (host_mask[i] != 0) {
9147-
return 0;
9134+
if (p) {
9135+
ret = -TARGET_EINVAL;
9136+
/*
9137+
* Since we only care about the empty/non-empty state of the cpumask_t
9138+
* not the individual bits, we do not need to repartition the bits
9139+
* from target abi_ulong to host unsigned long.
9140+
*
9141+
* Note that the kernel does not round up cpusetsize to a multiple of
9142+
* sizeof(abi_ulong). After bounding cpusetsize by cpumask_size(),
9143+
* it copies exactly cpusetsize bytes into a zeroed buffer.
9144+
*/
9145+
for (abi_ulong i = 0; i < cpusetsize; ++i) {
9146+
if (p[i]) {
9147+
ret = 0;
9148+
break;
9149+
}
91489150
}
9151+
unlock_user(p, target_cpus, 0);
91499152
}
9150-
return -TARGET_EINVAL;
9153+
return ret;
91519154
}
91529155

91539156
static abi_long do_riscv_hwprobe(CPUArchState *cpu_env, abi_long arg1,
@@ -9164,7 +9167,7 @@ static abi_long do_riscv_hwprobe(CPUArchState *cpu_env, abi_long arg1,
91649167

91659168
/* check cpu_set */
91669169
if (arg3 != 0) {
9167-
ret = cpu_set_valid(arg3, arg4);
9170+
ret = nonempty_cpu_set(arg3, arg4);
91689171
if (ret != 0) {
91699172
return ret;
91709173
}

0 commit comments

Comments
 (0)