The criu key caps the raw policy blobs a namespace retains for
checkpoint/restore.

Expose the retained bytes as .criu_size.

Signed-off-by: Maxime Bélair <[email protected]>
---
 security/apparmor/apparmorfs.c         |  4 ++-
 security/apparmor/include/apparmorfs.h |  1 +
 security/apparmor/include/policy_ns.h  |  4 +++
 security/apparmor/policy_ns.c          | 44 +++++++++++++++++++-------
 4 files changed, 40 insertions(+), 13 deletions(-)

diff --git a/security/apparmor/apparmorfs.c b/security/apparmor/apparmorfs.c
index 3ed00f5b30c4..235edbe33dcb 100644
--- a/security/apparmor/apparmorfs.c
+++ b/security/apparmor/apparmorfs.c
@@ -1503,6 +1503,7 @@ 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);
+SEQ_NS_ACCT(acct_criu_size, atomic_long_read(&ns->acct.criu_resident));
 
 
 /* policy/raw_data/ * file ops */
@@ -2273,6 +2274,7 @@ static const struct aa_ns_acct_file {
        { ".namespaces",  &seq_ns_acct_namespaces_fops, AAFS_NS_NAMESPACES },
        { ".depth",       &seq_ns_acct_depth_fops,      AAFS_NS_DEPTH },
        { ".criu",        &seq_ns_acct_criu_fops,       AAFS_NS_CRIU },
+       { ".criu_size",   &seq_ns_acct_criu_size_fops,  AAFS_NS_CRIU_SIZE },
 };
 
 static int __aafs_ns_mkdir_entries(struct aa_ns *ns, struct dentry *dir)
@@ -2697,7 +2699,7 @@ static struct aa_sfs_entry aa_sfs_entry_ns_quota[] = {
         * (subtree, criu, load_rate, descendants, root, name, mediation).
         */
        AA_SFS_FILE_STRING("mask",
-                          "self children percent local subtree mediation"),
+                          "self children percent local subtree criu 
mediation"),
        { }
 };
 
