Re: [PATCH 3.2 055/152] x86_64, switch_to(): Load TLS descriptors before switching DS and ES

Denys Vlasenko
2015-02-24T15:47:20Z

On Tue, Feb 17, 2015 at 2:46 AM, Ben Hutchings <ben@decadent.org.uk> wrote:
> 3.2.67-rc1 review patch. If anyone has any objections, please let me know.
>
> ------------------
>
> From: Andy Lutomirski <luto@amacapital.net>
>
> commit f647d7c155f069c1a068030255c300663516420e upstream.
>
> Otherwise, if buggy user code points DS or ES into the TLS
> array, they would be corrupted after a context switch.
>
> This also significantly improves the comments and documents some
> gotchas in the code.
>
> Before this patch, the both tests below failed. With this
> patch, the es test passes, although the gsbase test still fails.
>
> ----- begin es test -----
>
> /*
> * Copyright (c) 2014 Andy Lutomirski
> * GPL v2
> */
>
> static unsigned short GDT3(int idx)
> {
> return (idx << 3) | 3;
> }
>
> static int create_tls(int idx, unsigned int base)
> {
> struct user_desc desc = {
> .entry_number = idx,
> .base_addr = base,
> .limit = 0xfffff,
> .seg_32bit = 1,
> .contents = 0, /* Data, grow-up */
> .read_exec_only = 0,
> .limit_in_pages = 1,
> .seg_not_present = 0,
> .useable = 0,
> };
>
> if (syscall(SYS_set_thread_area, &desc) != 0)
> err(1, "set_thread_area");
>
> return desc.entry_number;
> }
>
> int main()
> {
> int idx = create_tls(-1, 0);
> printf("Allocated GDT index %d\n", idx);
>
> unsigned short orig_es;
> asm volatile ("mov %%es,%0" : "=rm" (orig_es));
>
> int errors = 0;
> int total = 1000;
> for (int i = 0; i < total; i++) {
> asm volatile ("mov %0,%%es" : : "rm" (GDT3(idx)));
> usleep(100);
>
> unsigned short es;
> asm volatile ("mov %%es,%0" : "=rm" (es));
> asm volatile ("mov %0,%%es" : : "rm" (orig_es));
> if (es != GDT3(idx)) {
> if (errors == 0)
> printf("[FAIL]\tES changed from 0x%hx to 0x%hx\n",
> GDT3(idx), es);
> errors++;
> }
> }
>
> if (errors) {
> printf("[FAIL]\tES was corrupted %d/%d times\n", errors, total);
> return 1;
> } else {
> printf("[OK]\tES was preserved\n");
> return 0;
> }
> }
>
> ----- end es test -----

This currently fails in 32-bit kernels (at least in qemu):

/ # ./es_test
Allocated GDT index 7
[FAIL] ES changed from 0x3b to 0x7b
[FAIL] ES was corrupted 1000/1000 times
/ # uname -a
Linux (none) 4.0.0-rc1 #1 SMP Tue Feb 24 16:41:58 CET 2015 i686 GNU/Linux