diff --git a/debian/changelog b/debian/changelog index 3b23e77d3..dbdf29032 100644 --- a/debian/changelog +++ b/debian/changelog @@ -11,6 +11,9 @@ linux (4.19.37-6) UNRELEASED; urgency=medium and HNS/ROCE Infiniband - Add module:drivers/scsi/hisi_sas/* to the ABI ignore list + [ Romain Perier ] + * [x86] x86/insn-eval: Fix use-after-free access to LDT entry (CVE-2019-13233) + -- Salvatore Bonaccorso Sun, 23 Jun 2019 16:15:17 +0200 linux (4.19.37-5+deb10u1) buster-security; urgency=high diff --git a/debian/patches/bugfix/x86/x86-insn-eval-Fix-use-after-free-access-to-LDT-entry.patch b/debian/patches/bugfix/x86/x86-insn-eval-Fix-use-after-free-access-to-LDT-entry.patch new file mode 100644 index 000000000..3498171c6 --- /dev/null +++ b/debian/patches/bugfix/x86/x86-insn-eval-Fix-use-after-free-access-to-LDT-entry.patch @@ -0,0 +1,176 @@ +From de9f869616dd95e95c00bdd6b0fcd3421e8a4323 Mon Sep 17 00:00:00 2001 +From: Jann Horn +Date: Sun, 2 Jun 2019 03:15:58 +0200 +Subject: x86/insn-eval: Fix use-after-free access to LDT entry +Origin: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=de9f869616dd95e95c00bdd6b0fcd3421e8a4323 +Bug-Debian-Security: https://security-tracker.debian.org/tracker/CVE-2019-13233 + +get_desc() computes a pointer into the LDT while holding a lock that +protects the LDT from being freed, but then drops the lock and returns the +(now potentially dangling) pointer to its caller. + +Fix it by giving the caller a copy of the LDT entry instead. + +Fixes: 670f928ba09b ("x86/insn-eval: Add utility function to get segment descriptor") +Cc: stable@vger.kernel.org +Signed-off-by: Jann Horn +Signed-off-by: Linus Torvalds +--- + arch/x86/lib/insn-eval.c | 47 ++++++++++++++++++++++++----------------------- + 1 file changed, 24 insertions(+), 23 deletions(-) + +diff --git a/arch/x86/lib/insn-eval.c b/arch/x86/lib/insn-eval.c +index cf00ab6c6621..306c3a0902ba 100644 +--- a/arch/x86/lib/insn-eval.c ++++ b/arch/x86/lib/insn-eval.c +@@ -557,7 +557,8 @@ static int get_reg_offset_16(struct insn *insn, struct pt_regs *regs, + } + + /** +- * get_desc() - Obtain pointer to a segment descriptor ++ * get_desc() - Obtain contents of a segment descriptor ++ * @out: Segment descriptor contents on success + * @sel: Segment selector + * + * Given a segment selector, obtain a pointer to the segment descriptor. +@@ -565,18 +566,18 @@ static int get_reg_offset_16(struct insn *insn, struct pt_regs *regs, + * + * Returns: + * +- * Pointer to segment descriptor on success. ++ * True on success, false on failure. + * + * NULL on error. + */ +-static struct desc_struct *get_desc(unsigned short sel) ++static bool get_desc(struct desc_struct *out, unsigned short sel) + { + struct desc_ptr gdt_desc = {0, 0}; + unsigned long desc_base; + + #ifdef CONFIG_MODIFY_LDT_SYSCALL + if ((sel & SEGMENT_TI_MASK) == SEGMENT_LDT) { +- struct desc_struct *desc = NULL; ++ bool success = false; + struct ldt_struct *ldt; + + /* Bits [15:3] contain the index of the desired entry. */ +@@ -584,12 +585,14 @@ static struct desc_struct *get_desc(unsigned short sel) + + mutex_lock(¤t->active_mm->context.lock); + ldt = current->active_mm->context.ldt; +- if (ldt && sel < ldt->nr_entries) +- desc = &ldt->entries[sel]; ++ if (ldt && sel < ldt->nr_entries) { ++ *out = ldt->entries[sel]; ++ success = true; ++ } + + mutex_unlock(¤t->active_mm->context.lock); + +- return desc; ++ return success; + } + #endif + native_store_gdt(&gdt_desc); +@@ -604,9 +607,10 @@ static struct desc_struct *get_desc(unsigned short sel) + desc_base = sel & ~(SEGMENT_RPL_MASK | SEGMENT_TI_MASK); + + if (desc_base > gdt_desc.size) +- return NULL; ++ return false; + +- return (struct desc_struct *)(gdt_desc.address + desc_base); ++ *out = *(struct desc_struct *)(gdt_desc.address + desc_base); ++ return true; + } + + /** +@@ -628,7 +632,7 @@ static struct desc_struct *get_desc(unsigned short sel) + */ + unsigned long insn_get_seg_base(struct pt_regs *regs, int seg_reg_idx) + { +- struct desc_struct *desc; ++ struct desc_struct desc; + short sel; + + sel = get_segment_selector(regs, seg_reg_idx); +@@ -666,11 +670,10 @@ unsigned long insn_get_seg_base(struct pt_regs *regs, int seg_reg_idx) + if (!sel) + return -1L; + +- desc = get_desc(sel); +- if (!desc) ++ if (!get_desc(&desc, sel)) + return -1L; + +- return get_desc_base(desc); ++ return get_desc_base(&desc); + } + + /** +@@ -692,7 +695,7 @@ unsigned long insn_get_seg_base(struct pt_regs *regs, int seg_reg_idx) + */ + static unsigned long get_seg_limit(struct pt_regs *regs, int seg_reg_idx) + { +- struct desc_struct *desc; ++ struct desc_struct desc; + unsigned long limit; + short sel; + +@@ -706,8 +709,7 @@ static unsigned long get_seg_limit(struct pt_regs *regs, int seg_reg_idx) + if (!sel) + return 0; + +- desc = get_desc(sel); +- if (!desc) ++ if (!get_desc(&desc, sel)) + return 0; + + /* +@@ -716,8 +718,8 @@ static unsigned long get_seg_limit(struct pt_regs *regs, int seg_reg_idx) + * not tested when checking the segment limits. In practice, + * this means that the segment ends in (limit << 12) + 0xfff. + */ +- limit = get_desc_limit(desc); +- if (desc->g) ++ limit = get_desc_limit(&desc); ++ if (desc.g) + limit = (limit << 12) + 0xfff; + + return limit; +@@ -741,7 +743,7 @@ static unsigned long get_seg_limit(struct pt_regs *regs, int seg_reg_idx) + */ + int insn_get_code_seg_params(struct pt_regs *regs) + { +- struct desc_struct *desc; ++ struct desc_struct desc; + short sel; + + if (v8086_mode(regs)) +@@ -752,8 +754,7 @@ int insn_get_code_seg_params(struct pt_regs *regs) + if (sel < 0) + return sel; + +- desc = get_desc(sel); +- if (!desc) ++ if (!get_desc(&desc, sel)) + return -EINVAL; + + /* +@@ -761,10 +762,10 @@ int insn_get_code_seg_params(struct pt_regs *regs) + * determines whether a segment contains data or code. If this is a data + * segment, return error. + */ +- if (!(desc->type & BIT(3))) ++ if (!(desc.type & BIT(3))) + return -EINVAL; + +- switch ((desc->l << 1) | desc->d) { ++ switch ((desc.l << 1) | desc.d) { + case 0: /* + * Legacy mode. CS.L=0, CS.D=0. Address and operand size are + * both 16-bit. +-- +cgit 1.2-0.3.lf.el7 + diff --git a/debian/patches/series b/debian/patches/series index 637ee8ba7..f1d4fea13 100644 --- a/debian/patches/series +++ b/debian/patches/series @@ -230,6 +230,7 @@ bugfix/all/tcp-add-tcp_min_snd_mss-sysctl.patch bugfix/all/tcp-enforce-tcp_min_snd_mss-in-tcp_mtu_probing.patch bugfix/all/tcp-refine-memory-limit-test-in-tcp_fragment.patch bugfix/all/ptrace-Fix-ptracer_cred-handling-for-PTRACE_TRACEME.patch +bugfix/x86/x86-insn-eval-Fix-use-after-free-access-to-LDT-entry.patch # Fix exported symbol versions bugfix/all/module-disable-matching-missing-version-crc.patch