Home Home > GIT Browse
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNicolai Stange <nstange@suse.de>2018-07-17 12:47:40 +0200
committerMiroslav Benes <mbenes@suse.cz>2018-07-19 14:46:45 +0200
commit06ca1d58eaa07765cc323e16aad3fd43bd94c2bc (patch)
treed596f2145e0524cdd62ae68efa2af921095a2ffd
parent58fddd5116b88802afb88ae66a6cd03998c79595 (diff)
Fix for CVE-2017-17053 ("The init_new_context function in arch/x86/include/asm/mmu_context.h in the Linux kernel before 4.12.10 does not correctly handle errors from LDT table allocation ...")
Live patch for CVE-2017-17053. Upstream commit ccd5b3235180 ("x86/mm: Fix use-after-free of ldt_struct"). KLP: CVE-2017-17053 References: bsc#1096679 CVE-2017-17053 Signed-off-by: Nicolai Stange <nstange@suse.de> Signed-off-by: Miroslav Benes <mbenes@suse.cz>
-rw-r--r--bsc1096679/kgr_patch_bsc1096679.c214
-rw-r--r--bsc1096679/kgr_patch_bsc1096679.h29
2 files changed, 243 insertions, 0 deletions
diff --git a/bsc1096679/kgr_patch_bsc1096679.c b/bsc1096679/kgr_patch_bsc1096679.c
new file mode 100644
index 0000000..0a9ebd5
--- /dev/null
+++ b/bsc1096679/kgr_patch_bsc1096679.c
@@ -0,0 +1,214 @@
+/*
+ * kgraft_patch_bsc1096679
+ *
+ * Fix for CVE-2017-17053, bsc#1096679
+ *
+ * Upstream commit:
+ * ccd5b3235180 ("x86/mm: Fix use-after-free of ldt_struct")
+ *
+ * SLE12 commit:
+ * not affected
+ *
+ * SLE12-SP1 commit:
+ * not affected
+ *
+ * SLE12-SP2 commit:
+ * not affected
+ *
+ * SLE12-SP3 commit:
+ * 33a84c676397c58c4b06497b261d267641f66475
+ *
+ * SLE15 commit:
+ * not affected
+ *
+ * Copyright (c) 2018 SUSE
+ * Author: Nicolai Stange <nstange@suse.de>
+ *
+ * Based on the original Linux kernel code. Other copyrights apply.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#if IS_ENABLED(CONFIG_X86_64)
+
+#include <linux/kernel.h>
+#include <linux/kallsyms.h>
+#include <linux/sched.h>
+#include <linux/mm.h>
+#include <linux/rbtree.h>
+#include <linux/rwsem.h>
+#include <linux/spinlock.h>
+#include <linux/mmu_notifier.h>
+#include <linux/slab.h>
+#include <linux/user_namespace.h>
+#include "kgr_patch_bsc1096679.h"
+
+#if !IS_ENABLED(CONFIG_MODIFY_LDT_SYSCALL)
+#error "Live patch supports only CONFIG_MODIFY_LDT_SYSCALL=y."
+#endif
+
+static struct kmem_cache **kgr_mm_cachep;
+static unsigned long *kgr_default_dump_filter;
+static int (*kgr_init_new_context_ldt)(struct task_struct *tsk,
+ struct mm_struct *mm);
+static pgd_t *(*kgr_pgd_alloc)(struct mm_struct *mm);
+static void (*kgr_pgd_free)(struct mm_struct *mm, pgd_t *pgd);
+
+static struct {
+ char *name;
+ char **addr;
+} kgr_funcs[] = {
+ { "mm_cachep", (void *)&kgr_mm_cachep },
+ { "default_dump_filter", (void *)&kgr_default_dump_filter},
+ { "init_new_context_ldt", (void *)&kgr_init_new_context_ldt },
+ { "pgd_alloc", (void *)&kgr_pgd_alloc },
+ { "pgd_free", (void *)&kgr_pgd_free },
+};
+
+
+/* from kernel/fork.c */
+/* inlined */
+static inline int kgr_mm_alloc_pgd(struct mm_struct *mm)
+{
+ mm->pgd = kgr_pgd_alloc(mm);
+ if (unlikely(!mm->pgd))
+ return -ENOMEM;
+ return 0;
+}
+
+/* inlined */
+static inline void kgr_mm_free_pgd(struct mm_struct *mm)
+{
+ kgr_pgd_free(mm, mm->pgd);
+}
+
+#define kgr_free_mm(mm) (kmem_cache_free(*kgr_mm_cachep, (mm)))
+
+/* inlined */
+static void kgr_mm_init_aio(struct mm_struct *mm)
+{
+#ifdef CONFIG_AIO
+ spin_lock_init(&mm->ioctx_lock);
+ mm->ioctx_table = NULL;
+#endif
+}
+
+/* inlined */
+static void kgr_mm_init_owner(struct mm_struct *mm, struct task_struct *p)
+{
+#ifdef CONFIG_MEMCG
+ mm->owner = p;
+#endif
+}
+
+
+
+/* patched, inlined */
+static inline int kgr_init_new_context(struct task_struct *tsk,
+ struct mm_struct *mm)
+{
+ #ifdef CONFIG_X86_INTEL_MEMORY_PROTECTION_KEYS
+ if (cpu_feature_enabled(X86_FEATURE_OSPKE)) {
+ /* pkey 0 is the default and always allocated */
+ mm->context.pkey_allocation_map = 0x1;
+ /* -1 means unallocated or invalid */
+ mm->context.execute_only_pkey = -1;
+ }
+ #endif
+ /*
+ * Fix CVE-2017-17053
+ * -3 lines, +1 line
+ */
+ return kgr_init_new_context_ldt(tsk, mm);
+}
+
+/* patched, calls init_new_context() */
+struct mm_struct *kgr_mm_init(struct mm_struct *mm, struct task_struct *p,
+ struct user_namespace *user_ns)
+{
+ mm->mmap = NULL;
+ mm->mm_rb = RB_ROOT;
+ mm->vmacache_seqnum = 0;
+ atomic_set(&mm->mm_users, 1);
+ atomic_set(&mm->mm_count, 1);
+ init_rwsem(&mm->mmap_sem);
+ INIT_LIST_HEAD(&mm->mmlist);
+ mm->core_state = NULL;
+ atomic_long_set(&mm->nr_ptes, 0);
+ mm_nr_pmds_init(mm);
+ mm->map_count = 0;
+ mm->locked_vm = 0;
+ mm->pinned_vm = 0;
+ memset(&mm->rss_stat, 0, sizeof(mm->rss_stat));
+ spin_lock_init(&mm->page_table_lock);
+ mm_init_cpumask(mm);
+ kgr_mm_init_aio(mm);
+ kgr_mm_init_owner(mm, p);
+ mmu_notifier_mm_init(mm);
+ clear_tlb_flush_pending(mm);
+#if defined(CONFIG_TRANSPARENT_HUGEPAGE) && !USE_SPLIT_PMD_PTLOCKS
+ mm->pmd_huge_pte = NULL;
+#endif
+
+ if (current->mm) {
+ mm->flags = current->mm->flags & MMF_INIT_MASK;
+ mm->def_flags = current->mm->def_flags & VM_INIT_DEF_MASK;
+ } else {
+ mm->flags = *kgr_default_dump_filter;
+ mm->def_flags = 0;
+ }
+
+ if (kgr_mm_alloc_pgd(mm))
+ goto fail_nopgd;
+
+ if (kgr_init_new_context(p, mm))
+ goto fail_nocontext;
+
+ mm->user_ns = get_user_ns(user_ns);
+ return mm;
+
+fail_nocontext:
+ kgr_mm_free_pgd(mm);
+fail_nopgd:
+ kgr_free_mm(mm);
+ return NULL;
+}
+
+
+
+static int kgr_patch_bsc1096679_kallsyms(void)
+{
+ unsigned long addr;
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(kgr_funcs); i++) {
+ addr = kallsyms_lookup_name(kgr_funcs[i].name);
+ if (!addr) {
+ pr_err("kgraft-patch: symbol %s not resolved\n",
+ kgr_funcs[i].name);
+ return -ENOENT;
+ }
+
+ *(kgr_funcs[i].addr) = (void *)addr;
+ }
+
+ return 0;
+}
+
+int kgr_patch_bsc1096679_init(void)
+{
+ return kgr_patch_bsc1096679_kallsyms();
+}
+
+#endif /* IS_ENABLED(CONFIG_X86_64) */
diff --git a/bsc1096679/kgr_patch_bsc1096679.h b/bsc1096679/kgr_patch_bsc1096679.h
new file mode 100644
index 0000000..b0571a8
--- /dev/null
+++ b/bsc1096679/kgr_patch_bsc1096679.h
@@ -0,0 +1,29 @@
+#ifndef _KGR_PATCH_BSC1096679_H
+#define _KGR_PATCH_BSC1096679_H
+
+#if IS_ENABLED(CONFIG_X86_64)
+
+int kgr_patch_bsc1096679_init(void);
+static inline void kgr_patch_bsc1096679_cleanup(void) {}
+
+
+struct mm_struct;
+struct task_struct;
+struct user_namespace;
+
+struct mm_struct *kgr_mm_init(struct mm_struct *mm, struct task_struct *p,
+ struct user_namespace *user_ns);
+
+#define KGR_PATCH_BSC1096679_FUNCS \
+ KGR_PATCH(mm_init, kgr_mm_init), \
+
+
+#else /* !IS_ENABLED(CONFIG_X86_64) */
+
+static inline int kgr_patch_bsc1096679_init(void) { return 0; }
+static inline void kgr_patch_bsc1096679_cleanup(void) {}
+
+#define KGR_PATCH_BSC1096679_FUNCS
+
+#endif /* IS_ENABLED(CONFIG_X86_64) */
+#endif