-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathdo_syscall_64.c
114 lines (93 loc) · 2.56 KB
/
do_syscall_64.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
// SPDX-License-Identifier: GPL-2.0
//#define DEBUG 1
#include <linux/kernel.h>
#include <linux/ptrace.h>
#include <asm/fsgsbase.h>
#include <asm/prctl.h>
#include <kern_util.h>
#include <sysdep/syscalls.h>
#include <os.h>
#ifndef CONFIG_MMU
static int os_x86_arch_prctl(int pid, int option, unsigned long *arg2)
{
if (os_has_fsgsbase()) {
switch (option) {
case ARCH_SET_FS:
wrfsbase(*arg2);
break;
case ARCH_SET_GS:
wrgsbase(*arg2);
break;
case ARCH_GET_FS:
*arg2 = rdfsbase();
break;
case ARCH_GET_GS:
*arg2 = rdgsbase();
break;
}
return 0;
}
else
return os_arch_prctl(pid, option, arg2);
return 0;
}
/*
* save/restore the return address stored in the stack, as the child overwrites
* the contents after returning to userspace (i.e., by push %rdx).
*
* see the detail in fork_handler().
*/
static void *vfork_save_stack(void)
{
unsigned char *stack_copy;
stack_copy = kzalloc(PAGE_SIZE << THREAD_SIZE_ORDER,
GFP_KERNEL);
if (!stack_copy)
return NULL;
memcpy(stack_copy,
(void *)current->thread.regs.regs.gp[HOST_SP], 8);
return stack_copy;
}
static void vfork_restore_stack(void *stack_copy)
{
WARN_ON_ONCE(!stack_copy);
memcpy((void *)current->thread.regs.regs.gp[HOST_SP],
stack_copy, 8);
}
__visible void do_syscall_64(struct pt_regs *regs)
{
int syscall;
unsigned char *stack_copy = NULL;
syscall = PT_SYSCALL_NR(regs->regs.gp);
UPT_SYSCALL_NR(®s->regs) = syscall;
pr_debug("syscall(%d) (current=%lx) (fn=%lx)\n",
syscall, (unsigned long)current,
(unsigned long)sys_call_table[syscall]);
if (syscall == __NR_vfork)
stack_copy = vfork_save_stack();
/* set fs register to the original host one */
os_x86_arch_prctl(0, ARCH_SET_FS, (void *)host_fs);
if (likely(syscall < NR_syscalls)) {
PT_REGS_SET_SYSCALL_RETURN(regs,
EXECUTE_SYSCALL(syscall, regs));
}
pr_debug("syscall(%d) --> %lx\n", syscall,
regs->regs.gp[HOST_AX]);
PT_REGS_SYSCALL_RET(regs) = regs->regs.gp[HOST_AX];
/* restore back fs register to userspace configured one */
os_x86_arch_prctl(0, ARCH_SET_FS,
(void *)(current->thread.regs.regs.gp[FS_BASE
/ sizeof(unsigned long)]));
/* force do_signal() --> is_syscall() */
set_thread_flag(TIF_SIGPENDING);
interrupt_end();
/* execve succeeded */
if (syscall == __NR_execve && regs->regs.gp[HOST_AX] == 0) {
userspace(¤t->thread.regs.regs,
current_thread_info()->aux_fp_regs);
}
/* only parents of vfork restores the contents of stack */
if (syscall == __NR_vfork && regs->regs.gp[HOST_AX] > 0)
vfork_restore_stack(stack_copy);
}
#endif