Wine-Devel
By thread
wine-devel@list.winehq.org
By month
Messages by month
- ----- 2026 -----
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2025 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2024 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2023 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2022 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2021 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2020 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2019 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2018 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2017 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2016 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2015 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2014 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2013 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2012 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2011 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2010 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2009 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2008 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2007 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2006 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2005 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2004 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2003 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2002 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2001 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
May 2017
- 52 participants
- 376 messages
[PATCH v7 23/26] x86/traps: Fixup general protection faults caused by UMIP
by Ricardo Neri
If the User-Mode Instruction Prevention CPU feature is available and
enabled, a general protection fault will be issued if the instructions
sgdt, sldt, sidt, str or smsw are executed from user-mode context
(CPL > 0). If the fault was caused by any of the instructions protected
by UMIP, fixup_umip_exception will emulate dummy results for these
instructions. If emulation is successful, the result is passed to the
user space program and no SIGSEGV signal is emitted.
Please note that fixup_umip_exception also caters for the case when
the fault originated while running in virtual-8086 mode.
Cc: Andy Lutomirski <luto(a)kernel.org>
Cc: Andrew Morton <akpm(a)linux-foundation.org>
Cc: H. Peter Anvin <hpa(a)zytor.com>
Cc: Borislav Petkov <bp(a)suse.de>
Cc: Brian Gerst <brgerst(a)gmail.com>
Cc: Chen Yucong <slaoub(a)gmail.com>
Cc: Chris Metcalf <cmetcalf(a)mellanox.com>
Cc: Dave Hansen <dave.hansen(a)linux.intel.com>
Cc: Fenghua Yu <fenghua.yu(a)intel.com>
Cc: Huang Rui <ray.huang(a)amd.com>
Cc: Jiri Slaby <jslaby(a)suse.cz>
Cc: Jonathan Corbet <corbet(a)lwn.net>
Cc: Michael S. Tsirkin <mst(a)redhat.com>
Cc: Paul Gortmaker <paul.gortmaker(a)windriver.com>
Cc: Peter Zijlstra <peterz(a)infradead.org>
Cc: Ravi V. Shankar <ravi.v.shankar(a)intel.com>
Cc: Shuah Khan <shuah(a)kernel.org>
Cc: Vlastimil Babka <vbabka(a)suse.cz>
Cc: Tony Luck <tony.luck(a)intel.com>
Cc: Paolo Bonzini <pbonzini(a)redhat.com>
Cc: Liang Z. Li <liang.z.li(a)intel.com>
Cc: Alexandre Julliard <julliard(a)winehq.org>
Cc: Stas Sergeev <stsp(a)list.ru>
Cc: x86(a)kernel.org
Cc: linux-msdos(a)vger.kernel.org
Reviewed-by: Andy Lutomirski <luto(a)kernel.org>
Signed-off-by: Ricardo Neri <ricardo.neri-calderon(a)linux.intel.com>
---
arch/x86/kernel/traps.c | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/arch/x86/kernel/traps.c b/arch/x86/kernel/traps.c
index 3995d3a..cec548d 100644
--- a/arch/x86/kernel/traps.c
+++ b/arch/x86/kernel/traps.c
@@ -65,6 +65,7 @@
#include <asm/trace/mpx.h>
#include <asm/mpx.h>
#include <asm/vm86.h>
+#include <asm/umip.h>
#ifdef CONFIG_X86_64
#include <asm/x86_init.h>
@@ -526,6 +527,9 @@ do_general_protection(struct pt_regs *regs, long error_code)
RCU_LOCKDEP_WARN(!rcu_is_watching(), "entry code didn't wake RCU");
cond_local_irq_enable(regs);
+ if (user_mode(regs) && fixup_umip_exception(regs))
+ return;
+
if (v8086_mode(regs)) {
local_irq_enable();
handle_vm86_fault((struct kernel_vm86_regs *) regs, error_code);
--
2.9.3
May 5, 2017
[PATCH v7 22/26] x86/umip: Force a page fault when unable to copy emulated result to user
by Ricardo Neri
fixup_umip_exception() will be called from do_general_protection. If the
former returns false, the latter will issue a SIGSEGV with SEND_SIG_PRIV.
However, when emulation is successful but the emulated result cannot be
copied to user space memory, it is more accurate to issue a SIGSEGV with
SEGV_MAPERR with the offending address. A new function is inspired in
force_sig_info_fault is introduced to model the page fault.
Cc: Andy Lutomirski <luto(a)kernel.org>
Cc: Andrew Morton <akpm(a)linux-foundation.org>
Cc: H. Peter Anvin <hpa(a)zytor.com>
Cc: Borislav Petkov <bp(a)suse.de>
Cc: Brian Gerst <brgerst(a)gmail.com>
Cc: Chen Yucong <slaoub(a)gmail.com>
Cc: Chris Metcalf <cmetcalf(a)mellanox.com>
Cc: Dave Hansen <dave.hansen(a)linux.intel.com>
Cc: Fenghua Yu <fenghua.yu(a)intel.com>
Cc: Huang Rui <ray.huang(a)amd.com>
Cc: Jiri Slaby <jslaby(a)suse.cz>
Cc: Jonathan Corbet <corbet(a)lwn.net>
Cc: Michael S. Tsirkin <mst(a)redhat.com>
Cc: Paul Gortmaker <paul.gortmaker(a)windriver.com>
Cc: Peter Zijlstra <peterz(a)infradead.org>
Cc: Ravi V. Shankar <ravi.v.shankar(a)intel.com>
Cc: Shuah Khan <shuah(a)kernel.org>
Cc: Vlastimil Babka <vbabka(a)suse.cz>
Cc: Tony Luck <tony.luck(a)intel.com>
Cc: Paolo Bonzini <pbonzini(a)redhat.com>
Cc: Liang Z. Li <liang.z.li(a)intel.com>
Cc: Alexandre Julliard <julliard(a)winehq.org>
Cc: Stas Sergeev <stsp(a)list.ru>
Cc: x86(a)kernel.org
Cc: linux-msdos(a)vger.kernel.org
Signed-off-by: Ricardo Neri <ricardo.neri-calderon(a)linux.intel.com>
---
arch/x86/kernel/umip.c | 45 +++++++++++++++++++++++++++++++++++++++++++--
1 file changed, 43 insertions(+), 2 deletions(-)
diff --git a/arch/x86/kernel/umip.c b/arch/x86/kernel/umip.c
index c7c5795..ff7366a 100644
--- a/arch/x86/kernel/umip.c
+++ b/arch/x86/kernel/umip.c
@@ -148,6 +148,41 @@ static int __emulate_umip_insn(struct insn *insn, enum umip_insn umip_inst,
}
/**
+ * __force_sig_info_umip_fault() - Force a SIGSEGV with SEGV_MAPERR
+ * @address: Address that caused the signal
+ * @regs: Register set containing the instruction pointer
+ *
+ * Force a SIGSEGV signal with SEGV_MAPERR as the error code. This function is
+ * intended to be used to provide a segmentation fault when the result of the
+ * UMIP emulation could not be copied to the user space memory.
+ *
+ * Return: none
+ */
+static void __force_sig_info_umip_fault(void __user *address,
+ struct pt_regs *regs)
+{
+ siginfo_t info;
+ struct task_struct *tsk = current;
+
+ if (show_unhandled_signals && unhandled_signal(tsk, SIGSEGV)) {
+ printk_ratelimited("%s[%d] umip emulation segfault ip:%lx sp:%lx error:%x in %lx\n",
+ tsk->comm, task_pid_nr(tsk), regs->ip,
+ regs->sp, X86_PF_USER | X86_PF_WRITE,
+ regs->ip);
+ }
+
+ tsk->thread.cr2 = (unsigned long)address;
+ tsk->thread.error_code = X86_PF_USER | X86_PF_WRITE;
+ tsk->thread.trap_nr = X86_TRAP_PF;
+
+ info.si_signo = SIGSEGV;
+ info.si_errno = 0;
+ info.si_code = SEGV_MAPERR;
+ info.si_addr = address;
+ force_sig_info(SIGSEGV, &info, tsk);
+}
+
+/**
* fixup_umip_exception() - Fixup #GP faults caused by UMIP
* @regs: Registers as saved when entering the #GP trap
*
@@ -235,8 +270,14 @@ bool fixup_umip_exception(struct pt_regs *regs)
if ((unsigned long)uaddr == -1L)
return false;
nr_copied = copy_to_user(uaddr, dummy_data, dummy_data_size);
- if (nr_copied > 0)
- return false;
+ if (nr_copied > 0) {
+ /*
+ * If copy fails, send a signal and tell caller that
+ * fault was fixed up
+ */
+ __force_sig_info_umip_fault(uaddr, regs);
+ return true;
+ }
}
/* increase IP to let the program keep going */
--
2.9.3
May 5, 2017
[PATCH v7 21/26] x86: Add emulation code for UMIP instructions
by Ricardo Neri
The feature User-Mode Instruction Prevention present in recent Intel
processor prevents a group of instructions from being executed with
CPL > 0. Otherwise, a general protection fault is issued.
Rather than relaying this fault to the user space (in the form of a SIGSEGV
signal), the instructions protected by UMIP can be emulated to provide
dummy results. This allows to conserve the current kernel behavior and not
reveal the system resources that UMIP intends to protect (the global
descriptor and interrupt descriptor tables, the segment selectors of the
local descriptor table and the task state and the machine status word).
This emulation is needed because certain applications (e.g., WineHQ and
DOSEMU2) rely on this subset of instructions to function.
The instructions protected by UMIP can be split in two groups. Those who
return a kernel memory address (sgdt and sidt) and those who return a
value (sldt, str and smsw).
For the instructions that return a kernel memory address, applications
such as WineHQ rely on the result being located in the kernel memory space.
The result is emulated as a hard-coded value that, lies close to the top
of the kernel memory. The limit for the GDT and the IDT are set to zero.
Given that sldt and str are not used in common in programs supported by
WineHQ and DOSEMU2, they are not emulated.
The instruction smsw is emulated to return the value that the register CR0
has at boot time as set in the head_32.
Care is taken to appropriately emulate the results when segmentation is
used. This is, rather than relying on USER_DS and USER_CS, the function
insn_get_addr_ref() inspects the segment descriptor pointed by the
registers in pt_regs. This ensures that we correctly obtain the segment
base address and the address and operand sizes even if the user space
application uses local descriptor table.
Cc: Andy Lutomirski <luto(a)kernel.org>
Cc: Andrew Morton <akpm(a)linux-foundation.org>
Cc: H. Peter Anvin <hpa(a)zytor.com>
Cc: Borislav Petkov <bp(a)suse.de>
Cc: Brian Gerst <brgerst(a)gmail.com>
Cc: Chen Yucong <slaoub(a)gmail.com>
Cc: Chris Metcalf <cmetcalf(a)mellanox.com>
Cc: Dave Hansen <dave.hansen(a)linux.intel.com>
Cc: Fenghua Yu <fenghua.yu(a)intel.com>
Cc: Huang Rui <ray.huang(a)amd.com>
Cc: Jiri Slaby <jslaby(a)suse.cz>
Cc: Jonathan Corbet <corbet(a)lwn.net>
Cc: Michael S. Tsirkin <mst(a)redhat.com>
Cc: Paul Gortmaker <paul.gortmaker(a)windriver.com>
Cc: Peter Zijlstra <peterz(a)infradead.org>
Cc: Ravi V. Shankar <ravi.v.shankar(a)intel.com>
Cc: Shuah Khan <shuah(a)kernel.org>
Cc: Vlastimil Babka <vbabka(a)suse.cz>
Cc: Tony Luck <tony.luck(a)intel.com>
Cc: Paolo Bonzini <pbonzini(a)redhat.com>
Cc: Liang Z. Li <liang.z.li(a)intel.com>
Cc: Alexandre Julliard <julliard(a)winehq.org>
Cc: Stas Sergeev <stsp(a)list.ru>
Cc: x86(a)kernel.org
Cc: linux-msdos(a)vger.kernel.org
Signed-off-by: Ricardo Neri <ricardo.neri-calderon(a)linux.intel.com>
---
arch/x86/include/asm/umip.h | 15 +++
arch/x86/kernel/Makefile | 1 +
arch/x86/kernel/umip.c | 245 ++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 261 insertions(+)
create mode 100644 arch/x86/include/asm/umip.h
create mode 100644 arch/x86/kernel/umip.c
diff --git a/arch/x86/include/asm/umip.h b/arch/x86/include/asm/umip.h
new file mode 100644
index 0000000..077b236
--- /dev/null
+++ b/arch/x86/include/asm/umip.h
@@ -0,0 +1,15 @@
+#ifndef _ASM_X86_UMIP_H
+#define _ASM_X86_UMIP_H
+
+#include <linux/types.h>
+#include <asm/ptrace.h>
+
+#ifdef CONFIG_X86_INTEL_UMIP
+bool fixup_umip_exception(struct pt_regs *regs);
+#else
+static inline bool fixup_umip_exception(struct pt_regs *regs)
+{
+ return false;
+}
+#endif /* CONFIG_X86_INTEL_UMIP */
+#endif /* _ASM_X86_UMIP_H */
diff --git a/arch/x86/kernel/Makefile b/arch/x86/kernel/Makefile
index 4b99423..cc1b7cc 100644
--- a/arch/x86/kernel/Makefile
+++ b/arch/x86/kernel/Makefile
@@ -123,6 +123,7 @@ obj-$(CONFIG_EFI) += sysfb_efi.o
obj-$(CONFIG_PERF_EVENTS) += perf_regs.o
obj-$(CONFIG_TRACING) += tracepoint.o
obj-$(CONFIG_SCHED_MC_PRIO) += itmt.o
+obj-$(CONFIG_X86_INTEL_UMIP) += umip.o
ifdef CONFIG_FRAME_POINTER
obj-y += unwind_frame.o
diff --git a/arch/x86/kernel/umip.c b/arch/x86/kernel/umip.c
new file mode 100644
index 0000000..c7c5795
--- /dev/null
+++ b/arch/x86/kernel/umip.c
@@ -0,0 +1,245 @@
+/*
+ * umip.c Emulation for instruction protected by the Intel User-Mode
+ * Instruction Prevention. The instructions are:
+ * sgdt
+ * sldt
+ * sidt
+ * str
+ * smsw
+ *
+ * Copyright (c) 2017, Intel Corporation.
+ * Ricardo Neri <ricardo.neri(a)linux.intel.com>
+ */
+
+#include <linux/uaccess.h>
+#include <asm/umip.h>
+#include <asm/traps.h>
+#include <asm/insn.h>
+#include <asm/insn-eval.h>
+#include <linux/ratelimit.h>
+
+/*
+ * == Base addresses of GDT and IDT
+ * Some applications to function rely finding the global descriptor table (GDT)
+ * and the interrupt descriptor table (IDT) in kernel memory.
+ * For x86_32, the selected values do not match any particular hole, but it
+ * suffices to provide a memory location within kernel memory.
+ *
+ * == CRO flags for SMSW
+ * Use the flags given when booting, as found in head_32.S
+ */
+
+#define CR0_STATE (X86_CR0_PE | X86_CR0_MP | X86_CR0_ET | X86_CR0_NE | \
+ X86_CR0_WP | X86_CR0_AM)
+#define UMIP_DUMMY_GDT_BASE 0xfffe0000
+#define UMIP_DUMMY_IDT_BASE 0xffff0000
+
+enum umip_insn {
+ UMIP_SGDT = 0, /* opcode 0f 01 ModR/M reg 0 */
+ UMIP_SIDT, /* opcode 0f 01 ModR/M reg 1 */
+ UMIP_SLDT, /* opcode 0f 00 ModR/M reg 0 */
+ UMIP_SMSW, /* opcode 0f 01 ModR/M reg 4 */
+ UMIP_STR, /* opcode 0f 00 ModR/M reg 1 */
+};
+
+/**
+ * __identify_insn() - Identify a UMIP-protected instruction
+ * @insn: Instruction structure with opcode and ModRM byte.
+ *
+ * From the instruction opcode and the reg part of the ModRM byte, identify,
+ * if any, a UMIP-protected instruction.
+ *
+ * Return: an enumeration of a UMIP-protected instruction; -EINVAL on failure.
+ */
+static int __identify_insn(struct insn *insn)
+{
+ /* By getting modrm we also get the opcode. */
+ insn_get_modrm(insn);
+
+ /* All the instructions of interest start with 0x0f. */
+ if (insn->opcode.bytes[0] != 0xf)
+ return -EINVAL;
+
+ if (insn->opcode.bytes[1] == 0x1) {
+ switch (X86_MODRM_REG(insn->modrm.value)) {
+ case 0:
+ return UMIP_SGDT;
+ case 1:
+ return UMIP_SIDT;
+ case 4:
+ return UMIP_SMSW;
+ default:
+ return -EINVAL;
+ }
+ }
+ /* SLDT AND STR are not emulated */
+ return -EINVAL;
+}
+
+/**
+ * __emulate_umip_insn() - Emulate UMIP instructions with dummy values
+ * @insn: Instruction structure with ModRM byte
+ * @umip_inst: Instruction to emulate
+ * @data: Buffer onto which the dummy values will be copied
+ * @data_size: Size of the emulated result
+ *
+ * Emulate an instruction protected by UMIP. The result of the emulation
+ * is saved in the provided buffer. The size of the results depends on both
+ * the instruction and type of operand (register vs memory address). Thus,
+ * the size of the result needs to be updated.
+ *
+ * Result: 0 if success, -EINVAL on failure to emulate
+ */
+static int __emulate_umip_insn(struct insn *insn, enum umip_insn umip_inst,
+ unsigned char *data, int *data_size)
+{
+ unsigned long dummy_base_addr;
+ unsigned short dummy_limit = 0;
+ unsigned int dummy_value = 0;
+
+ switch (umip_inst) {
+ /*
+ * These two instructions return the base address and limit of the
+ * global and interrupt descriptor table. The base address can be
+ * 24-bit, 32-bit or 64-bit. Limit is always 16-bit. If the operand
+ * size is 16-bit the returned value of the base address is supposed
+ * to be a zero-extended 24-byte number. However, it seems that a
+ * 32-byte number is always returned in legacy protected mode
+ * irrespective of the operand size.
+ */
+ case UMIP_SGDT:
+ /* fall through */
+ case UMIP_SIDT:
+ if (umip_inst == UMIP_SGDT)
+ dummy_base_addr = UMIP_DUMMY_GDT_BASE;
+ else
+ dummy_base_addr = UMIP_DUMMY_IDT_BASE;
+ if (X86_MODRM_MOD(insn->modrm.value) == 3) {
+ /* SGDT and SIDT do not take register as argument. */
+ return -EINVAL;
+ }
+
+ memcpy(data + 2, &dummy_base_addr, sizeof(dummy_base_addr));
+ memcpy(data, &dummy_limit, sizeof(dummy_limit));
+ *data_size = sizeof(dummy_base_addr) + sizeof(dummy_limit);
+ break;
+ case UMIP_SMSW:
+ /*
+ * Even though CR0_STATE contain 4 bytes, the number
+ * of bytes to be copied in the result buffer is determined
+ * by whether the operand is a register or a memory location.
+ */
+ dummy_value = CR0_STATE;
+ /*
+ * These two instructions return a 16-bit value. We return
+ * all zeros. This is equivalent to a null descriptor for
+ * str and sldt.
+ */
+ /* SLDT and STR are not emulated */
+ /* fall through */
+ case UMIP_SLDT:
+ /* fall through */
+ case UMIP_STR:
+ /* fall through */
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
+
+/**
+ * fixup_umip_exception() - Fixup #GP faults caused by UMIP
+ * @regs: Registers as saved when entering the #GP trap
+ *
+ * The instructions sgdt, sidt, str, smsw, sldt cause a general protection
+ * fault if with CPL > 0 (i.e., from user space). This function can be
+ * used to emulate the results of the aforementioned instructions with
+ * dummy values. Results are copied to user-space memory as indicated by
+ * the instruction pointed by EIP using the registers indicated in the
+ * instruction operands. This function also takes care of determining
+ * the address to which the results must be copied.
+ */
+bool fixup_umip_exception(struct pt_regs *regs)
+{
+ struct insn insn;
+ unsigned char buf[MAX_INSN_SIZE];
+ /* 10 bytes is the maximum size of the result of UMIP instructions */
+ unsigned char dummy_data[10] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+ unsigned long seg_base;
+ int not_copied, nr_copied, reg_offset, dummy_data_size;
+ void __user *uaddr;
+ unsigned long *reg_addr;
+ enum umip_insn umip_inst;
+ struct insn_code_seg_defaults seg_defs;
+
+ /*
+ * Use the segment base in case user space used a different code
+ * segment, either in protected (e.g., from an LDT) or virtual-8086
+ * modes. In most of the cases seg_base will be zero as in USER_CS.
+ */
+ seg_base = insn_get_seg_base(regs, &insn,
+ offsetof(struct pt_regs, ip));
+ not_copied = copy_from_user(buf, (void __user *)(seg_base + regs->ip),
+ sizeof(buf));
+ nr_copied = sizeof(buf) - not_copied;
+ /*
+ * The copy_from_user above could have failed if user code is protected
+ * by a memory protection key. Give up on emulation in such a case.
+ * Should we issue a page fault?
+ */
+ if (!nr_copied)
+ return false;
+
+ insn_init(&insn, buf, nr_copied, user_64bit_mode(regs));
+
+ /*
+ * Override the default operand and address sizes to what is specified
+ * in the code segment descriptor. The instruction decoder only sets
+ * the address size it to either 4 or 8 address bytes and does nothing
+ * for the operand bytes. This OK for most of the cases, but we could
+ * have special cases where, for instance, a 16-bit code segment
+ * descriptor is used.
+ * If there are overrides, the instruction decoder correctly updates
+ * these values, even for 16-bit defaults.
+ */
+ seg_defs = insn_get_code_seg_defaults(regs);
+ insn.addr_bytes = seg_defs.address_bytes;
+ insn.opnd_bytes = seg_defs.operand_bytes;
+
+ if (!insn.addr_bytes || !insn.opnd_bytes)
+ return false;
+
+ if (user_64bit_mode(regs))
+ return false;
+
+ insn_get_length(&insn);
+ if (nr_copied < insn.length)
+ return false;
+
+ umip_inst = __identify_insn(&insn);
+ /* Check if we found an instruction protected by UMIP */
+ if (umip_inst < 0)
+ return false;
+
+ if (__emulate_umip_insn(&insn, umip_inst, dummy_data, &dummy_data_size))
+ return false;
+
+ /* If operand is a register, write directly to it */
+ if (X86_MODRM_MOD(insn.modrm.value) == 3) {
+ reg_offset = insn_get_modrm_rm_off(&insn, regs);
+ reg_addr = (unsigned long *)((unsigned long)regs + reg_offset);
+ memcpy(reg_addr, dummy_data, dummy_data_size);
+ } else {
+ uaddr = insn_get_addr_ref(&insn, regs);
+ /* user address could not be determined, abort emulation */
+ if ((unsigned long)uaddr == -1L)
+ return false;
+ nr_copied = copy_to_user(uaddr, dummy_data, dummy_data_size);
+ if (nr_copied > 0)
+ return false;
+ }
+
+ /* increase IP to let the program keep going */
+ regs->ip += insn.length;
+ return true;
+}
--
2.9.3
May 5, 2017
[PATCH v7 20/26] x86/cpufeature: Add User-Mode Instruction Prevention definitions
by Ricardo Neri
User-Mode Instruction Prevention is a security feature present in new
Intel processors that, when set, prevents the execution of a subset of
instructions if such instructions are executed in user mode (CPL > 0).
Attempting to execute such instructions causes a general protection
exception.
The subset of instructions comprises:
* SGDT - Store Global Descriptor Table
* SIDT - Store Interrupt Descriptor Table
* SLDT - Store Local Descriptor Table
* SMSW - Store Machine Status Word
* STR - Store Task Register
This feature is also added to the list of disabled-features to allow
a cleaner handling of build-time configuration.
Cc: Andy Lutomirski <luto(a)kernel.org>
Cc: Andrew Morton <akpm(a)linux-foundation.org>
Cc: H. Peter Anvin <hpa(a)zytor.com>
Cc: Borislav Petkov <bp(a)suse.de>
Cc: Brian Gerst <brgerst(a)gmail.com>
Cc: Chen Yucong <slaoub(a)gmail.com>
Cc: Chris Metcalf <cmetcalf(a)mellanox.com>
Cc: Dave Hansen <dave.hansen(a)linux.intel.com>
Cc: Fenghua Yu <fenghua.yu(a)intel.com>
Cc: Huang Rui <ray.huang(a)amd.com>
Cc: Jiri Slaby <jslaby(a)suse.cz>
Cc: Jonathan Corbet <corbet(a)lwn.net>
Cc: Michael S. Tsirkin <mst(a)redhat.com>
Cc: Paul Gortmaker <paul.gortmaker(a)windriver.com>
Cc: Peter Zijlstra <peterz(a)infradead.org>
Cc: Ravi V. Shankar <ravi.v.shankar(a)intel.com>
Cc: Shuah Khan <shuah(a)kernel.org>
Cc: Vlastimil Babka <vbabka(a)suse.cz>
Cc: Tony Luck <tony.luck(a)intel.com>
Cc: Paolo Bonzini <pbonzini(a)redhat.com>
Cc: Liang Z. Li <liang.z.li(a)intel.com>
Cc: Alexandre Julliard <julliard(a)winehq.org>
Cc: Stas Sergeev <stsp(a)list.ru>
Cc: x86(a)kernel.org
Cc: linux-msdos(a)vger.kernel.org
Signed-off-by: Ricardo Neri <ricardo.neri-calderon(a)linux.intel.com>
---
arch/x86/include/asm/cpufeatures.h | 1 +
arch/x86/include/asm/disabled-features.h | 8 +++++++-
arch/x86/include/uapi/asm/processor-flags.h | 2 ++
3 files changed, 10 insertions(+), 1 deletion(-)
diff --git a/arch/x86/include/asm/cpufeatures.h b/arch/x86/include/asm/cpufeatures.h
index 2701e5f..f1d61d2 100644
--- a/arch/x86/include/asm/cpufeatures.h
+++ b/arch/x86/include/asm/cpufeatures.h
@@ -289,6 +289,7 @@
/* Intel-defined CPU features, CPUID level 0x00000007:0 (ecx), word 16 */
#define X86_FEATURE_AVX512VBMI (16*32+ 1) /* AVX512 Vector Bit Manipulation instructions*/
+#define X86_FEATURE_UMIP (16*32+ 2) /* User Mode Instruction Protection */
#define X86_FEATURE_PKU (16*32+ 3) /* Protection Keys for Userspace */
#define X86_FEATURE_OSPKE (16*32+ 4) /* OS Protection Keys Enable */
#define X86_FEATURE_AVX512_VPOPCNTDQ (16*32+14) /* POPCNT for vectors of DW/QW */
diff --git a/arch/x86/include/asm/disabled-features.h b/arch/x86/include/asm/disabled-features.h
index 5dff775..7adaef7 100644
--- a/arch/x86/include/asm/disabled-features.h
+++ b/arch/x86/include/asm/disabled-features.h
@@ -16,6 +16,12 @@
# define DISABLE_MPX (1<<(X86_FEATURE_MPX & 31))
#endif
+#ifdef CONFIG_X86_INTEL_UMIP
+# define DISABLE_UMIP 0
+#else
+# define DISABLE_UMIP (1<<(X86_FEATURE_UMIP & 31))
+#endif
+
#ifdef CONFIG_X86_64
# define DISABLE_VME (1<<(X86_FEATURE_VME & 31))
# define DISABLE_K6_MTRR (1<<(X86_FEATURE_K6_MTRR & 31))
@@ -61,7 +67,7 @@
#define DISABLED_MASK13 0
#define DISABLED_MASK14 0
#define DISABLED_MASK15 0
-#define DISABLED_MASK16 (DISABLE_PKU|DISABLE_OSPKE|DISABLE_LA57)
+#define DISABLED_MASK16 (DISABLE_PKU|DISABLE_OSPKE|DISABLE_LA57|DISABLE_UMIP)
#define DISABLED_MASK17 0
#define DISABLED_MASK_CHECK BUILD_BUG_ON_ZERO(NCAPINTS != 18)
diff --git a/arch/x86/include/uapi/asm/processor-flags.h b/arch/x86/include/uapi/asm/processor-flags.h
index 567de50..d2c2af8 100644
--- a/arch/x86/include/uapi/asm/processor-flags.h
+++ b/arch/x86/include/uapi/asm/processor-flags.h
@@ -104,6 +104,8 @@
#define X86_CR4_OSFXSR _BITUL(X86_CR4_OSFXSR_BIT)
#define X86_CR4_OSXMMEXCPT_BIT 10 /* enable unmasked SSE exceptions */
#define X86_CR4_OSXMMEXCPT _BITUL(X86_CR4_OSXMMEXCPT_BIT)
+#define X86_CR4_UMIP_BIT 11 /* enable UMIP support */
+#define X86_CR4_UMIP _BITUL(X86_CR4_UMIP_BIT)
#define X86_CR4_VMXE_BIT 13 /* enable VMX virtualization */
#define X86_CR4_VMXE _BITUL(X86_CR4_VMXE_BIT)
#define X86_CR4_SMXE_BIT 14 /* enable safer mode (TXT) */
--
2.9.3
May 5, 2017
[PATCH v7 19/26] x86/insn-eval: Add wrapper function for 16-bit and 32-bit address encodings
by Ricardo Neri
Convert the function insn_get_add_ref() into a wrapper function that calls
the correct static address-decoding function depending on the address size
In this way, callers do not need to worry about calling the correct
function and decreases the number of functions that need to be exposed.
To this end, the function insn_get_addr_ref() used to obtain linear
addresses from the 32/64-bit encodings is renamed as get_addr_ref_32_64()
to reflect the type of address encodings that it handles.
Documentation is added to the new wrapper function and the documentation
for the 32/64-bit address decoding function is improved.
Cc: Dave Hansen <dave.hansen(a)linux.intel.com>
Cc: Adam Buchbinder <adam.buchbinder(a)gmail.com>
Cc: Colin Ian King <colin.king(a)canonical.com>
Cc: Lorenzo Stoakes <lstoakes(a)gmail.com>
Cc: Qiaowei Ren <qiaowei.ren(a)intel.com>
Cc: Arnaldo Carvalho de Melo <acme(a)redhat.com>
Cc: Masami Hiramatsu <mhiramat(a)kernel.org>
Cc: Adrian Hunter <adrian.hunter(a)intel.com>
Cc: Kees Cook <keescook(a)chromium.org>
Cc: Thomas Garnier <thgarnie(a)google.com>
Cc: Peter Zijlstra <peterz(a)infradead.org>
Cc: Borislav Petkov <bp(a)suse.de>
Cc: Dmitry Vyukov <dvyukov(a)google.com>
Cc: Ravi V. Shankar <ravi.v.shankar(a)intel.com>
Cc: x86(a)kernel.org
Signed-off-by: Ricardo Neri <ricardo.neri-calderon(a)linux.intel.com>
---
arch/x86/lib/insn-eval.c | 48 +++++++++++++++++++++++++++++++++++++++++++-----
1 file changed, 43 insertions(+), 5 deletions(-)
diff --git a/arch/x86/lib/insn-eval.c b/arch/x86/lib/insn-eval.c
index 928a662..8914884 100644
--- a/arch/x86/lib/insn-eval.c
+++ b/arch/x86/lib/insn-eval.c
@@ -899,12 +899,22 @@ long get_mem_offset(struct pt_regs *regs, int reg_offset, int addr_size)
return -1L;
return offset;
}
-/*
- * return the address being referenced be instruction
- * for rm=3 returning the content of the rm reg
- * for rm!=3 calculates the address using SIB and Disp
+
+/**
+ * get_addr_ref_32_64() - Obtain a 32/64-bit linear address
+ * @insn: Instruction struct with ModRM and SiB bytes and displacement
+ * @regs: Structure with register values as seen when entering kernel mode
+ *
+ * This function is to be used with 32-bit and 64-bit address encodings to
+ * obtain the effective memory address referred by the instruction's ModRM,
+ * SIB, and displacement bytes, as applicable. Also, the segment base is used
+ * to compute the linear address. In protected mode, segment limits are
+ * enforced.
+ *
+ * Return: linear address referenced by instruction and registers on success.
+ * -1L on failure.
*/
-void __user *insn_get_addr_ref(struct insn *insn, struct pt_regs *regs)
+static void __user *get_addr_ref_32_64(struct insn *insn, struct pt_regs *regs)
{
unsigned long linear_addr, seg_base_addr, seg_limit;
long eff_addr, base, indx;
@@ -1026,3 +1036,31 @@ void __user *insn_get_addr_ref(struct insn *insn, struct pt_regs *regs)
out_err:
return (void __user *)-1;
}
+
+/**
+ * insn_get_addr_ref() - Obtain the linear address referred by instruction
+ * @insn: Instruction structure containing ModRM byte and displacement
+ * @regs: Structure with register values as seen when entering kernel mode
+ *
+ * Obtain the memory address referred by the instruction's ModRM bytes and
+ * displacement. Also, the segment used as base is determined by either any
+ * segment override prefixes in insn or the default segment of the registers
+ * involved in the address computation. In protected mode, segment limits
+ * are enforced.
+ *
+ * Return: linear address referenced by instruction and registers on success.
+ * -1L on failure.
+ */
+void __user *insn_get_addr_ref(struct insn *insn, struct pt_regs *regs)
+{
+ switch (insn->addr_bytes) {
+ case 2:
+ return get_addr_ref_16(insn, regs);
+ case 4:
+ /* fall through */
+ case 8:
+ return get_addr_ref_32_64(insn, regs);
+ default:
+ return (void __user *)-1;
+ }
+}
--
2.9.3
May 5, 2017
[PATCH v7 18/26] x86/insn-eval: Add support to resolve 16-bit addressing encodings
by Ricardo Neri
Tasks running in virtual-8086 mode or in protected mode with code
segment descriptors that specify 16-bit default address sizes via the
D bit will use 16-bit addressing form encodings as described in the Intel
64 and IA-32 Architecture Software Developer's Manual Volume 2A Section
2.1.5. 16-bit addressing encodings differ in several ways from the
32-bit/64-bit addressing form encodings: ModRM.rm points to different
registers and, in some cases, effective addresses are indicated by the
addition of the value of two registers. Also, there is no support for SIB
bytes. Thus, a separate function is needed to parse this form of
addressing.
A couple of functions are introduced. get_reg_offset_16() obtains the
offset from the base of pt_regs of the registers indicated by the ModRM
byte of the address encoding. get_addr_ref_16() computes the linear
address indicated by the instructions using the value of the registers
given by ModRM as well as the base address of the segment.
Cc: Dave Hansen <dave.hansen(a)linux.intel.com>
Cc: Adam Buchbinder <adam.buchbinder(a)gmail.com>
Cc: Colin Ian King <colin.king(a)canonical.com>
Cc: Lorenzo Stoakes <lstoakes(a)gmail.com>
Cc: Qiaowei Ren <qiaowei.ren(a)intel.com>
Cc: Arnaldo Carvalho de Melo <acme(a)redhat.com>
Cc: Masami Hiramatsu <mhiramat(a)kernel.org>
Cc: Adrian Hunter <adrian.hunter(a)intel.com>
Cc: Kees Cook <keescook(a)chromium.org>
Cc: Thomas Garnier <thgarnie(a)google.com>
Cc: Peter Zijlstra <peterz(a)infradead.org>
Cc: Borislav Petkov <bp(a)suse.de>
Cc: Dmitry Vyukov <dvyukov(a)google.com>
Cc: Ravi V. Shankar <ravi.v.shankar(a)intel.com>
Cc: x86(a)kernel.org
Signed-off-by: Ricardo Neri <ricardo.neri-calderon(a)linux.intel.com>
---
arch/x86/lib/insn-eval.c | 155 +++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 155 insertions(+)
diff --git a/arch/x86/lib/insn-eval.c b/arch/x86/lib/insn-eval.c
index 9822061..928a662 100644
--- a/arch/x86/lib/insn-eval.c
+++ b/arch/x86/lib/insn-eval.c
@@ -431,6 +431,73 @@ static int get_reg_offset(struct insn *insn, struct pt_regs *regs,
}
/**
+ * get_reg_offset_16 - Obtain offset of register indicated by instruction
+ * @insn: Instruction structure containing ModRM and SiB bytes
+ * @regs: Structure with register values as seen when entering kernel mode
+ * @offs1: Offset of the first operand register
+ * @offs2: Offset of the second opeand register, if applicable.
+ *
+ * Obtain the offset, in pt_regs, of the registers indicated by the ModRM byte
+ * within insn. This function is to be used with 16-bit address encodings. The
+ * offs1 and offs2 will be written with the offset of the two registers
+ * indicated by the instruction. In cases where any of the registers is not
+ * referenced by the instruction, the value will be set to -EDOM.
+ *
+ * Return: 0 on success, -EINVAL on failure.
+ */
+static int get_reg_offset_16(struct insn *insn, struct pt_regs *regs,
+ int *offs1, int *offs2)
+{
+ /* 16-bit addressing can use one or two registers */
+ static const int regoff1[] = {
+ offsetof(struct pt_regs, bx),
+ offsetof(struct pt_regs, bx),
+ offsetof(struct pt_regs, bp),
+ offsetof(struct pt_regs, bp),
+ offsetof(struct pt_regs, si),
+ offsetof(struct pt_regs, di),
+ offsetof(struct pt_regs, bp),
+ offsetof(struct pt_regs, bx),
+ };
+
+ static const int regoff2[] = {
+ offsetof(struct pt_regs, si),
+ offsetof(struct pt_regs, di),
+ offsetof(struct pt_regs, si),
+ offsetof(struct pt_regs, di),
+ -EDOM,
+ -EDOM,
+ -EDOM,
+ -EDOM,
+ };
+
+ if (!offs1 || !offs2)
+ return -EINVAL;
+
+ /* operand is a register, use the generic function */
+ if (X86_MODRM_MOD(insn->modrm.value) == 3) {
+ *offs1 = insn_get_modrm_rm_off(insn, regs);
+ *offs2 = -EDOM;
+ return 0;
+ }
+
+ *offs1 = regoff1[X86_MODRM_RM(insn->modrm.value)];
+ *offs2 = regoff2[X86_MODRM_RM(insn->modrm.value)];
+
+ /*
+ * If no displacement is indicated in the mod part of the ModRM byte,
+ * (mod part is 0) and the r/m part of the same byte is 6, no register
+ * is used caculate the operand address. An r/m part of 6 means that
+ * the second register offset is already invalid.
+ */
+ if ((X86_MODRM_MOD(insn->modrm.value) == 0) &&
+ (X86_MODRM_RM(insn->modrm.value) == 6))
+ *offs1 = -EDOM;
+
+ return 0;
+}
+
+/**
* get_desc() - Obtain address of segment descriptor
* @sel: Segment selector
*
@@ -689,6 +756,94 @@ int insn_get_modrm_rm_off(struct insn *insn, struct pt_regs *regs)
}
/**
+ * get_addr_ref_16() - Obtain the 16-bit address referred by instruction
+ * @insn: Instruction structure containing ModRM byte and displacement
+ * @regs: Structure with register values as seen when entering kernel mode
+ *
+ * This function is to be used with 16-bit address encodings. Obtain the memory
+ * address referred by the instruction's ModRM bytes and displacement. Also, the
+ * segment used as base is determined by either any segment override prefixes in
+ * insn or the default segment of the registers involved in the address
+ * computation. In protected mode, segment limits are enforced.
+ *
+ * Return: linear address referenced by instruction and registers on success.
+ * -1L on failure.
+ */
+static void __user *get_addr_ref_16(struct insn *insn, struct pt_regs *regs)
+{
+ unsigned long linear_addr, seg_base_addr, seg_limit;
+ short eff_addr, addr1 = 0, addr2 = 0;
+ int addr_offset1, addr_offset2;
+ int ret;
+
+ insn_get_modrm(insn);
+ insn_get_displacement(insn);
+
+ /*
+ * If operand is a register, the layout is the same as in
+ * 32-bit and 64-bit addressing.
+ */
+ if (X86_MODRM_MOD(insn->modrm.value) == 3) {
+ addr_offset1 = get_reg_offset(insn, regs, REG_TYPE_RM);
+ if (addr_offset1 < 0)
+ goto out_err;
+ eff_addr = regs_get_register(regs, addr_offset1);
+ seg_base_addr = insn_get_seg_base(regs, insn, addr_offset1);
+ if (seg_base_addr == -1L)
+ goto out_err;
+ seg_limit = get_seg_limit(regs, insn, addr_offset1);
+ } else {
+ ret = get_reg_offset_16(insn, regs, &addr_offset1,
+ &addr_offset2);
+ if (ret < 0)
+ goto out_err;
+ /*
+ * Don't fail on invalid offset values. They might be invalid
+ * because they cannot be used for this particular value of
+ * the ModRM. Instead, use them in the computation only if
+ * they contain a valid value.
+ */
+ if (addr_offset1 != -EDOM)
+ addr1 = 0xffff & regs_get_register(regs, addr_offset1);
+ if (addr_offset2 != -EDOM)
+ addr2 = 0xffff & regs_get_register(regs, addr_offset2);
+ eff_addr = addr1 + addr2;
+ /*
+ * The first register is in the operand implies the SS or DS
+ * segment selectors, the second register in the operand can
+ * only imply DS. Thus, use the first register to obtain
+ * the segment selector.
+ */
+ seg_base_addr = insn_get_seg_base(regs, insn, addr_offset1);
+ if (seg_base_addr == -1L)
+ goto out_err;
+ seg_limit = get_seg_limit(regs, insn, addr_offset1);
+
+ eff_addr += (insn->displacement.value & 0xffff);
+ }
+
+ linear_addr = (unsigned long)(eff_addr & 0xffff);
+
+ /*
+ * Make sure the effective address is within the limits of the
+ * segment. In long mode, the limit is -1L. Thus, the second part
+ * of the check always succeeds.
+ */
+ if (linear_addr > seg_limit)
+ goto out_err;
+
+ linear_addr += seg_base_addr;
+
+ /* Limit linear address to 20 bits */
+ if (v8086_mode(regs))
+ linear_addr &= 0xfffff;
+
+ return (void __user *)linear_addr;
+out_err:
+ return (void __user *)-1;
+}
+
+/**
* _to_signed_long() - Cast an unsigned long into signed long
* @val A 32-bit or 64-bit unsigned long
* @long_bytes The number of bytes used to represent a long number
--
2.9.3
May 5, 2017
[PATCH v7 17/26] x86/insn-eval: Handle 32-bit address encodings in virtual-8086 mode
by Ricardo Neri
It is possible to utilize 32-bit address encodings in virtual-8086 mode via
an address override instruction prefix. However, the range of address is
still limited to [0x-0xffff]. In such a case, return error.
Also, linear addresses in virtual-8086 mode are limited to 20 bits. Enforce
such limit by truncating the most significant bytes of the computed linear
address.
Cc: Dave Hansen <dave.hansen(a)linux.intel.com>
Cc: Adam Buchbinder <adam.buchbinder(a)gmail.com>
Cc: Colin Ian King <colin.king(a)canonical.com>
Cc: Lorenzo Stoakes <lstoakes(a)gmail.com>
Cc: Qiaowei Ren <qiaowei.ren(a)intel.com>
Cc: Arnaldo Carvalho de Melo <acme(a)redhat.com>
Cc: Masami Hiramatsu <mhiramat(a)kernel.org>
Cc: Adrian Hunter <adrian.hunter(a)intel.com>
Cc: Kees Cook <keescook(a)chromium.org>
Cc: Thomas Garnier <thgarnie(a)google.com>
Cc: Peter Zijlstra <peterz(a)infradead.org>
Cc: Borislav Petkov <bp(a)suse.de>
Cc: Dmitry Vyukov <dvyukov(a)google.com>
Cc: Ravi V. Shankar <ravi.v.shankar(a)intel.com>
Cc: x86(a)kernel.org
Signed-off-by: Ricardo Neri <ricardo.neri-calderon(a)linux.intel.com>
---
arch/x86/lib/insn-eval.c | 10 ++++++++++
1 file changed, 10 insertions(+)
diff --git a/arch/x86/lib/insn-eval.c b/arch/x86/lib/insn-eval.c
index c7c1239..9822061 100644
--- a/arch/x86/lib/insn-eval.c
+++ b/arch/x86/lib/insn-eval.c
@@ -848,6 +848,12 @@ void __user *insn_get_addr_ref(struct insn *insn, struct pt_regs *regs)
linear_addr &= 0xffffffff;
/*
+ * Even though 32-bit address encodings are allowed in virtual-8086
+ * mode, the address range is still limited to [0x-0xffff].
+ */
+ if (v8086_mode(regs) && (linear_addr & ~0xffff))
+ goto out_err;
+ /*
* Make sure the effective address is within the limits of the
* segment. In long mode, the limit is -1L. Thus, the second part
* of the check always succeeds.
@@ -857,6 +863,10 @@ void __user *insn_get_addr_ref(struct insn *insn, struct pt_regs *regs)
linear_addr += seg_base_addr;
+ /* Limit linear address to 20 bits */
+ if (v8086_mode(regs))
+ linear_addr &= 0xfffff;
+
return (void __user *)linear_addr;
out_err:
return (void __user *)-1;
--
2.9.3
May 5, 2017
[PATCH v7 16/26] x86/insn-eval: Support both signed 32-bit and 64-bit effective addresses
by Ricardo Neri
The 32-bit and 64-bit address encodings are identical. This means that we
can use the same function in both cases. In order to reuse the function
for 32-bit address encodings, we must sign-extend our 32-bit signed
operands to 64-bit signed variables (only for 64-bit builds). To decide on
whether sign extension is needed, we rely on the address size as given by
the instruction structure.
Once the effective address has been computed, a special verification is
needed for 32-bit processes. If running on a 64-bit kernel, such processes
can address up to 4GB of memory. Hence, for instance, an effective
address of 0xffff1234 would be misinterpreted as 0xffffffffffff1234 due to
the sign extension mentioned above. For this reason, the 4 must be
truncated to obtain the true effective address.
Lastly, before computing the linear address, we verify that the effective
address is within the limits of the segment. The check is kept for long
mode because in such a case the limit is set to -1L. This is the largest
unsigned number possible. This is equivalent to a limit-less segment.
Cc: Dave Hansen <dave.hansen(a)linux.intel.com>
Cc: Adam Buchbinder <adam.buchbinder(a)gmail.com>
Cc: Colin Ian King <colin.king(a)canonical.com>
Cc: Lorenzo Stoakes <lstoakes(a)gmail.com>
Cc: Qiaowei Ren <qiaowei.ren(a)intel.com>
Cc: Arnaldo Carvalho de Melo <acme(a)redhat.com>
Cc: Masami Hiramatsu <mhiramat(a)kernel.org>
Cc: Adrian Hunter <adrian.hunter(a)intel.com>
Cc: Kees Cook <keescook(a)chromium.org>
Cc: Thomas Garnier <thgarnie(a)google.com>
Cc: Peter Zijlstra <peterz(a)infradead.org>
Cc: Borislav Petkov <bp(a)suse.de>
Cc: Dmitry Vyukov <dvyukov(a)google.com>
Cc: Ravi V. Shankar <ravi.v.shankar(a)intel.com>
Cc: x86(a)kernel.org
Signed-off-by: Ricardo Neri <ricardo.neri-calderon(a)linux.intel.com>
---
arch/x86/lib/insn-eval.c | 99 ++++++++++++++++++++++++++++++++++++++++++------
1 file changed, 88 insertions(+), 11 deletions(-)
diff --git a/arch/x86/lib/insn-eval.c b/arch/x86/lib/insn-eval.c
index 1a5f5a6..c7c1239 100644
--- a/arch/x86/lib/insn-eval.c
+++ b/arch/x86/lib/insn-eval.c
@@ -688,6 +688,62 @@ int insn_get_modrm_rm_off(struct insn *insn, struct pt_regs *regs)
return get_reg_offset(insn, regs, REG_TYPE_RM);
}
+/**
+ * _to_signed_long() - Cast an unsigned long into signed long
+ * @val A 32-bit or 64-bit unsigned long
+ * @long_bytes The number of bytes used to represent a long number
+ * @out The casted signed long
+ *
+ * Return: A signed long of either 32 or 64 bits, as per the build configuration
+ * of the kernel.
+ */
+static int _to_signed_long(unsigned long val, int long_bytes, long *out)
+{
+ if (!out)
+ return -EINVAL;
+
+#ifdef CONFIG_X86_64
+ if (long_bytes == 4) {
+ /* higher bytes should all be zero */
+ if (val & ~0xffffffff)
+ return -EINVAL;
+
+ /* sign-extend to a 64-bit long */
+ *out = (long)((int)(val));
+ return 0;
+ } else if (long_bytes == 8) {
+ *out = (long)val;
+ return 0;
+ } else {
+ return -EINVAL;
+ }
+#else
+ *out = (long)val;
+ return 0;
+#endif
+}
+
+/** get_mem_offset() - Obtain the memory offset indicated in operand register
+ * @regs Structure with register values as seen when entering kernel mode
+ * @reg_offset Offset from the base of pt_regs of the operand register
+ * @addr_size Address size of the code segment in use
+ *
+ * Obtain the offset (a signed number with size as specified in addr_size)
+ * indicated in the register used for register-indirect memory adressing.
+ *
+ * Return: A memory offset to be used in the computation of effective address.
+ */
+long get_mem_offset(struct pt_regs *regs, int reg_offset, int addr_size)
+{
+ int ret;
+ long offset = -1L;
+ unsigned long uoffset = regs_get_register(regs, reg_offset);
+
+ ret = _to_signed_long(uoffset, addr_size, &offset);
+ if (ret)
+ return -1L;
+ return offset;
+}
/*
* return the address being referenced be instruction
* for rm=3 returning the content of the rm reg
@@ -697,18 +753,21 @@ void __user *insn_get_addr_ref(struct insn *insn, struct pt_regs *regs)
{
unsigned long linear_addr, seg_base_addr, seg_limit;
long eff_addr, base, indx;
- int addr_offset, base_offset, indx_offset;
+ int addr_offset, base_offset, indx_offset, addr_bytes;
insn_byte_t sib;
insn_get_modrm(insn);
insn_get_sib(insn);
sib = insn->sib.value;
+ addr_bytes = insn->addr_bytes;
if (X86_MODRM_MOD(insn->modrm.value) == 3) {
addr_offset = get_reg_offset(insn, regs, REG_TYPE_RM);
if (addr_offset < 0)
goto out_err;
- eff_addr = regs_get_register(regs, addr_offset);
+ eff_addr = get_mem_offset(regs, addr_offset, addr_bytes);
+ if (eff_addr == -1L)
+ goto out_err;
seg_base_addr = insn_get_seg_base(regs, insn, addr_offset);
if (seg_base_addr == -1L)
goto out_err;
@@ -722,20 +781,28 @@ void __user *insn_get_addr_ref(struct insn *insn, struct pt_regs *regs)
* in the address computation.
*/
base_offset = get_reg_offset(insn, regs, REG_TYPE_BASE);
- if (base_offset == -EDOM)
+ if (base_offset == -EDOM) {
base = 0;
- else if (base_offset < 0)
+ } else if (base_offset < 0) {
goto out_err;
- else
- base = regs_get_register(regs, base_offset);
+ } else {
+ base = get_mem_offset(regs, base_offset,
+ addr_bytes);
+ if (base == -1L)
+ goto out_err;
+ }
indx_offset = get_reg_offset(insn, regs, REG_TYPE_INDEX);
- if (indx_offset == -EDOM)
+ if (indx_offset == -EDOM) {
indx = 0;
- else if (indx_offset < 0)
+ } else if (indx_offset < 0) {
goto out_err;
- else
- indx = regs_get_register(regs, indx_offset);
+ } else {
+ indx = get_mem_offset(regs, indx_offset,
+ addr_bytes);
+ if (indx == -1L)
+ goto out_err;
+ }
eff_addr = base + indx * (1 << X86_SIB_SCALE(sib));
seg_base_addr = insn_get_seg_base(regs, insn,
@@ -758,7 +825,10 @@ void __user *insn_get_addr_ref(struct insn *insn, struct pt_regs *regs)
} else if (addr_offset < 0) {
goto out_err;
} else {
- eff_addr = regs_get_register(regs, addr_offset);
+ eff_addr = get_mem_offset(regs, addr_offset,
+ addr_bytes);
+ if (eff_addr == -1L)
+ goto out_err;
}
seg_base_addr = insn_get_seg_base(regs, insn,
addr_offset);
@@ -771,6 +841,13 @@ void __user *insn_get_addr_ref(struct insn *insn, struct pt_regs *regs)
linear_addr = (unsigned long)eff_addr;
/*
+ * If address size is 32-bit, truncate the 4 most significant bytes.
+ * This is to avoid phony negative offsets.
+ */
+ if (addr_bytes == 4)
+ linear_addr &= 0xffffffff;
+
+ /*
* Make sure the effective address is within the limits of the
* segment. In long mode, the limit is -1L. Thus, the second part
* of the check always succeeds.
--
2.9.3
May 5, 2017
[PATCH v7 15/26] x86/insn-eval: Incorporate segment base and limit in linear address computation
by Ricardo Neri
insn_get_addr_ref() returns the effective address as defined by the
section 3.7.5.1 Vol 1 of the Intel 64 and IA-32 Architectures Software
Developer's Manual. In order to compute the linear address, we must add
to the effective address the segment base address as set in the segment
descriptor. Furthermore, the segment descriptor to use depends on the
register that is used as the base of the effective address. The effective
base address varies depending on whether the operand is a register or a
memory address and on whether a SiB byte is used.
In most cases, the segment base address will be 0 if the USER_DS/USER32_DS
segment is used or if segmentation is not used. However, the base address
is not necessarily zero if a user programs defines its own segments. This
is possible by using a local descriptor table.
Since the effective address is a signed quantity, the unsigned segment
base address is saved in a separate variable and added to the final
effective address.
Before returning the linear address, we check if the computed effective
address is within the segment limit. In protected mode segment limits are
not enforced. We can keep the check as get_seg_limit() return -1L in this
case.
Cc: Dave Hansen <dave.hansen(a)linux.intel.com>
Cc: Adam Buchbinder <adam.buchbinder(a)gmail.com>
Cc: Colin Ian King <colin.king(a)canonical.com>
Cc: Lorenzo Stoakes <lstoakes(a)gmail.com>
Cc: Qiaowei Ren <qiaowei.ren(a)intel.com>
Cc: Arnaldo Carvalho de Melo <acme(a)redhat.com>
Cc: Masami Hiramatsu <mhiramat(a)kernel.org>
Cc: Adrian Hunter <adrian.hunter(a)intel.com>
Cc: Kees Cook <keescook(a)chromium.org>
Cc: Thomas Garnier <thgarnie(a)google.com>
Cc: Peter Zijlstra <peterz(a)infradead.org>
Cc: Borislav Petkov <bp(a)suse.de>
Cc: Dmitry Vyukov <dvyukov(a)google.com>
Cc: Ravi V. Shankar <ravi.v.shankar(a)intel.com>
Cc: x86(a)kernel.org
Signed-off-by: Ricardo Neri <ricardo.neri-calderon(a)linux.intel.com>
---
arch/x86/lib/insn-eval.c | 26 +++++++++++++++++++++++++-
1 file changed, 25 insertions(+), 1 deletion(-)
diff --git a/arch/x86/lib/insn-eval.c b/arch/x86/lib/insn-eval.c
index 4f600de..1a5f5a6 100644
--- a/arch/x86/lib/insn-eval.c
+++ b/arch/x86/lib/insn-eval.c
@@ -695,7 +695,7 @@ int insn_get_modrm_rm_off(struct insn *insn, struct pt_regs *regs)
*/
void __user *insn_get_addr_ref(struct insn *insn, struct pt_regs *regs)
{
- unsigned long linear_addr;
+ unsigned long linear_addr, seg_base_addr, seg_limit;
long eff_addr, base, indx;
int addr_offset, base_offset, indx_offset;
insn_byte_t sib;
@@ -709,6 +709,10 @@ void __user *insn_get_addr_ref(struct insn *insn, struct pt_regs *regs)
if (addr_offset < 0)
goto out_err;
eff_addr = regs_get_register(regs, addr_offset);
+ seg_base_addr = insn_get_seg_base(regs, insn, addr_offset);
+ if (seg_base_addr == -1L)
+ goto out_err;
+ seg_limit = get_seg_limit(regs, insn, addr_offset);
} else {
if (insn->sib.nbytes) {
/*
@@ -734,6 +738,11 @@ void __user *insn_get_addr_ref(struct insn *insn, struct pt_regs *regs)
indx = regs_get_register(regs, indx_offset);
eff_addr = base + indx * (1 << X86_SIB_SCALE(sib));
+ seg_base_addr = insn_get_seg_base(regs, insn,
+ base_offset);
+ if (seg_base_addr == -1L)
+ goto out_err;
+ seg_limit = get_seg_limit(regs, insn, base_offset);
} else {
addr_offset = get_reg_offset(insn, regs, REG_TYPE_RM);
/*
@@ -751,10 +760,25 @@ void __user *insn_get_addr_ref(struct insn *insn, struct pt_regs *regs)
} else {
eff_addr = regs_get_register(regs, addr_offset);
}
+ seg_base_addr = insn_get_seg_base(regs, insn,
+ addr_offset);
+ if (seg_base_addr == -1L)
+ goto out_err;
+ seg_limit = get_seg_limit(regs, insn, addr_offset);
}
eff_addr += insn->displacement.value;
}
+
linear_addr = (unsigned long)eff_addr;
+ /*
+ * Make sure the effective address is within the limits of the
+ * segment. In long mode, the limit is -1L. Thus, the second part
+ * of the check always succeeds.
+ */
+ if (linear_addr > seg_limit)
+ goto out_err;
+
+ linear_addr += seg_base_addr;
return (void __user *)linear_addr;
out_err:
--
2.9.3
May 5, 2017
[PATCH v7 14/26] x86/insn-eval: Indicate a 32-bit displacement if ModRM.mod is 0 and ModRM.rm is 5
by Ricardo Neri
Section 2.2.1.3 of the Intel 64 and IA-32 Architectures Software
Developer's Manual volume 2A states that when ModRM.mod is zero and
ModRM.rm is 101b, a 32-bit displacement follows the ModRM byte. This means
that none of the registers are used in the computation of the effective
address. A return value of -EDOM signals callers that they should not use
the value of registers when computing the effective address for the
instruction.
In IA-32e 64-bit mode (long mode), the effective address is given by the
32-bit displacement plus the value of RIP of the next instruction.
In IA-32e compatibility mode (protected mode), only the displacement is
used.
The instruction decoder takes care of obtaining the displacement.
Cc: Dave Hansen <dave.hansen(a)linux.intel.com>
Cc: Adam Buchbinder <adam.buchbinder(a)gmail.com>
Cc: Colin Ian King <colin.king(a)canonical.com>
Cc: Lorenzo Stoakes <lstoakes(a)gmail.com>
Cc: Qiaowei Ren <qiaowei.ren(a)intel.com>
Cc: Arnaldo Carvalho de Melo <acme(a)redhat.com>
Cc: Masami Hiramatsu <mhiramat(a)kernel.org>
Cc: Adrian Hunter <adrian.hunter(a)intel.com>
Cc: Kees Cook <keescook(a)chromium.org>
Cc: Thomas Garnier <thgarnie(a)google.com>
Cc: Peter Zijlstra <peterz(a)infradead.org>
Cc: Borislav Petkov <bp(a)suse.de>
Cc: Dmitry Vyukov <dvyukov(a)google.com>
Cc: Ravi V. Shankar <ravi.v.shankar(a)intel.com>
Cc: x86(a)kernel.org
Signed-off-by: Ricardo Neri <ricardo.neri-calderon(a)linux.intel.com>
---
arch/x86/lib/insn-eval.c | 22 ++++++++++++++++++++--
1 file changed, 20 insertions(+), 2 deletions(-)
diff --git a/arch/x86/lib/insn-eval.c b/arch/x86/lib/insn-eval.c
index 693e5a8..4f600de 100644
--- a/arch/x86/lib/insn-eval.c
+++ b/arch/x86/lib/insn-eval.c
@@ -379,6 +379,12 @@ static int get_reg_offset(struct insn *insn, struct pt_regs *regs,
switch (type) {
case REG_TYPE_RM:
regno = X86_MODRM_RM(insn->modrm.value);
+ /*
+ * ModRM.mod == 0 and ModRM.rm == 5 means a 32-bit displacement
+ * follows the ModRM byte.
+ */
+ if (!X86_MODRM_MOD(insn->modrm.value) && regno == 5)
+ return -EDOM;
if (X86_REX_B(insn->rex_prefix.value))
regno += 8;
break;
@@ -730,9 +736,21 @@ void __user *insn_get_addr_ref(struct insn *insn, struct pt_regs *regs)
eff_addr = base + indx * (1 << X86_SIB_SCALE(sib));
} else {
addr_offset = get_reg_offset(insn, regs, REG_TYPE_RM);
- if (addr_offset < 0)
+ /*
+ * -EDOM means that we must ignore the address_offset.
+ * In such a case, in 64-bit mode the effective address
+ * relative to the RIP of the following instruction.
+ */
+ if (addr_offset == -EDOM) {
+ eff_addr = 0;
+ if (user_64bit_mode(regs))
+ eff_addr = (long)regs->ip +
+ insn->length;
+ } else if (addr_offset < 0) {
goto out_err;
- eff_addr = regs_get_register(regs, addr_offset);
+ } else {
+ eff_addr = regs_get_register(regs, addr_offset);
+ }
}
eff_addr += insn->displacement.value;
}
--
2.9.3
May 5, 2017