Skip to content

Commit

Permalink
Merge branch 'x86-iopl-for-linus' of git://git.kernel.org/pub/scm/lin…
Browse files Browse the repository at this point in the history
…ux/kernel/git/tip/tip

Pull x86 iopl updates from Ingo Molnar:
 "This implements a nice simplification of the iopl and ioperm code that
  Thomas Gleixner discovered: we can implement the IO privilege features
  of the iopl system call by using the IO permission bitmap in
  permissive mode, while trapping CLI/STI/POPF/PUSHF uses in user-space
  if they change the interrupt flag.

  This implements that feature, with testing facilities and related
  cleanups"

[ "Simplification" may be an over-statement. The main goal is to avoid
  the cli/sti of iopl by effectively implementing the IO port access
  parts of iopl in terms of ioperm.

  This may end up not workign well in case people actually depend on
  cli/sti being available, or if there are mixed uses of iopl and
  ioperm. We will see..       - Linus ]

* 'x86-iopl-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip: (22 commits)
  x86/ioperm: Fix use of deprecated config option
  x86/entry/32: Clarify register saving in __switch_to_asm()
  selftests/x86/iopl: Extend test to cover IOPL emulation
  x86/ioperm: Extend IOPL config to control ioperm() as well
  x86/iopl: Remove legacy IOPL option
  x86/iopl: Restrict iopl() permission scope
  x86/iopl: Fixup misleading comment
  selftests/x86/ioperm: Extend testing so the shared bitmap is exercised
  x86/ioperm: Share I/O bitmap if identical
  x86/ioperm: Remove bitmap if all permissions dropped
  x86/ioperm: Move TSS bitmap update to exit to user work
  x86/ioperm: Add bitmap sequence number
  x86/ioperm: Move iobitmap data into a struct
  x86/tss: Move I/O bitmap data into a seperate struct
  x86/io: Speedup schedule out of I/O bitmap user
  x86/ioperm: Avoid bitmap allocation if no permissions are set
  x86/ioperm: Simplify first ioperm() invocation logic
  x86/iopl: Cleanup include maze
  x86/tss: Fix and move VMX BUILD_BUG_ON()
  x86/cpu: Unify cpu_init()
  ...
  • Loading branch information
torvalds committed Nov 26, 2019
2 parents 1d87200 + e3cb0c7 commit ab851d4
Show file tree
Hide file tree
Showing 25 changed files with 686 additions and 478 deletions.
18 changes: 18 additions & 0 deletions arch/x86/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -1224,6 +1224,24 @@ config X86_VSYSCALL_EMULATION
Disabling this option saves about 7K of kernel size and
possibly 4K of additional runtime pagetable memory.

config X86_IOPL_IOPERM
bool "IOPERM and IOPL Emulation"
default y
---help---
This enables the ioperm() and iopl() syscalls which are necessary
for legacy applications.

Legacy IOPL support is an overbroad mechanism which allows user
space aside of accessing all 65536 I/O ports also to disable
interrupts. To gain this access the caller needs CAP_SYS_RAWIO
capabilities and permission from potentially active security
modules.

The emulation restricts the functionality of the syscall to
only allowing the full range I/O port access, but prevents the
ability to disable interrupts from user space which would be
granted if the hardware IOPL mechanism would be used.

config TOSHIBA
tristate "Toshiba Laptop support"
depends on X86_32
Expand Down
4 changes: 4 additions & 0 deletions arch/x86/entry/common.c
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
#include <asm/cpufeature.h>
#include <asm/fpu/api.h>
#include <asm/nospec-branch.h>
#include <asm/io_bitmap.h>

#define CREATE_TRACE_POINTS
#include <trace/events/syscalls.h>
Expand Down Expand Up @@ -196,6 +197,9 @@ __visible inline void prepare_exit_to_usermode(struct pt_regs *regs)
/* Reload ti->flags; we may have rescheduled above. */
cached_flags = READ_ONCE(ti->flags);

if (unlikely(cached_flags & _TIF_IO_BITMAP))
tss_update_io_bitmap();

fpregs_assert_state_consistent();
if (unlikely(cached_flags & _TIF_NEED_FPU_LOAD))
switch_fpu_return();
Expand Down
8 changes: 7 additions & 1 deletion arch/x86/entry/entry_32.S
Original file line number Diff line number Diff line change
Expand Up @@ -739,6 +739,11 @@ SYM_CODE_START(__switch_to_asm)
pushl %ebx
pushl %edi
pushl %esi
/*
* Flags are saved to prevent AC leakage. This could go
* away if objtool would have 32bit support to verify
* the STAC/CLAC correctness.
*/
pushfl

