Skip site navigation (1)Skip section navigation (2)
Date:      Sun, 12 May 2002 23:36:58 -0700 (PDT)
From:      David Xu <davidx@viasoft.com.cn>
To:        freebsd-gnats-submit@FreeBSD.org
Subject:   i386/38021: i386_set_ldt can be cheated
Message-ID:  <200205130636.g4D6awRF036378@www.freebsd.org>

next in thread | raw e-mail | index | archive | help

>Number:         38021
>Category:       i386
>Synopsis:       i386_set_ldt can be cheated
>Confidential:   no
>Severity:       critical
>Priority:       high
>Responsible:    freebsd-bugs
>State:          open
>Quarter:        
>Keywords:       
>Date-Required:
>Class:          sw-bug
>Submitter-Id:   current-users
>Arrival-Date:   Sun May 12 23:40:01 PDT 2002
>Closed-Date:
>Last-Modified:
>Originator:     David Xu
>Release:        FreeBSD 5.0-CURRENT
>Organization:
Viatech
>Environment:
FreeBSD davidbsd.viasoft.com.cn 5.0-CURRENT FreeBSD 5.0-CURRENT #25: 
Mon May 13 13:54:20 CST 2002     
davidx@davidbsd.viasoft.com.cn:/usr/src/sys/i386/compile/xu  i386      

>Description:
i386_set_ldt() in sys_machdep.c can be cheated by user, it uses two 
step to copy LDT descriptors from user space, first, security check
is made, second, copying descriptors from user space. there exists 
a race condition that multi-processes or multi-threads program can 
pass a shared memory address to kernel, call i386_set_ldt() with the
address argument on one CPU, and modify the descriptors on another 
cpu, kernel can be cheated in the race condition and causes invalid 
descriptors be installed in kernel LDT table.

>How-To-Repeat:
      
>Fix:
--- /usr/src/sys/i386/i386/sys_machdep.c	Mon May 13 14:00:33 2002
+++ /usr/src/sys/i386/i386/sys_machdep.c.new2	Mon May 13 14:00:24 2002
@@ -412,6 +412,8 @@
 	caddr_t old_ldt_base;
 	int old_ldt_len;
 	register_t savecrit;
+	union descriptor *descs;
+	int descs_size;
 
 	if ((error = copyin(args, uap, sizeof(struct i386_ldt_args))) < 0)
 		return(error);
@@ -465,17 +467,24 @@
 #endif
 	}
 
+	descs_size = uap->num * sizeof(union descriptor); 
+	descs = (union descriptor *)kmem_alloc(kernel_map, descs_size);
+	if (descs == NULL)
+		return ENOMEM;
+	if ((error = copyin(&uap->descs[0], descs, descs_size)) < 0)
+	{
+		kmem_free(kernel_map, (vm_offset_t)descs, descs_size);
+		return error;
+	}
+
 	/* Check descriptors for access violations */
 	for (i = 0, n = uap->start; i < uap->num; i++, n++) {
-		union descriptor desc, *dp;
-		dp = &uap->descs[i];
-		error = copyin(dp, &desc, sizeof(union descriptor));
-		if (error)
-			return(error);
+		union descriptor *dp;
+		dp = &descs[i];
 
-		switch (desc.sd.sd_type) {
+		switch (dp->sd.sd_type) {
 		case SDT_SYSNULL:	/* system null */ 
-			desc.sd.sd_p = 0;
+			dp->sd.sd_p = 0;
 			break;
 		case SDT_SYS286TSS: /* system 286 TSS available */
 		case SDT_SYSLDT:    /* system local descriptor table */
@@ -496,6 +505,7 @@
 			 * to create a segment of these types.  They are
 			 * for OS use only.
 			 */
+			kmem_free(kernel_map, (vm_offset_t)descs, descs_size);
 			return EACCES;
 			/*NOTREACHED*/
 
@@ -505,8 +515,11 @@
 		case SDT_MEMERC:  /* memory execute read conforming */
 		case SDT_MEMERAC: /* memory execute read accessed conforming */
 			 /* Must be "present" if executable and conforming. */
-			if (desc.sd.sd_p == 0)
+			if (dp->sd.sd_p == 0)
+			{
+				kmem_free(kernel_map, (vm_offset_t)descs, descs_size);
 				return (EACCES);
+			}
 			break;
 		case SDT_MEMRO:   /* memory read only */
 		case SDT_MEMROA:  /* memory read only accessed */
@@ -522,23 +535,26 @@
 		case SDT_MEMERA:  /* memory execute read accessed */
 			break;
 		default:
+			kmem_free(kernel_map, (vm_offset_t)descs, descs_size);
 			return(EINVAL);
 			/*NOTREACHED*/
 		}
 
 		/* Only user (ring-3) descriptors may be present. */
-		if ((desc.sd.sd_p != 0) && (desc.sd.sd_dpl != SEL_UPL))
+		if ((dp->sd.sd_p != 0) && (dp->sd.sd_dpl != SEL_UPL))
+		{
+			kmem_free(kernel_map, (vm_offset_t)descs, descs_size);
 			return (EACCES);
+		}
 	}
 
 	/* Fill in range */
 	savecrit = intr_disable();
-	error = copyin(uap->descs, 
-	    &((union descriptor *)(pldt->ldt_base))[uap->start],
-	    uap->num * sizeof(union descriptor));
+	error = copyin(descs, 
+	    &((union descriptor *)(pldt->ldt_base))[uap->start], descs_size);
 	if (!error)
 		td->td_retval[0] = uap->start;
 	intr_restore(savecrit);
-
+	kmem_free(kernel_map, (vm_offset_t)descs, descs_size);
 	return(error);
 }

>Release-Note:
>Audit-Trail:
>Unformatted:

To Unsubscribe: send mail to majordomo@FreeBSD.org
with "unsubscribe freebsd-bugs" in the body of the message




Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?200205130636.g4D6awRF036378>