aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAndy Lutomirski <luto@amacapital.net>2014-12-04 19:48:16 -0500
committerIngo Molnar <mingo@kernel.org>2014-12-14 02:50:31 -0500
commit41bdc78544b8a93a9c6814b8bbbfef966272abbe (patch)
tree9c947f48aa4a6e357fc96a4a695271192252f480
parentf0905c5a32ce6e9b743b4d9c70e53d1ce447852d (diff)
x86/tls: Validate TLS entries to protect espfix
Installing a 16-bit RW data segment into the GDT defeats espfix. AFAICT this will not affect glibc, Wine, or dosemu at all. Signed-off-by: Andy Lutomirski <luto@amacapital.net> Acked-by: H. Peter Anvin <hpa@zytor.com> Cc: stable@vger.kernel.org Cc: Konrad Rzeszutek Wilk <konrad.wilk@oracle.com> Cc: Linus Torvalds <torvalds@linux-foundation.org> Cc: security@kernel.org <security@kernel.org> Cc: Willy Tarreau <w@1wt.eu> Signed-off-by: Ingo Molnar <mingo@kernel.org>
-rw-r--r--arch/x86/kernel/tls.c23
1 files changed, 23 insertions, 0 deletions
diff --git a/arch/x86/kernel/tls.c b/arch/x86/kernel/tls.c
index f7fec09e3e3a..e7650bd71109 100644
--- a/arch/x86/kernel/tls.c
+++ b/arch/x86/kernel/tls.c
@@ -27,6 +27,21 @@ static int get_free_idx(void)
27 return -ESRCH; 27 return -ESRCH;
28} 28}
29 29
30static bool tls_desc_okay(const struct user_desc *info)
31{
32 if (LDT_empty(info))
33 return true;
34
35 /*
36 * espfix is required for 16-bit data segments, but espfix
37 * only works for LDT segments.
38 */
39 if (!info->seg_32bit)
40 return false;
41
42 return true;
43}
44
30static void set_tls_desc(struct task_struct *p, int idx, 45static void set_tls_desc(struct task_struct *p, int idx,
31 const struct user_desc *info, int n) 46 const struct user_desc *info, int n)
32{ 47{
@@ -66,6 +81,9 @@ int do_set_thread_area(struct task_struct *p, int idx,
66 if (copy_from_user(&info, u_info, sizeof(info))) 81 if (copy_from_user(&info, u_info, sizeof(info)))
67 return -EFAULT; 82 return -EFAULT;
68 83
84 if (!tls_desc_okay(&info))
85 return -EINVAL;
86
69 if (idx == -1) 87 if (idx == -1)
70 idx = info.entry_number; 88 idx = info.entry_number;
71 89
@@ -192,6 +210,7 @@ int regset_tls_set(struct task_struct *target, const struct user_regset *regset,
192{ 210{
193 struct user_desc infobuf[GDT_ENTRY_TLS_ENTRIES]; 211 struct user_desc infobuf[GDT_ENTRY_TLS_ENTRIES];
194 const struct user_desc *info; 212 const struct user_desc *info;
213 int i;
195 214
196 if (pos >= GDT_ENTRY_TLS_ENTRIES * sizeof(struct user_desc) || 215 if (pos >= GDT_ENTRY_TLS_ENTRIES * sizeof(struct user_desc) ||
197 (pos % sizeof(struct user_desc)) != 0 || 216 (pos % sizeof(struct user_desc)) != 0 ||
@@ -205,6 +224,10 @@ int regset_tls_set(struct task_struct *target, const struct user_regset *regset,
205 else 224 else
206 info = infobuf; 225 info = infobuf;
207 226
227 for (i = 0; i < count / sizeof(struct user_desc); i++)
228 if (!tls_desc_okay(info + i))
229 return -EINVAL;
230
208 set_tls_desc(target, 231 set_tls_desc(target,
209 GDT_ENTRY_TLS_MIN + (pos / sizeof(struct user_desc)), 232 GDT_ENTRY_TLS_MIN + (pos / sizeof(struct user_desc)),
210 info, count / sizeof(struct user_desc)); 233 info, count / sizeof(struct user_desc));