Loading...
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 | /* * Copyright (C) 2001 Hewlett-Packard Co * Copyright (C) 2001 David Mosberger-Tang <davidm@hpl.hp.com> * * Adapted from arch/i386/kernel/ldt.c */ #include <linux/errno.h> #include <linux/sched.h> #include <linux/string.h> #include <linux/mm.h> #include <linux/smp.h> #include <linux/smp_lock.h> #include <linux/vmalloc.h> #include <asm/uaccess.h> #include <asm/ia32.h> /* * read_ldt() is not really atomic - this is not a problem since synchronization of reads * and writes done to the LDT has to be assured by user-space anyway. Writes are atomic, * to protect the security checks done on new descriptors. */ static int read_ldt (void *ptr, unsigned long bytecount) { char *src, *dst, buf[256]; /* temporary buffer (don't overflow kernel stack!) */ unsigned long bytes_left, n; if (bytecount > IA32_LDT_ENTRIES*IA32_LDT_ENTRY_SIZE) bytecount = IA32_LDT_ENTRIES*IA32_LDT_ENTRY_SIZE; bytes_left = bytecount; src = (void *) IA32_LDT_OFFSET; dst = ptr; while (bytes_left) { n = sizeof(buf); if (n > bytes_left) n = bytes_left; /* * We know we're reading valid memory, but we still must guard against * running out of memory. */ if (__copy_from_user(buf, src, n)) return -EFAULT; if (copy_to_user(dst, buf, n)) return -EFAULT; src += n; dst += n; bytes_left -= n; } return bytecount; } static int write_ldt (void * ptr, unsigned long bytecount, int oldmode) { struct ia32_modify_ldt_ldt_s ldt_info; __u64 entry; if (bytecount != sizeof(ldt_info)) return -EINVAL; if (copy_from_user(&ldt_info, ptr, sizeof(ldt_info))) return -EFAULT; if (ldt_info.entry_number >= IA32_LDT_ENTRIES) return -EINVAL; if (ldt_info.contents == 3) { if (oldmode) return -EINVAL; if (ldt_info.seg_not_present == 0) return -EINVAL; } if (ldt_info.base_addr == 0 && ldt_info.limit == 0 && (oldmode || (ldt_info.contents == 0 && ldt_info.read_exec_only == 1 && ldt_info.seg_32bit == 0 && ldt_info.limit_in_pages == 0 && ldt_info.seg_not_present == 1 && ldt_info.useable == 0))) /* allow LDTs to be cleared by the user */ entry = 0; else /* we must set the "Accessed" bit as IVE doesn't emulate it */ entry = IA32_SEG_DESCRIPTOR(ldt_info.base_addr, ldt_info.limit, (((ldt_info.read_exec_only ^ 1) << 1) | (ldt_info.contents << 2)) | 1, 1, 3, ldt_info.seg_not_present ^ 1, (oldmode ? 0 : ldt_info.useable), ldt_info.seg_32bit, ldt_info.limit_in_pages); /* * Install the new entry. We know we're accessing valid (mapped) user-level * memory, but we still need to guard against out-of-memory, hence we must use * put_user(). */ return __put_user(entry, (__u64 *) IA32_LDT_OFFSET + ldt_info.entry_number); } asmlinkage int sys32_modify_ldt (int func, void *ptr, unsigned int bytecount) { int ret = -ENOSYS; switch (func) { case 0: ret = read_ldt(ptr, bytecount); break; case 1: ret = write_ldt(ptr, bytecount, 1); break; case 0x11: ret = write_ldt(ptr, bytecount, 0); break; } return ret; } |