1 From de9f869616dd95e95c00bdd6b0fcd3421e8a4323 Mon Sep 17 00:00:00 2001
2 From: Jann Horn <jannh@google.com>
3 Date: Sun, 2 Jun 2019 03:15:58 +0200
4 Subject: x86/insn-eval: Fix use-after-free access to LDT entry
6 From: Jann Horn <jannh@google.com>
8 commit de9f869616dd95e95c00bdd6b0fcd3421e8a4323 upstream.
10 get_desc() computes a pointer into the LDT while holding a lock that
11 protects the LDT from being freed, but then drops the lock and returns the
12 (now potentially dangling) pointer to its caller.
14 Fix it by giving the caller a copy of the LDT entry instead.
16 Fixes: 670f928ba09b ("x86/insn-eval: Add utility function to get segment descriptor")
17 Cc: stable@vger.kernel.org
18 Signed-off-by: Jann Horn <jannh@google.com>
19 Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
20 Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
23 arch/x86/lib/insn-eval.c | 47 ++++++++++++++++++++++++-----------------------
24 1 file changed, 24 insertions(+), 23 deletions(-)
26 --- a/arch/x86/lib/insn-eval.c
27 +++ b/arch/x86/lib/insn-eval.c
28 @@ -557,7 +557,8 @@ static int get_reg_offset_16(struct insn
32 - * get_desc() - Obtain pointer to a segment descriptor
33 + * get_desc() - Obtain contents of a segment descriptor
34 + * @out: Segment descriptor contents on success
35 * @sel: Segment selector
37 * Given a segment selector, obtain a pointer to the segment descriptor.
38 @@ -565,18 +566,18 @@ static int get_reg_offset_16(struct insn
42 - * Pointer to segment descriptor on success.
43 + * True on success, false on failure.
47 -static struct desc_struct *get_desc(unsigned short sel)
48 +static bool get_desc(struct desc_struct *out, unsigned short sel)
50 struct desc_ptr gdt_desc = {0, 0};
51 unsigned long desc_base;
53 #ifdef CONFIG_MODIFY_LDT_SYSCALL
54 if ((sel & SEGMENT_TI_MASK) == SEGMENT_LDT) {
55 - struct desc_struct *desc = NULL;
56 + bool success = false;
57 struct ldt_struct *ldt;
59 /* Bits [15:3] contain the index of the desired entry. */
60 @@ -584,12 +585,14 @@ static struct desc_struct *get_desc(unsi
62 mutex_lock(¤t->active_mm->context.lock);
63 ldt = current->active_mm->context.ldt;
64 - if (ldt && sel < ldt->nr_entries)
65 - desc = &ldt->entries[sel];
66 + if (ldt && sel < ldt->nr_entries) {
67 + *out = ldt->entries[sel];
71 mutex_unlock(¤t->active_mm->context.lock);
77 native_store_gdt(&gdt_desc);
78 @@ -604,9 +607,10 @@ static struct desc_struct *get_desc(unsi
79 desc_base = sel & ~(SEGMENT_RPL_MASK | SEGMENT_TI_MASK);
81 if (desc_base > gdt_desc.size)
85 - return (struct desc_struct *)(gdt_desc.address + desc_base);
86 + *out = *(struct desc_struct *)(gdt_desc.address + desc_base);
91 @@ -628,7 +632,7 @@ static struct desc_struct *get_desc(unsi
93 unsigned long insn_get_seg_base(struct pt_regs *regs, int seg_reg_idx)
95 - struct desc_struct *desc;
96 + struct desc_struct desc;
99 sel = get_segment_selector(regs, seg_reg_idx);
100 @@ -666,11 +670,10 @@ unsigned long insn_get_seg_base(struct p
104 - desc = get_desc(sel);
106 + if (!get_desc(&desc, sel))
109 - return get_desc_base(desc);
110 + return get_desc_base(&desc);
114 @@ -692,7 +695,7 @@ unsigned long insn_get_seg_base(struct p
116 static unsigned long get_seg_limit(struct pt_regs *regs, int seg_reg_idx)
118 - struct desc_struct *desc;
119 + struct desc_struct desc;
123 @@ -706,8 +709,7 @@ static unsigned long get_seg_limit(struc
127 - desc = get_desc(sel);
129 + if (!get_desc(&desc, sel))
133 @@ -716,8 +718,8 @@ static unsigned long get_seg_limit(struc
134 * not tested when checking the segment limits. In practice,
135 * this means that the segment ends in (limit << 12) + 0xfff.
137 - limit = get_desc_limit(desc);
139 + limit = get_desc_limit(&desc);
141 limit = (limit << 12) + 0xfff;
144 @@ -741,7 +743,7 @@ static unsigned long get_seg_limit(struc
146 int insn_get_code_seg_params(struct pt_regs *regs)
148 - struct desc_struct *desc;
149 + struct desc_struct desc;
152 if (v8086_mode(regs))
153 @@ -752,8 +754,7 @@ int insn_get_code_seg_params(struct pt_r
157 - desc = get_desc(sel);
159 + if (!get_desc(&desc, sel))
163 @@ -761,10 +762,10 @@ int insn_get_code_seg_params(struct pt_r
164 * determines whether a segment contains data or code. If this is a data
165 * segment, return error.
167 - if (!(desc->type & BIT(3)))
168 + if (!(desc.type & BIT(3)))
171 - switch ((desc->l << 1) | desc->d) {
172 + switch ((desc.l << 1) | desc.d) {
174 * Legacy mode. CS.L=0, CS.D=0. Address and operand size are