/* binprm security operations */
 
+static int check_nnp_nosuid(const struct linux_binprm *bprm,
+                           const struct task_security_struct *old_tsec,
+                           const struct task_security_struct *new_tsec)
+{
+       int nnp = (bprm->unsafe & LSM_UNSAFE_NO_NEW_PRIVS);
+       int nosuid = (bprm->file->f_path.mnt->mnt_flags & MNT_NOSUID);
+       int rc;
+
+       if (!nnp && !nosuid)
+               return 0; /* neither NNP nor nosuid */
+
+       if (new_tsec->sid == old_tsec->sid)
+               return 0; /* No change in credentials */
+
+       /*
+        * The only transitions we permit under NNP or nosuid
+        * are transitions to bounded SIDs, i.e. SIDs that are
+        * guaranteed to only be allowed a subset of the permissions
+        * of the current SID.
+        */
+       rc = security_bounded_transition(old_tsec->sid, new_tsec->sid);
+       if (rc) {
+               /*
+                * On failure, preserve the errno values for NNP vs nosuid.
+                * NNP:  Operation not permitted for caller.
+                * nosuid:  Permission denied to file.
+                */
+               if (nnp)
+                       return -EPERM;
+               else
+                       return -EACCES;
+       }
+       return 0;
+}
+
 static int selinux_bprm_set_creds(struct linux_binprm *bprm)
 {
        const struct task_security_struct *old_tsec;
                /* Reset exec SID on execve. */
                new_tsec->exec_sid = 0;
 
-               /*
-                * Minimize confusion: if no_new_privs or nosuid and a
-                * transition is explicitly requested, then fail the exec.
-                */
-               if (bprm->unsafe & LSM_UNSAFE_NO_NEW_PRIVS)
-                       return -EPERM;
-               if (bprm->file->f_path.mnt->mnt_flags & MNT_NOSUID)
-                       return -EACCES;
+               /* Fail on NNP or nosuid if not an allowed transition. */
+               rc = check_nnp_nosuid(bprm, old_tsec, new_tsec);
+               if (rc)
+                       return rc;
        } else {
                /* Check for a default transition on this program. */
                rc = security_transition_sid(old_tsec->sid, isec->sid,
                                             &new_tsec->sid);
                if (rc)
                        return rc;
+
+               /*
+                * Fallback to old SID on NNP or nosuid if not an allowed
+                * transition.
+                */
+               rc = check_nnp_nosuid(bprm, old_tsec, new_tsec);
+               if (rc)
+                       new_tsec->sid = old_tsec->sid;
        }
 
        ad.type = LSM_AUDIT_DATA_PATH;
        ad.u.path = bprm->file->f_path;
 
-       if ((bprm->file->f_path.mnt->mnt_flags & MNT_NOSUID) ||
-           (bprm->unsafe & LSM_UNSAFE_NO_NEW_PRIVS))
-               new_tsec->sid = old_tsec->sid;
-
        if (new_tsec->sid == old_tsec->sid) {
                rc = avc_has_perm(old_tsec->sid, isec->sid,
                                  SECCLASS_FILE, FILE__EXECUTE_NO_TRANS, &ad);