| From 41bdc78544b8a93a9c6814b8bbbfef966272abbe Mon Sep 17 00:00:00 2001 |
| From: Andy Lutomirski <luto@amacapital.net> |
| Date: Thu, 4 Dec 2014 16:48:16 -0800 |
| Subject: x86/tls: Validate TLS entries to protect espfix |
| |
| From: Andy Lutomirski <luto@amacapital.net> |
| |
| commit 41bdc78544b8a93a9c6814b8bbbfef966272abbe upstream. |
| |
| 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: Konrad Rzeszutek Wilk <konrad.wilk@oracle.com> |
| Cc: Linus Torvalds <torvalds@linux-foundation.org> |
| Cc: Willy Tarreau <w@1wt.eu> |
| Signed-off-by: Ingo Molnar <mingo@kernel.org> |
| Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> |
| |
| --- |
| arch/x86/kernel/tls.c | 23 +++++++++++++++++++++++ |
| 1 file changed, 23 insertions(+) |
| |
| --- a/arch/x86/kernel/tls.c |
| +++ b/arch/x86/kernel/tls.c |
| @@ -27,6 +27,21 @@ static int get_free_idx(void) |
| return -ESRCH; |
| } |
| |
| +static bool tls_desc_okay(const struct user_desc *info) |
| +{ |
| + if (LDT_empty(info)) |
| + return true; |
| + |
| + /* |
| + * espfix is required for 16-bit data segments, but espfix |
| + * only works for LDT segments. |
| + */ |
| + if (!info->seg_32bit) |
| + return false; |
| + |
| + return true; |
| +} |
| + |
| static void set_tls_desc(struct task_struct *p, int idx, |
| const struct user_desc *info, int n) |
| { |
| @@ -66,6 +81,9 @@ int do_set_thread_area(struct task_struc |
| if (copy_from_user(&info, u_info, sizeof(info))) |
| return -EFAULT; |
| |
| + if (!tls_desc_okay(&info)) |
| + return -EINVAL; |
| + |
| if (idx == -1) |
| idx = info.entry_number; |
| |
| @@ -192,6 +210,7 @@ int regset_tls_set(struct task_struct *t |
| { |
| struct user_desc infobuf[GDT_ENTRY_TLS_ENTRIES]; |
| const struct user_desc *info; |
| + int i; |
| |
| if (pos >= GDT_ENTRY_TLS_ENTRIES * sizeof(struct user_desc) || |
| (pos % sizeof(struct user_desc)) != 0 || |
| @@ -205,6 +224,10 @@ int regset_tls_set(struct task_struct *t |
| else |
| info = infobuf; |
| |
| + for (i = 0; i < count / sizeof(struct user_desc); i++) |
| + if (!tls_desc_okay(info + i)) |
| + return -EINVAL; |
| + |
| set_tls_desc(target, |
| GDT_ENTRY_TLS_MIN + (pos / sizeof(struct user_desc)), |
| info, count / sizeof(struct user_desc)); |