From owner-freebsd-bugs Sun May 12 23:40:20 2002 Delivered-To: freebsd-bugs@hub.freebsd.org Received: from freefall.freebsd.org (freefall.FreeBSD.org [216.136.204.21]) by hub.freebsd.org (Postfix) with ESMTP id 7E03337B406 for ; Sun, 12 May 2002 23:40:02 -0700 (PDT) Received: (from gnats@localhost) by freefall.freebsd.org (8.11.6/8.11.6) id g4D6e2149107; Sun, 12 May 2002 23:40:02 -0700 (PDT) (envelope-from gnats) Received: from nwww.freebsd.org (www.FreeBSD.org [216.136.204.117]) by hub.freebsd.org (Postfix) with ESMTP id F0A5B37B401 for ; Sun, 12 May 2002 23:36:58 -0700 (PDT) Received: from www.freebsd.org (localhost [127.0.0.1]) by nwww.freebsd.org (8.12.2/8.12.2) with ESMTP id g4D6awhG036380 for ; Sun, 12 May 2002 23:36:58 -0700 (PDT) (envelope-from nobody@www.freebsd.org) Received: (from nobody@localhost) by www.freebsd.org (8.12.2/8.12.2/Submit) id g4D6awRF036378; Sun, 12 May 2002 23:36:58 -0700 (PDT) Message-Id: <200205130636.g4D6awRF036378@www.freebsd.org> Date: Sun, 12 May 2002 23:36:58 -0700 (PDT) From: David Xu To: freebsd-gnats-submit@FreeBSD.org X-Send-Pr-Version: www-1.0 Subject: i386/38021: i386_set_ldt can be cheated Sender: owner-freebsd-bugs@FreeBSD.ORG Precedence: bulk List-ID: List-Archive: (Web Archive) List-Help: (List Instructions) List-Subscribe: List-Unsubscribe: X-Loop: FreeBSD.org >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