diff options
author | Andy Lutomirski <luto@amacapital.net> | 2014-12-04 19:48:16 -0500 |
---|---|---|
committer | Ingo Molnar <mingo@kernel.org> | 2014-12-14 02:50:31 -0500 |
commit | 41bdc78544b8a93a9c6814b8bbbfef966272abbe (patch) | |
tree | 9c947f48aa4a6e357fc96a4a695271192252f480 | |
parent | f0905c5a32ce6e9b743b4d9c70e53d1ce447852d (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.c | 23 |
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 | ||
30 | static 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 | |||
30 | static void set_tls_desc(struct task_struct *p, int idx, | 45 | static 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)); |