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

@@ -66,12 +66,13 @@
  * and Libraries Guide" for more information about the standard and mapping
  * rules.
  *
  * Having mmapobj interpret and map objects will allow the kernel to make the
  * best decision for where to place the mappings for said objects.  Thus, we
- * can make optimizations inside of the kernel for specific platforms or
- * cache mapping information to make mapping objects faster.
+ * can make optimizations inside of the kernel for specific platforms or cache
+ * mapping information to make mapping objects faster.  The cache is ignored
+ * if ASLR is enabled.
  *
  * The lib_va_hash will be one such optimization.  For each ELF object that
  * mmapobj is asked to interpret, we will attempt to cache the information
  * about the PT_LOAD and PT_SUNWBSS sections to speed up future mappings of
  * the same objects.  We will cache up to LIBVA_CACHED_SEGS (see below) program

@@ -716,11 +717,11 @@
  * The caller is responsible for doing this making sure that any modifications
  * to lv_mps are visible before setting lv_num_segs.
  */
 static caddr_t
 mmapobj_alloc_start_addr(struct lib_va **lvpp, size_t len, int use_lib_va,
-    size_t align, vattr_t *vap)
+    int randomize, size_t align, vattr_t *vap)
 {
         proc_t *p = curproc;
         struct as *as = p->p_as;
         struct segvn_crargs crargs = SEGVN_ZFOD_ARGS(PROT_USER, PROT_ALL);
         int error;

@@ -731,10 +732,11 @@
         size_t lib_va_start;
         size_t lib_va_end;
         size_t lib_va_len;
 
         ASSERT(lvpp != NULL);
+        ASSERT((randomize & use_lib_va) != 1);
 
         MOBJ_STAT_ADD(alloc_start);
         model = get_udatamodel();
 
         if (model == DATAMODEL_LP64) {

@@ -746,10 +748,14 @@
         }
 
         if (align > 1) {
                 ma_flags |= MAP_ALIGN;
         }
+
+        if (randomize != 0)
+                ma_flags |= _MAP_RANDOMIZE;
+
         if (use_lib_va) {
                 /*
                  * The first time through, we need to setup the lib_va arenas.
                  * We call map_addr to find a suitable range of memory to map
                  * the given library, and we will set the highest address

@@ -859,11 +865,18 @@
 
         /*
          * If we don't have an expected base address, or the one that we want
          * to use is not available or acceptable, go get an acceptable
          * address range.
+         *
+         * If ASLR is enabled, we should never have used the cache, and should
+         * also start our real work here, in the consequent of the next
+         * condition.
          */
+        if (randomize != 0)
+                ASSERT(base == NULL);
+
         if (base == NULL || as_gap(as, len, &base, &len, 0, NULL) ||
             valid_usr_range(base, len, PROT_ALL, as, as->a_userlimit) !=
             RANGE_OKAY || OVERLAPS_STACK(base + len, p)) {
                 MOBJ_STAT_ADD(get_addr);
                 base = (caddr_t)align;

@@ -1523,11 +1536,11 @@
  * Walk through the ELF program headers and extract all useful information
  * for PT_LOAD and PT_SUNWBSS segments into mrp.
  * Return 0 on success or error on failure.
  */
 static int
-process_phdr(Ehdr *ehdrp, caddr_t phdrbase, int nphdrs, mmapobj_result_t *mrp,
+process_phdrs(Ehdr *ehdrp, caddr_t phdrbase, int nphdrs, mmapobj_result_t *mrp,
     vnode_t *vp, uint_t *num_mapped, size_t padding, cred_t *fcred)
 {
         int i;
         caddr_t start_addr = NULL;
         caddr_t vaddr;

@@ -1579,20 +1592,21 @@
                         MOBJ_STAT_ADD(phent_align32);
                         return (ENOTSUP);
                 }
         }
 
-        if (padding != 0) {
+        if ((padding != 0) || secflag_enabled(curproc, PROC_SEC_ASLR)) {
                 use_lib_va = 0;
         }
         if (e_type == ET_DYN) {
                 vattr.va_mask = AT_FSID | AT_NODEID | AT_CTIME | AT_MTIME;
                 error = VOP_GETATTR(vp, &vattr, 0, fcred, NULL);
                 if (error) {
                         return (error);
                 }
                 /* Check to see if we already have a description for this lib */
+                if (!secflag_enabled(curproc, PROC_SEC_ASLR))
                 lvp = lib_va_find(&vattr);
 
                 if (lvp != NULL) {
                         MOBJ_STAT_ADD(lvp_found);
                         if (use_lib_va) {

@@ -1699,11 +1713,13 @@
                  * to call mmapobj_alloc_start_addr and know that lvp
                  * will not be modified.
                  */
                 ASSERT(lvp ? use_lib_va == 0 : 1);
                 start_addr = mmapobj_alloc_start_addr(&lvp, len,
-                    use_lib_va, align, &vattr);
+                    use_lib_va,
+                    secflag_enabled(curproc, PROC_SEC_ASLR),
+                    align, &vattr);
                 if (start_addr == NULL) {
                         if (lvp) {
                                 lib_va_release(lvp);
                         }
                         MOBJ_STAT_ADD(alloc_start_fail);

@@ -2024,11 +2040,11 @@
                 kmem_free(phbasep, phsizep);
                 return (error);
         }
 
         /* Now process the phdr's */
-        error = process_phdr(ehdrp, phbasep, nphdrs, mrp, vp, num_mapped,
+        error = process_phdrs(ehdrp, phbasep, nphdrs, mrp, vp, num_mapped,
             padding, fcred);
         kmem_free(phbasep, phsizep);
         return (error);
 }
 

@@ -2310,11 +2326,12 @@
         /*
          * Check lib_va to see if we already have a full description
          * for this library.  This is the fast path and only used for
          * ET_DYN ELF files (dynamic libraries).
          */
-        if (padding == 0 && (lvp = lib_va_find(&vattr)) != NULL) {
+        if (padding == 0 && !secflag_enabled(curproc, PROC_SEC_ASLR) &&
+            ((lvp = lib_va_find(&vattr)) != NULL)) {
                 int num_segs;
 
                 model = get_udatamodel();
                 if ((model == DATAMODEL_ILP32 &&
                     lvp->lv_flags & LV_ELF64) ||