Skip to content

Commit aecc886

Browse files
agrafRiku Voipio
authored and
Riku Voipio
committed
linux-user: Fix up timer id handling
When creating a timer handle, we give the timer id a special magic offset of 0xcafe0000. However, we never mask that offset out of the timer id before we start using it to dereference our timer array. So we always end up aborting timer operations because the timer id is out of bounds. This was not an issue before my patch e52a99f ("linux-user: Simplify timerid checks on g_posix_timers range") because before we would blindly mask anything above the first 16 bits. This patch simplifies the code around timer id creation by introducing a proper target_timer_id typedef that is s32, just like Linux has it. It also changes the magic offset to a value that makes all timer ids be positive. Reported-by: Tom Musta <[email protected]> Signed-off-by: Alexander Graf <[email protected]> Reviewed-by: Peter Maydell <[email protected]> Reviewed-by: Tom Musta <[email protected]> Tested-by: Tom Musta <[email protected]> Signed-off-by: Riku Voipio <[email protected]>
1 parent ccf661f commit aecc886

File tree

2 files changed

+38
-21
lines changed

2 files changed

+38
-21
lines changed

linux-user/syscall.c

+37-17
Original file line numberDiff line numberDiff line change
@@ -5473,6 +5473,27 @@ static int do_openat(void *cpu_env, int dirfd, const char *pathname, int flags,
54735473
return get_errno(sys_openat(dirfd, path(pathname), flags, mode));
54745474
}
54755475

5476+
#define TIMER_MAGIC 0x0caf0000
5477+
#define TIMER_MAGIC_MASK 0xffff0000
5478+
5479+
/* Convert QEMU provided timer ID back to internal 16bit index format */
5480+
static target_timer_t get_timer_id(abi_long arg)
5481+
{
5482+
target_timer_t timerid = arg;
5483+
5484+
if ((timerid & TIMER_MAGIC_MASK) != TIMER_MAGIC) {
5485+
return -TARGET_EINVAL;
5486+
}
5487+
5488+
timerid &= 0xffff;
5489+
5490+
if (timerid >= ARRAY_SIZE(g_posix_timers)) {
5491+
return -TARGET_EINVAL;
5492+
}
5493+
5494+
return timerid;
5495+
}
5496+
54765497
/* do_syscall() should always have a single exit point at the end so
54775498
that actions, such as logging of syscall results, can be performed.
54785499
All errnos that do_syscall() returns must be -TARGET_<errcode>. */
@@ -9579,7 +9600,6 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1,
95799600
/* args: clockid_t clockid, struct sigevent *sevp, timer_t *timerid */
95809601

95819602
struct sigevent host_sevp = { {0}, }, *phost_sevp = NULL;
9582-
struct target_timer_t *ptarget_timer;
95839603

95849604
int clkid = arg1;
95859605
int timer_index = next_free_host_timer();
@@ -9601,11 +9621,9 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1,
96019621
if (ret) {
96029622
phtimer = NULL;
96039623
} else {
9604-
if (!lock_user_struct(VERIFY_WRITE, ptarget_timer, arg3, 1)) {
9624+
if (put_user(TIMER_MAGIC | timer_index, arg3, target_timer_t)) {
96059625
goto efault;
96069626
}
9607-
ptarget_timer->ptr = tswap32(0xcafe0000 | timer_index);
9608-
unlock_user_struct(ptarget_timer, arg3, 1);
96099627
}
96109628
}
96119629
break;
@@ -9617,9 +9635,11 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1,
96179635
{
96189636
/* args: timer_t timerid, int flags, const struct itimerspec *new_value,
96199637
* struct itimerspec * old_value */
9620-
target_ulong timerid = arg1;
9638+
target_timer_t timerid = get_timer_id(arg1);
96219639

9622-
if (arg3 == 0 || timerid >= ARRAY_SIZE(g_posix_timers)) {
9640+
if (timerid < 0) {
9641+
ret = timerid;
9642+
} else if (arg3 == 0) {
96239643
ret = -TARGET_EINVAL;
96249644
} else {
96259645
timer_t htimer = g_posix_timers[timerid];
@@ -9638,12 +9658,12 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1,
96389658
case TARGET_NR_timer_gettime:
96399659
{
96409660
/* args: timer_t timerid, struct itimerspec *curr_value */
9641-
target_ulong timerid = arg1;
9661+
target_timer_t timerid = get_timer_id(arg1);
96429662

9643-
if (!arg2) {
9644-
return -TARGET_EFAULT;
9645-
} else if (timerid >= ARRAY_SIZE(g_posix_timers)) {
9646-
ret = -TARGET_EINVAL;
9663+
if (timerid < 0) {
9664+
ret = timerid;
9665+
} else if (!arg2) {
9666+
ret = -TARGET_EFAULT;
96479667
} else {
96489668
timer_t htimer = g_posix_timers[timerid];
96499669
struct itimerspec hspec;
@@ -9661,10 +9681,10 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1,
96619681
case TARGET_NR_timer_getoverrun:
96629682
{
96639683
/* args: timer_t timerid */
9664-
target_ulong timerid = arg1;
9684+
target_timer_t timerid = get_timer_id(arg1);
96659685

9666-
if (timerid >= ARRAY_SIZE(g_posix_timers)) {
9667-
ret = -TARGET_EINVAL;
9686+
if (timerid < 0) {
9687+
ret = timerid;
96689688
} else {
96699689
timer_t htimer = g_posix_timers[timerid];
96709690
ret = get_errno(timer_getoverrun(htimer));
@@ -9677,10 +9697,10 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1,
96779697
case TARGET_NR_timer_delete:
96789698
{
96799699
/* args: timer_t timerid */
9680-
target_ulong timerid = arg1;
9700+
target_timer_t timerid = get_timer_id(arg1);
96819701

9682-
if (timerid >= ARRAY_SIZE(g_posix_timers)) {
9683-
ret = -TARGET_EINVAL;
9702+
if (timerid < 0) {
9703+
ret = timerid;
96849704
} else {
96859705
timer_t htimer = g_posix_timers[timerid];
96869706
ret = get_errno(timer_delete(htimer));

linux-user/syscall_defs.h

+1-4
Original file line numberDiff line numberDiff line change
@@ -2564,10 +2564,7 @@ struct target_ucred {
25642564

25652565
#endif
25662566

2567-
2568-
struct target_timer_t {
2569-
abi_ulong ptr;
2570-
};
2567+
typedef int32_t target_timer_t;
25712568

25722569
#define TARGET_SIGEV_MAX_SIZE 64
25732570

0 commit comments

Comments
 (0)