mirror of
https://github.com/t2linux/kernel.git
synced 2026-04-30 13:48:59 -07:00
Merge branch 'x86-mpx-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip
Pull x86 MPX support from Thomas Gleixner: "This enables support for x86 MPX. MPX is a new debug feature for bound checking in user space. It requires kernel support to handle the bound tables and decode the bound violating instruction in the trap handler" * 'x86-mpx-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip: asm-generic: Remove asm-generic arch_bprm_mm_init() mm: Make arch_unmap()/bprm_mm_init() available to all architectures x86: Cleanly separate use of asm-generic/mm_hooks.h x86 mpx: Change return type of get_reg_offset() fs: Do not include mpx.h in exec.c x86, mpx: Add documentation on Intel MPX x86, mpx: Cleanup unused bound tables x86, mpx: On-demand kernel allocation of bounds tables x86, mpx: Decode MPX instruction to get bound violation information x86, mpx: Add MPX-specific mmap interface x86, mpx: Introduce VM_MPX to indicate that a VMA is MPX specific x86, mpx: Add MPX to disabled features ia64: Sync struct siginfo with general version mips: Sync struct siginfo with general version mpx: Extend siginfo structure to include bound violation information x86, mpx: Rename cfg_reg_u and status_reg x86: mpx: Give bndX registers actual names x86: Remove arbitrary instruction size limit in instruction decoder
This commit is contained in:
@@ -0,0 +1,234 @@
|
||||
1. Intel(R) MPX Overview
|
||||
========================
|
||||
|
||||
Intel(R) Memory Protection Extensions (Intel(R) MPX) is a new capability
|
||||
introduced into Intel Architecture. Intel MPX provides hardware features
|
||||
that can be used in conjunction with compiler changes to check memory
|
||||
references, for those references whose compile-time normal intentions are
|
||||
usurped at runtime due to buffer overflow or underflow.
|
||||
|
||||
For more information, please refer to Intel(R) Architecture Instruction
|
||||
Set Extensions Programming Reference, Chapter 9: Intel(R) Memory Protection
|
||||
Extensions.
|
||||
|
||||
Note: Currently no hardware with MPX ISA is available but it is always
|
||||
possible to use SDE (Intel(R) Software Development Emulator) instead, which
|
||||
can be downloaded from
|
||||
http://software.intel.com/en-us/articles/intel-software-development-emulator
|
||||
|
||||
|
||||
2. How to get the advantage of MPX
|
||||
==================================
|
||||
|
||||
For MPX to work, changes are required in the kernel, binutils and compiler.
|
||||
No source changes are required for applications, just a recompile.
|
||||
|
||||
There are a lot of moving parts of this to all work right. The following
|
||||
is how we expect the compiler, application and kernel to work together.
|
||||
|
||||
1) Application developer compiles with -fmpx. The compiler will add the
|
||||
instrumentation as well as some setup code called early after the app
|
||||
starts. New instruction prefixes are noops for old CPUs.
|
||||
2) That setup code allocates (virtual) space for the "bounds directory",
|
||||
points the "bndcfgu" register to the directory and notifies the kernel
|
||||
(via the new prctl(PR_MPX_ENABLE_MANAGEMENT)) that the app will be using
|
||||
MPX.
|
||||
3) The kernel detects that the CPU has MPX, allows the new prctl() to
|
||||
succeed, and notes the location of the bounds directory. Userspace is
|
||||
expected to keep the bounds directory at that locationWe note it
|
||||
instead of reading it each time because the 'xsave' operation needed
|
||||
to access the bounds directory register is an expensive operation.
|
||||
4) If the application needs to spill bounds out of the 4 registers, it
|
||||
issues a bndstx instruction. Since the bounds directory is empty at
|
||||
this point, a bounds fault (#BR) is raised, the kernel allocates a
|
||||
bounds table (in the user address space) and makes the relevant entry
|
||||
in the bounds directory point to the new table.
|
||||
5) If the application violates the bounds specified in the bounds registers,
|
||||
a separate kind of #BR is raised which will deliver a signal with
|
||||
information about the violation in the 'struct siginfo'.
|
||||
6) Whenever memory is freed, we know that it can no longer contain valid
|
||||
pointers, and we attempt to free the associated space in the bounds
|
||||
tables. If an entire table becomes unused, we will attempt to free
|
||||
the table and remove the entry in the directory.
|
||||
|
||||
To summarize, there are essentially three things interacting here:
|
||||
|
||||
GCC with -fmpx:
|
||||
* enables annotation of code with MPX instructions and prefixes
|
||||
* inserts code early in the application to call in to the "gcc runtime"
|
||||
GCC MPX Runtime:
|
||||
* Checks for hardware MPX support in cpuid leaf
|
||||
* allocates virtual space for the bounds directory (malloc() essentially)
|
||||
* points the hardware BNDCFGU register at the directory
|
||||
* calls a new prctl(PR_MPX_ENABLE_MANAGEMENT) to notify the kernel to
|
||||
start managing the bounds directories
|
||||
Kernel MPX Code:
|
||||
* Checks for hardware MPX support in cpuid leaf
|
||||
* Handles #BR exceptions and sends SIGSEGV to the app when it violates
|
||||
bounds, like during a buffer overflow.
|
||||
* When bounds are spilled in to an unallocated bounds table, the kernel
|
||||
notices in the #BR exception, allocates the virtual space, then
|
||||
updates the bounds directory to point to the new table. It keeps
|
||||
special track of the memory with a VM_MPX flag.
|
||||
* Frees unused bounds tables at the time that the memory they described
|
||||
is unmapped.
|
||||
|
||||
|
||||
3. How does MPX kernel code work
|
||||
================================
|
||||
|
||||
Handling #BR faults caused by MPX
|
||||
---------------------------------
|
||||
|
||||
When MPX is enabled, there are 2 new situations that can generate
|
||||
#BR faults.
|
||||
* new bounds tables (BT) need to be allocated to save bounds.
|
||||
* bounds violation caused by MPX instructions.
|
||||
|
||||
We hook #BR handler to handle these two new situations.
|
||||
|
||||
On-demand kernel allocation of bounds tables
|
||||
--------------------------------------------
|
||||
|
||||
MPX only has 4 hardware registers for storing bounds information. If
|
||||
MPX-enabled code needs more than these 4 registers, it needs to spill
|
||||
them somewhere. It has two special instructions for this which allow
|
||||
the bounds to be moved between the bounds registers and some new "bounds
|
||||
tables".
|
||||
|
||||
#BR exceptions are a new class of exceptions just for MPX. They are
|
||||
similar conceptually to a page fault and will be raised by the MPX
|
||||
hardware during both bounds violations or when the tables are not
|
||||
present. The kernel handles those #BR exceptions for not-present tables
|
||||
by carving the space out of the normal processes address space and then
|
||||
pointing the bounds-directory over to it.
|
||||
|
||||
The tables need to be accessed and controlled by userspace because
|
||||
the instructions for moving bounds in and out of them are extremely
|
||||
frequent. They potentially happen every time a register points to
|
||||
memory. Any direct kernel involvement (like a syscall) to access the
|
||||
tables would obviously destroy performance.
|
||||
|
||||
Why not do this in userspace? MPX does not strictly require anything in
|
||||
the kernel. It can theoretically be done completely from userspace. Here
|
||||
are a few ways this could be done. We don't think any of them are practical
|
||||
in the real-world, but here they are.
|
||||
|
||||
Q: Can virtual space simply be reserved for the bounds tables so that we
|
||||
never have to allocate them?
|
||||
A: MPX-enabled application will possibly create a lot of bounds tables in
|
||||
process address space to save bounds information. These tables can take
|
||||
up huge swaths of memory (as much as 80% of the memory on the system)
|
||||
even if we clean them up aggressively. In the worst-case scenario, the
|
||||
tables can be 4x the size of the data structure being tracked. IOW, a
|
||||
1-page structure can require 4 bounds-table pages. An X-GB virtual
|
||||
area needs 4*X GB of virtual space, plus 2GB for the bounds directory.
|
||||
If we were to preallocate them for the 128TB of user virtual address
|
||||
space, we would need to reserve 512TB+2GB, which is larger than the
|
||||
entire virtual address space today. This means they can not be reserved
|
||||
ahead of time. Also, a single process's pre-popualated bounds directory
|
||||
consumes 2GB of virtual *AND* physical memory. IOW, it's completely
|
||||
infeasible to prepopulate bounds directories.
|
||||
|
||||
Q: Can we preallocate bounds table space at the same time memory is
|
||||
allocated which might contain pointers that might eventually need
|
||||
bounds tables?
|
||||
A: This would work if we could hook the site of each and every memory
|
||||
allocation syscall. This can be done for small, constrained applications.
|
||||
But, it isn't practical at a larger scale since a given app has no
|
||||
way of controlling how all the parts of the app might allocate memory
|
||||
(think libraries). The kernel is really the only place to intercept
|
||||
these calls.
|
||||
|
||||
Q: Could a bounds fault be handed to userspace and the tables allocated
|
||||
there in a signal handler intead of in the kernel?
|
||||
A: mmap() is not on the list of safe async handler functions and even
|
||||
if mmap() would work it still requires locking or nasty tricks to
|
||||
keep track of the allocation state there.
|
||||
|
||||
Having ruled out all of the userspace-only approaches for managing
|
||||
bounds tables that we could think of, we create them on demand in
|
||||
the kernel.
|
||||
|
||||
Decoding MPX instructions
|
||||
-------------------------
|
||||
|
||||
If a #BR is generated due to a bounds violation caused by MPX.
|
||||
We need to decode MPX instructions to get violation address and
|
||||
set this address into extended struct siginfo.
|
||||
|
||||
The _sigfault feild of struct siginfo is extended as follow:
|
||||
|
||||
87 /* SIGILL, SIGFPE, SIGSEGV, SIGBUS */
|
||||
88 struct {
|
||||
89 void __user *_addr; /* faulting insn/memory ref. */
|
||||
90 #ifdef __ARCH_SI_TRAPNO
|
||||
91 int _trapno; /* TRAP # which caused the signal */
|
||||
92 #endif
|
||||
93 short _addr_lsb; /* LSB of the reported address */
|
||||
94 struct {
|
||||
95 void __user *_lower;
|
||||
96 void __user *_upper;
|
||||
97 } _addr_bnd;
|
||||
98 } _sigfault;
|
||||
|
||||
The '_addr' field refers to violation address, and new '_addr_and'
|
||||
field refers to the upper/lower bounds when a #BR is caused.
|
||||
|
||||
Glibc will be also updated to support this new siginfo. So user
|
||||
can get violation address and bounds when bounds violations occur.
|
||||
|
||||
Cleanup unused bounds tables
|
||||
----------------------------
|
||||
|
||||
When a BNDSTX instruction attempts to save bounds to a bounds directory
|
||||
entry marked as invalid, a #BR is generated. This is an indication that
|
||||
no bounds table exists for this entry. In this case the fault handler
|
||||
will allocate a new bounds table on demand.
|
||||
|
||||
Since the kernel allocated those tables on-demand without userspace
|
||||
knowledge, it is also responsible for freeing them when the associated
|
||||
mappings go away.
|
||||
|
||||
Here, the solution for this issue is to hook do_munmap() to check
|
||||
whether one process is MPX enabled. If yes, those bounds tables covered
|
||||
in the virtual address region which is being unmapped will be freed also.
|
||||
|
||||
Adding new prctl commands
|
||||
-------------------------
|
||||
|
||||
Two new prctl commands are added to enable and disable MPX bounds tables
|
||||
management in kernel.
|
||||
|
||||
155 #define PR_MPX_ENABLE_MANAGEMENT 43
|
||||
156 #define PR_MPX_DISABLE_MANAGEMENT 44
|
||||
|
||||
Runtime library in userspace is responsible for allocation of bounds
|
||||
directory. So kernel have to use XSAVE instruction to get the base
|
||||
of bounds directory from BNDCFG register.
|
||||
|
||||
But XSAVE is expected to be very expensive. In order to do performance
|
||||
optimization, we have to get the base of bounds directory and save it
|
||||
into struct mm_struct to be used in future during PR_MPX_ENABLE_MANAGEMENT
|
||||
command execution.
|
||||
|
||||
|
||||
4. Special rules
|
||||
================
|
||||
|
||||
1) If userspace is requesting help from the kernel to do the management
|
||||
of bounds tables, it may not create or modify entries in the bounds directory.
|
||||
|
||||
Certainly users can allocate bounds tables and forcibly point the bounds
|
||||
directory at them through XSAVE instruction, and then set valid bit
|
||||
of bounds entry to have this entry valid. But, the kernel will decline
|
||||
to assist in managing these tables.
|
||||
|
||||
2) Userspace may not take multiple bounds directory entries and point
|
||||
them at the same bounds table.
|
||||
|
||||
This is allowed architecturally. See more information "Intel(R) Architecture
|
||||
Instruction Set Extensions Programming Reference" (9.3.4).
|
||||
|
||||
However, if users did this, the kernel might be fooled in to unmaping an
|
||||
in-use bounds table since it does not recognize sharing.
|
||||
@@ -63,6 +63,10 @@ typedef struct siginfo {
|
||||
unsigned int _flags; /* see below */
|
||||
unsigned long _isr; /* isr */
|
||||
short _addr_lsb; /* lsb of faulting address */
|
||||
struct {
|
||||
void __user *_lower;
|
||||
void __user *_upper;
|
||||
} _addr_bnd;
|
||||
} _sigfault;
|
||||
|
||||
/* SIGPOLL */
|
||||
@@ -110,9 +114,9 @@ typedef struct siginfo {
|
||||
/*
|
||||
* SIGSEGV si_codes
|
||||
*/
|
||||
#define __SEGV_PSTKOVF (__SI_FAULT|3) /* paragraph stack overflow */
|
||||
#define __SEGV_PSTKOVF (__SI_FAULT|4) /* paragraph stack overflow */
|
||||
#undef NSIGSEGV
|
||||
#define NSIGSEGV 3
|
||||
#define NSIGSEGV 4
|
||||
|
||||
#undef NSIGTRAP
|
||||
#define NSIGTRAP 4
|
||||
|
||||
@@ -92,6 +92,10 @@ typedef struct siginfo {
|
||||
int _trapno; /* TRAP # which caused the signal */
|
||||
#endif
|
||||
short _addr_lsb;
|
||||
struct {
|
||||
void __user *_lower;
|
||||
void __user *_upper;
|
||||
} _addr_bnd;
|
||||
} _sigfault;
|
||||
|
||||
/* SIGPOLL, SIGXFSZ (To do ...) */
|
||||
|
||||
@@ -120,4 +120,15 @@ static inline void arch_exit_mmap(struct mm_struct *mm)
|
||||
{
|
||||
}
|
||||
|
||||
static inline void arch_unmap(struct mm_struct *mm,
|
||||
struct vm_area_struct *vma,
|
||||
unsigned long start, unsigned long end)
|
||||
{
|
||||
}
|
||||
|
||||
static inline void arch_bprm_mm_init(struct mm_struct *mm,
|
||||
struct vm_area_struct *vma)
|
||||
{
|
||||
}
|
||||
|
||||
#endif /* __S390_MMU_CONTEXT_H */
|
||||
|
||||
@@ -10,7 +10,26 @@
|
||||
#include <asm/mmu.h>
|
||||
|
||||
extern void uml_setup_stubs(struct mm_struct *mm);
|
||||
/*
|
||||
* Needed since we do not use the asm-generic/mm_hooks.h:
|
||||
*/
|
||||
static inline void arch_dup_mmap(struct mm_struct *oldmm, struct mm_struct *mm)
|
||||
{
|
||||
uml_setup_stubs(mm);
|
||||
}
|
||||
extern void arch_exit_mmap(struct mm_struct *mm);
|
||||
static inline void arch_unmap(struct mm_struct *mm,
|
||||
struct vm_area_struct *vma,
|
||||
unsigned long start, unsigned long end)
|
||||
{
|
||||
}
|
||||
static inline void arch_bprm_mm_init(struct mm_struct *mm,
|
||||
struct vm_area_struct *vma)
|
||||
{
|
||||
}
|
||||
/*
|
||||
* end asm-generic/mm_hooks.h functions
|
||||
*/
|
||||
|
||||
#define deactivate_mm(tsk,mm) do { } while (0)
|
||||
|
||||
@@ -41,11 +60,6 @@ static inline void switch_mm(struct mm_struct *prev, struct mm_struct *next,
|
||||
}
|
||||
}
|
||||
|
||||
static inline void arch_dup_mmap(struct mm_struct *oldmm, struct mm_struct *mm)
|
||||
{
|
||||
uml_setup_stubs(mm);
|
||||
}
|
||||
|
||||
static inline void enter_lazy_tlb(struct mm_struct *mm,
|
||||
struct task_struct *tsk)
|
||||
{
|
||||
|
||||
@@ -86,4 +86,15 @@ static inline void arch_dup_mmap(struct mm_struct *oldmm,
|
||||
{
|
||||
}
|
||||
|
||||
static inline void arch_unmap(struct mm_struct *mm,
|
||||
struct vm_area_struct *vma,
|
||||
unsigned long start, unsigned long end)
|
||||
{
|
||||
}
|
||||
|
||||
static inline void arch_bprm_mm_init(struct mm_struct *mm,
|
||||
struct vm_area_struct *vma)
|
||||
{
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -248,6 +248,10 @@ config HAVE_INTEL_TXT
|
||||
def_bool y
|
||||
depends on INTEL_IOMMU && ACPI
|
||||
|
||||
config X86_INTEL_MPX
|
||||
def_bool y
|
||||
depends on CPU_SUP_INTEL
|
||||
|
||||
config X86_32_SMP
|
||||
def_bool y
|
||||
depends on X86_32 && SMP
|
||||
|
||||
@@ -10,6 +10,12 @@
|
||||
* cpu_feature_enabled().
|
||||
*/
|
||||
|
||||
#ifdef CONFIG_X86_INTEL_MPX
|
||||
# define DISABLE_MPX 0
|
||||
#else
|
||||
# define DISABLE_MPX (1<<(X86_FEATURE_MPX & 31))
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_X86_64
|
||||
# define DISABLE_VME (1<<(X86_FEATURE_VME & 31))
|
||||
# define DISABLE_K6_MTRR (1<<(X86_FEATURE_K6_MTRR & 31))
|
||||
@@ -34,6 +40,6 @@
|
||||
#define DISABLED_MASK6 0
|
||||
#define DISABLED_MASK7 0
|
||||
#define DISABLED_MASK8 0
|
||||
#define DISABLED_MASK9 0
|
||||
#define DISABLED_MASK9 (DISABLE_MPX)
|
||||
|
||||
#endif /* _ASM_X86_DISABLED_FEATURES_H */
|
||||
|
||||
@@ -65,6 +65,7 @@ struct insn {
|
||||
unsigned char x86_64;
|
||||
|
||||
const insn_byte_t *kaddr; /* kernel address of insn to analyze */
|
||||
const insn_byte_t *end_kaddr; /* kernel address of last insn in buffer */
|
||||
const insn_byte_t *next_byte;
|
||||
};
|
||||
|
||||
@@ -96,7 +97,7 @@ struct insn {
|
||||
#define X86_VEX_P(vex) ((vex) & 0x03) /* VEX3 Byte2, VEX2 Byte1 */
|
||||
#define X86_VEX_M_MAX 0x1f /* VEX3.M Maximum value */
|
||||
|
||||
extern void insn_init(struct insn *insn, const void *kaddr, int x86_64);
|
||||
extern void insn_init(struct insn *insn, const void *kaddr, int buf_len, int x86_64);
|
||||
extern void insn_get_prefixes(struct insn *insn);
|
||||
extern void insn_get_opcode(struct insn *insn);
|
||||
extern void insn_get_modrm(struct insn *insn);
|
||||
@@ -115,12 +116,13 @@ static inline void insn_get_attribute(struct insn *insn)
|
||||
extern int insn_rip_relative(struct insn *insn);
|
||||
|
||||
/* Init insn for kernel text */
|
||||
static inline void kernel_insn_init(struct insn *insn, const void *kaddr)
|
||||
static inline void kernel_insn_init(struct insn *insn,
|
||||
const void *kaddr, int buf_len)
|
||||
{
|
||||
#ifdef CONFIG_X86_64
|
||||
insn_init(insn, kaddr, 1);
|
||||
insn_init(insn, kaddr, buf_len, 1);
|
||||
#else /* CONFIG_X86_32 */
|
||||
insn_init(insn, kaddr, 0);
|
||||
insn_init(insn, kaddr, buf_len, 0);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
@@ -10,9 +10,8 @@
|
||||
#include <asm/pgalloc.h>
|
||||
#include <asm/tlbflush.h>
|
||||
#include <asm/paravirt.h>
|
||||
#include <asm/mpx.h>
|
||||
#ifndef CONFIG_PARAVIRT
|
||||
#include <asm-generic/mm_hooks.h>
|
||||
|
||||
static inline void paravirt_activate_mm(struct mm_struct *prev,
|
||||
struct mm_struct *next)
|
||||
{
|
||||
@@ -102,4 +101,27 @@ do { \
|
||||
} while (0)
|
||||
#endif
|
||||
|
||||
static inline void arch_dup_mmap(struct mm_struct *oldmm,
|
||||
struct mm_struct *mm)
|
||||
{
|
||||
paravirt_arch_dup_mmap(oldmm, mm);
|
||||
}
|
||||
|
||||
static inline void arch_exit_mmap(struct mm_struct *mm)
|
||||
{
|
||||
paravirt_arch_exit_mmap(mm);
|
||||
}
|
||||
|
||||
static inline void arch_bprm_mm_init(struct mm_struct *mm,
|
||||
struct vm_area_struct *vma)
|
||||
{
|
||||
mpx_mm_init(mm);
|
||||
}
|
||||
|
||||
static inline void arch_unmap(struct mm_struct *mm, struct vm_area_struct *vma,
|
||||
unsigned long start, unsigned long end)
|
||||
{
|
||||
mpx_notify_unmap(mm, vma, start, end);
|
||||
}
|
||||
|
||||
#endif /* _ASM_X86_MMU_CONTEXT_H */
|
||||
|
||||
@@ -0,0 +1,103 @@
|
||||
#ifndef _ASM_X86_MPX_H
|
||||
#define _ASM_X86_MPX_H
|
||||
|
||||
#include <linux/types.h>
|
||||
#include <asm/ptrace.h>
|
||||
#include <asm/insn.h>
|
||||
|
||||
/*
|
||||
* NULL is theoretically a valid place to put the bounds
|
||||
* directory, so point this at an invalid address.
|
||||
*/
|
||||
#define MPX_INVALID_BOUNDS_DIR ((void __user *)-1)
|
||||
#define MPX_BNDCFG_ENABLE_FLAG 0x1
|
||||
#define MPX_BD_ENTRY_VALID_FLAG 0x1
|
||||
|
||||
#ifdef CONFIG_X86_64
|
||||
|
||||
/* upper 28 bits [47:20] of the virtual address in 64-bit used to
|
||||
* index into bounds directory (BD).
|
||||
*/
|
||||
#define MPX_BD_ENTRY_OFFSET 28
|
||||
#define MPX_BD_ENTRY_SHIFT 3
|
||||
/* bits [19:3] of the virtual address in 64-bit used to index into
|
||||
* bounds table (BT).
|
||||
*/
|
||||
#define MPX_BT_ENTRY_OFFSET 17
|
||||
#define MPX_BT_ENTRY_SHIFT 5
|
||||
#define MPX_IGN_BITS 3
|
||||
#define MPX_BD_ENTRY_TAIL 3
|
||||
|
||||
#else
|
||||
|
||||
#define MPX_BD_ENTRY_OFFSET 20
|
||||
#define MPX_BD_ENTRY_SHIFT 2
|
||||
#define MPX_BT_ENTRY_OFFSET 10
|
||||
#define MPX_BT_ENTRY_SHIFT 4
|
||||
#define MPX_IGN_BITS 2
|
||||
#define MPX_BD_ENTRY_TAIL 2
|
||||
|
||||
#endif
|
||||
|
||||
#define MPX_BD_SIZE_BYTES (1UL<<(MPX_BD_ENTRY_OFFSET+MPX_BD_ENTRY_SHIFT))
|
||||
#define MPX_BT_SIZE_BYTES (1UL<<(MPX_BT_ENTRY_OFFSET+MPX_BT_ENTRY_SHIFT))
|
||||
|
||||
#define MPX_BNDSTA_TAIL 2
|
||||
#define MPX_BNDCFG_TAIL 12
|
||||
#define MPX_BNDSTA_ADDR_MASK (~((1UL<<MPX_BNDSTA_TAIL)-1))
|
||||
#define MPX_BNDCFG_ADDR_MASK (~((1UL<<MPX_BNDCFG_TAIL)-1))
|
||||
#define MPX_BT_ADDR_MASK (~((1UL<<MPX_BD_ENTRY_TAIL)-1))
|
||||
|
||||
#define MPX_BNDCFG_ADDR_MASK (~((1UL<<MPX_BNDCFG_TAIL)-1))
|
||||
#define MPX_BNDSTA_ERROR_CODE 0x3
|
||||
|
||||
#define MPX_BD_ENTRY_MASK ((1<<MPX_BD_ENTRY_OFFSET)-1)
|
||||
#define MPX_BT_ENTRY_MASK ((1<<MPX_BT_ENTRY_OFFSET)-1)
|
||||
#define MPX_GET_BD_ENTRY_OFFSET(addr) ((((addr)>>(MPX_BT_ENTRY_OFFSET+ \
|
||||
MPX_IGN_BITS)) & MPX_BD_ENTRY_MASK) << MPX_BD_ENTRY_SHIFT)
|
||||
#define MPX_GET_BT_ENTRY_OFFSET(addr) ((((addr)>>MPX_IGN_BITS) & \
|
||||
MPX_BT_ENTRY_MASK) << MPX_BT_ENTRY_SHIFT)
|
||||
|
||||
#ifdef CONFIG_X86_INTEL_MPX
|
||||
siginfo_t *mpx_generate_siginfo(struct pt_regs *regs,
|
||||
struct xsave_struct *xsave_buf);
|
||||
int mpx_handle_bd_fault(struct xsave_struct *xsave_buf);
|
||||
static inline int kernel_managing_mpx_tables(struct mm_struct *mm)
|
||||
{
|
||||
return (mm->bd_addr != MPX_INVALID_BOUNDS_DIR);
|
||||
}
|
||||
static inline void mpx_mm_init(struct mm_struct *mm)
|
||||
{
|
||||
/*
|
||||
* NULL is theoretically a valid place to put the bounds
|
||||
* directory, so point this at an invalid address.
|
||||
*/
|
||||
mm->bd_addr = MPX_INVALID_BOUNDS_DIR;
|
||||
}
|
||||
void mpx_notify_unmap(struct mm_struct *mm, struct vm_area_struct *vma,
|
||||
unsigned long start, unsigned long end);
|
||||
#else
|
||||
static inline siginfo_t *mpx_generate_siginfo(struct pt_regs *regs,
|
||||
struct xsave_struct *xsave_buf)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
static inline int mpx_handle_bd_fault(struct xsave_struct *xsave_buf)
|
||||
{
|
||||
return -EINVAL;
|
||||
}
|
||||
static inline int kernel_managing_mpx_tables(struct mm_struct *mm)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
static inline void mpx_mm_init(struct mm_struct *mm)
|
||||
{
|
||||
}
|
||||
static inline void mpx_notify_unmap(struct mm_struct *mm,
|
||||
struct vm_area_struct *vma,
|
||||
unsigned long start, unsigned long end)
|
||||
{
|
||||
}
|
||||
#endif /* CONFIG_X86_INTEL_MPX */
|
||||
|
||||
#endif /* _ASM_X86_MPX_H */
|
||||
@@ -330,13 +330,13 @@ static inline void paravirt_activate_mm(struct mm_struct *prev,
|
||||
PVOP_VCALL2(pv_mmu_ops.activate_mm, prev, next);
|
||||
}
|
||||
|
||||
static inline void arch_dup_mmap(struct mm_struct *oldmm,
|
||||
struct mm_struct *mm)
|
||||
static inline void paravirt_arch_dup_mmap(struct mm_struct *oldmm,
|
||||
struct mm_struct *mm)
|
||||
{
|
||||
PVOP_VCALL2(pv_mmu_ops.dup_mmap, oldmm, mm);
|
||||
}
|
||||
|
||||
static inline void arch_exit_mmap(struct mm_struct *mm)
|
||||
static inline void paravirt_arch_exit_mmap(struct mm_struct *mm)
|
||||
{
|
||||
PVOP_VCALL1(pv_mmu_ops.exit_mmap, mm);
|
||||
}
|
||||
@@ -986,5 +986,15 @@ extern void default_banner(void);
|
||||
#endif /* __ASSEMBLY__ */
|
||||
#else /* CONFIG_PARAVIRT */
|
||||
# define default_banner x86_init_noop
|
||||
#ifndef __ASSEMBLY__
|
||||
static inline void paravirt_arch_dup_mmap(struct mm_struct *oldmm,
|
||||
struct mm_struct *mm)
|
||||
{
|
||||
}
|
||||
|
||||
static inline void paravirt_arch_exit_mmap(struct mm_struct *mm)
|
||||
{
|
||||
}
|
||||
#endif /* __ASSEMBLY__ */
|
||||
#endif /* !CONFIG_PARAVIRT */
|
||||
#endif /* _ASM_X86_PARAVIRT_H */
|
||||
|
||||
@@ -374,13 +374,14 @@ struct lwp_struct {
|
||||
u8 reserved[128];
|
||||
};
|
||||
|
||||
struct bndregs_struct {
|
||||
u64 bndregs[8];
|
||||
struct bndreg {
|
||||
u64 lower_bound;
|
||||
u64 upper_bound;
|
||||
} __packed;
|
||||
|
||||
struct bndcsr_struct {
|
||||
u64 cfg_reg_u;
|
||||
u64 status_reg;
|
||||
struct bndcsr {
|
||||
u64 bndcfgu;
|
||||
u64 bndstatus;
|
||||
} __packed;
|
||||
|
||||
struct xsave_hdr_struct {
|
||||
@@ -394,8 +395,8 @@ struct xsave_struct {
|
||||
struct xsave_hdr_struct xsave_hdr;
|
||||
struct ymmh_struct ymmh;
|
||||
struct lwp_struct lwp;
|
||||
struct bndregs_struct bndregs;
|
||||
struct bndcsr_struct bndcsr;
|
||||
struct bndreg bndreg[4];
|
||||
struct bndcsr bndcsr;
|
||||
/* new processor state extensions will go here */
|
||||
} __attribute__ ((packed, aligned (64)));
|
||||
|
||||
@@ -953,6 +954,24 @@ extern void start_thread(struct pt_regs *regs, unsigned long new_ip,
|
||||
extern int get_tsc_mode(unsigned long adr);
|
||||
extern int set_tsc_mode(unsigned int val);
|
||||
|
||||
/* Register/unregister a process' MPX related resource */
|
||||
#define MPX_ENABLE_MANAGEMENT(tsk) mpx_enable_management((tsk))
|
||||
#define MPX_DISABLE_MANAGEMENT(tsk) mpx_disable_management((tsk))
|
||||
|
||||
#ifdef CONFIG_X86_INTEL_MPX
|
||||
extern int mpx_enable_management(struct task_struct *tsk);
|
||||
extern int mpx_disable_management(struct task_struct *tsk);
|
||||
#else
|
||||
static inline int mpx_enable_management(struct task_struct *tsk)
|
||||
{
|
||||
return -EINVAL;
|
||||
}
|
||||
static inline int mpx_disable_management(struct task_struct *tsk)
|
||||
{
|
||||
return -EINVAL;
|
||||
}
|
||||
#endif /* CONFIG_X86_INTEL_MPX */
|
||||
|
||||
extern u16 amd_get_nb_id(int cpu);
|
||||
|
||||
static inline uint32_t hypervisor_cpuid_base(const char *sig, uint32_t leaves)
|
||||
|
||||
@@ -724,6 +724,7 @@ static int intel_pmu_pebs_fixup_ip(struct pt_regs *regs)
|
||||
unsigned long ip = regs->ip;
|
||||
int is_64bit = 0;
|
||||
void *kaddr;
|
||||
int size;
|
||||
|
||||
/*
|
||||
* We don't need to fixup if the PEBS assist is fault like
|
||||
@@ -758,11 +759,12 @@ static int intel_pmu_pebs_fixup_ip(struct pt_regs *regs)
|
||||
return 1;
|
||||
}
|
||||
|
||||
size = ip - to;
|
||||
if (!kernel_ip(ip)) {
|
||||
int size, bytes;
|
||||
int bytes;
|
||||
u8 *buf = this_cpu_read(insn_buffer);
|
||||
|
||||
size = ip - to; /* Must fit our buffer, see above */
|
||||
/* 'size' must fit our buffer, see above */
|
||||
bytes = copy_from_user_nmi(buf, (void __user *)to, size);
|
||||
if (bytes != 0)
|
||||
return 0;
|
||||
@@ -780,11 +782,20 @@ static int intel_pmu_pebs_fixup_ip(struct pt_regs *regs)
|
||||
#ifdef CONFIG_X86_64
|
||||
is_64bit = kernel_ip(to) || !test_thread_flag(TIF_IA32);
|
||||
#endif
|
||||
insn_init(&insn, kaddr, is_64bit);
|
||||
insn_init(&insn, kaddr, size, is_64bit);
|
||||
insn_get_length(&insn);
|
||||
/*
|
||||
* Make sure there was not a problem decoding the
|
||||
* instruction and getting the length. This is
|
||||
* doubly important because we have an infinite
|
||||
* loop if insn.length=0.
|
||||
*/
|
||||
if (!insn.length)
|
||||
break;
|
||||
|
||||
to += insn.length;
|
||||
kaddr += insn.length;
|
||||
size -= insn.length;
|
||||
} while (to < ip);
|
||||
|
||||
if (to == ip) {
|
||||
|
||||
@@ -465,7 +465,7 @@ static int branch_type(unsigned long from, unsigned long to, int abort)
|
||||
{
|
||||
struct insn insn;
|
||||
void *addr;
|
||||
int bytes, size = MAX_INSN_SIZE;
|
||||
int bytes_read, bytes_left;
|
||||
int ret = X86_BR_NONE;
|
||||
int ext, to_plm, from_plm;
|
||||
u8 buf[MAX_INSN_SIZE];
|
||||
@@ -493,8 +493,10 @@ static int branch_type(unsigned long from, unsigned long to, int abort)
|
||||
return X86_BR_NONE;
|
||||
|
||||
/* may fail if text not present */
|
||||
bytes = copy_from_user_nmi(buf, (void __user *)from, size);
|
||||
if (bytes != 0)
|
||||
bytes_left = copy_from_user_nmi(buf, (void __user *)from,
|
||||
MAX_INSN_SIZE);
|
||||
bytes_read = MAX_INSN_SIZE - bytes_left;
|
||||
if (!bytes_read)
|
||||
return X86_BR_NONE;
|
||||
|
||||
addr = buf;
|
||||
@@ -505,10 +507,19 @@ static int branch_type(unsigned long from, unsigned long to, int abort)
|
||||
* Ensure we don't blindy read any address by validating it is
|
||||
* a known text address.
|
||||
*/
|
||||
if (kernel_text_address(from))
|
||||
if (kernel_text_address(from)) {
|
||||
addr = (void *)from;
|
||||
else
|
||||
/*
|
||||
* Assume we can get the maximum possible size
|
||||
* when grabbing kernel data. This is not
|
||||
* _strictly_ true since we could possibly be
|
||||
* executing up next to a memory hole, but
|
||||
* it is very unlikely to be a problem.
|
||||
*/
|
||||
bytes_read = MAX_INSN_SIZE;
|
||||
} else {
|
||||
return X86_BR_NONE;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -518,8 +529,10 @@ static int branch_type(unsigned long from, unsigned long to, int abort)
|
||||
#ifdef CONFIG_X86_64
|
||||
is64 = kernel_ip((unsigned long)addr) || !test_thread_flag(TIF_IA32);
|
||||
#endif
|
||||
insn_init(&insn, addr, is64);
|
||||
insn_init(&insn, addr, bytes_read, is64);
|
||||
insn_get_opcode(&insn);
|
||||
if (!insn.opcode.got)
|
||||
return X86_BR_ABORT;
|
||||
|
||||
switch (insn.opcode.bytes[0]) {
|
||||
case 0xf:
|
||||
|
||||
@@ -285,7 +285,7 @@ static int can_probe(unsigned long paddr)
|
||||
* normally used, we just go through if there is no kprobe.
|
||||
*/
|
||||
__addr = recover_probed_instruction(buf, addr);
|
||||
kernel_insn_init(&insn, (void *)__addr);
|
||||
kernel_insn_init(&insn, (void *)__addr, MAX_INSN_SIZE);
|
||||
insn_get_length(&insn);
|
||||
|
||||
/*
|
||||
@@ -330,8 +330,10 @@ int __copy_instruction(u8 *dest, u8 *src)
|
||||
{
|
||||
struct insn insn;
|
||||
kprobe_opcode_t buf[MAX_INSN_SIZE];
|
||||
unsigned long recovered_insn =
|
||||
recover_probed_instruction(buf, (unsigned long)src);
|
||||
|
||||
kernel_insn_init(&insn, (void *)recover_probed_instruction(buf, (unsigned long)src));
|
||||
kernel_insn_init(&insn, (void *)recovered_insn, MAX_INSN_SIZE);
|
||||
insn_get_length(&insn);
|
||||
/* Another subsystem puts a breakpoint, failed to recover */
|
||||
if (insn.opcode.bytes[0] == BREAKPOINT_INSTRUCTION)
|
||||
@@ -342,7 +344,7 @@ int __copy_instruction(u8 *dest, u8 *src)
|
||||
if (insn_rip_relative(&insn)) {
|
||||
s64 newdisp;
|
||||
u8 *disp;
|
||||
kernel_insn_init(&insn, dest);
|
||||
kernel_insn_init(&insn, dest, insn.length);
|
||||
insn_get_displacement(&insn);
|
||||
/*
|
||||
* The copied instruction uses the %rip-relative addressing
|
||||
|
||||
@@ -251,13 +251,15 @@ static int can_optimize(unsigned long paddr)
|
||||
/* Decode instructions */
|
||||
addr = paddr - offset;
|
||||
while (addr < paddr - offset + size) { /* Decode until function end */
|
||||
unsigned long recovered_insn;
|
||||
if (search_exception_tables(addr))
|
||||
/*
|
||||
* Since some fixup code will jumps into this function,
|
||||
* we can't optimize kprobe in this function.
|
||||
*/
|
||||
return 0;
|
||||
kernel_insn_init(&insn, (void *)recover_probed_instruction(buf, addr));
|
||||
recovered_insn = recover_probed_instruction(buf, addr);
|
||||
kernel_insn_init(&insn, (void *)recovered_insn, MAX_INSN_SIZE);
|
||||
insn_get_length(&insn);
|
||||
/* Another subsystem puts a breakpoint */
|
||||
if (insn.opcode.bytes[0] == BREAKPOINT_INSTRUCTION)
|
||||
|
||||
@@ -960,6 +960,8 @@ void __init setup_arch(char **cmdline_p)
|
||||
init_mm.end_data = (unsigned long) _edata;
|
||||
init_mm.brk = _brk_end;
|
||||
|
||||
mpx_mm_init(&init_mm);
|
||||
|
||||
code_resource.start = __pa_symbol(_text);
|
||||
code_resource.end = __pa_symbol(_etext)-1;
|
||||
data_resource.start = __pa_symbol(_etext);
|
||||
|
||||
+84
-1
@@ -60,6 +60,7 @@
|
||||
#include <asm/fixmap.h>
|
||||
#include <asm/mach_traps.h>
|
||||
#include <asm/alternative.h>
|
||||
#include <asm/mpx.h>
|
||||
|
||||
#ifdef CONFIG_X86_64
|
||||
#include <asm/x86_init.h>
|
||||
@@ -228,7 +229,6 @@ dotraplinkage void do_##name(struct pt_regs *regs, long error_code) \
|
||||
|
||||
DO_ERROR(X86_TRAP_DE, SIGFPE, "divide error", divide_error)
|
||||
DO_ERROR(X86_TRAP_OF, SIGSEGV, "overflow", overflow)
|
||||
DO_ERROR(X86_TRAP_BR, SIGSEGV, "bounds", bounds)
|
||||
DO_ERROR(X86_TRAP_UD, SIGILL, "invalid opcode", invalid_op)
|
||||
DO_ERROR(X86_TRAP_OLD_MF, SIGFPE, "coprocessor segment overrun",coprocessor_segment_overrun)
|
||||
DO_ERROR(X86_TRAP_TS, SIGSEGV, "invalid TSS", invalid_TSS)
|
||||
@@ -286,6 +286,89 @@ dotraplinkage void do_double_fault(struct pt_regs *regs, long error_code)
|
||||
}
|
||||
#endif
|
||||
|
||||
dotraplinkage void do_bounds(struct pt_regs *regs, long error_code)
|
||||
{
|
||||
struct task_struct *tsk = current;
|
||||
struct xsave_struct *xsave_buf;
|
||||
enum ctx_state prev_state;
|
||||
struct bndcsr *bndcsr;
|
||||
siginfo_t *info;
|
||||
|
||||
prev_state = exception_enter();
|
||||
if (notify_die(DIE_TRAP, "bounds", regs, error_code,
|
||||
X86_TRAP_BR, SIGSEGV) == NOTIFY_STOP)
|
||||
goto exit;
|
||||
conditional_sti(regs);
|
||||
|
||||
if (!user_mode(regs))
|
||||
die("bounds", regs, error_code);
|
||||
|
||||
if (!cpu_feature_enabled(X86_FEATURE_MPX)) {
|
||||
/* The exception is not from Intel MPX */
|
||||
goto exit_trap;
|
||||
}
|
||||
|
||||
/*
|
||||
* We need to look at BNDSTATUS to resolve this exception.
|
||||
* It is not directly accessible, though, so we need to
|
||||
* do an xsave and then pull it out of the xsave buffer.
|
||||
*/
|
||||
fpu_save_init(&tsk->thread.fpu);
|
||||
xsave_buf = &(tsk->thread.fpu.state->xsave);
|
||||
bndcsr = get_xsave_addr(xsave_buf, XSTATE_BNDCSR);
|
||||
if (!bndcsr)
|
||||
goto exit_trap;
|
||||
|
||||
/*
|
||||
* The error code field of the BNDSTATUS register communicates status
|
||||
* information of a bound range exception #BR or operation involving
|
||||
* bound directory.
|
||||
*/
|
||||
switch (bndcsr->bndstatus & MPX_BNDSTA_ERROR_CODE) {
|
||||
case 2: /* Bound directory has invalid entry. */
|
||||
if (mpx_handle_bd_fault(xsave_buf))
|
||||
goto exit_trap;
|
||||
break; /* Success, it was handled */
|
||||
case 1: /* Bound violation. */
|
||||
info = mpx_generate_siginfo(regs, xsave_buf);
|
||||
if (PTR_ERR(info)) {
|
||||
/*
|
||||
* We failed to decode the MPX instruction. Act as if
|
||||
* the exception was not caused by MPX.
|
||||
*/
|
||||
goto exit_trap;
|
||||
}
|
||||
/*
|
||||
* Success, we decoded the instruction and retrieved
|
||||
* an 'info' containing the address being accessed
|
||||
* which caused the exception. This information
|
||||
* allows and application to possibly handle the
|
||||
* #BR exception itself.
|
||||
*/
|
||||
do_trap(X86_TRAP_BR, SIGSEGV, "bounds", regs, error_code, info);
|
||||
kfree(info);
|
||||
break;
|
||||
case 0: /* No exception caused by Intel MPX operations. */
|
||||
goto exit_trap;
|
||||
default:
|
||||
die("bounds", regs, error_code);
|
||||
}
|
||||
|
||||
exit:
|
||||
exception_exit(prev_state);
|
||||
return;
|
||||
exit_trap:
|
||||
/*
|
||||
* This path out is for all the cases where we could not
|
||||
* handle the exception in some way (like allocating a
|
||||
* table or telling userspace about it. We will also end
|
||||
* up here if the kernel has MPX turned off at compile
|
||||
* time..
|
||||
*/
|
||||
do_trap(X86_TRAP_BR, SIGSEGV, "bounds", regs, error_code, NULL);
|
||||
exception_exit(prev_state);
|
||||
}
|
||||
|
||||
dotraplinkage void
|
||||
do_general_protection(struct pt_regs *regs, long error_code)
|
||||
{
|
||||
|
||||
@@ -219,7 +219,7 @@ static int uprobe_init_insn(struct arch_uprobe *auprobe, struct insn *insn, bool
|
||||
{
|
||||
u32 volatile *good_insns;
|
||||
|
||||
insn_init(insn, auprobe->insn, x86_64);
|
||||
insn_init(insn, auprobe->insn, sizeof(auprobe->insn), x86_64);
|
||||
/* has the side-effect of processing the entire instruction */
|
||||
insn_get_length(insn);
|
||||
if (WARN_ON_ONCE(!insn_complete(insn)))
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user