patch-2.1.109 linux/arch/i386/kernel/ldt.c
Next file: linux/arch/i386/kernel/mtrr.c
Previous file: linux/arch/i386/kernel/i386_ksyms.c
Back to the patch index
Back to the overall index
- Lines: 175
- Date:
Tue Jul 7 12:35:14 1998
- Orig file:
v2.1.108/linux/arch/i386/kernel/ldt.c
- Orig date:
Thu Apr 23 20:21:28 1998
diff -u --recursive --new-file v2.1.108/linux/arch/i386/kernel/ldt.c linux/arch/i386/kernel/ldt.c
@@ -35,21 +35,29 @@
static int write_ldt(void * ptr, unsigned long bytecount, int oldmode)
{
+ struct mm_struct * mm = current->mm;
+ void * ldt;
+ __u32 entry_1, entry_2, *lp;
+ __u16 selector, reg_fs, reg_gs;
+ int error;
struct modify_ldt_ldt_s ldt_info;
- unsigned long *lp;
- struct mm_struct * mm;
- int error, i;
+ error = -EINVAL;
if (bytecount != sizeof(ldt_info))
- return -EINVAL;
- error = copy_from_user(&ldt_info, ptr, sizeof(ldt_info));
- if (error)
- return -EFAULT;
-
- if ((ldt_info.contents == 3 && (oldmode || ldt_info.seg_not_present == 0)) || ldt_info.entry_number >= LDT_ENTRIES)
- return -EINVAL;
-
- mm = current->mm;
+ goto out;
+ error = -EFAULT;
+ if (copy_from_user(&ldt_info, ptr, sizeof(ldt_info)))
+ goto out;
+
+ error = -EINVAL;
+ if (ldt_info.entry_number >= LDT_ENTRIES)
+ goto out;
+ if (ldt_info.contents == 3) {
+ if (oldmode)
+ goto out;
+ if (ldt_info.seg_not_present == 0)
+ goto out;
+ }
/*
* Horrible dependencies! Try to get rid of this. This is wrong,
@@ -62,60 +70,97 @@
* For no good reason except historical, the GDT index of the LDT
* is chosen to follow the index number in the task[] array.
*/
- if (!mm->segments) {
- for (i=1 ; i<NR_TASKS ; i++) {
- if (task[i] == current) {
- if (!(mm->segments = (void *) vmalloc(LDT_ENTRIES*LDT_ENTRY_SIZE)))
- return -ENOMEM;
- memset(mm->segments, 0, LDT_ENTRIES*LDT_ENTRY_SIZE);
- set_ldt_desc(gdt+(i<<1)+FIRST_LDT_ENTRY, mm->segments, LDT_ENTRIES);
- load_ldt(i);
- }
+ ldt = mm->segments;
+ if (!ldt) {
+ error = -ENOMEM;
+ ldt = vmalloc(LDT_ENTRIES*LDT_ENTRY_SIZE);
+ if (!ldt)
+ goto out;
+ memset(ldt, 0, LDT_ENTRIES*LDT_ENTRY_SIZE);
+ /*
+ * Make sure someone else hasn't allocated it for us ...
+ */
+ if (!mm->segments) {
+ int i = current->tarray_ptr - &task[0];
+ mm->segments = ldt;
+ set_ldt_desc(gdt+(i<<1)+FIRST_LDT_ENTRY, ldt, LDT_ENTRIES);
+ load_ldt(i);
+ if (mm->count > 1)
+ printk(KERN_WARNING
+ "LDT allocated for cloned task!\n");
+ } else {
+ vfree(ldt);
}
}
-
- lp = (unsigned long *) (LDT_ENTRY_SIZE * ldt_info.entry_number + (unsigned long) mm->segments);
+
+ /*
+ * Check whether the entry to be changed is currently in use.
+ * If it is, we may need extra validation checks in case the
+ * kernel is forced to save and restore the selector.
+ *
+ * Note: we check the fs and gs values as well, as these are
+ * loaded by the signal code and during a task switch.
+ */
+ selector = (ldt_info.entry_number << 3) | 4;
+ __asm__("movw %%fs,%0" : "=r"(reg_fs));
+ __asm__("movw %%gs,%0" : "=r"(reg_gs));
+
+ lp = (__u32 *) ((selector & ~7) + (char *) ldt);
+
/* Allow LDTs to be cleared by the user. */
- 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 )) ) {
- *lp = 0;
- *(lp+1) = 0;
- return 0;
+ if (ldt_info.base_addr == 0 && ldt_info.limit == 0) {
+ if (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 )) {
+ entry_1 = 0;
+ entry_2 = 0;
+ goto out_check;
+ }
}
- *lp = ((ldt_info.base_addr & 0x0000ffff) << 16) |
+
+ entry_1 = ((ldt_info.base_addr & 0x0000ffff) << 16) |
(ldt_info.limit & 0x0ffff);
- *(lp+1) = (ldt_info.base_addr & 0xff000000) |
- ((ldt_info.base_addr & 0x00ff0000)>>16) |
+ entry_2 = (ldt_info.base_addr & 0xff000000) |
+ ((ldt_info.base_addr & 0x00ff0000) >> 16) |
(ldt_info.limit & 0xf0000) |
- (ldt_info.contents << 10) |
((ldt_info.read_exec_only ^ 1) << 9) |
+ (ldt_info.contents << 10) |
+ ((ldt_info.seg_not_present ^ 1) << 15) |
(ldt_info.seg_32bit << 22) |
(ldt_info.limit_in_pages << 23) |
- ((ldt_info.seg_not_present ^1) << 15) |
0x7000;
- if (!oldmode) *(lp+1) |= (ldt_info.useable << 20);
- return 0;
+ if (!oldmode)
+ entry_2 |= (ldt_info.useable << 20);
+
+out_check:
+ /* OK to change the entry ... */
+ *lp = entry_1;
+ *(lp+1) = entry_2;
+ error = 0;
+out:
+ return error;
}
asmlinkage int sys_modify_ldt(int func, void *ptr, unsigned long bytecount)
{
- int ret;
+ int ret = -ENOSYS;
lock_kernel();
- if (func == 0)
+ switch (func) {
+ case 0:
ret = read_ldt(ptr, bytecount);
- else if (func == 1)
+ break;
+ case 1:
ret = write_ldt(ptr, bytecount, 1);
- else if (func == 0x11)
+ break;
+ case 0x11:
ret = write_ldt(ptr, bytecount, 0);
- else
- ret = -ENOSYS;
+ break;
+ }
unlock_kernel();
return ret;
}
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen, slshen@lbl.gov