[PATCH v11 00/12] x86: Enable User-Mode Instruction Prevention

From: Ricardo Neri
Date: Sun Nov 05 2017 - 21:30:06 EST


This the 11th revision of a patchset to enable User-Mode Instruction
Prevention (UMIP) in Linux. This is the second part of two series. After
having completed review, the first series [1] has been merged in the tip
tree. v10 of this series can be found here [2].

In this series, support is added to handle 32-bit and 16-bit addresses in
protected and virtual-8086 modes plus effectively enabling UMIP. See below
for a description on why this support is needed.

=== What is UMIP?

User-Mode Instruction Prevention (UMIP) is a security feature present in
new Intel Processors. If enabled, it prevents the execution of certain
instructions if the Current Privilege Level (CPL) is greater than 0. If
these instructions were executed while in CPL > 0, user space applications
could have access to system-wide settings such as the global and local
descriptor tables, the segment selectors to the current task state and the
local descriptor table. Hiding these system resources reduces the tools
available to craft privilege escalation attacks such as [3].

These are the instructions covered by UMIP:
* SGDT - Store Global Descriptor Table
* SIDT - Store Interrupt Descriptor Table
* SLDT - Store Local Descriptor Table
* SMSW - Store Machine Status Word
* STR - Store Task Register

If any of these instructions is executed with CPL > 0, a general protection
exception is issued when UMIP is enabled. This means that any process that
attempts to use the aforementioned instructions would see a SIGSEGV signal.

=== How does it impact applications?

When enabled, however, UMIP will change the behavior that certain
applications expect from the operating system. For instance, programs
running on WineHQ and DOSEMU2 rely on some of these instructions to
function. Stas Sergeev found that Microsoft Windows 3.1 and dos4gw use the
instruction SMSW when running in virtual-8086 mode [4]. SGDT and SIDT can
also be used on virtual-8086 mode.

In order to not change the behavior of the system (i.e., a SIGSEGV signal
should not be generated when using these instructions), this implementation
traps the #GP fault generated by the CPU and emulates SGDT, SIDT and SMSW.
with dummy returned values. This should be sufficient to not break the
applications mentioned above. Regarding the two remaining instructions,
STR and SLDT, the WineHQ team has shown interest catching the general
protection fault and use it as a vehicle to fix broken applications[5].

Thus, emulation is only provided for protected and virtual-8086 modes. No
emulation is implemented for processes running in long mode. Also the
instructions SLDT and STR are not emulated in any case.

DOSEMU2 emulates virtual-8086 mode via KVM. No applications will be broken
unless DOSEMU2 decides to enable the CR4.UMIP bit in platforms that support
it. Also, this should not pose a security risk as no system resources would
be revealed. Instead, code running inside the KVM would only see the KVM's
GDT, IDT and MSW.

=== How is this series laid out?

++ Extend the insn-eval library
This library is extended to also support 32-bit and 16 bit addresses. This
also implies to add functionality to enforce segment limits in protected
mode and linear address sizes in virtual-8086 mode as described in the
section 20.1.1 of the Intel 64 and IA-32 Architectures Software Development
Manual Vol. 3.

++ Emulate UMIP instructions
A new fixup_umip_exception() functions inspect the instruction at the
instruction pointer. If it is an UMIP-protected instruction, it executes
the emulation code.

++ Add self-tests
Lastly, self-tests are added to entry_from_v86.c to exercise the most
typical use cases of UMIP-protected instructions in a virtual-8086 mode.

++ Extensive tests
Extensive tests were performed to test all the combinations of ModRM,
SiB and displacements for 16-bit and 32-bit encodings for the SS, DS,
ES, FS and GS segments. Tests also include a 64-bit program that uses
segmentation via FS and GS. For this purpose, I temporarily enabled UMIP
support for 64-bit process. This change is not part of this patchset.
The intention is to test the computations of linear addresses in 64-bit
mode, including the extra R8-R15 registers. Extensive test is also
implemented for virtual-8086 tasks. Code of these tests can be found here
[6] and here [7].

Thanks and BR,
Ricardo

[1]. https://www.spinics.net/lists/kernel/msg2635138.html
[2]. https://lkml.org/lkml/2017/10/27/699
[3]. http://timetobleed.com/a-closer-look-at-a-recent-privilege-escalation-bug-in-linux-cve-2013-2094/
[4]. https://www.winehq.org/pipermail/wine-devel/2017-April/117159.html
[5]. https://marc.info/?l=linux-kernel&m=147876798717927&w=2
[6]. https://github.com/01org/luv-yocto/tree/rneri/umip/meta-luv/recipes-core/umip/files
[7]. https://github.com/01org/luv-yocto/commit/a72a7fe7d68693c0f4100ad86de6ecabde57334f#diff-3860c136a63add269bce4ea50222c248R1

Changes since V10:
*Patch 1 ("x86/insn-eval: Extend get_seg_base_addr() to also obtain segment
limit") of v10 has been dropped has it has been merged in the tip tree.
*Removed unnecessary wrap-around of function calls to enforce the 80
chars-per-line rule. In some cases, variables were renamed.
*Removed unnecessary casts between variables of the same width.
*Reworked casts between variables of different width in favor of bit
masks. Casts were kept for arithmetic operations.
*Reworded patch descriptions and documentation of several functions to
improve clarity.

Changes since V9:
*All the changes described in [1], plus:
*Created new utility functions utility functions to handle each x86
memory addressing mode separately. Also, such functions are extended to
support 32-bit addresses. A separate set of function is used for 16-bit
addresses.
*Extended and rename the function get_seg_base_addr() as
get_seg_base_addr() to also return the limit of the segment associated
with the instruction operand.

Ricardo Neri (12):
x86/insn-eval: Compute linear address in several utility functions
x86/insn-eval: Add support to resolve 32-bit address encodings
x86/insn-eval: Add wrapper function for 32 and 64-bit addresses
x86/insn-eval: Handle 32-bit address encodings in virtual-8086 mode
x86/insn-eval: Add support to resolve 16-bit address encodings
x86/cpufeature: Add User-Mode Instruction Prevention definitions
x86: Add emulation code for UMIP instructions
x86/umip: Force a page fault when unable to copy emulated result to
user
x86: Enable User-Mode Instruction Prevention at runtime
x86/traps: Fixup general protection faults caused by UMIP
selftests/x86: Add tests for User-Mode Instruction Prevention
selftests/x86: Add tests for instruction str and sldt

arch/x86/Kconfig | 10 +
arch/x86/include/asm/cpufeatures.h | 1 +
arch/x86/include/asm/disabled-features.h | 8 +-
arch/x86/include/asm/umip.h | 12 +
arch/x86/include/uapi/asm/processor-flags.h | 2 +
arch/x86/kernel/Makefile | 1 +
arch/x86/kernel/cpu/common.c | 25 +-
arch/x86/kernel/traps.c | 5 +
arch/x86/kernel/umip.c | 366 ++++++++++++++++
arch/x86/lib/insn-eval.c | 609 +++++++++++++++++++++++---
tools/testing/selftests/x86/entry_from_vm86.c | 89 +++-
11 files changed, 1070 insertions(+), 58 deletions(-)
create mode 100644 arch/x86/include/asm/umip.h
create mode 100644 arch/x86/kernel/umip.c

--
2.7.4