@@ -230,6 +230,8 @@ void init_task_state(TaskState *ts)
ts->start_boottime += bt.tv_nsec * (uint64_t) ticks_per_sec /
NANOSECONDS_PER_SECOND;
}
+
+ ts->sys_dispatch_len = (abi_ulong)~0ULL;
}
CPUArchState *cpu_copy(CPUArchState *env)
@@ -161,6 +161,11 @@ struct TaskState {
/* This thread's sigaltstack, if it has one */
struct target_sigaltstack sigaltstack_used;
+ /* This thread's SYSCALL_USER_DISPATCH state, len=~0 means disabled */
+ abi_ulong sys_dispatch;
+ abi_ulong sys_dispatch_len;
+ abi_ulong sys_dispatch_selector;
+
/* Start time of task after system boot in clock ticks */
uint64_t start_boottime;
};
@@ -6317,6 +6317,10 @@ abi_long do_arch_prctl(CPUX86State *env, int code, abi_ulong addr)
#endif
#ifndef PR_SET_SYSCALL_USER_DISPATCH
# define PR_SET_SYSCALL_USER_DISPATCH 59
+# define PR_SYS_DISPATCH_OFF 0
+# define PR_SYS_DISPATCH_ON 1
+# define SYSCALL_DISPATCH_FILTER_ALLOW 0
+# define SYSCALL_DISPATCH_FILTER_BLOCK 1
#endif
#ifndef PR_SME_SET_VL
# define PR_SME_SET_VL 63
@@ -6446,6 +6450,37 @@ static abi_long do_prctl(CPUArchState *env, abi_long option, abi_long arg2,
case PR_SET_UNALIGN:
return do_prctl_set_unalign(env, arg2);
+ case PR_SET_SYSCALL_USER_DISPATCH:
+ {
+ TaskState *ts = get_task_state(env_cpu(env));
+ switch (arg2) {
+ case PR_SYS_DISPATCH_OFF:
+ if (arg3 || arg4 || arg5) {
+ return -TARGET_EINVAL;
+ }
+ ts->sys_dispatch_len = (abi_ulong)~0ULL;
+ return 0;
+ case PR_SYS_DISPATCH_ON:
+ {
+ uint8_t sb;
+
+ if (arg3 && arg3 + arg4 <= arg3) {
+ return -TARGET_EINVAL;
+ }
+ if (arg5 && get_user_u8(sb, arg5)) {
+ return -TARGET_EFAULT;
+ }
+ (void)sb; /* used later, only checked for access */
+ ts->sys_dispatch = arg3;
+ ts->sys_dispatch_len = arg4;
+ ts->sys_dispatch_selector = arg5;
+ return 0;
+ }
+ default:
+ return -EINVAL;
+ }
+ }
+
case PR_CAP_AMBIENT:
case PR_CAPBSET_READ:
case PR_CAPBSET_DROP:
@@ -6500,7 +6535,6 @@ static abi_long do_prctl(CPUArchState *env, abi_long option, abi_long arg2,
case PR_SET_MM:
case PR_GET_SECCOMP:
case PR_SET_SECCOMP:
- case PR_SET_SYSCALL_USER_DISPATCH:
case PR_GET_THP_DISABLE:
case PR_SET_THP_DISABLE:
case PR_GET_TSC:
@@ -13852,12 +13886,34 @@ static abi_long do_syscall1(CPUArchState *cpu_env, int num, abi_long arg1,
return ret;
}
+static bool sys_dispatch(abi_ulong pc, TaskState *ts)
+{
+ if (likely(ts->sys_dispatch_selector)) {
+ uint8_t sb;
+ if (get_user_u8(sb, ts->sys_dispatch_selector)) {
+ force_sig(SIGSEGV);
+ return true;
+ }
+ if (likely(sb == SYSCALL_DISPATCH_FILTER_ALLOW)) {
+ return false;
+ }
+ if (unlikely(sb != SYSCALL_DISPATCH_FILTER_BLOCK)) {
+ force_sig(SIGSYS);
+ return true;
+ }
+ }
+ force_sig_fault(TARGET_SIGSYS, TARGET_SYS_USER_DISPATCH, pc);
+ return true;
+}
+
abi_long do_syscall(CPUArchState *cpu_env, int num, abi_long arg1,
abi_long arg2, abi_long arg3, abi_long arg4,
abi_long arg5, abi_long arg6, abi_long arg7,
abi_long arg8)
{
CPUState *cpu = env_cpu(cpu_env);
+ TaskState *ts = get_task_state(cpu);
+ vaddr pc = cpu->cc->get_pc(cpu);
abi_long ret;
#ifdef DEBUG_ERESTARTSYS
@@ -13874,6 +13930,12 @@ abi_long do_syscall(CPUArchState *cpu_env, int num, abi_long arg1,
}
#endif
+ if (unlikely(pc - ts->sys_dispatch >= ts->sys_dispatch_len)) {
+ if (sys_dispatch(pc, ts)) {
+ return -QEMU_ESIGRETURN;
+ }
+ }
+
record_syscall_start(cpu, num, arg1,
arg2, arg3, arg4, arg5, arg6, arg7, arg8);
@@ -693,6 +693,12 @@ typedef struct target_siginfo {
#define TARGET_TRAP_HWBKPT (4) /* hardware breakpoint/watchpoint */
#define TARGET_TRAP_UNK (5) /* undiagnosed trap */
+/*
+ * SIGSYS si_codes
+ */
+#define TARGET_SYS_SECCOMP (1) /* seccomp triggered */
+#define TARGET_SYS_USER_DISPATCH (2) /* syscall user dispatch triggered */
+
/*
* SIGEMT si_codes
*/
This commit adds support for the `prctl(PR_SET_SYSCALL_DISPATCH)` function in the Linux userspace emulator. It is implemented as a fully host-independent function, by forcing a SIGSYS early during syscall handling, if the PC is outside the allowed range. Tested on [uglendix][1], will probably also apply to recent versions of Wine that use syscall dispatch. [1]: https://sr.ht/~arusekk/uglendix Signed-off-by: Arusekk <floss@arusekk.pl> --- linux-user/main.c | 2 ++ linux-user/qemu.h | 5 +++ linux-user/syscall.c | 64 ++++++++++++++++++++++++++++++++++++++- linux-user/syscall_defs.h | 6 ++++ 4 files changed, 76 insertions(+), 1 deletion(-)