/* switch stack */
Expand All @@ -761,8 +766,9 @@ SYM_CODE_START(__switch_to_asm)
FILL_RETURN_BUFFER %ebx, RSB_CLEAR_LOOPS, X86_FEATURE_RSB_CTXSW
#endif

/* restore callee-saved registers */
/* Restore flags or the incoming task to restore AC state. */
popfl
/* restore callee-saved registers */
popl %esi
popl %edi
popl %ebx
Expand Down
29 changes: 29 additions & 0 deletions arch/x86/include/asm/io_bitmap.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
/* SPDX-License-Identifier: GPL-2.0 */
#ifndef _ASM_X86_IOBITMAP_H
#define _ASM_X86_IOBITMAP_H

#include <linux/refcount.h>
#include <asm/processor.h>

struct io_bitmap {
u64 sequence;
refcount_t refcnt;
/* The maximum number of bytes to copy so all zero bits are covered */
unsigned int max;
unsigned long bitmap[IO_BITMAP_LONGS];
};

struct task_struct;

#ifdef CONFIG_X86_IOPL_IOPERM
void io_bitmap_share(struct task_struct *tsk);
void io_bitmap_exit(void);

void tss_update_io_bitmap(void);
#else
static inline void io_bitmap_share(struct task_struct *tsk) { }
static inline void io_bitmap_exit(void) { }
static inline void tss_update_io_bitmap(void) { }
#endif

#endif
4 changes: 0 additions & 4 deletions arch/x86/include/asm/paravirt.h
Original file line number Diff line number Diff line change
Expand Up @@ -294,10 +294,6 @@ static inline void write_idt_entry(gate_desc *dt, int entry, const gate_desc *g)
{
PVOP_VCALL3(cpu.write_idt_entry, dt, entry, g);
}
static inline void set_iopl_mask(unsigned mask)
{
PVOP_VCALL1(cpu.set_iopl_mask, mask);
}

