diff options
author | peter <peter@FreeBSD.org> | 2002-09-28 22:44:45 +0000 |
---|---|---|
committer | peter <peter@FreeBSD.org> | 2002-09-28 22:44:45 +0000 |
commit | b78a1645cfd5756e224a45a9b2046dc3160d6ef6 (patch) | |
tree | 9f2eb4dd5faf804413e9d18cb2a7b0728928ab10 /sys | |
parent | a172a51db3e68d54b14aefa0d6837b3b70b0c201 (diff) | |
download | FreeBSD-src-b78a1645cfd5756e224a45a9b2046dc3160d6ef6.zip FreeBSD-src-b78a1645cfd5756e224a45a9b2046dc3160d6ef6.tar.gz |
Deal with some SMP races by doing the entire copyin at once rather
than doing the checks piecemeal and then doing a second copyin later.
PR: 38021
Submitted by: davidx (I've tweaked the patch a bit)
Diffstat (limited to 'sys')
-rw-r--r-- | sys/amd64/amd64/sys_machdep.c | 43 | ||||
-rw-r--r-- | sys/i386/i386/sys_machdep.c | 43 |
2 files changed, 56 insertions, 30 deletions
diff --git a/sys/amd64/amd64/sys_machdep.c b/sys/amd64/amd64/sys_machdep.c index 3e3a4c4..93da912 100644 --- a/sys/amd64/amd64/sys_machdep.c +++ b/sys/amd64/amd64/sys_machdep.c @@ -409,8 +409,9 @@ i386_set_ldt(td, args) struct mdproc *mdp = &td->td_proc->p_md; struct proc_ldt *pldt = mdp->md_ldt; struct i386_ldt_args ua, *uap = &ua; + union descriptor *descs; caddr_t old_ldt_base; - int old_ldt_len; + int descs_size, old_ldt_len; register_t savecrit; if ((error = copyin(args, uap, sizeof(struct i386_ldt_args))) < 0) @@ -465,17 +466,23 @@ i386_set_ldt(td, args) #endif } + descs_size = uap->num * sizeof(union descriptor); + descs = (union descriptor *)kmem_alloc(kernel_map, descs_size); + if (descs == NULL) + return (ENOMEM); + error = copyin(&uap->descs[0], descs, descs_size); + if (error) { + 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 +503,7 @@ i386_set_ldt(td, args) * 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 +513,11 @@ i386_set_ldt(td, args) 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 +533,25 @@ i386_set_ldt(td, args) 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, + bcopy(uap->descs, &((union descriptor *)(pldt->ldt_base))[uap->start], uap->num * sizeof(union descriptor)); - if (!error) - td->td_retval[0] = uap->start; + td->td_retval[0] = uap->start; intr_restore(savecrit); - - return(error); + kmem_free(kernel_map, (vm_offset_t)descs, descs_size); + return (0); } diff --git a/sys/i386/i386/sys_machdep.c b/sys/i386/i386/sys_machdep.c index 3e3a4c4..93da912 100644 --- a/sys/i386/i386/sys_machdep.c +++ b/sys/i386/i386/sys_machdep.c @@ -409,8 +409,9 @@ i386_set_ldt(td, args) struct mdproc *mdp = &td->td_proc->p_md; struct proc_ldt *pldt = mdp->md_ldt; struct i386_ldt_args ua, *uap = &ua; + union descriptor *descs; caddr_t old_ldt_base; - int old_ldt_len; + int descs_size, old_ldt_len; register_t savecrit; if ((error = copyin(args, uap, sizeof(struct i386_ldt_args))) < 0) @@ -465,17 +466,23 @@ i386_set_ldt(td, args) #endif } + descs_size = uap->num * sizeof(union descriptor); + descs = (union descriptor *)kmem_alloc(kernel_map, descs_size); + if (descs == NULL) + return (ENOMEM); + error = copyin(&uap->descs[0], descs, descs_size); + if (error) { + 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 +503,7 @@ i386_set_ldt(td, args) * 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 +513,11 @@ i386_set_ldt(td, args) 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 +533,25 @@ i386_set_ldt(td, args) 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, + bcopy(uap->descs, &((union descriptor *)(pldt->ldt_base))[uap->start], uap->num * sizeof(union descriptor)); - if (!error) - td->td_retval[0] = uap->start; + td->td_retval[0] = uap->start; intr_restore(savecrit); - - return(error); + kmem_free(kernel_map, (vm_offset_t)descs, descs_size); + return (0); } |