| From 9390afebe1d3f5a0be18b1afdd0ce09d67cebf9e Mon Sep 17 00:00:00 2001 |
| From: Ricardo Neri <ricardo.neri-calderon@linux.intel.com> |
| Date: Sun, 5 Nov 2017 18:27:56 -0800 |
| Subject: selftests/x86: Add tests for User-Mode Instruction Prevention |
| |
| From: Ricardo Neri <ricardo.neri-calderon@linux.intel.com> |
| |
| commit 9390afebe1d3f5a0be18b1afdd0ce09d67cebf9e upstream. |
| |
| Certain user space programs that run on virtual-8086 mode may utilize |
| instructions protected by the User-Mode Instruction Prevention (UMIP) |
| security feature present in new Intel processors: SGDT, SIDT and SMSW. In |
| such a case, a general protection fault is issued if UMIP is enabled. When |
| such a fault happens, the kernel traps it and emulates the results of |
| these instructions with dummy values. The purpose of this new |
| test is to verify whether the impacted instructions can be executed |
| without causing such #GP. If no #GP exceptions occur, we expect to exit |
| virtual-8086 mode from INT3. |
| |
| The instructions protected by UMIP are executed in representative use |
| cases: |
| |
| a) displacement-only memory addressing |
| b) register-indirect memory addressing |
| c) results stored directly in operands |
| |
| Unfortunately, it is not possible to check the results against a set of |
| expected values because no emulation will occur in systems that do not |
| have the UMIP feature. Instead, results are printed for verification. A |
| simple verification is done to ensure that results of all tests are |
| identical. |
| |
| Signed-off-by: Ricardo Neri <ricardo.neri-calderon@linux.intel.com> |
| Reviewed-by: Thomas Gleixner <tglx@linutronix.de> |
| Cc: Andrew Morton <akpm@linux-foundation.org> |
| Cc: Andy Lutomirski <luto@kernel.org> |
| Cc: Borislav Petkov <bp@alien8.de> |
| Cc: Borislav Petkov <bp@suse.de> |
| Cc: Brian Gerst <brgerst@gmail.com> |
| Cc: Chen Yucong <slaoub@gmail.com> |
| Cc: Chris Metcalf <cmetcalf@mellanox.com> |
| Cc: Dave Hansen <dave.hansen@linux.intel.com> |
| Cc: Denys Vlasenko <dvlasenk@redhat.com> |
| Cc: Fenghua Yu <fenghua.yu@intel.com> |
| Cc: H. Peter Anvin <hpa@zytor.com> |
| Cc: Huang Rui <ray.huang@amd.com> |
| Cc: Jiri Slaby <jslaby@suse.cz> |
| Cc: Jonathan Corbet <corbet@lwn.net> |
| Cc: Josh Poimboeuf <jpoimboe@redhat.com> |
| Cc: Linus Torvalds <torvalds@linux-foundation.org> |
| Cc: Masami Hiramatsu <mhiramat@kernel.org> |
| Cc: Michael S. Tsirkin <mst@redhat.com> |
| Cc: Paolo Bonzini <pbonzini@redhat.com> |
| Cc: Paul Gortmaker <paul.gortmaker@windriver.com> |
| Cc: Peter Zijlstra <peterz@infradead.org> |
| Cc: Ravi V. Shankar <ravi.v.shankar@intel.com> |
| Cc: Shuah Khan <shuah@kernel.org> |
| Cc: Tony Luck <tony.luck@intel.com> |
| Cc: Vlastimil Babka <vbabka@suse.cz> |
| Cc: ricardo.neri@intel.com |
| Link: http://lkml.kernel.org/r/1509935277-22138-12-git-send-email-ricardo.neri-calderon@linux.intel.com |
| Signed-off-by: Ingo Molnar <mingo@kernel.org> |
| Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> |
| |
| --- |
| tools/testing/selftests/x86/entry_from_vm86.c | 73 +++++++++++++++++++++++++- |
| 1 file changed, 72 insertions(+), 1 deletion(-) |
| |
| --- a/tools/testing/selftests/x86/entry_from_vm86.c |
| +++ b/tools/testing/selftests/x86/entry_from_vm86.c |
| @@ -95,6 +95,22 @@ asm ( |
| "int3\n\t" |
| "vmcode_int80:\n\t" |
| "int $0x80\n\t" |
| + "vmcode_umip:\n\t" |
| + /* addressing via displacements */ |
| + "smsw (2052)\n\t" |
| + "sidt (2054)\n\t" |
| + "sgdt (2060)\n\t" |
| + /* addressing via registers */ |
| + "mov $2066, %bx\n\t" |
| + "smsw (%bx)\n\t" |
| + "mov $2068, %bx\n\t" |
| + "sidt (%bx)\n\t" |
| + "mov $2074, %bx\n\t" |
| + "sgdt (%bx)\n\t" |
| + /* register operands, only for smsw */ |
| + "smsw %ax\n\t" |
| + "mov %ax, (2080)\n\t" |
| + "int3\n\t" |
| ".size vmcode, . - vmcode\n\t" |
| "end_vmcode:\n\t" |
| ".code32\n\t" |
| @@ -103,7 +119,7 @@ asm ( |
| |
| extern unsigned char vmcode[], end_vmcode[]; |
| extern unsigned char vmcode_bound[], vmcode_sysenter[], vmcode_syscall[], |
| - vmcode_sti[], vmcode_int3[], vmcode_int80[]; |
| + vmcode_sti[], vmcode_int3[], vmcode_int80[], vmcode_umip[]; |
| |
| /* Returns false if the test was skipped. */ |
| static bool do_test(struct vm86plus_struct *v86, unsigned long eip, |
| @@ -160,6 +176,58 @@ static bool do_test(struct vm86plus_stru |
| return true; |
| } |
| |
| +void do_umip_tests(struct vm86plus_struct *vm86, unsigned char *test_mem) |
| +{ |
| + struct table_desc { |
| + unsigned short limit; |
| + unsigned long base; |
| + } __attribute__((packed)); |
| + |
| + /* Initialize variables with arbitrary values */ |
| + struct table_desc gdt1 = { .base = 0x3c3c3c3c, .limit = 0x9999 }; |
| + struct table_desc gdt2 = { .base = 0x1a1a1a1a, .limit = 0xaeae }; |
| + struct table_desc idt1 = { .base = 0x7b7b7b7b, .limit = 0xf1f1 }; |
| + struct table_desc idt2 = { .base = 0x89898989, .limit = 0x1313 }; |
| + unsigned short msw1 = 0x1414, msw2 = 0x2525, msw3 = 3737; |
| + |
| + /* UMIP -- exit with INT3 unless kernel emulation did not trap #GP */ |
| + do_test(vm86, vmcode_umip - vmcode, VM86_TRAP, 3, "UMIP tests"); |
| + |
| + /* Results from displacement-only addressing */ |
| + msw1 = *(unsigned short *)(test_mem + 2052); |
| + memcpy(&idt1, test_mem + 2054, sizeof(idt1)); |
| + memcpy(&gdt1, test_mem + 2060, sizeof(gdt1)); |
| + |
| + /* Results from register-indirect addressing */ |
| + msw2 = *(unsigned short *)(test_mem + 2066); |
| + memcpy(&idt2, test_mem + 2068, sizeof(idt2)); |
| + memcpy(&gdt2, test_mem + 2074, sizeof(gdt2)); |
| + |
| + /* Results when using register operands */ |
| + msw3 = *(unsigned short *)(test_mem + 2080); |
| + |
| + printf("[INFO]\tResult from SMSW:[0x%04x]\n", msw1); |
| + printf("[INFO]\tResult from SIDT: limit[0x%04x]base[0x%08lx]\n", |
| + idt1.limit, idt1.base); |
| + printf("[INFO]\tResult from SGDT: limit[0x%04x]base[0x%08lx]\n", |
| + gdt1.limit, gdt1.base); |
| + |
| + if (msw1 != msw2 || msw1 != msw3) |
| + printf("[FAIL]\tAll the results of SMSW should be the same.\n"); |
| + else |
| + printf("[PASS]\tAll the results from SMSW are identical.\n"); |
| + |
| + if (memcmp(&gdt1, &gdt2, sizeof(gdt1))) |
| + printf("[FAIL]\tAll the results of SGDT should be the same.\n"); |
| + else |
| + printf("[PASS]\tAll the results from SGDT are identical.\n"); |
| + |
| + if (memcmp(&idt1, &idt2, sizeof(idt1))) |
| + printf("[FAIL]\tAll the results of SIDT should be the same.\n"); |
| + else |
| + printf("[PASS]\tAll the results from SIDT are identical.\n"); |
| +} |
| + |
| int main(void) |
| { |
| struct vm86plus_struct v86; |
| @@ -218,6 +286,9 @@ int main(void) |
| v86.regs.eax = (unsigned int)-1; |
| do_test(&v86, vmcode_int80 - vmcode, VM86_INTx, 0x80, "int80"); |
| |
| + /* UMIP -- should exit with INTx 0x80 unless UMIP was not disabled */ |
| + do_umip_tests(&v86, addr); |
| + |
| /* Execute a null pointer */ |
| v86.regs.cs = 0; |
| v86.regs.ss = 0; |