patch-2.0.28 linux/arch/i386/kernel/ldt.c

Next file: linux/arch/i386/kernel/signal.c
Previous file: linux/arch/i386/kernel/entry.S
Back to the patch index
Back to the overall index

diff -u --recursive --new-file v2.0.27/linux/arch/i386/kernel/ldt.c linux/arch/i386/kernel/ldt.c
@@ -59,7 +59,7 @@
 	return (last >= first && last < TASK_SIZE);
 }
 
-static int write_ldt(void * ptr, unsigned long bytecount)
+static int write_ldt(void * ptr, unsigned long bytecount, int oldmode)
 {
 	struct modify_ldt_ldt_s ldt_info;
 	unsigned long *lp;
@@ -73,10 +73,10 @@
 
 	memcpy_fromfs(&ldt_info, ptr, sizeof(ldt_info));
 
-	if (ldt_info.contents == 3 || ldt_info.entry_number >= LDT_ENTRIES)
+	if ((ldt_info.contents == 3 && (oldmode || ldt_info.seg_not_present == 0)) || ldt_info.entry_number >= LDT_ENTRIES)
 		return -EINVAL;
 
-	if (!limits_ok(&ldt_info))
+	if (!limits_ok(&ldt_info) && (oldmode || ldt_info.seg_not_present == 0))
 		return -EINVAL;
 
 	if (!current->ldt) {
@@ -93,7 +93,14 @@
 	
 	lp = (unsigned long *) &current->ldt[ldt_info.entry_number];
    	/* Allow LDTs to be cleared by the user. */
-   	if (ldt_info.base_addr == 0 && ldt_info.limit == 0) {
+   	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;
@@ -109,6 +116,7 @@
 		  (ldt_info.limit_in_pages << 23) |
 		  ((ldt_info.seg_not_present ^1) << 15) |
 		  0x7000;
+	if (!oldmode) *(lp+1) |= (ldt_info.useable << 20);
 	return 0;
 }
 
@@ -117,6 +125,8 @@
 	if (func == 0)
 		return read_ldt(ptr, bytecount);
 	if (func == 1)
-		return write_ldt(ptr, bytecount);
+		return write_ldt(ptr, bytecount, 1);
+	if (func == 0x11)
+		return write_ldt(ptr, bytecount, 0);
 	return -ENOSYS;
 }

FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen, slshen@lbl.gov