diff options
Diffstat (limited to 'arch/x86/kernel/tls.c')
-rw-r--r-- | arch/x86/kernel/tls.c | 25 |
1 files changed, 23 insertions, 2 deletions
diff --git a/arch/x86/kernel/tls.c b/arch/x86/kernel/tls.c index 4e942f31b1a7..7fc5e843f247 100644 --- a/arch/x86/kernel/tls.c +++ b/arch/x86/kernel/tls.c | |||
@@ -29,7 +29,28 @@ static int get_free_idx(void) | |||
29 | 29 | ||
30 | static bool tls_desc_okay(const struct user_desc *info) | 30 | static bool tls_desc_okay(const struct user_desc *info) |
31 | { | 31 | { |
32 | if (LDT_empty(info)) | 32 | /* |
33 | * For historical reasons (i.e. no one ever documented how any | ||
34 | * of the segmentation APIs work), user programs can and do | ||
35 | * assume that a struct user_desc that's all zeros except for | ||
36 | * entry_number means "no segment at all". This never actually | ||
37 | * worked. In fact, up to Linux 3.19, a struct user_desc like | ||
38 | * this would create a 16-bit read-write segment with base and | ||
39 | * limit both equal to zero. | ||
40 | * | ||
41 | * That was close enough to "no segment at all" until we | ||
42 | * hardened this function to disallow 16-bit TLS segments. Fix | ||
43 | * it up by interpreting these zeroed segments the way that they | ||
44 | * were almost certainly intended to be interpreted. | ||
45 | * | ||
46 | * The correct way to ask for "no segment at all" is to specify | ||
47 | * a user_desc that satisfies LDT_empty. To keep everything | ||
48 | * working, we accept both. | ||
49 | * | ||
50 | * Note that there's a similar kludge in modify_ldt -- look at | ||
51 | * the distinction between modes 1 and 0x11. | ||
52 | */ | ||
53 | if (LDT_empty(info) || LDT_zero(info)) | ||
33 | return true; | 54 | return true; |
34 | 55 | ||
35 | /* | 56 | /* |
@@ -71,7 +92,7 @@ static void set_tls_desc(struct task_struct *p, int idx, | |||
71 | cpu = get_cpu(); | 92 | cpu = get_cpu(); |
72 | 93 | ||
73 | while (n-- > 0) { | 94 | while (n-- > 0) { |
74 | if (LDT_empty(info)) | 95 | if (LDT_empty(info) || LDT_zero(info)) |
75 | desc->a = desc->b = 0; | 96 | desc->a = desc->b = 0; |
76 | else | 97 | else |
77 | fill_ldt(desc, info); | 98 | fill_ldt(desc, info); |