x86/ldt: Make modify_ldt() optional
authorAndy Lutomirski <luto@kernel.org>
Thu, 30 Jul 2015 21:31:34 +0000 (14:31 -0700)
committerIngo Molnar <mingo@kernel.org>
Fri, 31 Jul 2015 11:30:45 +0000 (13:30 +0200)
The modify_ldt syscall exposes a large attack surface and is
unnecessary for modern userspace.  Make it optional.

Signed-off-by: Andy Lutomirski <luto@kernel.org>
Reviewed-by: Kees Cook <keescook@chromium.org>
Cc: Andrew Cooper <andrew.cooper3@citrix.com>
Cc: Andy Lutomirski <luto@amacapital.net>
Cc: Boris Ostrovsky <boris.ostrovsky@oracle.com>
Cc: Borislav Petkov <bp@alien8.de>
Cc: Brian Gerst <brgerst@gmail.com>
Cc: Denys Vlasenko <dvlasenk@redhat.com>
Cc: H. Peter Anvin <hpa@zytor.com>
Cc: Jan Beulich <jbeulich@suse.com>
Cc: Konrad Rzeszutek Wilk <konrad.wilk@oracle.com>
Cc: Linus Torvalds <torvalds@linux-foundation.org>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Sasha Levin <sasha.levin@oracle.com>
Cc: Steven Rostedt <rostedt@goodmis.org>
Cc: Thomas Gleixner <tglx@linutronix.de>
Cc: security@kernel.org <security@kernel.org>
Cc: xen-devel <xen-devel@lists.xen.org>
Link: http://lkml.kernel.org/r/a605166a771c343fd64802dece77a903507333bd.1438291540.git.luto@kernel.org
[ Made MATH_EMULATION dependent on MODIFY_LDT_SYSCALL. ]
Signed-off-by: Ingo Molnar <mingo@kernel.org>
arch/x86/Kconfig
arch/x86/include/asm/mmu.h
arch/x86/include/asm/mmu_context.h
arch/x86/kernel/Makefile
arch/x86/kernel/cpu/perf_event.c
arch/x86/kernel/process_64.c
arch/x86/kernel/step.c
kernel/sys_ni.c

index e26fe7a5b9e6141efb05dd8356ecdc343ed709de..798658048db30ffa0dc8d43a03038e05c98a601f 100644 (file)
@@ -1036,6 +1036,7 @@ config VM86
 config X86_16BIT
        bool "Enable support for 16-bit segments" if EXPERT
        default y
+       depends on MODIFY_LDT_SYSCALL
        ---help---
          This option is required by programs like Wine to run 16-bit
          protected mode legacy code on x86 processors.  Disabling
@@ -1530,6 +1531,7 @@ config X86_RESERVE_LOW
 
 config MATH_EMULATION
        bool
