um: Drop support for hosts without SYSEMU_SINGLESTEP support
These features have existed since Linux 2.6.14 and can be considered widely available at this point. Also drop the backward compatibility code for PTRACE_SETOPTIONS. Signed-off-by: Benjamin Berg <benjamin@sipsolutions.net> ---- v2: * Continue to define PTRACE_SYSEMU_SINGLESTEP as glibc only added it in version 2.27. Signed-off-by: Richard Weinberger <richard@nod.at>
This commit is contained in:
parent
a8e75902f4
commit
a55719847d
@ -22,7 +22,6 @@ struct mm_struct;
|
||||
struct thread_struct {
|
||||
struct pt_regs regs;
|
||||
struct pt_regs *segv_regs;
|
||||
int singlestep_syscall;
|
||||
void *fault_addr;
|
||||
jmp_buf *fault_catcher;
|
||||
struct task_struct *prev_sched;
|
||||
|
@ -34,7 +34,6 @@ extern int handle_page_fault(unsigned long address, unsigned long ip,
|
||||
|
||||
extern unsigned int do_IRQ(int irq, struct uml_pt_regs *regs);
|
||||
extern void initial_thread_cb(void (*proc)(void *), void *arg);
|
||||
extern int is_syscall(unsigned long addr);
|
||||
|
||||
extern void timer_handler(int sig, struct siginfo *unused_si, struct uml_pt_regs *regs);
|
||||
|
||||
@ -58,7 +57,7 @@ extern char *uml_strdup(const char *string);
|
||||
extern unsigned long to_irq_stack(unsigned long *mask_out);
|
||||
extern unsigned long from_irq_stack(int nested);
|
||||
|
||||
extern int singlestepping(void *t);
|
||||
extern int singlestepping(void);
|
||||
|
||||
extern void segv_handler(int sig, struct siginfo *unused_si, struct uml_pt_regs *regs);
|
||||
extern void bus_handler(int sig, struct siginfo *si, struct uml_pt_regs *regs);
|
||||
|
@ -12,45 +12,4 @@
|
||||
extern int ptrace_getregs(long pid, unsigned long *regs_out);
|
||||
extern int ptrace_setregs(long pid, unsigned long *regs_in);
|
||||
|
||||
/* syscall emulation path in ptrace */
|
||||
|
||||
#ifndef PTRACE_SYSEMU
|
||||
#define PTRACE_SYSEMU 31
|
||||
#endif
|
||||
#ifndef PTRACE_SYSEMU_SINGLESTEP
|
||||
#define PTRACE_SYSEMU_SINGLESTEP 32
|
||||
#endif
|
||||
|
||||
/* On architectures, that started to support PTRACE_O_TRACESYSGOOD
|
||||
* in linux 2.4, there are two different definitions of
|
||||
* PTRACE_SETOPTIONS: linux 2.4 uses 21 while linux 2.6 uses 0x4200.
|
||||
* For binary compatibility, 2.6 also supports the old "21", named
|
||||
* PTRACE_OLDSETOPTION. On these architectures, UML always must use
|
||||
* "21", to ensure the kernel runs on 2.4 and 2.6 host without
|
||||
* recompilation. So, we use PTRACE_OLDSETOPTIONS in UML.
|
||||
* We also want to be able to build the kernel on 2.4, which doesn't
|
||||
* have PTRACE_OLDSETOPTIONS. So, if it is missing, we declare
|
||||
* PTRACE_OLDSETOPTIONS to be the same as PTRACE_SETOPTIONS.
|
||||
*
|
||||
* On architectures, that start to support PTRACE_O_TRACESYSGOOD on
|
||||
* linux 2.6, PTRACE_OLDSETOPTIONS never is defined, and also isn't
|
||||
* supported by the host kernel. In that case, our trick lets us use
|
||||
* the new 0x4200 with the name PTRACE_OLDSETOPTIONS.
|
||||
*/
|
||||
#ifndef PTRACE_OLDSETOPTIONS
|
||||
#define PTRACE_OLDSETOPTIONS PTRACE_SETOPTIONS
|
||||
#endif
|
||||
|
||||
void set_using_sysemu(int value);
|
||||
int get_using_sysemu(void);
|
||||
extern int sysemu_supported;
|
||||
|
||||
#define SELECT_PTRACE_OPERATION(sysemu_mode, singlestep_mode) \
|
||||
(((int[3][3] ) { \
|
||||
{ PTRACE_SYSCALL, PTRACE_SYSCALL, PTRACE_SINGLESTEP }, \
|
||||
{ PTRACE_SYSEMU, PTRACE_SYSEMU, PTRACE_SINGLESTEP }, \
|
||||
{ PTRACE_SYSEMU, PTRACE_SYSEMU_SINGLESTEP, \
|
||||
PTRACE_SYSEMU_SINGLESTEP } }) \
|
||||
[sysemu_mode][singlestep_mode])
|
||||
|
||||
#endif
|
||||
|
@ -332,17 +332,9 @@ int __init make_proc_sysemu(void)
|
||||
|
||||
late_initcall(make_proc_sysemu);
|
||||
|
||||
int singlestepping(void * t)
|
||||
int singlestepping(void)
|
||||
{
|
||||
struct task_struct *task = t ? t : current;
|
||||
|
||||
if (!test_thread_flag(TIF_SINGLESTEP))
|
||||
return 0;
|
||||
|
||||
if (task->thread.singlestep_syscall)
|
||||
return 1;
|
||||
|
||||
return 2;
|
||||
return test_thread_flag(TIF_SINGLESTEP);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -12,7 +12,6 @@
|
||||
void user_enable_single_step(struct task_struct *child)
|
||||
{
|
||||
set_tsk_thread_flag(child, TIF_SINGLESTEP);
|
||||
child->thread.singlestep_syscall = 0;
|
||||
|
||||
#ifdef SUBARCH_SET_SINGLESTEPPING
|
||||
SUBARCH_SET_SINGLESTEPPING(child, 1);
|
||||
@ -22,7 +21,6 @@ void user_enable_single_step(struct task_struct *child)
|
||||
void user_disable_single_step(struct task_struct *child)
|
||||
{
|
||||
clear_tsk_thread_flag(child, TIF_SINGLESTEP);
|
||||
child->thread.singlestep_syscall = 0;
|
||||
|
||||
#ifdef SUBARCH_SET_SINGLESTEPPING
|
||||
SUBARCH_SET_SINGLESTEPPING(child, 0);
|
||||
|
@ -120,18 +120,6 @@ void do_signal(struct pt_regs *regs)
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* This closes a way to execute a system call on the host. If
|
||||
* you set a breakpoint on a system call instruction and singlestep
|
||||
* from it, the tracing thread used to PTRACE_SINGLESTEP the process
|
||||
* rather than PTRACE_SYSCALL it, allowing the system call to execute
|
||||
* on the host. The tracing thread will check this flag and
|
||||
* PTRACE_SYSCALL if necessary.
|
||||
*/
|
||||
if (test_thread_flag(TIF_SINGLESTEP))
|
||||
current->thread.singlestep_syscall =
|
||||
is_syscall(PT_REGS_IP(¤t->thread.regs));
|
||||
|
||||
/*
|
||||
* if there's no signal to deliver, we just put the saved sigmask
|
||||
* back
|
||||
|
@ -177,48 +177,11 @@ static void handle_segv(int pid, struct uml_pt_regs *regs, unsigned long *aux_fp
|
||||
segv(regs->faultinfo, 0, 1, NULL);
|
||||
}
|
||||
|
||||
/*
|
||||
* To use the same value of using_sysemu as the caller, ask it that value
|
||||
* (in local_using_sysemu
|
||||
*/
|
||||
static void handle_trap(int pid, struct uml_pt_regs *regs,
|
||||
int local_using_sysemu)
|
||||
static void handle_trap(int pid, struct uml_pt_regs *regs)
|
||||
{
|
||||
int err, status;
|
||||
|
||||
if ((UPT_IP(regs) >= STUB_START) && (UPT_IP(regs) < STUB_END))
|
||||
fatal_sigsegv();
|
||||
|
||||
if (!local_using_sysemu)
|
||||
{
|
||||
err = ptrace(PTRACE_POKEUSER, pid, PT_SYSCALL_NR_OFFSET,
|
||||
__NR_getpid);
|
||||
if (err < 0) {
|
||||
printk(UM_KERN_ERR "%s - nullifying syscall failed, errno = %d\n",
|
||||
__func__, errno);
|
||||
fatal_sigsegv();
|
||||
}
|
||||
|
||||
err = ptrace(PTRACE_SYSCALL, pid, 0, 0);
|
||||
if (err < 0) {
|
||||
printk(UM_KERN_ERR "%s - continuing to end of syscall failed, errno = %d\n",
|
||||
__func__, errno);
|
||||
fatal_sigsegv();
|
||||
}
|
||||
|
||||
CATCH_EINTR(err = waitpid(pid, &status, WUNTRACED | __WALL));
|
||||
if ((err < 0) || !WIFSTOPPED(status) ||
|
||||
(WSTOPSIG(status) != SIGTRAP + 0x80)) {
|
||||
err = ptrace_dump_regs(pid);
|
||||
if (err)
|
||||
printk(UM_KERN_ERR "Failed to get registers from process, errno = %d\n",
|
||||
-err);
|
||||
printk(UM_KERN_ERR "%s - failed to wait at end of syscall, errno = %d, status = %d\n",
|
||||
__func__, errno, status);
|
||||
fatal_sigsegv();
|
||||
}
|
||||
}
|
||||
|
||||
handle_syscall(regs);
|
||||
}
|
||||
|
||||
@ -355,10 +318,10 @@ int start_userspace(unsigned long stub_stack)
|
||||
goto out_kill;
|
||||
}
|
||||
|
||||
if (ptrace(PTRACE_OLDSETOPTIONS, pid, NULL,
|
||||
if (ptrace(PTRACE_SETOPTIONS, pid, NULL,
|
||||
(void *) PTRACE_O_TRACESYSGOOD) < 0) {
|
||||
err = -errno;
|
||||
printk(UM_KERN_ERR "%s : PTRACE_OLDSETOPTIONS failed, errno = %d\n",
|
||||
printk(UM_KERN_ERR "%s : PTRACE_SETOPTIONS failed, errno = %d\n",
|
||||
__func__, errno);
|
||||
goto out_kill;
|
||||
}
|
||||
@ -380,8 +343,6 @@ int start_userspace(unsigned long stub_stack)
|
||||
void userspace(struct uml_pt_regs *regs, unsigned long *aux_fp_regs)
|
||||
{
|
||||
int err, status, op, pid = userspace_pid[0];
|
||||
/* To prevent races if using_sysemu changes under us.*/
|
||||
int local_using_sysemu;
|
||||
siginfo_t si;
|
||||
|
||||
/* Handle any immediate reschedules or signals */
|
||||
@ -411,11 +372,10 @@ void userspace(struct uml_pt_regs *regs, unsigned long *aux_fp_regs)
|
||||
fatal_sigsegv();
|
||||
}
|
||||
|
||||
/* Now we set local_using_sysemu to be used for one loop */
|
||||
local_using_sysemu = get_using_sysemu();
|
||||
|
||||
op = SELECT_PTRACE_OPERATION(local_using_sysemu,
|
||||
singlestepping(NULL));
|
||||
if (singlestepping())
|
||||
op = PTRACE_SYSEMU_SINGLESTEP;
|
||||
else
|
||||
op = PTRACE_SYSEMU;
|
||||
|
||||
if (ptrace(op, pid, 0, 0)) {
|
||||
printk(UM_KERN_ERR "%s - ptrace continue failed, op = %d, errno = %d\n",
|
||||
@ -474,7 +434,7 @@ void userspace(struct uml_pt_regs *regs, unsigned long *aux_fp_regs)
|
||||
else handle_segv(pid, regs, aux_fp_regs);
|
||||
break;
|
||||
case SIGTRAP + 0x80:
|
||||
handle_trap(pid, regs, local_using_sysemu);
|
||||
handle_trap(pid, regs);
|
||||
break;
|
||||
case SIGTRAP:
|
||||
relay_signal(SIGTRAP, (struct siginfo *)&si, regs);
|
||||
@ -597,10 +557,10 @@ int copy_context_skas0(unsigned long new_stack, int pid)
|
||||
goto out_kill;
|
||||
}
|
||||
|
||||
if (ptrace(PTRACE_OLDSETOPTIONS, pid, NULL,
|
||||
if (ptrace(PTRACE_SETOPTIONS, pid, NULL,
|
||||
(void *)PTRACE_O_TRACESYSGOOD) < 0) {
|
||||
err = -errno;
|
||||
printk(UM_KERN_ERR "%s : PTRACE_OLDSETOPTIONS failed, errno = %d\n",
|
||||
printk(UM_KERN_ERR "%s : PTRACE_SETOPTIONS failed, errno = %d\n",
|
||||
__func__, errno);
|
||||
goto out_kill;
|
||||
}
|
||||
|
@ -143,71 +143,16 @@ static int stop_ptraced_child(int pid, int exitcode, int mustexit)
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Changed only during early boot */
|
||||
static int force_sysemu_disabled = 0;
|
||||
|
||||
static int __init nosysemu_cmd_param(char *str, int* add)
|
||||
{
|
||||
force_sysemu_disabled = 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
__uml_setup("nosysemu", nosysemu_cmd_param,
|
||||
"nosysemu\n"
|
||||
" Turns off syscall emulation patch for ptrace (SYSEMU).\n"
|
||||
" SYSEMU is a performance-patch introduced by Laurent Vivier. It changes\n"
|
||||
" behaviour of ptrace() and helps reduce host context switch rates.\n"
|
||||
" To make it work, you need a kernel patch for your host, too.\n"
|
||||
" See http://perso.wanadoo.fr/laurent.vivier/UML/ for further \n"
|
||||
" information.\n\n");
|
||||
|
||||
static void __init check_sysemu(void)
|
||||
{
|
||||
unsigned long regs[MAX_REG_NR];
|
||||
int pid, n, status, count=0;
|
||||
|
||||
os_info("Checking syscall emulation patch for ptrace...");
|
||||
sysemu_supported = 0;
|
||||
os_info("Checking syscall emulation for ptrace...");
|
||||
pid = start_ptraced_child();
|
||||
|
||||
if (ptrace(PTRACE_SYSEMU, pid, 0, 0) < 0)
|
||||
goto fail;
|
||||
|
||||
CATCH_EINTR(n = waitpid(pid, &status, WUNTRACED));
|
||||
if (n < 0)
|
||||
fatal_perror("check_sysemu : wait failed");
|
||||
if (!WIFSTOPPED(status) || (WSTOPSIG(status) != SIGTRAP))
|
||||
fatal("check_sysemu : expected SIGTRAP, got status = %d\n",
|
||||
status);
|
||||
|
||||
if (ptrace(PTRACE_GETREGS, pid, 0, regs) < 0)
|
||||
fatal_perror("check_sysemu : PTRACE_GETREGS failed");
|
||||
if (PT_SYSCALL_NR(regs) != __NR_getpid) {
|
||||
non_fatal("check_sysemu got system call number %d, "
|
||||
"expected %d...", PT_SYSCALL_NR(regs), __NR_getpid);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
n = ptrace(PTRACE_POKEUSER, pid, PT_SYSCALL_RET_OFFSET, os_getpid());
|
||||
if (n < 0) {
|
||||
non_fatal("check_sysemu : failed to modify system call "
|
||||
"return");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (stop_ptraced_child(pid, 0, 0) < 0)
|
||||
goto fail_stopped;
|
||||
|
||||
sysemu_supported = 1;
|
||||
os_info("OK\n");
|
||||
set_using_sysemu(!force_sysemu_disabled);
|
||||
|
||||
os_info("Checking advanced syscall emulation patch for ptrace...");
|
||||
pid = start_ptraced_child();
|
||||
|
||||
if ((ptrace(PTRACE_OLDSETOPTIONS, pid, 0,
|
||||
if ((ptrace(PTRACE_SETOPTIONS, pid, 0,
|
||||
(void *) PTRACE_O_TRACESYSGOOD) < 0))
|
||||
fatal_perror("check_sysemu: PTRACE_OLDSETOPTIONS failed");
|
||||
fatal_perror("check_sysemu: PTRACE_SETOPTIONS failed");
|
||||
|
||||
while (1) {
|
||||
count++;
|
||||
@ -243,17 +188,14 @@ static void __init check_sysemu(void)
|
||||
if (stop_ptraced_child(pid, 0, 0) < 0)
|
||||
goto fail_stopped;
|
||||
|
||||
sysemu_supported = 2;
|
||||
os_info("OK\n");
|
||||
|
||||
if (!force_sysemu_disabled)
|
||||
set_using_sysemu(sysemu_supported);
|
||||
return;
|
||||
|
||||
fail:
|
||||
stop_ptraced_child(pid, 1, 0);
|
||||
fail_stopped:
|
||||
non_fatal("missing\n");
|
||||
fatal("missing\n");
|
||||
}
|
||||
|
||||
static void __init check_ptrace(void)
|
||||
@ -263,9 +205,9 @@ static void __init check_ptrace(void)
|
||||
os_info("Checking that ptrace can change system call numbers...");
|
||||
pid = start_ptraced_child();
|
||||
|
||||
if ((ptrace(PTRACE_OLDSETOPTIONS, pid, 0,
|
||||
if ((ptrace(PTRACE_SETOPTIONS, pid, 0,
|
||||
(void *) PTRACE_O_TRACESYSGOOD) < 0))
|
||||
fatal_perror("check_ptrace: PTRACE_OLDSETOPTIONS failed");
|
||||
fatal_perror("check_ptrace: PTRACE_SETOPTIONS failed");
|
||||
|
||||
while (1) {
|
||||
if (ptrace(PTRACE_SYSCALL, pid, 0, 0) < 0)
|
||||
|
@ -25,30 +25,6 @@ void arch_switch_to(struct task_struct *to)
|
||||
printk(KERN_WARNING "arch_switch_tls failed, errno = EINVAL\n");
|
||||
}
|
||||
|
||||
int is_syscall(unsigned long addr)
|
||||
{
|
||||
unsigned short instr;
|
||||
int n;
|
||||
|
||||
n = copy_from_user(&instr, (void __user *) addr, sizeof(instr));
|
||||
if (n) {
|
||||
/* access_process_vm() grants access to vsyscall and stub,
|
||||
* while copy_from_user doesn't. Maybe access_process_vm is
|
||||
* slow, but that doesn't matter, since it will be called only
|
||||
* in case of singlestepping, if copy_from_user failed.
|
||||
*/
|
||||
n = access_process_vm(current, addr, &instr, sizeof(instr),
|
||||
FOLL_FORCE);
|
||||
if (n != sizeof(instr)) {
|
||||
printk(KERN_ERR "is_syscall : failed to read "
|
||||
"instruction from 0x%lx\n", addr);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
/* int 0x80 or sysenter */
|
||||
return (instr == 0x80cd) || (instr == 0x340f);
|
||||
}
|
||||
|
||||
/* determines which flags the user has access to. */
|
||||
/* 1 = access 0 = no access */
|
||||
#define FLAG_MASK 0x00044dd5
|
||||
|
@ -188,32 +188,6 @@ int peek_user(struct task_struct *child, long addr, long data)
|
||||
return put_user(tmp, (unsigned long *) data);
|
||||
}
|
||||
|
||||
/* XXX Mostly copied from sys-i386 */
|
||||
int is_syscall(unsigned long addr)
|
||||
{
|
||||
unsigned short instr;
|
||||
int n;
|
||||
|
||||
n = copy_from_user(&instr, (void __user *) addr, sizeof(instr));
|
||||
if (n) {
|
||||
/*
|
||||
* access_process_vm() grants access to vsyscall and stub,
|
||||
* while copy_from_user doesn't. Maybe access_process_vm is
|
||||
* slow, but that doesn't matter, since it will be called only
|
||||
* in case of singlestepping, if copy_from_user failed.
|
||||
*/
|
||||
n = access_process_vm(current, addr, &instr, sizeof(instr),
|
||||
FOLL_FORCE);
|
||||
if (n != sizeof(instr)) {
|
||||
printk("is_syscall : failed to read instruction from "
|
||||
"0x%lx\n", addr);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
/* sysenter */
|
||||
return instr == 0x050f;
|
||||
}
|
||||
|
||||
static int get_fpregs(struct user_i387_struct __user *buf, struct task_struct *child)
|
||||
{
|
||||
int err, n, cpu = ((struct thread_info *) child->stack)->cpu;
|
||||
|
@ -8,10 +8,6 @@
|
||||
|
||||
#define MAX_FP_NR HOST_FPX_SIZE
|
||||
|
||||
void set_using_sysemu(int value);
|
||||
int get_using_sysemu(void);
|
||||
extern int sysemu_supported;
|
||||
|
||||
#define UPT_SYSCALL_ARG1(r) UPT_BX(r)
|
||||
#define UPT_SYSCALL_ARG2(r) UPT_CX(r)
|
||||
#define UPT_SYSCALL_ARG3(r) UPT_DX(r)
|
||||
|
@ -15,14 +15,12 @@
|
||||
#define FP_SIZE ((HOST_FPX_SIZE > HOST_FP_SIZE) ? HOST_FPX_SIZE : HOST_FP_SIZE)
|
||||
#else
|
||||
#define FP_SIZE HOST_FP_SIZE
|
||||
#endif
|
||||
|
||||
/*
|
||||
* x86_64 FC3 doesn't define this in /usr/include/linux/ptrace.h even though
|
||||
* it's defined in the kernel's include/linux/ptrace.h. Additionally, use the
|
||||
* 2.4 name and value for 2.4 host compatibility.
|
||||
* glibc before 2.27 does not include PTRACE_SYSEMU_SINGLESTEP in its enum,
|
||||
* ensure we have a definition by (re-)defining it here.
|
||||
*/
|
||||
#ifndef PTRACE_OLDSETOPTIONS
|
||||
#define PTRACE_OLDSETOPTIONS 21
|
||||
#endif
|
||||
|
||||
#ifndef PTRACE_SYSEMU_SINGLESTEP
|
||||
#define PTRACE_SYSEMU_SINGLESTEP 32
|
||||
#endif
|
||||
|
Loading…
Reference in New Issue
Block a user