Collect the self caps and the children template into struct aa_ns_capset,
so a policy load stages, checks and commits one tentative copy of the
whole set.

Signed-off-by: Maxime Bélair <[email protected]>
---
 security/apparmor/apparmorfs.c        | 12 ++---
 security/apparmor/include/policy_ns.h | 27 +++++++---
 security/apparmor/policy.c            | 16 +++---
 security/apparmor/policy_ns.c         | 72 +++++++++++++++++----------
 4 files changed, 79 insertions(+), 48 deletions(-)

diff --git a/security/apparmor/apparmorfs.c b/security/apparmor/apparmorfs.c
index 27962b51002b..98029e7f9d29 100644
--- a/security/apparmor/apparmorfs.c
+++ b/security/apparmor/apparmorfs.c
@@ -1494,12 +1494,12 @@ SEQ_NS_FOPS(NAME)
 
 SEQ_NS_ACCT(acct_count, atomic_long_read(&ns->acct.profile_count));
 SEQ_NS_ACCT(acct_size, atomic_long_read(&ns->acct.resident));
-SEQ_NS_ACCT(acct_max_count, ns->acct.limits.profiles);
-SEQ_NS_ACCT(acct_max_size, ns->acct.limits.memory);
-SEQ_NS_ACCT(acct_max_profile, ns->acct.limits.max_profile);
-SEQ_NS_ACCT(acct_namespaces, ns->acct.limits.namespaces);
-SEQ_NS_ACCT(acct_depth, ns->acct.limits.depth);
-SEQ_NS_ACCT(acct_criu, ns->acct.limits.criu);
+SEQ_NS_ACCT(acct_max_count, ns->acct.caps.limits.profiles);
+SEQ_NS_ACCT(acct_max_size, ns->acct.caps.limits.memory);
+SEQ_NS_ACCT(acct_max_profile, ns->acct.caps.limits.max_profile);
+SEQ_NS_ACCT(acct_namespaces, ns->acct.caps.limits.namespaces);
+SEQ_NS_ACCT(acct_depth, ns->acct.caps.limits.depth);
+SEQ_NS_ACCT(acct_criu, ns->acct.caps.limits.criu);
 
 
 /* policy/raw_data/ * file ops */
diff --git a/security/apparmor/include/policy_ns.h 
b/security/apparmor/include/policy_ns.h
index 4248b1f3fbf8..dce753feb91f 100644
--- a/security/apparmor/include/policy_ns.h
+++ b/security/apparmor/include/policy_ns.h
@@ -28,17 +28,33 @@ struct apparmor_audit_data;
 #define AA_NS_QUOTA_RATELIMIT_INTERVAL (5 * HZ)
 #define AA_NS_QUOTA_RATELIMIT_BURST    10
 
