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

selftests/x86: Test SYSCALL and SYSENTER manually with TF set

Make sure that both variants of the nasty TF-in-compat-syscall are
exercised regardless of what vendor's CPU is running the tests.

Also change the intentional signal after SYSCALL to use ud2, which
is a lot more comprehensible.

This crashes the kernel due to an FSGSBASE bug right now.

This test *also* detects a bug in KVM when run on an Intel host. KVM
people, feel free to use it to help debug. There's a bunch of code in this
test to warn instead of going into an infinite looping when the bug gets
triggered.

Reported-by: Vegard Nossum <vegard.nossum@oracle.com>
Signed-off-by: Andy Lutomirski <luto@kernel.org>
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Cc: "BaeChang Seok" <chang.seok.bae@intel.com>
Cc: Borislav Petkov <bp@alien8.de>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Paolo Bonzini <pbonzini@redhat.com>
Cc: kvm@vger.kernel.org
Cc: "Bae, Chang Seok" <chang.seok.bae@intel.com>
Link: https://lkml.kernel.org/r/5f5de10441ab2e3005538b4c33be9b1965d1bb63.1562035429.git.luto@kernel.org

authored by

Andy Lutomirski and committed by
Thomas Gleixner
9402eaf4 fd329f27

+110 -7
+3 -2
tools/testing/selftests/x86/Makefile
··· 12 12 13 13 TARGETS_C_BOTHBITS := single_step_syscall sysret_ss_attrs syscall_nt test_mremap_vdso \ 14 14 check_initial_reg_state sigreturn iopl mpx-mini-test ioperm \ 15 - protection_keys test_vdso test_vsyscall mov_ss_trap 16 - TARGETS_C_32BIT_ONLY := entry_from_vm86 syscall_arg_fault test_syscall_vdso unwind_vdso \ 15 + protection_keys test_vdso test_vsyscall mov_ss_trap \ 16 + syscall_arg_fault 17 + TARGETS_C_32BIT_ONLY := entry_from_vm86 test_syscall_vdso unwind_vdso \ 17 18 test_FCMOV test_FCOMI test_FISTTP \ 18 19 vdso_restorer 19 20 TARGETS_C_64BIT_ONLY := fsgsbase sysret_rip
+107 -5
tools/testing/selftests/x86/syscall_arg_fault.c
··· 15 15 #include <setjmp.h> 16 16 #include <errno.h> 17 17 18 + #ifdef __x86_64__ 19 + # define WIDTH "q" 20 + #else 21 + # define WIDTH "l" 22 + #endif 23 + 18 24 /* Our sigaltstack scratch space. */ 19 25 static unsigned char altstack_data[SIGSTKSZ]; 26 + 27 + static unsigned long get_eflags(void) 28 + { 29 + unsigned long eflags; 30 + asm volatile ("pushf" WIDTH "\n\tpop" WIDTH " %0" : "=rm" (eflags)); 31 + return eflags; 32 + } 33 + 34 + static void set_eflags(unsigned long eflags) 35 + { 36 + asm volatile ("push" WIDTH " %0\n\tpopf" WIDTH 37 + : : "rm" (eflags) : "flags"); 38 + } 39 + 40 + #define X86_EFLAGS_TF (1UL << 8) 20 41 21 42 static void sethandler(int sig, void (*handler)(int, siginfo_t *, void *), 22 43 int flags) ··· 56 35 57 36 static volatile sig_atomic_t n_errs; 58 37 38 + #ifdef __x86_64__ 39 + #define REG_AX REG_RAX 40 + #define REG_IP REG_RIP 41 + #else 42 + #define REG_AX REG_EAX 43 + #define REG_IP REG_EIP 44 + #endif 45 + 59 46 static void sigsegv_or_sigbus(int sig, siginfo_t *info, void *ctx_void) 60 47 { 61 48 ucontext_t *ctx = (ucontext_t*)ctx_void; 49 + long ax = (long)ctx->uc_mcontext.gregs[REG_AX]; 62 50 63 - if (ctx->uc_mcontext.gregs[REG_EAX] != -EFAULT) { 64 - printf("[FAIL]\tAX had the wrong value: 0x%x\n", 65 - ctx->uc_mcontext.gregs[REG_EAX]); 51 + if (ax != -EFAULT && ax != -ENOSYS) { 52 + printf("[FAIL]\tAX had the wrong value: 0x%lx\n", 53 + (unsigned long)ax); 66 54 n_errs++; 67 55 } else { 68 56 printf("[OK]\tSeems okay\n"); ··· 80 50 siglongjmp(jmpbuf, 1); 81 51 } 82 52 53 + static volatile sig_atomic_t sigtrap_consecutive_syscalls; 54 + 55 + static void sigtrap(int sig, siginfo_t *info, void *ctx_void) 56 + { 57 + /* 58 + * KVM has some bugs that can cause us to stop making progress. 59 + * detect them and complain, but don't infinite loop or fail the 60 + * test. 61 + */ 62 + 63 + ucontext_t *ctx = (ucontext_t*)ctx_void; 64 + unsigned short *ip = (unsigned short *)ctx->uc_mcontext.gregs[REG_IP]; 65 + 66 + if (*ip == 0x340f || *ip == 0x050f) { 67 + /* The trap was on SYSCALL or SYSENTER */ 68 + sigtrap_consecutive_syscalls++; 69 + if (sigtrap_consecutive_syscalls > 3) { 70 + printf("[WARN]\tGot stuck single-stepping -- you probably have a KVM bug\n"); 71 + siglongjmp(jmpbuf, 1); 72 + } 73 + } else { 74 + sigtrap_consecutive_syscalls = 0; 75 + } 76 + } 77 + 83 78 static void sigill(int sig, siginfo_t *info, void *ctx_void) 84 79 { 85 - printf("[SKIP]\tIllegal instruction\n"); 80 + ucontext_t *ctx = (ucontext_t*)ctx_void; 81 + unsigned short *ip = (unsigned short *)ctx->uc_mcontext.gregs[REG_IP]; 82 + 83 + if (*ip == 0x0b0f) { 84 + /* one of the ud2 instructions faulted */ 85 + printf("[OK]\tSYSCALL returned normally\n"); 86 + } else { 87 + printf("[SKIP]\tIllegal instruction\n"); 88 + } 86 89 siglongjmp(jmpbuf, 1); 87 90 } 88 91 ··· 183 120 "movl $-1, %%ebp\n\t" 184 121 "movl $-1, %%esp\n\t" 185 122 "syscall\n\t" 186 - "pushl $0" /* make sure we segfault cleanly */ 123 + "ud2" /* make sure we recover cleanly */ 187 124 : : : "memory", "flags"); 188 125 } 126 + 127 + printf("[RUN]\tSYSENTER with TF and invalid state\n"); 128 + sethandler(SIGTRAP, sigtrap, SA_ONSTACK); 129 + 130 + if (sigsetjmp(jmpbuf, 1) == 0) { 131 + sigtrap_consecutive_syscalls = 0; 132 + set_eflags(get_eflags() | X86_EFLAGS_TF); 133 + asm volatile ( 134 + "movl $-1, %%eax\n\t" 135 + "movl $-1, %%ebx\n\t" 136 + "movl $-1, %%ecx\n\t" 137 + "movl $-1, %%edx\n\t" 138 + "movl $-1, %%esi\n\t" 139 + "movl $-1, %%edi\n\t" 140 + "movl $-1, %%ebp\n\t" 141 + "movl $-1, %%esp\n\t" 142 + "sysenter" 143 + : : : "memory", "flags"); 144 + } 145 + set_eflags(get_eflags() & ~X86_EFLAGS_TF); 146 + 147 + printf("[RUN]\tSYSCALL with TF and invalid state\n"); 148 + if (sigsetjmp(jmpbuf, 1) == 0) { 149 + sigtrap_consecutive_syscalls = 0; 150 + set_eflags(get_eflags() | X86_EFLAGS_TF); 151 + asm volatile ( 152 + "movl $-1, %%eax\n\t" 153 + "movl $-1, %%ebx\n\t" 154 + "movl $-1, %%ecx\n\t" 155 + "movl $-1, %%edx\n\t" 156 + "movl $-1, %%esi\n\t" 157 + "movl $-1, %%edi\n\t" 158 + "movl $-1, %%ebp\n\t" 159 + "movl $-1, %%esp\n\t" 160 + "syscall\n\t" 161 + "ud2" /* make sure we recover cleanly */ 162 + : : : "memory", "flags"); 163 + } 164 + set_eflags(get_eflags() & ~X86_EFLAGS_TF); 189 165 190 166 return 0; 191 167 }