[v3 PATCH 10/10] selftests/x86: Add tests for User-Mode Instruction Prevention

Ricardo Neri
2017-01-25T20:23:53Z

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 catches 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 INT 0x80.

The instructions protected by UMIP are executed in representative use
cases:
a) the memory address of the result is given in the form of a displacement
from the base of the data segment
b) the memory address of the result is given in a general purpose register
c) the result is stored directly in a general purpose register.

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.

Cc: Andy Lutomirski <luto@kernel.org>
Cc: Andrew Morton <akpm@linux-foundation.org>
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: Fenghua Yu <fenghua.yu@intel.com>
Cc: Huang Rui <ray.huang@amd.com>
Cc: Jiri Slaby <jslaby@suse.cz>
Cc: Jonathan Corbet <corbet@lwn.net>
Cc: Michael S. Tsirkin <mst@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: Vlastimil Babka <vbabka@suse.cz>
Signed-off-by: Ricardo Neri <ricardo.neri-calderon@linux.intel.com>
---
tools/testing/selftests/x86/entry_from_vm86.c | 39 ++++++++++++++++++++++++++-
1 file changed, 38 insertions(+), 1 deletion(-)

diff --git a/tools/testing/selftests/x86/entry_from_vm86.c b/tools/testing/selftests/x86/entry_from_vm86.c
index d075ea0..377b773 100644
--- 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"
+ "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"
+ "int $0x80\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[], umip[];

/* Returns false if the test was skipped. */
static bool do_test(struct vm86plus_struct *v86, unsigned long eip,
@@ -218,6 +234,27 @@ 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_test(&v86, umip - vmcode, VM86_INTx, 0x80, "UMIP tests");
+ printf("[INFO]\tResults of UMIP-protected instructions via displacements:\n");
+ printf("[INFO]\tSMSW:[0x%04x]\n", *(unsigned short *)(addr + 2052));
+ printf("[INFO]\tSIDT: limit[0x%04x]base[0x%08lx]\n",
+ *(unsigned short *)(addr + 2054),
+ *(unsigned long *)(addr + 2056));
+ printf("[INFO]\tSGDT: limit[0x%04x]base[0x%08lx]\n",
+ *(unsigned short *)(addr + 2060),
+ *(unsigned long *)(addr + 2062));
+ printf("[INFO]\tResults of UMIP-protected instructions via addressing in registers:\n");
+ printf("[INFO]\tSMSW:[0x%04x]\n", *(unsigned short *)(addr + 2066));
+ printf("[INFO]\tSIDT: limit[0x%04x]base[0x%08lx]\n",
+ *(unsigned short *)(addr + 2068),
+ *(unsigned long *)(addr + 2070));
+ printf("[INFO]\tSGDT: limit[0x%04x]base[0x%08lx]\n",
+ *(unsigned short *)(addr + 2074),
+ *(unsigned long *)(addr + 2076));
+ printf("[INFO]\tResults of SMSW via register operands:\n");
+ printf("[INFO]\tSMSW:[0x%04x]\n", *(unsigned short *)(addr + 2080));
+
/* Execute a null pointer */
v86.regs.cs = 0;
v86.regs.ss = 0;
--
2.9.3