-/* struct aa_ns_acct - per-namespace resource accounting and caps
+/* struct aa_ns_capset - the standing caps a namespace enforces and stamps
  * @limits: caps enforced against this namespace (self)
  * @child: template caps stamped onto namespaces this namespace creates 
(children)
+ * @child_percent: percentage (0-100) of the parent's cap, resolved per child 
at creation
+ */
+struct aa_ns_capset {
+       struct aa_ns_caps limits;
+       struct aa_ns_caps child;
+       u32 child_percent;
+};
+
+static inline void aa_ns_capset_init_unset(struct aa_ns_capset *caps)
+{
+       aa_ns_caps_init_unset(&caps->limits);
+       aa_ns_caps_init_unset(&caps->child);
+       caps->child_percent = 0;
+}
+
+/* struct aa_ns_acct - per-namespace resource accounting and caps
+ * @caps: the caps enforced against and stamped by this namespace
  * @resident: current resident policy bytes charged to this ns (local scope)
  * @profile_count: current count of non-null profiles in this ns (local)
  * @ns_count: current number of direct child namespaces
  * @ratelimit: bounds OP_NS_QUOTA audit emission
  */
 struct aa_ns_acct {
-       struct aa_ns_caps limits;
-       struct aa_ns_caps child;
+       struct aa_ns_capset caps;
        atomic_long_t resident;
        atomic_long_t profile_count;
        atomic_long_t ns_count;
@@ -130,11 +146,10 @@ struct aa_load_ent;
 int aa_ns_admit_load_set(struct aa_ns *ns, struct list_head *lh,
                         struct aa_ns_caps *limits, struct aa_loaddata *udata,
                         struct aa_load_ent **fail_ent, const char **info);
-/* apply one parsed "policyns limits" block to a (tentative) caps pair;
+/* apply one parsed "policyns limits" block to a (tentative) capset;
  * returns -EOPNOTSUPP for a construct this kernel does not yet enforce
  */
-int aa_ns_apply_budget(struct aa_ns_caps *limits, struct aa_ns_caps *child,
-                      struct aa_ns_budget *budget);
+int aa_ns_apply_budget(struct aa_ns_capset *caps, struct aa_ns_budget *budget);
 /* mediate the policyns permission rule (create/load/replace/remove) */
 int aa_policyns_perm(struct aa_label *label, struct aa_ns *target,
                     u32 request, const char *op);
diff --git a/security/apparmor/policy.c b/security/apparmor/policy.c
index 67e63a31641a..cef057b98b27 100644
--- a/security/apparmor/policy.c
+++ b/security/apparmor/policy.c
@@ -1275,7 +1275,7 @@ ssize_t aa_replace_profiles(struct aa_ns *policy_ns, 
struct aa_label *label,
        struct aa_ns *ns = NULL;
        struct aa_load_ent *ent, *tmp;
        struct aa_loaddata *rawdata_ent;
-       struct aa_ns_caps pend_limits, pend_child;
+       struct aa_ns_capset pend_caps;
        const char *op;
        ssize_t count, error;
        LIST_HEAD(lh);
@@ -1341,9 +1341,8 @@ ssize_t aa_replace_profiles(struct aa_ns *policy_ns, 
struct aa_label *label,
        }
 
        mutex_lock_nested(&ns->lock, ns->level);
-       /* Tentative copies of the ns caps */
-       pend_limits = ns->acct.limits;
-       pend_child = ns->acct.child;
+       /* Tentative copy of the ns caps */
+       pend_caps = ns->acct.caps;
        /* check for duplicate rawdata blobs: space and file dedup */
        if (!list_empty(&ns->rawdata_list)) {
                list_for_each_entry(rawdata_ent, &ns->rawdata_list, list) {
@@ -1435,8 +1434,7 @@ ssize_t aa_replace_profiles(struct aa_ns *policy_ns, 
struct aa_label *label,
                        int b;
 
                        for (b = 0; b < ent->new->n_budgets; b++) {
-                               error = aa_ns_apply_budget(&pend_limits,
-                                                          &pend_child,
+                               error = aa_ns_apply_budget(&pend_caps,
                                                           
&ent->new->budgets[b]);
                                if (error) {
                                        info = "policyns limits: unsupported 
construct";
@@ -1460,7 +1458,8 @@ ssize_t aa_replace_profiles(struct aa_ns *policy_ns, 
struct aa_label *label,
         * Admission: check the whole load set against the tentative caps
         * before installing anything, so a breach rejects the whole set.
         */
-       error = aa_ns_admit_load_set(ns, &lh, &pend_limits, udata, &ent, &info);
+       error = aa_ns_admit_load_set(ns, &lh, &pend_caps.limits, udata, &ent,
+                                    &info);
        if (error)
                goto fail_lock;
 
@@ -1493,8 +1492,7 @@ ssize_t aa_replace_profiles(struct aa_ns *policy_ns, 
struct aa_label *label,
 
        /* Done with checks that may fail - do actual replacement */
        /* commit the caps the load was admitted against */
-       ns->acct.limits = pend_limits;
-       ns->acct.child = pend_child;
+       ns->acct.caps = pend_caps;
        __aa_bump_ns_revision(ns);
        if (aa_g_export_binary)
                __aa_loaddata_update(udata, ns->revision);
diff --git a/security/apparmor/policy_ns.c b/security/apparmor/policy_ns.c
index ee29ca54b7a8..fb67557548b8 100644
--- a/security/apparmor/policy_ns.c
+++ b/security/apparmor/policy_ns.c
@@ -206,8 +206,7 @@ void aa_ns_acct_init(struct aa_ns *ns)
 {
        struct aa_ns_acct *acct = &ns->acct;
 
-       aa_ns_caps_init_unset(&acct->limits);
-       aa_ns_caps_init_unset(&acct->child);
+       aa_ns_capset_init_unset(&acct->caps);
        atomic_long_set(&acct->resident, 0);
        atomic_long_set(&acct->profile_count, 0);
        atomic_long_set(&acct->ns_count, 0);
@@ -298,7 +297,7 @@ static int cap_admit_delta(struct aa_ns *ns, enum 
aa_policyns_key key,
  */
 int aa_ns_admit_create(struct aa_ns *parent)
 {
-       struct aa_ns_caps *pl = &parent->acct.limits;
+       struct aa_ns_caps *pl = &parent->acct.caps.limits;
        int error;
 
        if (!aa_g_policy_ns_quota)
@@ -519,9 +518,12 @@ void aa_ns_uncharge_profile(struct aa_profile *profile)
  * @dst: destination caps, treated as a long array in wire-key order
  * @b: parsed budget block
  * @tighten: cap_min() against the existing value (self) vs overwrite 
(children)
+ *
+ * Percentage values are stored raw and are resolved against the parent's
+ * cap when a child is created (avoiding load-order dependence).
  */
 static void apply_budget_keys(struct aa_ns_caps *dst, struct aa_ns_budget *b,
-                             bool tighten, const struct aa_ns_caps *pct_base)
+                             bool tighten)
 {
        long *cap = (long *)dst;
        int k;
@@ -531,23 +533,17 @@ static void apply_budget_keys(struct aa_ns_caps *dst, 
struct aa_ns_budget *b,
 
                if (!(b->specified & (1u << k)))
                        continue;
-               if (b->percent & (1u << k))
-                       /* N% of the parent's own cap for key @k */
-                       v = cap_percent(((const long *)pct_base)[k], 
b->values[k]);
-               else
-                       v = b->values[k];
+               v = b->values[k];
                cap[k] = tighten ? cap_min(cap[k], v) : v;
        }
 }
 
 /**
- * aa_ns_apply_budget - apply one parsed "policyns limits" block to a caps pair
- * @limits: the (tentative) self caps of the namespace the load targets
- * @child: the (tentative) children template of that namespace
+ * aa_ns_apply_budget - apply one parsed "policyns limits" block to a capset
+ * @caps: the (tentative) capset of the namespace the load targets
  * @b: one parsed budget block
  */
-int aa_ns_apply_budget(struct aa_ns_caps *limits, struct aa_ns_caps *child,
-                      struct aa_ns_budget *b)
+int aa_ns_apply_budget(struct aa_ns_capset *caps, struct aa_ns_budget *b)
 {
        /* Some features remains to be implemented and are rejected with 
-EOPNOTSUPP. */
        if (b->scope == AA_POLICYNS_SCOPE_SUBTREE)
@@ -560,38 +556,60 @@ int aa_ns_apply_budget(struct aa_ns_caps *limits, struct 
aa_ns_caps *child,
        case AA_POLICYNS_TGT_SELF:
                if (b->percent)         /* % is a per-child ratio only */
                        return -EOPNOTSUPP;
-               apply_budget_keys(limits, b, true, NULL);
+               apply_budget_keys(&caps->limits, b, true);
                return 0;
        case AA_POLICYNS_TGT_CHILDREN:
-               aa_ns_caps_init_unset(child);
-               apply_budget_keys(child, b, false, limits);
+               /* the block is the template: last children block wins */
+               aa_ns_caps_init_unset(&caps->child);
+               apply_budget_keys(&caps->child, b, false);
+               caps->child_percent = b->percent;
                return 0;
        default:        /* descendants/root/:NAME: not yet enforced */
                return -EOPNOTSUPP;
        }
 }
 
+/* resolve_percent_caps - resolve raw percentage keys against @base's caps */
+static void resolve_percent_caps(struct aa_ns_caps *caps, u32 percent,
+                                const struct aa_ns_caps *base)
+{
+       long *cap = (long *)caps;
+       const long *b = (const long *)base;
+       int k;
+
+       for (k = 0; k < AA_POLICYNS_KEY_MAX; k++)
+               if (percent & (1u << k))
+                       cap[k] = cap_percent(b[k], cap[k]);
+}
+
 /* inherit_child_caps - compute a new child's caps from @parent's template */
 static void inherit_child_caps(struct aa_ns *child, struct aa_ns *parent)
 {
-       struct aa_ns_caps *t = &parent->acct.child;
-       struct aa_ns_caps *pl = &parent->acct.limits;
-       struct aa_ns_caps *cl = &child->acct.limits;
+       struct aa_ns_caps *pl = &parent->acct.caps.limits;
+       struct aa_ns_caps *cl = &child->acct.caps.limits;
        struct aa_ns_acct *pa = &parent->acct;
+       struct aa_ns_caps t = parent->acct.caps.child;
+
+       /*
+        * Resolve percentage keys against the parent's cap in a local copy of
+        * the template so every child gets the ratio of the parent's caps as
+        * they stand at its creation.
+        */
+       resolve_percent_caps(&t, parent->acct.caps.child_percent, pl);
 
-       cl->memory = cap_min(t->memory,
+       cl->memory = cap_min(t.memory,
                             cap_remaining(pl->memory,
                                           atomic_long_read(&pa->resident)));
-       cl->profiles = cap_min(t->profiles,
+       cl->profiles = cap_min(t.profiles,
                               cap_remaining(pl->profiles,
                                             
atomic_long_read(&pa->profile_count)));
-       cl->namespaces = cap_min(t->namespaces,
+       cl->namespaces = cap_min(t.namespaces,
                                 cap_remaining(pl->namespaces,
                                               
atomic_long_read(&pa->ns_count)));
-       cl->max_profile = cap_min(t->max_profile, pl->max_profile);
-       cl->criu = cap_min(t->criu, pl->criu);
-       cl->load_rate = cap_min(t->load_rate, pl->load_rate);
-       cl->depth = cap_min(t->depth, cap_dec(pl->depth));
+       cl->max_profile = cap_min(t.max_profile, pl->max_profile);
+       cl->criu = cap_min(t.criu, pl->criu);
+       cl->load_rate = cap_min(t.load_rate, pl->load_rate);
+       cl->depth = cap_min(t.depth, cap_dec(pl->depth));
 }
 
 /*
-- 
2.51.0


Reply via email to