]> Pileus Git - ~andy/linux/blobdiff - security/selinux/avc.c
SELinux: do not allocate stack space for AVC data unless needed
[~andy/linux] / security / selinux / avc.c
index 6989472d0957d89ab80e773c4ce1e2751de56cbe..36c42bb52d816fffcc29e1ed32e1571f7340184b 100644 (file)
@@ -436,9 +436,9 @@ static void avc_audit_pre_callback(struct audit_buffer *ab, void *a)
 {
        struct common_audit_data *ad = a;
        audit_log_format(ab, "avc:  %s ",
-                        ad->selinux_audit_data.denied ? "denied" : "granted");
-       avc_dump_av(ab, ad->selinux_audit_data.tclass,
-                       ad->selinux_audit_data.audited);
+                        ad->selinux_audit_data->slad->denied ? "denied" : "granted");
+       avc_dump_av(ab, ad->selinux_audit_data->slad->tclass,
+                       ad->selinux_audit_data->slad->audited);
        audit_log_format(ab, " for ");
 }
 
@@ -452,22 +452,25 @@ static void avc_audit_post_callback(struct audit_buffer *ab, void *a)
 {
        struct common_audit_data *ad = a;
        audit_log_format(ab, " ");
-       avc_dump_query(ab, ad->selinux_audit_data.ssid,
-                          ad->selinux_audit_data.tsid,
-                          ad->selinux_audit_data.tclass);
+       avc_dump_query(ab, ad->selinux_audit_data->slad->ssid,
+                          ad->selinux_audit_data->slad->tsid,
+                          ad->selinux_audit_data->slad->tclass);
 }
 
 /* This is the slow part of avc audit with big stack footprint */
 static noinline int slow_avc_audit(u32 ssid, u32 tsid, u16 tclass,
                u32 requested, u32 audited, u32 denied,
-               struct av_decision *avd, struct common_audit_data *a,
+               struct common_audit_data *a,
                unsigned flags)
 {
        struct common_audit_data stack_data;
+       struct selinux_audit_data sad = {0,};
+       struct selinux_late_audit_data slad;
 
        if (!a) {
                a = &stack_data;
                COMMON_AUDIT_DATA_INIT(a, NONE);
+               a->selinux_audit_data = &sad;
        }
 
        /*
@@ -481,12 +484,14 @@ static noinline int slow_avc_audit(u32 ssid, u32 tsid, u16 tclass,
            (flags & MAY_NOT_BLOCK))
                return -ECHILD;
 
-       a->selinux_audit_data.tclass = tclass;
-       a->selinux_audit_data.requested = requested;
-       a->selinux_audit_data.ssid = ssid;
-       a->selinux_audit_data.tsid = tsid;
-       a->selinux_audit_data.audited = audited;
-       a->selinux_audit_data.denied = denied;
+       slad.tclass = tclass;
+       slad.requested = requested;
+       slad.ssid = ssid;
+       slad.tsid = tsid;
+       slad.audited = audited;
+       slad.denied = denied;
+
+       a->selinux_audit_data->slad = &slad;
        a->lsm_pre_audit = avc_audit_pre_callback;
        a->lsm_post_audit = avc_audit_post_callback;
        common_lsm_audit(a);
@@ -513,7 +518,7 @@ static noinline int slow_avc_audit(u32 ssid, u32 tsid, u16 tclass,
  * be performed under a lock, to allow the lock to be released
  * before calling the auditing code.
  */
-int avc_audit(u32 ssid, u32 tsid,
+inline int avc_audit(u32 ssid, u32 tsid,
               u16 tclass, u32 requested,
               struct av_decision *avd, int result, struct common_audit_data *a,
               unsigned flags)
@@ -523,7 +528,7 @@ int avc_audit(u32 ssid, u32 tsid,
        if (unlikely(denied)) {
                audited = denied & avd->auditdeny;
                /*
-                * a->selinux_audit_data.auditdeny is TRICKY!  Setting a bit in
+                * a->selinux_audit_data->auditdeny is TRICKY!  Setting a bit in
                 * this field means that ANY denials should NOT be audited if
                 * the policy contains an explicit dontaudit rule for that
                 * permission.  Take notice that this is unrelated to the
@@ -532,15 +537,15 @@ int avc_audit(u32 ssid, u32 tsid,
                 *
                 * denied == READ
                 * avd.auditdeny & ACCESS == 0 (not set means explicit rule)
-                * selinux_audit_data.auditdeny & ACCESS == 1
+                * selinux_audit_data->auditdeny & ACCESS == 1
                 *
                 * We will NOT audit the denial even though the denied
                 * permission was READ and the auditdeny checks were for
                 * ACCESS
                 */
                if (a &&
-                   a->selinux_audit_data.auditdeny &&
-                   !(a->selinux_audit_data.auditdeny & avd->auditdeny))
+                   a->selinux_audit_data->auditdeny &&
+                   !(a->selinux_audit_data->auditdeny & avd->auditdeny))
                        audited = 0;
        } else if (result)
                audited = denied = requested;
@@ -551,7 +556,7 @@ int avc_audit(u32 ssid, u32 tsid,
 
        return slow_avc_audit(ssid, tsid, tclass,
                requested, audited, denied,
-               avd, a, flags);
+               a, flags);
 }
 
 /**
@@ -741,6 +746,41 @@ int avc_ss_reset(u32 seqno)
        return rc;
 }
 
+/*
+ * Slow-path helper function for avc_has_perm_noaudit,
+ * when the avc_node lookup fails. We get called with
+ * the RCU read lock held, and need to return with it
+ * still held, but drop if for the security compute.
+ *
+ * Don't inline this, since it's the slow-path and just
+ * results in a bigger stack frame.
+ */
+static noinline struct avc_node *avc_compute_av(u32 ssid, u32 tsid,
+                        u16 tclass, struct av_decision *avd)
+{
+       rcu_read_unlock();
+       security_compute_av(ssid, tsid, tclass, avd);
+       rcu_read_lock();
+       return avc_insert(ssid, tsid, tclass, avd);
+}
+
+static noinline int avc_denied(u32 ssid, u32 tsid,
+                        u16 tclass, u32 requested,
+                        unsigned flags,
+                        struct av_decision *avd)
+{
+       if (flags & AVC_STRICT)
+               return -EACCES;
+
+       if (selinux_enforcing && !(avd->flags & AVD_FLAGS_PERMISSIVE))
+               return -EACCES;
+
+       avc_update_node(AVC_CALLBACK_GRANT, requested, ssid,
+                               tsid, tclass, avd->seqno);
+       return 0;
+}
+
+
 /**
  * avc_has_perm_noaudit - Check permissions but perform no auditing.
  * @ssid: source security identifier
@@ -761,7 +801,7 @@ int avc_ss_reset(u32 seqno)
  * auditing, e.g. in cases where a lock must be held for the check but
  * should be released for the auditing.
  */
-int avc_has_perm_noaudit(u32 ssid, u32 tsid,
+inline int avc_has_perm_noaudit(u32 ssid, u32 tsid,
                         u16 tclass, u32 requested,
                         unsigned flags,
                         struct av_decision *avd)
@@ -776,26 +816,15 @@ int avc_has_perm_noaudit(u32 ssid, u32 tsid,
 
        node = avc_lookup(ssid, tsid, tclass);
        if (unlikely(!node)) {
-               rcu_read_unlock();
-               security_compute_av(ssid, tsid, tclass, avd);
-               rcu_read_lock();
-               node = avc_insert(ssid, tsid, tclass, avd);
+               node = avc_compute_av(ssid, tsid, tclass, avd);
        } else {
                memcpy(avd, &node->ae.avd, sizeof(*avd));
                avd = &node->ae.avd;
        }
 
        denied = requested & ~(avd->allowed);
-
-       if (denied) {
-               if (flags & AVC_STRICT)
-                       rc = -EACCES;
-               else if (!selinux_enforcing || (avd->flags & AVD_FLAGS_PERMISSIVE))
-                       avc_update_node(AVC_CALLBACK_GRANT, requested, ssid,
-                                       tsid, tclass, avd->seqno);
-               else
-                       rc = -EACCES;
-       }
+       if (unlikely(denied))
+               rc = avc_denied(ssid, tsid, tclass, requested, flags, avd);
 
        rcu_read_unlock();
        return rc;