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,10 +40,11 @@
 #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,12 +64,14 @@
 #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,10 +163,47 @@
         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,11 +293,12 @@
         aux_entry_t     *aux;
         int             error;
         ssize_t         resid;
         int             fd = -1;
         intptr_t        voffset;
-        Phdr            *dyphdr = NULL;
+        Phdr            *intphdr = NULL;
+        Phdr            *dynamicphdr = NULL;
         Phdr            *stphdr = NULL;
         Phdr            *uphdr = NULL;
         Phdr            *junk = NULL;
         size_t          len;
         ssize_t         phdrsize;

@@ -267,13 +308,14 @@
         Phdr            *dataphdrp = NULL;
         Phdr            *dtrphdr;
         Phdr            *capphdr = NULL;
         Cap             *cap = NULL;
         ssize_t         capsize;
+        Dyn             *dyn = NULL;
         int             hasu = 0;
         int             hasauxv = 0;
-        int             hasdy = 0;
+        int             hasintp = 0;
         int             branded = 0;
 
         struct proc *p = ttoproc(curthread);
         struct user *up = PTOU(p);
         struct bigwad {

@@ -368,11 +410,11 @@
         hsize = ehdrp->e_phentsize;
         phdrp = (Phdr *)phdrbase;
         for (i = nphdrs; i > 0; i--) {
                 switch (phdrp->p_type) {
                 case PT_INTERP:
-                        hasauxv = hasdy = 1;
+                        hasauxv = hasintp = 1;
                         break;
                 case PT_PHDR:
                         hasu = 1;
                         break;
                 case PT_SUNWSTACK:

@@ -388,10 +430,13 @@
                         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,11 +475,11 @@
                  *      AT_SUN_EXECNAME (added in stk_copyout)
                  *      AT_NULL
                  *
                  * total == 9
                  */
-                if (hasdy && hasu) {
+                if (hasintp && hasu) {
                         /*
                          * Has PT_INTERP & PT_PHDR - the auxvectors that
                          * will be built are:
                          *
                          *      AT_PHDR

@@ -444,11 +489,11 @@
                          *      AT_LDDATA
                          *
                          * total = 5
                          */
                         args->auxsize = (9 + 5) * sizeof (aux_entry_t);
-                } else if (hasdy) {
+                } else if (hasintp) {
                         /*
                          * Has PT_INTERP but no PT_PHDR
                          *
                          *      AT_EXECFD
                          *      AT_LDDATA

@@ -477,10 +522,54 @@
                  * 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,39 +616,39 @@
         else
                 len = 0;
 
         dtrphdr = NULL;
 
-        if ((error = mapelfexec(vp, ehdrp, nphdrs, phdrbase, &uphdr, &dyphdr,
+        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 && dyphdr == NULL)
+        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 (dyphdr != NULL) {
+        if (intphdr != NULL) {
                 size_t          len;
                 uintptr_t       lddata;
                 char            *p;
                 struct vnode    *nvp;
 
-                dlnsize = dyphdr->p_filesz;
+                dlnsize = intphdr->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,
+                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,10 +862,11 @@
                 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,11 +1272,11 @@
         vnode_t *vp,
         Ehdr *ehdr,
         int nphdrs,
         caddr_t phdrbase,
         Phdr **uphdr,
-        Phdr **dyphdr,
+        Phdr **intphdr,
         Phdr **stphdr,
         Phdr **dtphdr,
         Phdr *dataphdrp,
         caddr_t *bssbase,
         caddr_t *brkbase,

@@ -1206,15 +1296,19 @@
         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".
                  */
-                map_addr(&addr, len, (offset_t)0, 1, 0);
+                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,11 +1331,11 @@
         }
         phdr = (Phdr *)phdrbase;
         for (i = nphdrs; i > 0; i--) {
                 switch (phdr->p_type) {
                 case PT_LOAD:
-                        if ((*dyphdr != NULL) && (*uphdr == NULL))
+                        if ((*intphdr != NULL) && (*uphdr == NULL))
                                 return (0);
 
                         ptload = 1;
                         prot = PROT_USER;
                         if (phdr->p_flags & PF_R)

@@ -1292,10 +1386,17 @@
                         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,11 +1425,11 @@
                         break;
 
                 case PT_INTERP:
                         if (ptload)
                                 goto bad;
-                        *dyphdr = phdr;
+                        *intphdr = phdr;
                         break;
 
                 case PT_SHLIB:
                         *stphdr = phdr;
                         break;

@@ -1358,10 +1459,35 @@
         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);