Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

selftests/x86/single_step_syscall: Check SYSENTER directly

We used to test SYSENTER only through the vDSO. Test it directly
too, just in case.

Signed-off-by: Andy Lutomirski <luto@kernel.org>
Cc: Borislav Petkov <bp@alien8.de>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Thomas Gleixner <tglx@linutronix.de>
Cc: Linus Torvalds <torvalds@linux-foundation.org>
Signed-off-by: Ingo Molnar <mingo@kernel.org>

authored by

Andy Lutomirski and committed by
Ingo Molnar
3300c4f3 9a62d200

+86 -10
+86 -10
tools/testing/selftests/x86/single_step_syscall.c
··· 43 43 err(1, "sigaction"); 44 44 } 45 45 46 - static volatile sig_atomic_t sig_traps; 46 + static void clearhandler(int sig) 47 + { 48 + struct sigaction sa; 49 + memset(&sa, 0, sizeof(sa)); 50 + sa.sa_handler = SIG_DFL; 51 + sigemptyset(&sa.sa_mask); 52 + if (sigaction(sig, &sa, 0)) 53 + err(1, "sigaction"); 54 + } 55 + 56 + static volatile sig_atomic_t sig_traps, sig_eflags; 57 + sigjmp_buf jmpbuf; 58 + static unsigned char altstack_data[SIGSTKSZ]; 47 59 48 60 #ifdef __x86_64__ 49 61 # define REG_IP REG_RIP ··· 102 90 } 103 91 } 104 92 93 + static char const * const signames[] = { 94 + [SIGSEGV] = "SIGSEGV", 95 + [SIGBUS] = "SIBGUS", 96 + [SIGTRAP] = "SIGTRAP", 97 + [SIGILL] = "SIGILL", 98 + }; 99 + 100 + static void print_and_longjmp(int sig, siginfo_t *si, void *ctx_void) 101 + { 102 + ucontext_t *ctx = ctx_void; 103 + 104 + printf("\tGot %s with RIP=%lx, TF=%ld\n", signames[sig], 105 + (unsigned long)ctx->uc_mcontext.gregs[REG_IP], 106 + (unsigned long)ctx->uc_mcontext.gregs[REG_EFL] & X86_EFLAGS_TF); 107 + 108 + sig_eflags = (unsigned long)ctx->uc_mcontext.gregs[REG_EFL]; 109 + siglongjmp(jmpbuf, 1); 110 + } 111 + 105 112 static void check_result(void) 106 113 { 107 114 unsigned long new_eflags = get_eflags(); ··· 138 107 139 108 printf("[OK]\tSurvived with TF set and %d traps\n", (int)sig_traps); 140 109 sig_traps = 0; 110 + } 111 + 112 + static void fast_syscall_no_tf(void) 113 + { 114 + sig_traps = 0; 115 + printf("[RUN]\tFast syscall with TF cleared\n"); 116 + fflush(stdout); /* Force a syscall */ 117 + if (get_eflags() & X86_EFLAGS_TF) { 118 + printf("[FAIL]\tTF is now set\n"); 119 + exit(1); 120 + } 121 + if (sig_traps) { 122 + printf("[FAIL]\tGot SIGTRAP\n"); 123 + exit(1); 124 + } 125 + printf("[OK]\tNothing unexpected happened\n"); 141 126 } 142 127 143 128 int main() ··· 210 163 check_result(); 211 164 212 165 /* Now make sure that another fast syscall doesn't set TF again. */ 213 - printf("[RUN]\tFast syscall with TF cleared\n"); 214 - fflush(stdout); /* Force a syscall */ 215 - if (get_eflags() & X86_EFLAGS_TF) { 216 - printf("[FAIL]\tTF is now set\n"); 166 + fast_syscall_no_tf(); 167 + 168 + /* 169 + * And do a forced SYSENTER to make sure that this works even if 170 + * fast syscalls don't use SYSENTER. 171 + * 172 + * Invoking SYSENTER directly breaks all the rules. Just handle 173 + * the SIGSEGV. 174 + */ 175 + if (sigsetjmp(jmpbuf, 1) == 0) { 176 + unsigned long nr = SYS_getpid; 177 + printf("[RUN]\tSet TF and check SYSENTER\n"); 178 + stack_t stack = { 179 + .ss_sp = altstack_data, 180 + .ss_size = SIGSTKSZ, 181 + }; 182 + if (sigaltstack(&stack, NULL) != 0) 183 + err(1, "sigaltstack"); 184 + sethandler(SIGSEGV, print_and_longjmp, 185 + SA_RESETHAND | SA_ONSTACK); 186 + sethandler(SIGILL, print_and_longjmp, SA_RESETHAND); 187 + set_eflags(get_eflags() | X86_EFLAGS_TF); 188 + /* Clear EBP first to make sure we segfault cleanly. */ 189 + asm volatile ("xorl %%ebp, %%ebp; SYSENTER" : "+a" (nr) :: "flags", "rcx" 190 + #ifdef __x86_64__ 191 + , "r11" 192 + #endif 193 + ); 194 + 195 + /* We're unreachable here. SYSENTER forgets RIP. */ 196 + } 197 + clearhandler(SIGSEGV); 198 + clearhandler(SIGILL); 199 + if (!(sig_eflags & X86_EFLAGS_TF)) { 200 + printf("[FAIL]\tTF was cleared\n"); 217 201 exit(1); 218 202 } 219 - if (sig_traps) { 220 - printf("[FAIL]\tGot SIGTRAP\n"); 221 - exit(1); 222 - } 223 - printf("[OK]\tNothing unexpected happened\n"); 203 + 204 + /* Now make sure that another fast syscall doesn't set TF again. */ 205 + fast_syscall_no_tf(); 224 206 225 207 return 0; 226 208 }