Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

at v2.6.24 500 lines 12 kB view raw
1/* 2 * Copyright (C) 2001 - 2007 Jeff Dike (jdike@{addtoit,linux.intel}.com) 3 * Licensed under the GPL 4 */ 5 6#include "linux/mm.h" 7#include "asm/unistd.h" 8#include "os.h" 9#include "proc_mm.h" 10#include "skas.h" 11#include "skas_ptrace.h" 12#include "sysdep/tls.h" 13 14extern int modify_ldt(int func, void *ptr, unsigned long bytecount); 15 16long write_ldt_entry(struct mm_id * mm_idp, int func, struct user_desc * desc, 17 void **addr, int done) 18{ 19 long res; 20 21 if (proc_mm) { 22 /* 23 * This is a special handling for the case, that the mm to 24 * modify isn't current->active_mm. 25 * If this is called directly by modify_ldt, 26 * (current->active_mm->context.skas.u == mm_idp) 27 * will be true. So no call to __switch_mm(mm_idp) is done. 28 * If this is called in case of init_new_ldt or PTRACE_LDT, 29 * mm_idp won't belong to current->active_mm, but child->mm. 30 * So we need to switch child's mm into our userspace, then 31 * later switch back. 32 * 33 * Note: I'm unsure: should interrupts be disabled here? 34 */ 35 if (!current->active_mm || current->active_mm == &init_mm || 36 mm_idp != &current->active_mm->context.id) 37 __switch_mm(mm_idp); 38 } 39 40 if (ptrace_ldt) { 41 struct ptrace_ldt ldt_op = (struct ptrace_ldt) { 42 .func = func, 43 .ptr = desc, 44 .bytecount = sizeof(*desc)}; 45 u32 cpu; 46 int pid; 47 48 if (!proc_mm) 49 pid = mm_idp->u.pid; 50 else { 51 cpu = get_cpu(); 52 pid = userspace_pid[cpu]; 53 } 54 55 res = os_ptrace_ldt(pid, 0, (unsigned long) &ldt_op); 56 57 if (proc_mm) 58 put_cpu(); 59 } 60 else { 61 void *stub_addr; 62 res = syscall_stub_data(mm_idp, (unsigned long *)desc, 63 (sizeof(*desc) + sizeof(long) - 1) & 64 ~(sizeof(long) - 1), 65 addr, &stub_addr); 66 if (!res) { 67 unsigned long args[] = { func, 68 (unsigned long)stub_addr, 69 sizeof(*desc), 70 0, 0, 0 }; 71 res = run_syscall_stub(mm_idp, __NR_modify_ldt, args, 72 0, addr, done); 73 } 74 } 75 76 if (proc_mm) { 77 /* 78 * This is the second part of special handling, that makes 79 * PTRACE_LDT possible to implement. 80 */ 81 if (current->active_mm && current->active_mm != &init_mm && 82 mm_idp != &current->active_mm->context.id) 83 __switch_mm(&current->active_mm->context.id); 84 } 85 86 return res; 87} 88 89static long read_ldt_from_host(void __user * ptr, unsigned long bytecount) 90{ 91 int res, n; 92 struct ptrace_ldt ptrace_ldt = (struct ptrace_ldt) { 93 .func = 0, 94 .bytecount = bytecount, 95 .ptr = kmalloc(bytecount, GFP_KERNEL)}; 96 u32 cpu; 97 98 if (ptrace_ldt.ptr == NULL) 99 return -ENOMEM; 100 101 /* 102 * This is called from sys_modify_ldt only, so userspace_pid gives 103 * us the right number 104 */ 105 106 cpu = get_cpu(); 107 res = os_ptrace_ldt(userspace_pid[cpu], 0, (unsigned long) &ptrace_ldt); 108 put_cpu(); 109 if (res < 0) 110 goto out; 111 112 n = copy_to_user(ptr, ptrace_ldt.ptr, res); 113 if (n != 0) 114 res = -EFAULT; 115 116 out: 117 kfree(ptrace_ldt.ptr); 118 119 return res; 120} 121 122/* 123 * In skas mode, we hold our own ldt data in UML. 124 * Thus, the code implementing sys_modify_ldt_skas 125 * is very similar to (and mostly stolen from) sys_modify_ldt 126 * for arch/i386/kernel/ldt.c 127 * The routines copied and modified in part are: 128 * - read_ldt 129 * - read_default_ldt 130 * - write_ldt 131 * - sys_modify_ldt_skas 132 */ 133 134static int read_ldt(void __user * ptr, unsigned long bytecount) 135{ 136 int i, err = 0; 137 unsigned long size; 138 uml_ldt_t * ldt = &current->mm->context.ldt; 139 140 if (!ldt->entry_count) 141 goto out; 142 if (bytecount > LDT_ENTRY_SIZE*LDT_ENTRIES) 143 bytecount = LDT_ENTRY_SIZE*LDT_ENTRIES; 144 err = bytecount; 145 146 if (ptrace_ldt) 147 return read_ldt_from_host(ptr, bytecount); 148 149 down(&ldt->semaphore); 150 if (ldt->entry_count <= LDT_DIRECT_ENTRIES) { 151 size = LDT_ENTRY_SIZE*LDT_DIRECT_ENTRIES; 152 if (size > bytecount) 153 size = bytecount; 154 if (copy_to_user(ptr, ldt->u.entries, size)) 155 err = -EFAULT; 156 bytecount -= size; 157 ptr += size; 158 } 159 else { 160 for (i=0; i<ldt->entry_count/LDT_ENTRIES_PER_PAGE && bytecount; 161 i++) { 162 size = PAGE_SIZE; 163 if (size > bytecount) 164 size = bytecount; 165 if (copy_to_user(ptr, ldt->u.pages[i], size)) { 166 err = -EFAULT; 167 break; 168 } 169 bytecount -= size; 170 ptr += size; 171 } 172 } 173 up(&ldt->semaphore); 174 175 if (bytecount == 0 || err == -EFAULT) 176 goto out; 177 178 if (clear_user(ptr, bytecount)) 179 err = -EFAULT; 180 181out: 182 return err; 183} 184 185static int read_default_ldt(void __user * ptr, unsigned long bytecount) 186{ 187 int err; 188 189 if (bytecount > 5*LDT_ENTRY_SIZE) 190 bytecount = 5*LDT_ENTRY_SIZE; 191 192 err = bytecount; 193 /* 194 * UML doesn't support lcall7 and lcall27. 195 * So, we don't really have a default ldt, but emulate 196 * an empty ldt of common host default ldt size. 197 */ 198 if (clear_user(ptr, bytecount)) 199 err = -EFAULT; 200 201 return err; 202} 203 204static int write_ldt(void __user * ptr, unsigned long bytecount, int func) 205{ 206 uml_ldt_t * ldt = &current->mm->context.ldt; 207 struct mm_id * mm_idp = &current->mm->context.id; 208 int i, err; 209 struct user_desc ldt_info; 210 struct ldt_entry entry0, *ldt_p; 211 void *addr = NULL; 212 213 err = -EINVAL; 214 if (bytecount != sizeof(ldt_info)) 215 goto out; 216 err = -EFAULT; 217 if (copy_from_user(&ldt_info, ptr, sizeof(ldt_info))) 218 goto out; 219 220 err = -EINVAL; 221 if (ldt_info.entry_number >= LDT_ENTRIES) 222 goto out; 223 if (ldt_info.contents == 3) { 224 if (func == 1) 225 goto out; 226 if (ldt_info.seg_not_present == 0) 227 goto out; 228 } 229 230 if (!ptrace_ldt) 231 down(&ldt->semaphore); 232 233 err = write_ldt_entry(mm_idp, func, &ldt_info, &addr, 1); 234 if (err) 235 goto out_unlock; 236 else if (ptrace_ldt) { 237 /* With PTRACE_LDT available, this is used as a flag only */ 238 ldt->entry_count = 1; 239 goto out; 240 } 241 242 if (ldt_info.entry_number >= ldt->entry_count && 243 ldt_info.entry_number >= LDT_DIRECT_ENTRIES) { 244 for (i=ldt->entry_count/LDT_ENTRIES_PER_PAGE; 245 i*LDT_ENTRIES_PER_PAGE <= ldt_info.entry_number; 246 i++) { 247 if (i == 0) 248 memcpy(&entry0, ldt->u.entries, 249 sizeof(entry0)); 250 ldt->u.pages[i] = (struct ldt_entry *) 251 __get_free_page(GFP_KERNEL|__GFP_ZERO); 252 if (!ldt->u.pages[i]) { 253 err = -ENOMEM; 254 /* Undo the change in host */ 255 memset(&ldt_info, 0, sizeof(ldt_info)); 256 write_ldt_entry(mm_idp, 1, &ldt_info, &addr, 1); 257 goto out_unlock; 258 } 259 if (i == 0) { 260 memcpy(ldt->u.pages[0], &entry0, 261 sizeof(entry0)); 262 memcpy(ldt->u.pages[0]+1, ldt->u.entries+1, 263 sizeof(entry0)*(LDT_DIRECT_ENTRIES-1)); 264 } 265 ldt->entry_count = (i + 1) * LDT_ENTRIES_PER_PAGE; 266 } 267 } 268 if (ldt->entry_count <= ldt_info.entry_number) 269 ldt->entry_count = ldt_info.entry_number + 1; 270 271 if (ldt->entry_count <= LDT_DIRECT_ENTRIES) 272 ldt_p = ldt->u.entries + ldt_info.entry_number; 273 else 274 ldt_p = ldt->u.pages[ldt_info.entry_number/LDT_ENTRIES_PER_PAGE] + 275 ldt_info.entry_number%LDT_ENTRIES_PER_PAGE; 276 277 if (ldt_info.base_addr == 0 && ldt_info.limit == 0 && 278 (func == 1 || LDT_empty(&ldt_info))) { 279 ldt_p->a = 0; 280 ldt_p->b = 0; 281 } 282 else{ 283 if (func == 1) 284 ldt_info.useable = 0; 285 ldt_p->a = LDT_entry_a(&ldt_info); 286 ldt_p->b = LDT_entry_b(&ldt_info); 287 } 288 err = 0; 289 290out_unlock: 291 up(&ldt->semaphore); 292out: 293 return err; 294} 295 296static long do_modify_ldt_skas(int func, void __user *ptr, 297 unsigned long bytecount) 298{ 299 int ret = -ENOSYS; 300 301 switch (func) { 302 case 0: 303 ret = read_ldt(ptr, bytecount); 304 break; 305 case 1: 306 case 0x11: 307 ret = write_ldt(ptr, bytecount, func); 308 break; 309 case 2: 310 ret = read_default_ldt(ptr, bytecount); 311 break; 312 } 313 return ret; 314} 315 316static DEFINE_SPINLOCK(host_ldt_lock); 317static short dummy_list[9] = {0, -1}; 318static short * host_ldt_entries = NULL; 319 320static void ldt_get_host_info(void) 321{ 322 long ret; 323 struct ldt_entry * ldt; 324 short *tmp; 325 int i, size, k, order; 326 327 spin_lock(&host_ldt_lock); 328 329 if (host_ldt_entries != NULL) { 330 spin_unlock(&host_ldt_lock); 331 return; 332 } 333 host_ldt_entries = dummy_list+1; 334 335 spin_unlock(&host_ldt_lock); 336 337 for (i = LDT_PAGES_MAX-1, order=0; i; i>>=1, order++) 338 ; 339 340 ldt = (struct ldt_entry *) 341 __get_free_pages(GFP_KERNEL|__GFP_ZERO, order); 342 if (ldt == NULL) { 343 printk(KERN_ERR "ldt_get_host_info: couldn't allocate buffer " 344 "for host ldt\n"); 345 return; 346 } 347 348 ret = modify_ldt(0, ldt, (1<<order)*PAGE_SIZE); 349 if (ret < 0) { 350 printk(KERN_ERR "ldt_get_host_info: couldn't read host ldt\n"); 351 goto out_free; 352 } 353 if (ret == 0) { 354 /* default_ldt is active, simply write an empty entry 0 */ 355 host_ldt_entries = dummy_list; 356 goto out_free; 357 } 358 359 for (i=0, size=0; i<ret/LDT_ENTRY_SIZE; i++) { 360 if (ldt[i].a != 0 || ldt[i].b != 0) 361 size++; 362 } 363 364 if (size < ARRAY_SIZE(dummy_list)) 365 host_ldt_entries = dummy_list; 366 else { 367 size = (size + 1) * sizeof(dummy_list[0]); 368 tmp = kmalloc(size, GFP_KERNEL); 369 if (tmp == NULL) { 370 printk(KERN_ERR "ldt_get_host_info: couldn't allocate " 371 "host ldt list\n"); 372 goto out_free; 373 } 374 host_ldt_entries = tmp; 375 } 376 377 for (i=0, k=0; i<ret/LDT_ENTRY_SIZE; i++) { 378 if (ldt[i].a != 0 || ldt[i].b != 0) 379 host_ldt_entries[k++] = i; 380 } 381 host_ldt_entries[k] = -1; 382 383out_free: 384 free_pages((unsigned long)ldt, order); 385} 386 387long init_new_ldt(struct mm_context *new_mm, struct mm_context *from_mm) 388{ 389 struct user_desc desc; 390 short * num_p; 391 int i; 392 long page, err=0; 393 void *addr = NULL; 394 struct proc_mm_op copy; 395 396 397 if (!ptrace_ldt) 398 init_MUTEX(&new_mm->ldt.semaphore); 399 400 if (!from_mm) { 401 memset(&desc, 0, sizeof(desc)); 402 /* 403 * We have to initialize a clean ldt. 404 */ 405 if (proc_mm) { 406 /* 407 * If the new mm was created using proc_mm, host's 408 * default-ldt currently is assigned, which normally 409 * contains the call-gates for lcall7 and lcall27. 410 * To remove these gates, we simply write an empty 411 * entry as number 0 to the host. 412 */ 413 err = write_ldt_entry(&new_mm->id, 1, &desc, &addr, 1); 414 } 415 else{ 416 /* 417 * Now we try to retrieve info about the ldt, we 418 * inherited from the host. All ldt-entries found 419 * will be reset in the following loop 420 */ 421 ldt_get_host_info(); 422 for (num_p=host_ldt_entries; *num_p != -1; num_p++) { 423 desc.entry_number = *num_p; 424 err = write_ldt_entry(&new_mm->id, 1, &desc, 425 &addr, *(num_p + 1) == -1); 426 if (err) 427 break; 428 } 429 } 430 new_mm->ldt.entry_count = 0; 431 432 goto out; 433 } 434 435 if (proc_mm) { 436 /* 437 * We have a valid from_mm, so we now have to copy the LDT of 438 * from_mm to new_mm, because using proc_mm an new mm with 439 * an empty/default LDT was created in new_mm() 440 */ 441 copy = ((struct proc_mm_op) { .op = MM_COPY_SEGMENTS, 442 .u = 443 { .copy_segments = 444 from_mm->id.u.mm_fd } } ); 445 i = os_write_file(new_mm->id.u.mm_fd, &copy, sizeof(copy)); 446 if (i != sizeof(copy)) 447 printk(KERN_ERR "new_mm : /proc/mm copy_segments " 448 "failed, err = %d\n", -i); 449 } 450 451 if (!ptrace_ldt) { 452 /* 453 * Our local LDT is used to supply the data for 454 * modify_ldt(READLDT), if PTRACE_LDT isn't available, 455 * i.e., we have to use the stub for modify_ldt, which 456 * can't handle the big read buffer of up to 64kB. 457 */ 458 down(&from_mm->ldt.semaphore); 459 if (from_mm->ldt.entry_count <= LDT_DIRECT_ENTRIES) 460 memcpy(new_mm->ldt.u.entries, from_mm->ldt.u.entries, 461 sizeof(new_mm->ldt.u.entries)); 462 else { 463 i = from_mm->ldt.entry_count / LDT_ENTRIES_PER_PAGE; 464 while (i-->0) { 465 page = __get_free_page(GFP_KERNEL|__GFP_ZERO); 466 if (!page) { 467 err = -ENOMEM; 468 break; 469 } 470 new_mm->ldt.u.pages[i] = 471 (struct ldt_entry *) page; 472 memcpy(new_mm->ldt.u.pages[i], 473 from_mm->ldt.u.pages[i], PAGE_SIZE); 474 } 475 } 476 new_mm->ldt.entry_count = from_mm->ldt.entry_count; 477 up(&from_mm->ldt.semaphore); 478 } 479 480 out: 481 return err; 482} 483 484 485void free_ldt(struct mm_context *mm) 486{ 487 int i; 488 489 if (!ptrace_ldt && mm->ldt.entry_count > LDT_DIRECT_ENTRIES) { 490 i = mm->ldt.entry_count / LDT_ENTRIES_PER_PAGE; 491 while (i-- > 0) 492 free_page((long) mm->ldt.u.pages[i]); 493 } 494 mm->ldt.entry_count = 0; 495} 496 497int sys_modify_ldt(int func, void __user *ptr, unsigned long bytecount) 498{ 499 return do_modify_ldt_skas(func, ptr, bytecount); 500}