summaryrefslogtreecommitdiffstats
path: root/x86-tls-Validate-TLS-entries-to-protect-espfix.patch
diff options
context:
space:
mode:
Diffstat (limited to 'x86-tls-Validate-TLS-entries-to-protect-espfix.patch')
-rw-r--r--x86-tls-Validate-TLS-entries-to-protect-espfix.patch77
1 files changed, 77 insertions, 0 deletions
diff --git a/x86-tls-Validate-TLS-entries-to-protect-espfix.patch b/x86-tls-Validate-TLS-entries-to-protect-espfix.patch
new file mode 100644
index 000000000..52c049767
--- /dev/null
+++ b/x86-tls-Validate-TLS-entries-to-protect-espfix.patch
@@ -0,0 +1,77 @@
+From: Andy Lutomirski <luto@amacapital.net>
+Date: Thu, 4 Dec 2014 16:48:16 -0800
+Subject: [PATCH] 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>
+---
+ arch/x86/kernel/tls.c | 23 +++++++++++++++++++++++
+ 1 file changed, 23 insertions(+)
+
+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)
+ 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_struct *p, int idx,
+ 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 *target, const struct user_regset *regset,
+ {
+ 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 *target, const struct user_regset *regset,
+ 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));
+--
+2.1.0
+