+       depends on MODIFY_LDT_SYSCALL
        prompt "Math emulation" if X86_32
        ---help---
          Linux can emulate a math coprocessor (used for floating point
@@ -2074,6 +2076,22 @@ config CMDLINE_OVERRIDE
          This is used to work around broken boot loaders.  This should
          be set to 'N' under normal conditions.
 
+config MODIFY_LDT_SYSCALL
+       bool "Enable the LDT (local descriptor table)" if EXPERT
+       default y
+       ---help---
+         Linux can allow user programs to install a per-process x86
+         Local Descriptor Table (LDT) using the modify_ldt(2) system
+         call.  This is required to run 16-bit or segmented code such as
+         DOSEMU or some Wine programs.  It is also used by some very old
+         threading libraries.
+
+         Enabling this feature adds a small amount of overhead to
+         context switches and increases the low-level kernel attack
+         surface.  Disabling it removes the modify_ldt(2) system call.
+
+         Saying 'N' here may make sense for embedded or server kernels.
+
 source "kernel/livepatch/Kconfig"
 
 endmenu
index 364d27481a52a34f5031933ecc577af0ebf3c129..55234d5e7160db83bd9854a40c87b8e70593b3c7 100644 (file)
@@ -9,7 +9,9 @@
  * we put the segment information here.
  */
 typedef struct {
+#ifdef CONFIG_MODIFY_LDT_SYSCALL
        struct ldt_struct *ldt;
+#endif
 
 #ifdef CONFIG_X86_64
        /* True if mm supports a task running in 32 bit compatibility mode. */
index 984abfe47edc85db7b1a1809d5e0b2d22a7c0449..379cd3658799f3fb0623e5dfca5c3e7fde4a7a23 100644 (file)
@@ -33,6 +33,7 @@ static inline void load_mm_cr4(struct mm_struct *mm)
 static inline void load_mm_cr4(struct mm_struct *mm) {}
 #endif
 
+#ifdef CONFIG_MODIFY_LDT_SYSCALL
 /*
  * ldt_structs can be allocated, used, and freed, but they are never
  * modified while live.
@@ -48,8 +49,23 @@ struct ldt_struct {
        int size;
 };
 
+/*
+ * Used for LDT copy/destruction.
+ */
+int init_new_context(struct task_struct *tsk, struct mm_struct *mm);
+void destroy_context(struct mm_struct *mm);
+#else  /* CONFIG_MODIFY_LDT_SYSCALL */
+static inline int init_new_context(struct task_struct *tsk,
+                                  struct mm_struct *mm)
+{
+       return 0;
+}
+static inline void destroy_context(struct mm_struct *mm) {}
+#endif
+
 static inline void load_mm_ldt(struct mm_struct *mm)
 {
+#ifdef CONFIG_MODIFY_LDT_SYSCALL
        struct ldt_struct *ldt;
 
        /* lockless_dereference synchronizes with smp_store_release */
@@ -73,17 +89,13 @@ static inline void load_mm_ldt(struct mm_struct *mm)
                set_ldt(ldt->entries, ldt->size);
        else
                clear_LDT();
+#else
+       clear_LDT();
+#endif
 
        DEBUG_LOCKS_WARN_ON(preemptible());
 }
 
-/*
- * Used for LDT copy/destruction.
- */
-int init_new_context(struct task_struct *tsk, struct mm_struct *mm);
-void destroy_context(struct mm_struct *mm);
-
-
 static inline void enter_lazy_tlb(struct mm_struct *mm, struct task_struct *tsk)
 {
 #ifdef CONFIG_SMP
@@ -114,6 +126,7 @@ static inline void switch_mm(struct mm_struct *prev, struct mm_struct *next,
                /* Load per-mm CR4 state */
                load_mm_cr4(next);
 
+#ifdef CONFIG_MODIFY_LDT_SYSCALL
                /*
                 * Load the LDT, if the LDT is different.
                 *
@@ -128,6 +141,7 @@ static inline void switch_mm(struct mm_struct *prev, struct mm_struct *next,
                 */
                if (unlikely(prev->context.ldt != next->context.ldt))
                        load_mm_ldt(next);
+#endif
        }
 #ifdef CONFIG_SMP
          else {
index dc19730ad0dbb58a799d71fb71d02e1f9e5ed3aa..514064897d55178254987c9f9624525f558d5976 100644 (file)
@@ -25,7 +25,8 @@ CFLAGS_irq.o := -I$(src)/../include/asm/trace
 obj-y                  := process_$(BITS).o signal.o
 obj-$(CONFIG_COMPAT)   += signal_compat.o
 obj-y                  += traps.o irq.o irq_$(BITS).o dumpstack_$(BITS).o
-obj-y                  += time.o ioport.o ldt.o dumpstack.o nmi.o
+obj-y                  += time.o ioport.o dumpstack.o nmi.o
+obj-$(CONFIG_MODIFY_LDT_SYSCALL)       += ldt.o
 obj-y                  += setup.o x86_init.o i8259.o irqinit.o jump_label.o
 obj-$(CONFIG_IRQ_WORK)  += irq_work.o
 obj-y                  += probe_roms.o
index 09f9ff271df4ade02c1494c8d3e1edc38c7b310d..cc25c8f3512de4e29b98d2b51fae29b358c740af 100644 (file)
@@ -2179,6 +2179,7 @@ static unsigned long get_segment_base(unsigned int segment)
        int idx = segment >> 3;
 
        if ((segment & SEGMENT_TI_MASK) == SEGMENT_LDT) {
+#ifdef CONFIG_MODIFY_LDT_SYSCALL
                struct ldt_struct *ldt;
 
                if (idx > LDT_ENTRIES)
@@ -2190,6 +2191,9 @@ static unsigned long get_segment_base(unsigned int segment)
                        return 0;
 
                desc = &ldt->entries[idx];
+#else
+               return 0;
+#endif
        } else {
                if (idx > GDT_ENTRIES)
                        return 0;
index 7ff035c6c54da17c9aaea96eb2e90e49da4605eb..3c1bbcf129245aa7909708af46489d73f2e9c297 100644 (file)
@@ -121,6 +121,7 @@ void __show_regs(struct pt_regs *regs, int all)
 void release_thread(struct task_struct *dead_task)
 {
        if (dead_task->mm) {
+#ifdef CONFIG_MODIFY_LDT_SYSCALL
                if (dead_task->mm->context.ldt) {
                        pr_warn("WARNING: dead process %s still has LDT? <%p/%d>\n",
                                dead_task->comm,
@@ -128,6 +129,7 @@ void release_thread(struct task_struct *dead_task)
                                dead_task->mm->context.ldt->size);
                        BUG();
                }
+#endif
        }
 }
 
index 6273324186ac5ca7adba69be5ded69f23d8882f7..fd88e152d58425c4200188a0a30d58d72952159f 100644 (file)
@@ -18,6 +18,7 @@ unsigned long convert_ip_to_linear(struct task_struct *child, struct pt_regs *re
                return addr;
        }
 
+#ifdef CONFIG_MODIFY_LDT_SYSCALL
        /*
         * We'll assume that the code segments in the GDT
         * are all zero-based. That is largely true: the
@@ -45,6 +46,7 @@ unsigned long convert_ip_to_linear(struct task_struct *child, struct pt_regs *re
                }
                mutex_unlock(&child->mm->context.lock);
        }
+#endif
 
        return addr;
 }
index 7995ef5868d8f3f2c5ec299209c3d3160457ba7a..ca7d84f438f1ed5ab79f41e95baee0fc1ac1926d 100644 (file)
@@ -140,6 +140,7 @@ cond_syscall(sys_sgetmask);
 cond_syscall(sys_ssetmask);
 cond_syscall(sys_vm86old);
 cond_syscall(sys_vm86);
+cond_syscall(sys_modify_ldt);
 cond_syscall(sys_ipc);
 cond_syscall(compat_sys_ipc);
 cond_syscall(compat_sys_sysctl);