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.

@@ -57,10 +57,11 @@
 #include <sys/exechdr.h>
 #include <sys/debug.h>
 #include <sys/vmsystm.h>
 #include <sys/swap.h>
 #include <sys/dumphdr.h>
+#include <sys/random.h>
 
 #include <vm/hat.h>
 #include <vm/as.h>
 #include <vm/seg.h>
 #include <vm/seg_kp.h>

@@ -78,10 +79,11 @@
 #include <sys/x86_archext.h>
 #include <sys/elf_386.h>
 #include <sys/cmn_err.h>
 #include <sys/archsystm.h>
 #include <sys/machsystm.h>
+#include <sys/secflags.h>
 
 #include <sys/vtrace.h>
 #include <sys/ddidmareq.h>
 #include <sys/promif.h>
 #include <sys/memnode.h>

@@ -635,10 +637,17 @@
 {
         return (0);
 }
 
 /*
+ * The maximum amount a randomized mapping will be slewed.  We should perhaps
+ * arrange things so these tunables can be separate for mmap, mmapobj, and
+ * ld.so
+ */
+size_t aslr_max_map_skew = 256 * 1024 * 1024; /* 256MB */
+
+/*
  * map_addr_proc() is the routine called when the system is to
  * choose an address for the user.  We will pick an address
  * range which is the highest available below userlimit.
  *
  * Every mapping will have a redzone of a single page on either side of

@@ -750,10 +759,11 @@
 
         ASSERT(ISP2(align_amount));
         ASSERT(align_amount == 0 || align_amount >= PAGESIZE);
 
         off = off & (align_amount - 1);
+
         /*
          * Look for a large enough hole starting below userlimit.
          * After finding it, use the upper part.
          */
         if (as_gap_aligned(as, len, &base, &slen, AH_HI, NULL, align_amount,

@@ -777,10 +787,24 @@
                 addr += (uintptr_t)off;
                 if (addr > as_addr) {
                         addr -= align_amount;
                 }
 
+                /*
+                 * If randomization is requested, slew the allocation
+                 * backwards, within the same gap, by a random amount.
+                 */
+                if (flags & _MAP_RANDOMIZE) {
+                        uint32_t slew;
+
+                        (void) random_get_pseudo_bytes((uint8_t *)&slew,
+                            sizeof (slew));
+
+                        slew = slew % MIN(aslr_max_map_skew, (addr - base));
+                        addr -= P2ALIGN(slew, align_amount);
+                }
+
                 ASSERT(addr > base);
                 ASSERT(addr + len < base + slen);
                 ASSERT(((uintptr_t)addr & (align_amount - 1)) ==
                     ((uintptr_t)(off)));
                 *addrp = addr;

@@ -902,10 +926,17 @@
 {
         return (valid_va_range_aligned(basep, lenp, minlen, dir, 0, 0, 0));
 }
 
 /*
+ * Default to forbidding the first 64k of address space.  This protects most
+ * reasonably sized structures from dereferences through NULL:
+ *     ((foo_t *)0)->bar
+ */
+uintptr_t forbidden_null_mapping_sz = 0x10000;
+
+/*
  * Determine whether [addr, addr+len] are valid user addresses.
  */
 /*ARGSUSED*/
 int
 valid_usr_range(caddr_t addr, size_t len, uint_t prot, struct as *as,

@@ -914,10 +945,14 @@
         caddr_t eaddr = addr + len;
 
         if (eaddr <= addr || addr >= userlimit || eaddr > userlimit)
                 return (RANGE_BADADDR);
 
+        if ((addr <= (caddr_t)forbidden_null_mapping_sz) &&
+            secflag_enabled(as->a_proc, PROC_SEC_FORBIDNULLMAP))
+                return (RANGE_BADADDR);
+
 #if defined(__amd64)
         /*
          * Check for the VA hole
          */
         if (eaddr > (caddr_t)hole_start && addr < (caddr_t)hole_end)

@@ -3924,16 +3959,10 @@
  */
 void
 dcache_flushall()
 {}
 
-size_t
-exec_get_spslew(void)
-{
-        return (0);
-}
-
 /*
  * Allocate a memory page.  The argument 'seed' can be any pseudo-random
  * number to vary where the pages come from.  This is quite a hacked up
  * method -- it works for now, but really needs to be fixed up a bit.
  *