static inline void paravirt_activate_mm(struct mm_struct *prev,
struct mm_struct *next)
Expand Down
2 changes: 0 additions & 2 deletions arch/x86/include/asm/paravirt_types.h
Original file line number Diff line number Diff line change
Expand Up @@ -140,8 +140,6 @@ struct pv_cpu_ops {

void (*load_sp0)(unsigned long sp0);

void (*set_iopl_mask)(unsigned mask);

void (*wbinvd)(void);

/* cpuid emulation, mostly so that caps bits can be disabled */
Expand Down
2 changes: 1 addition & 1 deletion arch/x86/include/asm/pgtable_32_types.h
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ extern bool __vmalloc_start_set; /* set once high_memory is set */
* Define this here and validate with BUILD_BUG_ON() in pgtable_32.c
* to avoid include recursion hell
*/
#define CPU_ENTRY_AREA_PAGES (NR_CPUS * 39)
#define CPU_ENTRY_AREA_PAGES (NR_CPUS * 41)

/* The +1 is for the readonly IDT page: */
#define CPU_ENTRY_AREA_BASE \
Expand Down
113 changes: 68 additions & 45 deletions arch/x86/include/asm/processor.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
/* Forward declaration, a strange C thing */
struct task_struct;
struct mm_struct;
struct io_bitmap;
struct vm86;

#include <asm/math_emu.h>
Expand Down Expand Up @@ -336,10 +337,32 @@ struct x86_hw_tss {
* IO-bitmap sizes:
*/
#define IO_BITMAP_BITS 65536
#define IO_BITMAP_BYTES (IO_BITMAP_BITS/8)
#define IO_BITMAP_LONGS (IO_BITMAP_BYTES/sizeof(long))
#define IO_BITMAP_OFFSET (offsetof(struct tss_struct, io_bitmap) - offsetof(struct tss_struct, x86_tss))
#define INVALID_IO_BITMAP_OFFSET 0x8000
#define IO_BITMAP_BYTES (IO_BITMAP_BITS / BITS_PER_BYTE)
#define IO_BITMAP_LONGS (IO_BITMAP_BYTES / sizeof(long))

#define IO_BITMAP_OFFSET_VALID_MAP \
(offsetof(struct tss_struct, io_bitmap.bitmap) - \
offsetof(struct tss_struct, x86_tss))

#define IO_BITMAP_OFFSET_VALID_ALL \
(offsetof(struct tss_struct, io_bitmap.mapall) - \
offsetof(struct tss_struct, x86_tss))

#ifdef CONFIG_X86_IOPL_IOPERM
/*
* sizeof(unsigned long) coming from an extra "long" at the end of the
* iobitmap. The limit is inclusive, i.e. the last valid byte.
*/
# define __KERNEL_TSS_LIMIT \
(IO_BITMAP_OFFSET_VALID_ALL + IO_BITMAP_BYTES + \
sizeof(unsigned long) - 1)
#else
# define __KERNEL_TSS_LIMIT \
(offsetof(struct tss_struct, x86_tss) + sizeof(struct x86_hw_tss) - 1)
#endif

/* Base offset outside of TSS_LIMIT so unpriviledged IO causes #GP */
#define IO_BITMAP_OFFSET_INVALID (__KERNEL_TSS_LIMIT + 1)

struct entry_stack {
unsigned long words[64];
Expand All @@ -349,35 +372,52 @@ struct entry_stack_page {
struct entry_stack stack;
} __aligned(PAGE_SIZE);

struct tss_struct {
/*
* All IO bitmap related data stored in the TSS:
*/
struct x86_io_bitmap {
/* The sequence number of the last active bitmap. */
u64 prev_sequence;

/*
* The fixed hardware portion. This must not cross a page boundary
* at risk of violating the SDM's advice and potentially triggering
* errata.
* Store the dirty size of the last io bitmap offender. The next
* one will have to do the cleanup as the switch out to a non io
* bitmap user will just set x86_tss.io_bitmap_base to a value
* outside of the TSS limit. So for sane tasks there is no need to
* actually touch the io_bitmap at all.
*/
struct x86_hw_tss x86_tss;
unsigned int prev_max;

/*
* The extra 1 is there because the CPU will access an
* additional byte beyond the end of the IO permission
* bitmap. The extra byte must be all 1 bits, and must
* be within the limit.
*/
unsigned long io_bitmap[IO_BITMAP_LONGS + 1];
unsigned long bitmap[IO_BITMAP_LONGS + 1];

/*
* Special I/O bitmap to emulate IOPL(3). All bytes zero,
* except the additional byte at the end.
*/
unsigned long mapall[IO_BITMAP_LONGS + 1];
};

struct tss_struct {
/*
* The fixed hardware portion. This must not cross a page boundary
* at risk of violating the SDM's advice and potentially triggering
* errata.
*/
struct x86_hw_tss x86_tss;

#ifdef CONFIG_X86_IOPL_IOPERM
struct x86_io_bitmap io_bitmap;
#endif
} __aligned(PAGE_SIZE);

DECLARE_PER_CPU_PAGE_ALIGNED(struct tss_struct, cpu_tss_rw);

/*
* sizeof(unsigned long) coming from an extra "long" at the end
* of the iobitmap.
*
* -1? seg base+limit should be pointing to the address of the
* last valid byte
*/
#define __KERNEL_TSS_LIMIT \
(IO_BITMAP_OFFSET + IO_BITMAP_BYTES + sizeof(unsigned long) - 1)

/* Per CPU interrupt stacks */
struct irq_stack {
char stack[IRQ_STACK_SIZE];
Expand Down Expand Up @@ -488,10 +528,14 @@ struct thread_struct {
struct vm86 *vm86;
#endif
/* IO permissions: */
unsigned long *io_bitmap_ptr;
unsigned long iopl;
/* Max allowed port in the bitmap, in bytes: */
unsigned io_bitmap_max;
struct io_bitmap *io_bitmap;

/*
* IOPL. Priviledge level dependent I/O permission which is
* emulated via the I/O bitmap to prevent user space from disabling
* interrupts.
*/
unsigned long iopl_emul;

mm_segment_t addr_limit;

Expand Down Expand Up @@ -523,25 +567,6 @@ static inline void arch_thread_struct_whitelist(unsigned long *offset,
*/
#define TS_COMPAT 0x0002 /* 32bit syscall active (64BIT)*/

/*
* Set IOPL bits in EFLAGS from given mask
*/
static inline void native_set_iopl_mask(unsigned mask)
{
#ifdef CONFIG_X86_32
unsigned int reg;

asm volatile ("pushfl;"
"popl %0;"
"andl %1, %0;"
"orl %2, %0;"
"pushl %0;"
"popfl"
: "=&r" (reg)
: "i" (~X86_EFLAGS_IOPL), "r" (mask));
#endif
}

static inline void
native_load_sp0(unsigned long sp0)
{
Expand Down Expand Up @@ -581,7 +606,6 @@ static inline void load_sp0(unsigned long sp0)
native_load_sp0(sp0);
}

#define set_iopl_mask native_set_iopl_mask
#endif /* CONFIG_PARAVIRT_XXL */

/* Free all resources held by a thread. */
Expand Down Expand Up @@ -849,7 +873,6 @@ static inline void spin_lock_prefetch(const void *x)
#define INIT_THREAD { \
.sp0 = TOP_OF_INIT_STACK, \
.sysenter_cs = __KERNEL_CS, \
.io_bitmap_ptr = NULL, \
.addr_limit = KERNEL_DS, \
}

Expand Down
6 changes: 6 additions & 0 deletions arch/x86/include/asm/ptrace.h
Original file line number Diff line number Diff line change
Expand Up @@ -361,5 +361,11 @@ extern int do_get_thread_area(struct task_struct *p, int idx,
extern int do_set_thread_area(struct task_struct *p, int idx,
struct user_desc __user *info, int can_allocate);

#ifdef CONFIG_X86_64
# define do_set_thread_area_64(p, s, t) do_arch_prctl_64(p, s, t)
#else
# define do_set_thread_area_64(p, s, t) (0)
#endif

#endif /* !__ASSEMBLY__ */
#endif /* _ASM_X86_PTRACE_H */
10 changes: 10 additions & 0 deletions arch/x86/include/asm/switch_to.h
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,17 @@ static inline void update_task_stack(struct task_struct *task)
if (static_cpu_has(X86_FEATURE_XENPV))
load_sp0(task_top_of_stack(task));
#endif
}

static inline void kthread_frame_init(struct inactive_task_frame *frame,
unsigned long fun, unsigned long arg)
{
frame->bx = fun;
#ifdef CONFIG_X86_32
frame->di = arg;
#else
frame->r12 = arg;
#endif
}

#endif /* _ASM_X86_SWITCH_TO_H */
14 changes: 10 additions & 4 deletions arch/x86/include/asm/thread_info.h
Original file line number Diff line number Diff line change
Expand Up @@ -143,8 +143,8 @@ struct thread_info {
_TIF_NOHZ)

/* flags to check in __switch_to() */
#define _TIF_WORK_CTXSW_BASE \
(_TIF_IO_BITMAP|_TIF_NOCPUID|_TIF_NOTSC|_TIF_BLOCKSTEP| \
#define _TIF_WORK_CTXSW_BASE \
(_TIF_NOCPUID | _TIF_NOTSC | _TIF_BLOCKSTEP | \
_TIF_SSBD | _TIF_SPEC_FORCE_UPDATE)

/*
Expand All @@ -156,8 +156,14 @@ struct thread_info {
# define _TIF_WORK_CTXSW (_TIF_WORK_CTXSW_BASE)
#endif

#define _TIF_WORK_CTXSW_PREV (_TIF_WORK_CTXSW|_TIF_USER_RETURN_NOTIFY)
#define _TIF_WORK_CTXSW_NEXT (_TIF_WORK_CTXSW)
#ifdef CONFIG_X86_IOPL_IOPERM
# define _TIF_WORK_CTXSW_PREV (_TIF_WORK_CTXSW| _TIF_USER_RETURN_NOTIFY | \
_TIF_IO_BITMAP)
#else
# define _TIF_WORK_CTXSW_PREV (_TIF_WORK_CTXSW| _TIF_USER_RETURN_NOTIFY)
#endif

#define _TIF_WORK_CTXSW_NEXT (_TIF_WORK_CTXSW)

#define STACK_WARN (THREAD_SIZE/8)

Expand Down
2 changes: 0 additions & 2 deletions arch/x86/include/asm/xen/hypervisor.h
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,4 @@ void xen_arch_register_cpu(int num);
void xen_arch_unregister_cpu(int num);
#endif

extern void xen_set_iopl_mask(unsigned mask);

#endif /* _ASM_X86_XEN_HYPERVISOR_H */
Loading

0 comments on commit ab851d4

Please sign in to comment.