diff --git a/security/apparmor/include/apparmorfs.h 
b/security/apparmor/include/apparmorfs.h
index b8e198b529b4..4b7cd92fe350 100644
--- a/security/apparmor/include/apparmorfs.h
+++ b/security/apparmor/include/apparmorfs.h
@@ -83,6 +83,7 @@ enum aafs_ns_type {
        AAFS_NS_NAMESPACES,
        AAFS_NS_DEPTH,
        AAFS_NS_CRIU,
+       AAFS_NS_CRIU_SIZE,
        AAFS_NS_SUBTREE_COUNT,
        AAFS_NS_SUBTREE_SIZE,
        AAFS_NS_SIZEOF,
diff --git a/security/apparmor/include/policy_ns.h 
b/security/apparmor/include/policy_ns.h
index 9c5af84ea023..ed6be9f9b3fa 100644
--- a/security/apparmor/include/policy_ns.h
+++ b/security/apparmor/include/policy_ns.h
@@ -62,6 +62,8 @@ static inline void aa_ns_capset_init_unset(struct 
aa_ns_capset *caps)
  * @ns_count: current number of direct child namespaces
  * @subtree_resident: resident policy bytes of this ns plus all descendants
  * @subtree_profile_count: non-null profiles of this ns plus all descendants
+ * @criu_resident: retained raw policy bytes charged to this ns (local)
+ * @subtree_criu: retained raw policy bytes of this ns plus all descendants
  * @ratelimit: bounds OP_NS_QUOTA audit emission
  */
 struct aa_ns_acct {
@@ -71,6 +73,8 @@ struct aa_ns_acct {
        atomic_long_t ns_count;
        atomic_long_t subtree_resident;
        atomic_long_t subtree_profile_count;
+       atomic_long_t criu_resident;
+       atomic_long_t subtree_criu;
        struct ratelimit_state ratelimit;
 };
 
diff --git a/security/apparmor/policy_ns.c b/security/apparmor/policy_ns.c
index 225384273209..0643c8319627 100644
--- a/security/apparmor/policy_ns.c
+++ b/security/apparmor/policy_ns.c
@@ -218,6 +218,8 @@ void aa_ns_acct_init(struct aa_ns *ns)
        atomic_long_set(&acct->ns_count, 0);
        atomic_long_set(&acct->subtree_resident, 0);
        atomic_long_set(&acct->subtree_profile_count, 0);
+       atomic_long_set(&acct->criu_resident, 0);
+       atomic_long_set(&acct->subtree_criu, 0);
        ratelimit_state_init(&acct->ratelimit,
                             AA_NS_QUOTA_RATELIMIT_INTERVAL,
                             AA_NS_QUOTA_RATELIMIT_BURST);
@@ -409,7 +411,7 @@ static long effective_max_profile(struct aa_ns *ns, struct 
aa_ns_capset *pend,
  * of these caps is set.
  */
 static int admit_subtree_agg(struct aa_ns *ns, struct aa_ns_capset *pend,
-                            long bytes, long profiles)
+                            long bytes, long profiles, long criu)
 {
        struct aa_ns *a;
        int error;
@@ -429,6 +431,10 @@ static int admit_subtree_agg(struct aa_ns *ns, struct 
aa_ns_capset *pend,
                                        profiles, -EDQUOT);
                if (error)
                        return error;
+               error = cap_admit_delta(a, AA_POLICYNS_KEY_CRIU, sc->criu,
+                                       &a->acct.subtree_criu, criu, -ENOSPC);
+               if (error)
+                       return error;
        }
        return 0;
 }
@@ -489,6 +495,7 @@ int aa_ns_admit_load_set(struct aa_ns *ns, struct list_head 
*lh,
 {
        long new_bytes = 0, old_bytes = 0;
        long new_count = 0, old_count = 0;
+       long raw_bytes = 0;
        struct aa_load_ent *ent;
        struct aa_ns *mp_owner;
        long mp_limit;
@@ -527,10 +534,13 @@ int aa_ns_admit_load_set(struct aa_ns *ns, struct 
list_head *lh,
                }
        }
        /* a newly retained rawdata blob is charged too (same condition as
-        * the __aa_fs_create_rawdata call in aa_replace_profiles)
+        * the __aa_fs_create_rawdata call in aa_replace_profiles); it is
+        * also what the criu reserve caps
         */
-       if (!udata->dents[AAFS_LOADDATA_DIR] && aa_g_export_binary)
-               new_bytes += aa_loaddata_resident_size(udata);
+       if (!udata->dents[AAFS_LOADDATA_DIR] && aa_g_export_binary) {
+               raw_bytes = aa_loaddata_resident_size(udata);
+               new_bytes += raw_bytes;
+       }
 
        error = aa_ns_admit_resident(ns, &pend->limits, new_bytes - old_bytes);
        if (error) {
@@ -544,8 +554,15 @@ int aa_ns_admit_load_set(struct aa_ns *ns, struct 
list_head *lh,
                *info = "namespace profile cap exceeded";
                return error;
        }
+       error = cap_admit_delta(ns, AA_POLICYNS_KEY_CRIU, pend->limits.criu,
+                               &ns->acct.criu_resident, raw_bytes, -ENOSPC);
+       if (error) {
+               *fail_ent = NULL;
+               *info = "namespace criu reserve exceeded";
+               return error;
+       }
        error = admit_subtree_agg(ns, pend, new_bytes - old_bytes,
-                                 new_count - old_count);
+                                 new_count - old_count, raw_bytes);
        if (error) {
                *fail_ent = NULL;
                *info = "subtree cap exceeded";
@@ -559,14 +576,16 @@ int aa_ns_admit_load_set(struct aa_ns *ns, struct 
list_head *lh,
  * @ns: the namespace the delta was charged to  (NOT NULL)
  * @bytes: resident byte delta (may be negative)
  * @profiles: non-null profile count delta (may be negative)
+ * @criu: retained raw policy byte delta (may be negative)
  *
  * The parent chain is stable for the life of @ns.
  */
-static void acct_rollup(struct aa_ns *ns, long bytes, long profiles)
+static void acct_rollup(struct aa_ns *ns, long bytes, long profiles, long criu)
 {
        for (; ns; ns = ns->parent) {
                atomic_long_add(bytes, &ns->acct.subtree_resident);
                atomic_long_add(profiles, &ns->acct.subtree_profile_count);
+               atomic_long_add(criu, &ns->acct.subtree_criu);
        }
 }
 
@@ -591,7 +610,7 @@ void aa_ns_charge_profile(struct aa_profile *profile)
        atomic_long_add(bytes, &ns->acct.resident);
        if (counted)
                atomic_long_inc(&ns->acct.profile_count);
-       acct_rollup(ns, bytes, counted ? 1 : 0);
+       acct_rollup(ns, bytes, counted ? 1 : 0, 0);
 }
 
 /**
@@ -606,7 +625,8 @@ void aa_ns_charge_rawdata(struct aa_ns *ns, struct 
aa_loaddata *data)
        long bytes = aa_loaddata_resident_size(data);
 
        atomic_long_add(bytes, &ns->acct.resident);
-       acct_rollup(ns, bytes, 0);
+       atomic_long_add(bytes, &ns->acct.criu_resident);
+       acct_rollup(ns, bytes, 0, bytes);
 }
 
 /**
@@ -621,7 +641,8 @@ void aa_ns_uncharge_rawdata(struct aa_ns *ns, struct 
aa_loaddata *data)
        long bytes = aa_loaddata_resident_size(data);
 
        atomic_long_sub(bytes, &ns->acct.resident);
-       acct_rollup(ns, -bytes, 0);
+       atomic_long_sub(bytes, &ns->acct.criu_resident);
+       acct_rollup(ns, -bytes, 0, -bytes);
 }
 
 /**
@@ -642,7 +663,7 @@ void aa_ns_uncharge_profile(struct aa_profile *profile)
        atomic_long_sub(profile->acct_resident, &ns->acct.resident);
        if (counted)
                atomic_long_dec(&ns->acct.profile_count);
-       acct_rollup(ns, -profile->acct_resident, counted ? -1 : 0);
+       acct_rollup(ns, -profile->acct_resident, counted ? -1 : 0, 0);
        profile->acct_resident = 0;
 }
 
@@ -681,8 +702,7 @@ int aa_ns_apply_budget(struct aa_ns_capset *caps, struct 
aa_ns_budget *b)
        bool subtree = b->scope == AA_POLICYNS_SCOPE_SUBTREE;
 
        /* Some features remains to be implemented and are rejected with 
-EOPNOTSUPP. */
-       if (b->specified & ((1u << AA_POLICYNS_KEY_CRIU) |
-                           (1u << AA_POLICYNS_KEY_LOAD_RATE)))
+       if (b->specified & (1u << AA_POLICYNS_KEY_LOAD_RATE))
                return -EOPNOTSUPP;
        /* the parser rejects subtree scope on the other keys at parse time */
        if (subtree && (b->specified & ~AA_POLICYNS_SUBTREE_KEYS))
-- 
2.51.0


Reply via email to