Print this page
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,209 ----
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_inherit, flag);
+ 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_inherit, flag);
+ 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;
--- 293,304 ----
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 {
--- 308,321 ----
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:
--- 410,420 ----
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 ****
--- 430,442 ----
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
--- 475,485 ----
* 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
--- 489,499 ----
* 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 ****
--- 522,575 ----
* 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;
}
--- 616,654 ----
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 ****
--- 862,872 ----
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,
--- 1272,1282 ----
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;
/*
--- 1296,1314 ----
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)
--- 1331,1341 ----
}
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 ****
--- 1386,1402 ----
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;
--- 1425,1435 ----
break;
case PT_INTERP:
if (ptload)
goto bad;
! *intphdr = phdr;
break;
case PT_SHLIB:
*stphdr = phdr;
break;
*** 1358,1367 ****
--- 1459,1493 ----
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);