Print this page
uts: Allow for address space randomisation.
Randomise the base addresses of shared objects, non-fixed mappings, the
stack and the heap.  Introduce a service, svc:/system/process-security,
and a tool psecflags(1) to control and observe it

*** 63,74 **** --- 63,76 ---- #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 *);
*** 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; --- 255,266 ---- 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 { --- 270,284 ---- Phdr *dataphdrp = NULL; Phdr *dtrphdr; Phdr *capphdr = NULL; Cap *cap = NULL; ssize_t capsize; + Dyn *dyn = NULL; + ssize_t dynsize; int hasu = 0; int hasauxv = 0; ! int hasintp = 0; int branded = 0; struct proc *p = ttoproc(curthread); struct user *up = PTOU(p); struct bigwad {
*** 362,372 **** 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: --- 367,377 ---- 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:
*** 382,391 **** --- 387,399 ---- 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) {
*** 418,434 **** * AT_FLAGS * AT_PAGESZ * AT_SUN_AUXFLAGS * AT_SUN_HWCAP * AT_SUN_HWCAP2 * AT_SUN_PLATFORM (added in stk_copyout) * 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 --- 426,443 ---- * AT_FLAGS * AT_PAGESZ * AT_SUN_AUXFLAGS * AT_SUN_HWCAP * AT_SUN_HWCAP2 + * AT_SUN_SECFLAGS * AT_SUN_PLATFORM (added in stk_copyout) * AT_SUN_EXECNAME (added in stk_copyout) * AT_NULL * ! * total == 10 */ ! if (hasintp && hasu) { /* * Has PT_INTERP & PT_PHDR - the auxvectors that * will be built are: * * AT_PHDR
*** 437,459 **** * AT_ENTRY * 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 * * total = 2 */ ! args->auxsize = (9 + 2) * sizeof (aux_entry_t); } else { ! args->auxsize = 9 * sizeof (aux_entry_t); } } else { args->auxsize = 0; } --- 446,468 ---- * AT_ENTRY * AT_LDDATA * * total = 5 */ ! args->auxsize = (10 + 5) * sizeof (aux_entry_t); ! } else if (hasintp) { /* * Has PT_INTERP but no PT_PHDR * * AT_EXECFD * AT_LDDATA * * total = 2 */ ! args->auxsize = (10 + 2) * sizeof (aux_entry_t); } else { ! args->auxsize = 10 * sizeof (aux_entry_t); } } else { args->auxsize = 0; }
*** 471,480 **** --- 480,527 ---- * 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) { + Dyn *dp; + + dynsize = dynamicphdr->p_filesz; + dyn = kmem_alloc(dynsize, KM_SLEEP); + + if ((error = vn_rdwr(UIO_READ, vp, (caddr_t)dyn, dynsize, + (offset_t)dynamicphdr->p_offset, UIO_SYSSPACE, 0, (rlim64_t)0, + CRED(), &resid)) != 0) { + uprintf("%s: cannot read .dynamic section\n", + exec_file); + goto out; + } + + if (resid != 0) + goto out; + + dp = dyn; + while (dp->d_tag != DT_NULL) { + if (dp->d_tag == DT_SUNW_ASLR) { + if (dp->d_un.d_val != 0) { + curproc->p_secflags.psf_effective |= + PROC_SEC_ASLR; + curproc->p_secflags.psf_inherit |= + PROC_SEC_ASLR; + + } else { + curproc->p_secflags.psf_effective &= + ~PROC_SEC_ASLR; + curproc->p_secflags.psf_inherit &= + ~PROC_SEC_ASLR; + } + } + dp++; + } + } + /* Hardware/Software capabilities */ if (capphdr != NULL && (capsize = capphdr->p_filesz) > 0 && capsize <= 16 * sizeof (*cap)) { int ncaps = capsize / sizeof (*cap);
*** 521,559 **** 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; } --- 568,606 ---- 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; }
*** 758,767 **** --- 805,821 ---- args->auxp_auxflags = (char *)((char *)args->stackend + ((char *)&aux->a_type - (char *)bigwad->elfargs)); ADDAUX(aux, AT_SUN_AUXFLAGS, auxf); + + /* + * Put the effective security-flags into the aux vector, for + * the sake of flags that need partial (or complete) + * implementation in userland. + */ + ADDAUX(aux, AT_SUN_SECFLAGS, p->p_secflags.psf_effective); /* * Hardware capability flag word (performance hints) * Used for choosing faster library routines. * (Potentially different between 32-bit and 64-bit ABIs) */
*** 898,907 **** --- 952,963 ---- out: if (phdrbase != NULL) kmem_free(phdrbase, phdrsize); if (cap != NULL) kmem_free(cap, capsize); + if (dyn != NULL) + kmem_free(dyn, dynsize); kmem_free(bigwad, sizeof (struct bigwad)); return (error); } /*
*** 1167,1177 **** 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, --- 1223,1233 ---- 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,
*** 1191,1205 **** 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; /* --- 1247,1265 ---- int hsize = ehdr->e_phentsize; caddr_t mintmp = (caddr_t)-1; extern int use_brk_lpg; if (ehdr->e_type == ET_DYN) { + uint_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; /*
*** 1222,1232 **** } 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) --- 1282,1292 ---- } 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)
*** 1277,1286 **** --- 1337,1353 ---- 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);
*** 1309,1319 **** break; case PT_INTERP: if (ptload) goto bad; ! *dyphdr = phdr; break; case PT_SHLIB: *stphdr = phdr; break; --- 1376,1386 ---- break; case PT_INTERP: if (ptload) goto bad; ! *intphdr = phdr; break; case PT_SHLIB: *stphdr = phdr; break;
*** 1343,1352 **** --- 1410,1444 ---- 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);