diff --git a/arch/x86/entry/vsyscall/vsyscall_64.c b/arch/x86/entry/vsyscall/vsyscall_64.c index 398b1ed16f1e..e740f3b42278 100644 --- a/arch/x86/entry/vsyscall/vsyscall_64.c +++ b/arch/x86/entry/vsyscall/vsyscall_64.c @@ -23,7 +23,7 @@ * soon be no new userspace code that will ever use a vsyscall. * * The code in this file emulates vsyscalls when notified of a page - * fault to a vsyscall address. + * fault or a general protection fault to a vsyscall address. */ #include @@ -118,10 +118,9 @@ static bool __emulate_vsyscall(struct pt_regs *regs, unsigned long address) long ret; unsigned long orig_dx; - /* - * No point in checking CS -- the only way to get here is a user mode - * trap to a high address, which means that we're in 64-bit user code. - */ + /* Confirm that the fault happened in 64-bit user mode */ + if (!user_64bit_mode(regs)) + return false; if (vsyscall_mode == NONE) { warn_bad_vsyscall(KERN_INFO, regs, @@ -284,6 +283,19 @@ bool emulate_vsyscall_pf(unsigned long error_code, struct pt_regs *regs, return __emulate_vsyscall(regs, address); } +bool emulate_vsyscall_gp(struct pt_regs *regs) +{ + /* Without LASS, vsyscall accesses are expected to generate a #PF */ + if (!cpu_feature_enabled(X86_FEATURE_LASS)) + return false; + + /* Emulate only if the RIP points to the vsyscall address */ + if (!is_vsyscall_vaddr(regs->ip)) + return false; + + return __emulate_vsyscall(regs, regs->ip); +} + /* * A pseudo VMA to allow ptrace access for the vsyscall page. This only * covers the 64bit vsyscall page now. 32bit has a real VMA now and does diff --git a/arch/x86/include/asm/vsyscall.h b/arch/x86/include/asm/vsyscall.h index f34902364972..538053b1656a 100644 --- a/arch/x86/include/asm/vsyscall.h +++ b/arch/x86/include/asm/vsyscall.h @@ -15,6 +15,7 @@ extern void set_vsyscall_pgtable_user_bits(pgd_t *root); * Returns true if handled. */ bool emulate_vsyscall_pf(unsigned long error_code, struct pt_regs *regs, unsigned long address); +bool emulate_vsyscall_gp(struct pt_regs *regs); #else static inline void map_vsyscall(void) {} static inline bool emulate_vsyscall_pf(unsigned long error_code, @@ -22,6 +23,11 @@ static inline bool emulate_vsyscall_pf(unsigned long error_code, { return false; } + +static inline bool emulate_vsyscall_gp(struct pt_regs *regs) +{ + return false; +} #endif /* diff --git a/arch/x86/kernel/traps.c b/arch/x86/kernel/traps.c index 614a281bd419..0ca3912ecb7f 100644 --- a/arch/x86/kernel/traps.c +++ b/arch/x86/kernel/traps.c @@ -70,6 +70,7 @@ #include #include #include +#include #ifdef CONFIG_X86_64 #include @@ -938,6 +939,9 @@ DEFINE_IDTENTRY_ERRORCODE(exc_general_protection) if (fixup_umip_exception(regs)) goto exit; + if (emulate_vsyscall_gp(regs)) + goto exit; + gp_user_force_sig_segv(regs, X86_TRAP_GP, error_code, desc); goto exit; }