[x86] x86/insn-eval: Fix use-after-free access to LDT entry (CVE-2019-13233)
This commit is contained in:
parent
aa3c23fe0e
commit
7e902dbcd3
|
@ -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 <carnil@debian.org> Sun, 23 Jun 2019 16:15:17 +0200
|
||||
|
||||
linux (4.19.37-5+deb10u1) buster-security; urgency=high
|
||||
|
|
176
debian/patches/bugfix/x86/x86-insn-eval-Fix-use-after-free-access-to-LDT-entry.patch
vendored
Normal file
176
debian/patches/bugfix/x86/x86-insn-eval-Fix-use-after-free-access-to-LDT-entry.patch
vendored
Normal file
|
@ -0,0 +1,176 @@
|
|||
From de9f869616dd95e95c00bdd6b0fcd3421e8a4323 Mon Sep 17 00:00:00 2001
|
||||
From: Jann Horn <jannh@google.com>
|
||||
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 <jannh@google.com>
|
||||
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
|
||||
---
|
||||
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
|
||||
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue