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.

@@ -67,10 +67,11 @@
 #include <sys/lwpchan_impl.h>
 #include <sys/pool.h>
 #include <sys/sdt.h>
 #include <sys/brand.h>
 #include <sys/klpd.h>
+#include <sys/random.h>
 
 #include <c2/audit.h>
 
 #include <vm/hat.h>
 #include <vm/anon.h>

@@ -97,10 +98,25 @@
 #endif
 
 #define PSUIDFLAGS              (SNOCD|SUGID)
 
 /*
+ * These are consumed within the specific exec modules, but are defined here
+ * because
+ *
+ * 1) The exec modules are unloadable, which would make this near useless.
+ *
+ * 2) We want them to be common across all of them, should more than ELF come
+ *    to support them.
+ *
+ * All must be powers of 2.
+ */
+size_t aslr_max_brk_skew = 16 * 1024 * 1024; /* 16MB */
+#pragma weak exec_stackgap = aslr_max_stack_skew /* Old, compatible name */
+size_t aslr_max_stack_skew = 64 * 1024; /* 64KB */
+
+/*
  * exece() - system call wrapper around exec_common()
  */
 int
 exece(const char *fname, const char **argp, const char **envp)
 {

@@ -558,10 +574,13 @@
         int setid;
         cred_t *oldcred, *newcred = NULL;
         int privflags = 0;
         int setidfl;
         priv_set_t fset;
+        secflagset_t old_secflags;
+
+        secflags_copy(&old_secflags, &pp->p_secflags.psf_effective);
 
         /*
          * If the SNOCD or SUGID flag is set, turn it off and remember the
          * previous setting so we can restore it if we encounter an error.
          */

@@ -658,10 +677,13 @@
                 priv_intersect(&CR_LPRIV(cred), &CR_IPRIV(cred));
                 CR_EPRIV(cred) = CR_PPRIV(cred) = CR_IPRIV(cred);
                 priv_adjust_PA(cred);
         }
 
+        /* The new image gets the inheritable secflags as its secflags */
+        secflags_promote(pp);
+
         /* SunOS 4.x buy-back */
         if ((vp->v_vfsp->vfs_flag & VFS_NOSETUID) &&
             (vattr.va_mode & (VSUID|VSGID))) {
                 char path[MAXNAMELEN];
                 refstr_t *mntpt = NULL;

@@ -718,11 +740,12 @@
 
         /*
          * Use /etc/system variable to determine if the stack
          * should be marked as executable by default.
          */
-        if (noexec_user_stack)
+        if ((noexec_user_stack != 0) ||
+            secflag_enabled(pp, PROC_SEC_NOEXECSTACK))
                 args->stk_prot &= ~PROT_EXEC;
 
         args->execswp = eswp; /* Save execsw pointer in uarg for exec_func */
         args->ex_vp = vp;
 

@@ -804,11 +827,11 @@
                         oldcred = curthread->t_cred;
                         curthread->t_cred = cred;
                         crfree(oldcred);
 
                         if (priv_basic_test >= 0 &&
-                            !PRIV_ISASSERT(&CR_IPRIV(newcred),
+                            !PRIV_ISMEMBER(&CR_IPRIV(newcred),
                             priv_basic_test)) {
                                 pid_t pid = pp->p_pid;
                                 char *fn = PTOU(pp)->u_comm;
 
                                 cmn_err(CE_WARN, "%s[%d]: exec: basic_test "

@@ -874,15 +897,21 @@
         if (newcred != NULL)
                 crfree(newcred);
         if (error == 0)
                 error = ENOEXEC;
 
-        if (suidflags) {
                 mutex_enter(&pp->p_lock);
+        if (suidflags) {
                 pp->p_flag |= suidflags;
-                mutex_exit(&pp->p_lock);
         }
+        /*
+         * Restore the effective secflags, to maintain the invariant they
+         * never change for a given process
+         */
+        secflags_copy(&pp->p_secflags.psf_effective, &old_secflags);
+        mutex_exit(&pp->p_lock);
+
         return (error);
 }
 
 extern char *execswnames[];
 

@@ -1785,10 +1814,48 @@
 
         return (0);
 }
 
 /*
+ * Though the actual stack base is constant, slew the %sp by a random aligned
+ * amount in [0,aslr_max_stack_skew).  Mostly, this makes life slightly more
+ * complicated for buffer overflows hoping to overwrite the return address.
+ *
+ * On some platforms this helps avoid cache thrashing when identical processes
+ * simultaneously share caches that don't provide enough associativity
+ * (e.g. sun4v systems). In this case stack slewing makes the same hot stack
+ * variables in different processes live in different cache sets increasing
+ * effective associativity.
+ */
+size_t
+exec_get_spslew(void)
+{
+#ifdef sun4v
+        static uint_t sp_color_stride = 16;
+        static uint_t sp_color_mask = 0x1f;
+        static uint_t sp_current_color = (uint_t)-1;
+#endif
+        size_t off;
+
+        ASSERT(ISP2(aslr_max_stack_skew));
+
+        if ((aslr_max_stack_skew == 0) ||
+            !secflag_enabled(curproc, PROC_SEC_ASLR)) {
+#ifdef sun4v
+                uint_t spcolor = atomic_inc_32_nv(&sp_current_color);
+                return ((size_t)((spcolor & sp_color_mask) *
+                    SA(sp_color_stride)));
+#else
+                return (0);
+#endif
+        }
+
+        (void) random_get_pseudo_bytes((uint8_t *)&off, sizeof (off));
+        return (SA(P2PHASE(off, aslr_max_stack_skew)));
+}
+
+/*
  * Initialize a new user stack with the specified arguments and environment.
  * The initial user stack layout is as follows:
  *
  *      User Stack
  *      +---------------+ <--- curproc->p_usrstack

@@ -2014,21 +2081,14 @@
 
         mutex_enter(&p->p_lock);
         p->p_flag |= SAUTOLPG;  /* kernel controls page sizes */
         mutex_exit(&p->p_lock);
 
-        /*
-         * Some platforms may choose to randomize real stack start by adding a
-         * small slew (not more than a few hundred bytes) to the top of the
-         * stack. This helps avoid cache thrashing when identical processes
-         * simultaneously share caches that don't provide enough associativity
-         * (e.g. sun4v systems). In this case stack slewing makes the same hot
-         * stack variables in different processes to live in different cache
-         * sets increasing effective associativity.
-         */
         sp_slew = exec_get_spslew();
         ASSERT(P2PHASE(sp_slew, args->stk_align) == 0);
+        /* Be certain we don't underflow */
+        VERIFY((curproc->p_usrstack - (size + sp_slew)) < curproc->p_usrstack);
         exec_set_sp(size + sp_slew);
 
         as = as_alloc();
         p->p_as = as;
         as->a_proc = p;