Print this page
sync further changes from uts/aslr
7029 want per-process exploit mitigation features (secflags)
7030 want basic address space layout randomization (aslr)
7031 noexec_user_stack should be a secflag
7032 want a means to forbid mappings around NULL.

*** 40,49 **** --- 40,50 ---- #include <sys/vnode.h> #include <sys/mman.h> #include <sys/kmem.h> #include <sys/proc.h> #include <sys/pathname.h> + #include <sys/policy.h> #include <sys/cmn_err.h> #include <sys/systm.h> #include <sys/elf.h> #include <sys/vmsystm.h> #include <sys/debug.h>
*** 63,74 **** --- 64,77 ---- #include <sys/fasttrap.h> #include <sys/brand.h> #include "elf_impl.h" #include <sys/sdt.h> #include <sys/siginfo.h> + #include <sys/random.h> extern int at_flags; + extern volatile size_t aslr_max_brk_skew; #define ORIGIN_STR "ORIGIN" #define ORIGIN_STR_SIZE 6 static int getelfhead(vnode_t *, cred_t *, Ehdr *, int *, int *, int *);
*** 160,169 **** --- 163,207 ---- args->thrptr = phdrp->p_vaddr + base; return (0); } + static int + handle_secflag_dt(proc_t *p, uint_t dt, uint_t val) + { + uint_t flag; + + switch (dt) { + case DT_SUNW_ASLR: + flag = PROC_SEC_ASLR; + break; + default: + return (EINVAL); + } + + if (val == 0) { + if (secflag_isset(p->p_secflags.psf_lower, flag)) + return (EPERM); + if ((secpolicy_psecflags(CRED(), p, p) != 0) && + secflag_isset(p->p_secflags.psf_inherit, flag)) + return (EPERM); + + secflag_clear(&p->p_secflags.psf_effective, flag); + } else { + if (!secflag_isset(p->p_secflags.psf_upper, flag)) + return (EPERM); + + if ((secpolicy_psecflags(CRED(), p, p) != 0) && + !secflag_isset(p->p_secflags.psf_inherit, flag)) + return (EPERM); + + secflag_set(&p->p_secflags.psf_effective, flag); + } + + return (0); + } + /* * Map in the executable pointed to by vp. Returns 0 on success. */ int mapexec_brand(vnode_t *vp, uarg_t *args, Ehdr *ehdr, Addr *uphdr_vaddr,
*** 253,263 **** aux_entry_t *aux; int error; ssize_t resid; int fd = -1; intptr_t voffset; ! Phdr *dyphdr = NULL; Phdr *stphdr = NULL; Phdr *uphdr = NULL; Phdr *junk = NULL; size_t len; ssize_t phdrsize; --- 291,302 ---- aux_entry_t *aux; int error; ssize_t resid; int fd = -1; intptr_t voffset; ! Phdr *intphdr = NULL; ! Phdr *dynamicphdr = NULL; Phdr *stphdr = NULL; Phdr *uphdr = NULL; Phdr *junk = NULL; size_t len; ssize_t phdrsize;
*** 267,279 **** Phdr *dataphdrp = NULL; Phdr *dtrphdr; Phdr *capphdr = NULL; Cap *cap = NULL; ssize_t capsize; int hasu = 0; int hasauxv = 0; ! int hasdy = 0; int branded = 0; struct proc *p = ttoproc(curthread); struct user *up = PTOU(p); struct bigwad { --- 306,319 ---- Phdr *dataphdrp = NULL; Phdr *dtrphdr; Phdr *capphdr = NULL; Cap *cap = NULL; ssize_t capsize; + Dyn *dyn = NULL; int hasu = 0; int hasauxv = 0; ! int hasintp = 0; int branded = 0; struct proc *p = ttoproc(curthread); struct user *up = PTOU(p); struct bigwad {
*** 368,378 **** hsize = ehdrp->e_phentsize; phdrp = (Phdr *)phdrbase; for (i = nphdrs; i > 0; i--) { switch (phdrp->p_type) { case PT_INTERP: ! hasauxv = hasdy = 1; break; case PT_PHDR: hasu = 1; break; case PT_SUNWSTACK: --- 408,418 ---- hsize = ehdrp->e_phentsize; phdrp = (Phdr *)phdrbase; for (i = nphdrs; i > 0; i--) { switch (phdrp->p_type) { case PT_INTERP: ! hasauxv = hasintp = 1; break; case PT_PHDR: hasu = 1; break; case PT_SUNWSTACK:
*** 388,397 **** --- 428,440 ---- dataphdrp = phdrp; break; case PT_SUNWCAP: capphdr = phdrp; break; + case PT_DYNAMIC: + dynamicphdr = phdrp; + break; } phdrp = (Phdr *)((caddr_t)phdrp + hsize); } if (ehdrp->e_type != ET_EXEC) {
*** 430,440 **** * AT_SUN_EXECNAME (added in stk_copyout) * AT_NULL * * total == 9 */ ! if (hasdy && hasu) { /* * Has PT_INTERP & PT_PHDR - the auxvectors that * will be built are: * * AT_PHDR --- 473,483 ---- * AT_SUN_EXECNAME (added in stk_copyout) * AT_NULL * * total == 9 */ ! if (hasintp && hasu) { /* * Has PT_INTERP & PT_PHDR - the auxvectors that * will be built are: * * AT_PHDR
*** 444,454 **** * AT_LDDATA * * total = 5 */ args->auxsize = (9 + 5) * sizeof (aux_entry_t); ! } else if (hasdy) { /* * Has PT_INTERP but no PT_PHDR * * AT_EXECFD * AT_LDDATA --- 487,497 ---- * AT_LDDATA * * total = 5 */ args->auxsize = (9 + 5) * sizeof (aux_entry_t); ! } else if (hasintp) { /* * Has PT_INTERP but no PT_PHDR * * AT_EXECFD * AT_LDDATA
*** 477,486 **** --- 520,573 ---- * the the brandname and 3 for the brand specific aux vectors. */ args->auxsize += 4 * sizeof (aux_entry_t); } + /* If the binary has an explicit ASLR flag, it must be honoured */ + if ((dynamicphdr != NULL) && + (dynamicphdr->p_filesz > 0)) { + Dyn *dp; + off_t i = 0; + + #define DYN_STRIDE 100 + for (i = 0; i < dynamicphdr->p_filesz; + i += sizeof (*dyn) * DYN_STRIDE) { + int ndyns = (dynamicphdr->p_filesz - i) / sizeof (*dyn); + size_t dynsize; + + ndyns = MIN(DYN_STRIDE, ndyns); + dynsize = ndyns * sizeof (*dyn); + + dyn = kmem_alloc(dynsize, KM_SLEEP); + + if ((error = vn_rdwr(UIO_READ, vp, (caddr_t)dyn, + dynsize, (offset_t)(dynamicphdr->p_offset + i), + UIO_SYSSPACE, 0, (rlim64_t)0, + CRED(), &resid)) != 0) { + uprintf("%s: cannot read .dynamic section\n", + exec_file); + goto out; + } + + for (dp = dyn; dp < (dyn + ndyns); dp++) { + if (dp->d_tag == DT_SUNW_ASLR) { + if ((error = handle_secflag_dt(p, + DT_SUNW_ASLR, + dp->d_un.d_val)) != 0) { + uprintf("%s: error setting " + "security-flag from " + "DT_SUNW_ASLR: %d\n", + exec_file, error); + goto out; + } + } + } + + kmem_free(dyn, dynsize); + } + } + /* Hardware/Software capabilities */ if (capphdr != NULL && (capsize = capphdr->p_filesz) > 0 && capsize <= 16 * sizeof (*cap)) { int ncaps = capsize / sizeof (*cap);
*** 527,565 **** else len = 0; dtrphdr = NULL; ! if ((error = mapelfexec(vp, ehdrp, nphdrs, phdrbase, &uphdr, &dyphdr, &stphdr, &dtrphdr, dataphdrp, &bssbase, &brkbase, &voffset, NULL, len, execsz, &brksize)) != 0) goto bad; ! if (uphdr != NULL && dyphdr == NULL) goto bad; if (dtrphdr != NULL && dtrace_safe_phdr(dtrphdr, args, voffset) != 0) { uprintf("%s: Bad DTrace phdr in %s\n", exec_file, exec_file); goto bad; } ! if (dyphdr != NULL) { size_t len; uintptr_t lddata; char *p; struct vnode *nvp; ! dlnsize = dyphdr->p_filesz; if (dlnsize > MAXPATHLEN || dlnsize <= 0) goto bad; /* * Read in "interpreter" pathname. */ ! if ((error = vn_rdwr(UIO_READ, vp, dlnp, dyphdr->p_filesz, ! (offset_t)dyphdr->p_offset, UIO_SYSSPACE, 0, (rlim64_t)0, CRED(), &resid)) != 0) { uprintf("%s: Cannot obtain interpreter pathname\n", exec_file); goto bad; } --- 614,652 ---- else len = 0; dtrphdr = NULL; ! if ((error = mapelfexec(vp, ehdrp, nphdrs, phdrbase, &uphdr, &intphdr, &stphdr, &dtrphdr, dataphdrp, &bssbase, &brkbase, &voffset, NULL, len, execsz, &brksize)) != 0) goto bad; ! if (uphdr != NULL && intphdr == NULL) goto bad; if (dtrphdr != NULL && dtrace_safe_phdr(dtrphdr, args, voffset) != 0) { uprintf("%s: Bad DTrace phdr in %s\n", exec_file, exec_file); goto bad; } ! if (intphdr != NULL) { size_t len; uintptr_t lddata; char *p; struct vnode *nvp; ! dlnsize = intphdr->p_filesz; if (dlnsize > MAXPATHLEN || dlnsize <= 0) goto bad; /* * Read in "interpreter" pathname. */ ! if ((error = vn_rdwr(UIO_READ, vp, dlnp, intphdr->p_filesz, ! (offset_t)intphdr->p_offset, UIO_SYSSPACE, 0, (rlim64_t)0, CRED(), &resid)) != 0) { uprintf("%s: Cannot obtain interpreter pathname\n", exec_file); goto bad; }
*** 773,782 **** --- 860,870 ---- args->auxp_auxflags = (char *)((char *)args->stackend + ((char *)&aux->a_type - (char *)bigwad->elfargs)); ADDAUX(aux, AT_SUN_AUXFLAGS, auxf); + /* * Hardware capability flag word (performance hints) * Used for choosing faster library routines. * (Potentially different between 32-bit and 64-bit ABIs) */
*** 1182,1192 **** vnode_t *vp, Ehdr *ehdr, int nphdrs, caddr_t phdrbase, Phdr **uphdr, ! Phdr **dyphdr, Phdr **stphdr, Phdr **dtphdr, Phdr *dataphdrp, caddr_t *bssbase, caddr_t *brkbase, --- 1270,1280 ---- vnode_t *vp, Ehdr *ehdr, int nphdrs, caddr_t phdrbase, Phdr **uphdr, ! Phdr **intphdr, Phdr **stphdr, Phdr **dtphdr, Phdr *dataphdrp, caddr_t *bssbase, caddr_t *brkbase,
*** 1206,1220 **** int hsize = ehdr->e_phentsize; caddr_t mintmp = (caddr_t)-1; extern int use_brk_lpg; if (ehdr->e_type == ET_DYN) { /* * Obtain the virtual address of a hole in the * address space to map the "interpreter". */ ! map_addr(&addr, len, (offset_t)0, 1, 0); if (addr == NULL) return (ENOMEM); *voffset = (intptr_t)addr; /* --- 1294,1312 ---- int hsize = ehdr->e_phentsize; caddr_t mintmp = (caddr_t)-1; extern int use_brk_lpg; if (ehdr->e_type == ET_DYN) { + secflagset_t flags = 0; /* * Obtain the virtual address of a hole in the * address space to map the "interpreter". */ ! if (secflag_enabled(curproc, PROC_SEC_ASLR)) ! flags |= _MAP_RANDOMIZE; ! ! map_addr(&addr, len, (offset_t)0, 1, flags); if (addr == NULL) return (ENOMEM); *voffset = (intptr_t)addr; /*
*** 1237,1247 **** } phdr = (Phdr *)phdrbase; for (i = nphdrs; i > 0; i--) { switch (phdr->p_type) { case PT_LOAD: ! if ((*dyphdr != NULL) && (*uphdr == NULL)) return (0); ptload = 1; prot = PROT_USER; if (phdr->p_flags & PF_R) --- 1329,1339 ---- } phdr = (Phdr *)phdrbase; for (i = nphdrs; i > 0; i--) { switch (phdr->p_type) { case PT_LOAD: ! if ((*intphdr != NULL) && (*uphdr == NULL)) return (0); ptload = 1; prot = PROT_USER; if (phdr->p_flags & PF_R)
*** 1292,1301 **** --- 1384,1400 ---- if (curproc->p_brkpageszc != 0 && phdr == dataphdrp && (prot & PROT_WRITE)) { uint_t szc = curproc->p_brkpageszc; size_t pgsz = page_get_pagesize(szc); caddr_t ebss = addr + phdr->p_memsz; + /* + * If we need extra space to keep the BSS an + * integral number of pages in size, some of + * that space may fall beyond p_brkbase, so we + * need to set p_brksize to account for it + * being (logically) part of the brk. + */ size_t extra_zfodsz; ASSERT(pgsz > PAGESIZE); extra_zfodsz = P2NPHASE((uintptr_t)ebss, pgsz);
*** 1324,1334 **** break; case PT_INTERP: if (ptload) goto bad; ! *dyphdr = phdr; break; case PT_SHLIB: *stphdr = phdr; break; --- 1423,1433 ---- break; case PT_INTERP: if (ptload) goto bad; ! *intphdr = phdr; break; case PT_SHLIB: *stphdr = phdr; break;
*** 1358,1367 **** --- 1457,1491 ---- if (minaddr != NULL) { ASSERT(mintmp != (caddr_t)-1); *minaddr = (intptr_t)mintmp; } + if (brkbase != NULL && secflag_enabled(curproc, PROC_SEC_ASLR)) { + size_t off; + uintptr_t base = (uintptr_t)*brkbase; + uintptr_t oend = base + *brksize; + + ASSERT(ISP2(aslr_max_brk_skew)); + + (void) random_get_pseudo_bytes((uint8_t *)&off, sizeof (off)); + base += P2PHASE(off, aslr_max_brk_skew); + base = P2ROUNDUP(base, PAGESIZE); + *brkbase = (caddr_t)base; + /* + * Above, we set *brksize to account for the possibility we + * had to grow the 'brk' in padding out the BSS to a page + * boundary. + * + * We now need to adjust that based on where we now are + * actually putting the brk. + */ + if (oend > base) + *brksize = oend - base; + else + *brksize = 0; + } + return (0); bad: if (error == 0) error = EINVAL; return (error);