/*
* mpx.c - Memory Protection eXtensions
*
* Copyright (c) 2014, Intel Corporation.
* Qiaowei Ren <qiaowei.ren@intel.com>
* Dave Hansen <dave.hansen@intel.com>
*/
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/mm_types.h>
#include <linux/syscalls.h>
#include <linux/sched/sysctl.h>
#include <asm/insn.h>
#include <asm/mman.h>
#include <asm/mmu_context.h>
#include <asm/mpx.h>
#include <asm/processor.h>
#include <asm/fpu/internal.h>
#define CREATE_TRACE_POINTS
#include <asm/trace/mpx.h>
static inline unsigned long mpx_bd_size_bytes(struct mm_struct *mm)
{
if (is_64bit_mm(mm))
return MPX_BD_SIZE_BYTES_64;
else
return MPX_BD_SIZE_BYTES_32;
}
static inline unsigned long mpx_bt_size_bytes(struct mm_struct *mm)
{
if (is_64bit_mm(mm))
return MPX_BT_SIZE_BYTES_64;
else
return MPX_BT_SIZE_BYTES_32;
}
/*
* This is really a simplified "vm_mmap". it only handles MPX
* bounds tables (the bounds directory is user-allocated).
*/
static unsigned long mpx_mmap(unsigned long len)
{
struct mm_struct *mm = current->mm;
unsigned long addr, populate;
/* Only bounds table can be allocated here */
if (len != mpx_bt_size_bytes(mm))
return -EINVAL;
down_write(&mm->mmap_sem);
addr = do_mmap(NULL, 0, len, PROT_READ | PROT_WRITE,
MAP_ANONYMOUS | MAP_PRIVATE, VM_MPX, 0, &populate, NULL);
up_write(&mm->mmap_sem);
if (populate)
mm_populate(addr, populate);
return addr;
}
enum reg_type {
REG_TYPE_RM = 0,
REG_TYPE_INDEX,
REG_TYPE_BASE,
};
static int get_reg_offset(struct insn *insn, struct pt_regs *regs,
enum reg_type type)
{
int regno = 0;
static const int regoff[] = {
offsetof(struct pt_regs, ax),
offsetof(struct pt_regs, cx),
offsetof(struct pt_regs, dx),
offsetof(struct pt_regs, bx),
offsetof(struct pt_regs, sp),
offsetof(struct pt_regs, bp),
offsetof(struct pt_regs, si),
offsetof(struct pt_regs, di),
#ifdef CONFIG_X86_64
offsetof(struct pt_regs, r8),
offsetof(struct pt_regs, r9),
offsetof(struct pt_regs, r10),
offsetof(struct pt_regs, r11),
offsetof(struct pt_regs, r12),
offsetof(struct pt_regs, r13),
offsetof(struct pt_regs, r14),
offsetof(struct pt_regs, r15),
#endif
};
int nr_registers = ARRAY_SIZE(regoff);
/*
* Don't possibly decode a 32-bit instructions as
* reading a 64-bit-only register.
*/
if (IS_ENABLED(CONFIG_X86_64) && !insn->x86_64)
nr_registers -= 8;
switch (type) {
case REG_TYPE_RM:
regno = X86_MODRM_RM(insn->modrm.value);
if (X86_REX_B(insn->rex_prefix.value))
regno += 8;
break;
case REG_TYPE_INDEX:
regno = X86_SIB_INDEX(insn->sib.value);
if (X86_REX_X(insn->rex_prefix.value))
regno += 8;
break;
case REG_TYPE_BASE:
regno = X86_SIB_BASE(insn->sib.value);
if (X86_REX_B(insn->rex_prefix.value))
regno += 8;
break;
default:
pr_err("invalid register type");
BUG();
break;
}
if (regno >= nr_registers) {
WARN_ONCE(1, "decoded an instruction with an invalid register");
return -EINVAL;
}
return regoff[regno];
}
/*
* 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
*/
static void __user *mpx_get_addr_ref(struct insn *insn, struct pt_regs *regs)
{
unsigned long addr, base, indx;
int addr_offset, base_offset, indx_offset;
insn_byte_t sib;
insn_get_modrm(insn);
insn_get_sib(insn);
sib =