diff options
Diffstat (limited to 'arch/x86/entry/common.c')
| -rw-r--r-- | arch/x86/entry/common.c | 31 | 
1 files changed, 21 insertions, 10 deletions
| diff --git a/arch/x86/entry/common.c b/arch/x86/entry/common.c index 48512c7944e7..870efeec8bda 100644 --- a/arch/x86/entry/common.c +++ b/arch/x86/entry/common.c @@ -60,16 +60,10 @@ __visible noinstr void do_syscall_64(unsigned long nr, struct pt_regs *regs)  #if defined(CONFIG_X86_32) || defined(CONFIG_IA32_EMULATION)  static __always_inline unsigned int syscall_32_enter(struct pt_regs *regs)  { -	unsigned int nr = (unsigned int)regs->orig_ax; -  	if (IS_ENABLED(CONFIG_IA32_EMULATION))  		current_thread_info()->status |= TS_COMPAT; -	/* -	 * Subtlety here: if ptrace pokes something larger than 2^32-1 into -	 * orig_ax, the unsigned int return value truncates it.  This may -	 * or may not be necessary, but it matches the old asm behavior. -	 */ -	return (unsigned int)syscall_enter_from_user_mode(regs, nr); + +	return (unsigned int)regs->orig_ax;  }  /* @@ -91,15 +85,29 @@ __visible noinstr void do_int80_syscall_32(struct pt_regs *regs)  {  	unsigned int nr = syscall_32_enter(regs); +	/* +	 * Subtlety here: if ptrace pokes something larger than 2^32-1 into +	 * orig_ax, the unsigned int return value truncates it.  This may +	 * or may not be necessary, but it matches the old asm behavior. +	 */ +	nr = (unsigned int)syscall_enter_from_user_mode(regs, nr); +  	do_syscall_32_irqs_on(regs, nr);  	syscall_exit_to_user_mode(regs);  }  static noinstr bool __do_fast_syscall_32(struct pt_regs *regs)  { -	unsigned int nr	= syscall_32_enter(regs); +	unsigned int nr = syscall_32_enter(regs);  	int res; +	/* +	 * This cannot use syscall_enter_from_user_mode() as it has to +	 * fetch EBP before invoking any of the syscall entry work +	 * functions. +	 */ +	syscall_enter_from_user_mode_prepare(regs); +  	instrumentation_begin();  	/* Fetch EBP from where the vDSO stashed it. */  	if (IS_ENABLED(CONFIG_X86_64)) { @@ -122,6 +130,9 @@ static noinstr bool __do_fast_syscall_32(struct pt_regs *regs)  		return false;  	} +	/* The case truncates any ptrace induced syscall nr > 2^32 -1 */ +	nr = (unsigned int)syscall_enter_from_user_mode_work(regs, nr); +  	/* Now this is just like a normal syscall. */  	do_syscall_32_irqs_on(regs, nr);  	syscall_exit_to_user_mode(regs); @@ -288,7 +299,7 @@ __visible noinstr void xen_pv_evtchn_do_upcall(struct pt_regs *regs)  	old_regs = set_irq_regs(regs);  	instrumentation_begin(); -	run_on_irqstack_cond(__xen_pv_evtchn_do_upcall, NULL, regs); +	run_on_irqstack_cond(__xen_pv_evtchn_do_upcall, regs);  	instrumentation_begin();  	set_irq_regs(old_regs); |