Blame view
arch/x86/kernel/tls.c
7.46 KB
b24413180 License cleanup: ... |
1 |
// SPDX-License-Identifier: GPL-2.0 |
1da177e4c Linux-2.6.12-rc2 |
2 3 4 5 |
#include <linux/kernel.h> #include <linux/errno.h> #include <linux/sched.h> #include <linux/user.h> |
4c79a2d8e x86: x86 user_reg... |
6 |
#include <linux/regset.h> |
2cf096668 make SYSCALL_DEFI... |
7 |
#include <linux/syscalls.h> |
1da177e4c Linux-2.6.12-rc2 |
8 |
|
7c0f6ba68 Replace <asm/uacc... |
9 |
#include <linux/uaccess.h> |
1da177e4c Linux-2.6.12-rc2 |
10 |
#include <asm/desc.h> |
1da177e4c Linux-2.6.12-rc2 |
11 12 13 |
#include <asm/ldt.h> #include <asm/processor.h> #include <asm/proto.h> |
4c79a2d8e x86: x86 user_reg... |
14 |
#include "tls.h" |
1da177e4c Linux-2.6.12-rc2 |
15 16 17 18 19 20 21 22 23 |
/* * sys_alloc_thread_area: get a yet unused TLS descriptor index. */ static int get_free_idx(void) { struct thread_struct *t = ¤t->thread; int idx; for (idx = 0; idx < GDT_ENTRY_TLS_ENTRIES; idx++) |
efd1ca52d x86: TLS cleanup |
24 |
if (desc_empty(&t->tls_array[idx])) |
1da177e4c Linux-2.6.12-rc2 |
25 26 27 |
return idx + GDT_ENTRY_TLS_MIN; return -ESRCH; } |
41bdc7854 x86/tls: Validate... |
28 29 |
static bool tls_desc_okay(const struct user_desc *info) { |
3669ef9fa x86, tls: Interpr... |
30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 |
/* * 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)) |
41bdc7854 x86/tls: Validate... |
52 53 54 55 56 57 58 59 |
return true; /* * espfix is required for 16-bit data segments, but espfix * only works for LDT segments. */ if (!info->seg_32bit) return false; |
0e58af4e1 x86/tls: Disallow... |
60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 |
/* Only allow data segments in the TLS array. */ if (info->contents > 1) return false; /* * Non-present segments with DPL 3 present an interesting attack * surface. The kernel should handle such segments correctly, * but TLS is very difficult to protect in a sandbox, so prevent * such segments from being created. * * If userspace needs to remove a TLS entry, it can still delete * it outright. */ if (info->seg_not_present) return false; |
41bdc7854 x86/tls: Validate... |
75 76 |
return true; } |
1bd5718ce x86: x86 TLS desc... |
77 |
static void set_tls_desc(struct task_struct *p, int idx, |
4c79a2d8e x86: x86 user_reg... |
78 |
const struct user_desc *info, int n) |
1bd5718ce x86: x86 TLS desc... |
79 80 81 82 83 84 85 86 87 |
{ struct thread_struct *t = &p->thread; struct desc_struct *desc = &t->tls_array[idx - GDT_ENTRY_TLS_MIN]; int cpu; /* * We must not get preempted while modifying the TLS. */ cpu = get_cpu(); |
4c79a2d8e x86: x86 user_reg... |
88 |
while (n-- > 0) { |
e08acdb96 x86/ldt: Make the... |
89 |
if (LDT_empty(info) || LDT_zero(info)) |
9a98e7780 x86/asm: Replace ... |
90 |
memset(desc, 0, sizeof(*desc)); |
e08acdb96 x86/ldt: Make the... |
91 |
else |
4c79a2d8e x86: x86 user_reg... |
92 93 94 95 |
fill_ldt(desc, info); ++info; ++desc; } |
1bd5718ce x86: x86 TLS desc... |
96 97 98 99 100 101 |
if (t == ¤t->thread) load_TLS(t, cpu); put_cpu(); } |
1da177e4c Linux-2.6.12-rc2 |
102 103 |
/* * Set a given TLS descriptor: |
1da177e4c Linux-2.6.12-rc2 |
104 |
*/ |
efd1ca52d x86: TLS cleanup |
105 106 107 |
int do_set_thread_area(struct task_struct *p, int idx, struct user_desc __user *u_info, int can_allocate) |
1da177e4c Linux-2.6.12-rc2 |
108 109 |
{ struct user_desc info; |
c9867f863 x86/tls: Synchron... |
110 |
unsigned short __maybe_unused sel, modified_sel; |
1da177e4c Linux-2.6.12-rc2 |
111 112 113 |
if (copy_from_user(&info, u_info, sizeof(info))) return -EFAULT; |
41bdc7854 x86/tls: Validate... |
114 115 |
if (!tls_desc_okay(&info)) return -EINVAL; |
efd1ca52d x86: TLS cleanup |
116 117 |
if (idx == -1) idx = info.entry_number; |
1da177e4c Linux-2.6.12-rc2 |
118 119 120 121 122 |
/* * index -1 means the kernel should try to find and * allocate an empty descriptor: */ |
efd1ca52d x86: TLS cleanup |
123 |
if (idx == -1 && can_allocate) { |
1da177e4c Linux-2.6.12-rc2 |
124 125 126 127 128 129 130 131 132 |
idx = get_free_idx(); if (idx < 0) return idx; if (put_user(idx, &u_info->entry_number)) return -EFAULT; } if (idx < GDT_ENTRY_TLS_MIN || idx > GDT_ENTRY_TLS_MAX) return -EINVAL; |
4c79a2d8e x86: x86 user_reg... |
133 |
set_tls_desc(p, idx, &info, 1); |
1da177e4c Linux-2.6.12-rc2 |
134 |
|
c9867f863 x86/tls: Synchron... |
135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 |
/* * If DS, ES, FS, or GS points to the modified segment, forcibly * refresh it. Only needed on x86_64 because x86_32 reloads them * on return to user mode. */ modified_sel = (idx << 3) | 3; if (p == current) { #ifdef CONFIG_X86_64 savesegment(ds, sel); if (sel == modified_sel) loadsegment(ds, sel); savesegment(es, sel); if (sel == modified_sel) loadsegment(es, sel); savesegment(fs, sel); if (sel == modified_sel) loadsegment(fs, sel); savesegment(gs, sel); if (sel == modified_sel) load_gs_index(sel); #endif #ifdef CONFIG_X86_32_LAZY_GS savesegment(gs, sel); if (sel == modified_sel) loadsegment(gs, sel); #endif } else { #ifdef CONFIG_X86_64 if (p->thread.fsindex == modified_sel) p->thread.fsbase = info.base_addr; if (p->thread.gsindex == modified_sel) p->thread.gsbase = info.base_addr; #endif } |
1da177e4c Linux-2.6.12-rc2 |
175 176 |
return 0; } |
2cf096668 make SYSCALL_DEFI... |
177 |
SYSCALL_DEFINE1(set_thread_area, struct user_desc __user *, u_info) |
13abd0e50 x86: tls32 moved |
178 |
{ |
2cf096668 make SYSCALL_DEFI... |
179 |
return do_set_thread_area(current, -1, u_info, 1); |
13abd0e50 x86: tls32 moved |
180 |
} |
1da177e4c Linux-2.6.12-rc2 |
181 182 183 184 185 |
/* * Get the current Thread-Local Storage area: */ |
1bd5718ce x86: x86 TLS desc... |
186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 |
static void fill_user_desc(struct user_desc *info, int idx, const struct desc_struct *desc) { memset(info, 0, sizeof(*info)); info->entry_number = idx; info->base_addr = get_desc_base(desc); info->limit = get_desc_limit(desc); info->seg_32bit = desc->d; info->contents = desc->type >> 2; info->read_exec_only = !(desc->type & 2); info->limit_in_pages = desc->g; info->seg_not_present = !desc->p; info->useable = desc->avl; #ifdef CONFIG_X86_64 info->lm = desc->l; #endif } |
efd1ca52d x86: TLS cleanup |
204 205 206 |
int do_get_thread_area(struct task_struct *p, int idx, struct user_desc __user *u_info) |
1da177e4c Linux-2.6.12-rc2 |
207 208 |
{ struct user_desc info; |
1da177e4c Linux-2.6.12-rc2 |
209 |
|
efd1ca52d x86: TLS cleanup |
210 |
if (idx == -1 && get_user(idx, &u_info->entry_number)) |
1da177e4c Linux-2.6.12-rc2 |
211 |
return -EFAULT; |
1bd5718ce x86: x86 TLS desc... |
212 |
|
1da177e4c Linux-2.6.12-rc2 |
213 214 |
if (idx < GDT_ENTRY_TLS_MIN || idx > GDT_ENTRY_TLS_MAX) return -EINVAL; |
1bd5718ce x86: x86 TLS desc... |
215 216 |
fill_user_desc(&info, idx, &p->thread.tls_array[idx - GDT_ENTRY_TLS_MIN]); |
1da177e4c Linux-2.6.12-rc2 |
217 218 219 220 221 |
if (copy_to_user(u_info, &info, sizeof(info))) return -EFAULT; return 0; } |
2cf096668 make SYSCALL_DEFI... |
222 |
SYSCALL_DEFINE1(get_thread_area, struct user_desc __user *, u_info) |
1da177e4c Linux-2.6.12-rc2 |
223 |
{ |
2cf096668 make SYSCALL_DEFI... |
224 |
return do_get_thread_area(current, -1, u_info); |
1da177e4c Linux-2.6.12-rc2 |
225 |
} |
4c79a2d8e x86: x86 user_reg... |
226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 |
int regset_tls_active(struct task_struct *target, const struct user_regset *regset) { struct thread_struct *t = &target->thread; int n = GDT_ENTRY_TLS_ENTRIES; while (n > 0 && desc_empty(&t->tls_array[n - 1])) --n; return n; } int regset_tls_get(struct task_struct *target, const struct user_regset *regset, unsigned int pos, unsigned int count, void *kbuf, void __user *ubuf) { const struct desc_struct *tls; |
8f0750f19 x86, tls: Off by ... |
242 |
if (pos >= GDT_ENTRY_TLS_ENTRIES * sizeof(struct user_desc) || |
4c79a2d8e x86: x86 user_reg... |
243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 |
(pos % sizeof(struct user_desc)) != 0 || (count % sizeof(struct user_desc)) != 0) return -EINVAL; pos /= sizeof(struct user_desc); count /= sizeof(struct user_desc); tls = &target->thread.tls_array[pos]; if (kbuf) { struct user_desc *info = kbuf; while (count-- > 0) fill_user_desc(info++, GDT_ENTRY_TLS_MIN + pos++, tls++); } else { struct user_desc __user *u_info = ubuf; while (count-- > 0) { struct user_desc info; fill_user_desc(&info, GDT_ENTRY_TLS_MIN + pos++, tls++); if (__copy_to_user(u_info++, &info, sizeof(info))) return -EFAULT; } } return 0; } int regset_tls_set(struct task_struct *target, const struct user_regset *regset, unsigned int pos, unsigned int count, const void *kbuf, const void __user *ubuf) { struct user_desc infobuf[GDT_ENTRY_TLS_ENTRIES]; const struct user_desc *info; |
41bdc7854 x86/tls: Validate... |
276 |
int i; |
4c79a2d8e x86: x86 user_reg... |
277 |
|
8f0750f19 x86, tls: Off by ... |
278 |
if (pos >= GDT_ENTRY_TLS_ENTRIES * sizeof(struct user_desc) || |
4c79a2d8e x86: x86 user_reg... |
279 280 281 282 283 284 285 286 287 288 |
(pos % sizeof(struct user_desc)) != 0 || (count % sizeof(struct user_desc)) != 0) return -EINVAL; if (kbuf) info = kbuf; else if (__copy_from_user(infobuf, ubuf, count)) return -EFAULT; else info = infobuf; |
41bdc7854 x86/tls: Validate... |
289 290 291 |
for (i = 0; i < count / sizeof(struct user_desc); i++) if (!tls_desc_okay(info + i)) return -EINVAL; |
4c79a2d8e x86: x86 user_reg... |
292 293 294 295 296 297 |
set_tls_desc(target, GDT_ENTRY_TLS_MIN + (pos / sizeof(struct user_desc)), info, count / sizeof(struct user_desc)); return 0; } |