Home Home > GIT Browse
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBorislav Petkov <bp@suse.de>2015-03-05 15:49:24 +0100
committerJiri Kosina <jkosina@suse.cz>2015-03-05 15:52:53 +0100
commit47249383bf008f673c6d31fe51fe6e83c2617b55 (patch)
tree761b6e916c9f79d6f862a48607a88aeddaa578eb
parentc2eed1da28301ebcb9396fb546953fdbc1e73851 (diff)
x86, tls: Interpret an all-zero struct user_desc as "no segment"
(bsc#920250).
-rw-r--r--patches.arch/02-x86-tls-interpret-an-all-zero-struct-user_desc-as-no-segment.patch115
-rw-r--r--series.conf1
2 files changed, 116 insertions, 0 deletions
diff --git a/patches.arch/02-x86-tls-interpret-an-all-zero-struct-user_desc-as-no-segment.patch b/patches.arch/02-x86-tls-interpret-an-all-zero-struct-user_desc-as-no-segment.patch
new file mode 100644
index 0000000000..43650a8a28
--- /dev/null
+++ b/patches.arch/02-x86-tls-interpret-an-all-zero-struct-user_desc-as-no-segment.patch
@@ -0,0 +1,115 @@
+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"
+Git-commit: 3669ef9fa7d35f573ec9c0e0341b29251c2734a7
+Patch-mainline: v3.19-rc6
+References: bsc#920250
+
+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: stable@vger.kernel.org
+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>
+Acked-by: Borislav Petkov <bp@suse.de>
+---
+ arch/x86/include/asm/desc.h | 13 +++++++++++++
+ arch/x86/kernel/tls.c | 25 +++++++++++++++++++++++--
+ 2 files changed, 36 insertions(+), 2 deletions(-)
+
+Index: kernel/arch/x86/include/asm/desc.h
+===================================================================
+--- kernel.orig/arch/x86/include/asm/desc.h
++++ kernel/arch/x86/include/asm/desc.h
+@@ -259,6 +259,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);
+Index: kernel/arch/x86/kernel/tls.c
+===================================================================
+--- kernel.orig/arch/x86/kernel/tls.c
++++ kernel/arch/x86/kernel/tls.c
+@@ -30,7 +30,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;
+
+ /*
+@@ -56,7 +77,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);
diff --git a/series.conf b/series.conf
index 84b46f6b04..0a8b8bcf19 100644
--- a/series.conf
+++ b/series.conf
@@ -17072,6 +17072,7 @@
# bsc#920250
patches.arch/01-x86-tls-ldt-stop-checking-lm-in-ldt_empty.patch
+ patches.arch/02-x86-tls-interpret-an-all-zero-struct-user_desc-as-no-segment.patch
########################################################
# You'd better have a good reason for adding a patch