| From 3669ef9fa7d35f573ec9c0e0341b29251c2734a7 Mon Sep 17 00:00:00 2001 |
| From: Andy Lutomirski <luto@amacapital.net> |
| Date: Thu, 22 Jan 2015 11:27:59 -0800 |
| Subject: x86, tls: Interpret an all-zero struct user_desc as "no segment" |
| |
| From: Andy Lutomirski <luto@amacapital.net> |
| |
| commit 3669ef9fa7d35f573ec9c0e0341b29251c2734a7 upstream. |
| |
| The Witcher 2 did something like this to allocate a TLS segment index: |
| |
| struct user_desc u_info; |
| bzero(&u_info, sizeof(u_info)); |
| u_info.entry_number = (uint32_t)-1; |
| |
| syscall(SYS_set_thread_area, &u_info); |
| |
| Strictly speaking, this code was never correct. It should have set |
| read_exec_only and seg_not_present to 1 to indicate that it wanted |
| to find a free slot without putting anything there, or it should |
| have put something sensible in the TLS slot if it wanted to allocate |
| a TLS entry for real. The actual effect of this code was to |
| allocate a bogus segment that could be used to exploit espfix. |
| |
| The set_thread_area hardening patches changed the behavior, causing |
| set_thread_area to return -EINVAL and crashing the game. |
| |
| This changes set_thread_area to interpret this as a request to find |
| a free slot and to leave it empty, which isn't *quite* what the game |
| expects but should be close enough to keep it working. In |
| particular, using the code above to allocate two segments will |
| allocate the same segment both times. |
| |
| According to FrostbittenKing on Github, this fixes The Witcher 2. |
| |
| If this somehow still causes problems, we could instead allocate |
| a limit==0 32-bit data segment, but that seems rather ugly to me. |
| |
| Fixes: 41bdc78544b8 x86/tls: Validate TLS entries to protect espfix |
| Signed-off-by: Andy Lutomirski <luto@amacapital.net> |
| Cc: torvalds@linux-foundation.org |
| Link: http://lkml.kernel.org/r/0cb251abe1ff0958b8e468a9a9a905b80ae3a746.1421954363.git.luto@amacapital.net |
| Signed-off-by: Thomas Gleixner <tglx@linutronix.de> |
| Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> |
| |
| --- |
| arch/x86/include/asm/desc.h | 13 +++++++++++++ |
| arch/x86/kernel/tls.c | 25 +++++++++++++++++++++++-- |
| 2 files changed, 36 insertions(+), 2 deletions(-) |
| |
| --- a/arch/x86/include/asm/desc.h |
| +++ b/arch/x86/include/asm/desc.h |
| @@ -262,6 +262,19 @@ static inline void native_load_tls(struc |
| (info)->seg_not_present == 1 && \ |
| (info)->useable == 0) |
| |
| +/* Lots of programs expect an all-zero user_desc to mean "no segment at all". */ |
| +static inline bool LDT_zero(const struct user_desc *info) |
| +{ |
| + return (info->base_addr == 0 && |
| + info->limit == 0 && |
| + info->contents == 0 && |
| + info->read_exec_only == 0 && |
| + info->seg_32bit == 0 && |
| + info->limit_in_pages == 0 && |
| + info->seg_not_present == 0 && |
| + info->useable == 0); |
| +} |
| + |
| static inline void clear_LDT(void) |
| { |
| set_ldt(NULL, 0); |
| --- a/arch/x86/kernel/tls.c |
| +++ b/arch/x86/kernel/tls.c |
| @@ -29,7 +29,28 @@ static int get_free_idx(void) |
| |
| static bool tls_desc_okay(const struct user_desc *info) |
| { |
| - if (LDT_empty(info)) |
| + /* |
| + * For historical reasons (i.e. no one ever documented how any |
| + * of the segmentation APIs work), user programs can and do |
| + * assume that a struct user_desc that's all zeros except for |
| + * entry_number means "no segment at all". This never actually |
| + * worked. In fact, up to Linux 3.19, a struct user_desc like |
| + * this would create a 16-bit read-write segment with base and |
| + * limit both equal to zero. |
| + * |
| + * That was close enough to "no segment at all" until we |
| + * hardened this function to disallow 16-bit TLS segments. Fix |
| + * it up by interpreting these zeroed segments the way that they |
| + * were almost certainly intended to be interpreted. |
| + * |
| + * The correct way to ask for "no segment at all" is to specify |
| + * a user_desc that satisfies LDT_empty. To keep everything |
| + * working, we accept both. |
| + * |
| + * Note that there's a similar kludge in modify_ldt -- look at |
| + * the distinction between modes 1 and 0x11. |
| + */ |
| + if (LDT_empty(info) || LDT_zero(info)) |
| return true; |
| |
| /* |
| @@ -71,7 +92,7 @@ static void set_tls_desc(struct task_str |
| cpu = get_cpu(); |
| |
| while (n-- > 0) { |
| - if (LDT_empty(info)) |
| + if (LDT_empty(info) || LDT_zero(info)) |
| desc->a = desc->b = 0; |
| else |
| fill_ldt(desc, info); |