Add helpers that compute the resident (standing) byte footprint of loaded
policy.

A profile's policy is immutable after load so the value, computed at
profile construction, is stable.

Signed-off-by: Maxime Bélair <[email protected]>
---
 security/apparmor/include/lib.h           |  1 +
 security/apparmor/include/match.h         |  1 +
 security/apparmor/include/policy.h        |  6 ++
 security/apparmor/include/policy_unpack.h | 10 +++
 security/apparmor/lib.c                   | 21 ++++++
 security/apparmor/match.c                 | 25 +++++++
 security/apparmor/policy.c                | 80 +++++++++++++++++++++++
 security/apparmor/policy_unpack.c         |  6 ++
 8 files changed, 150 insertions(+)

diff --git a/security/apparmor/include/lib.h b/security/apparmor/include/lib.h
index e3c8cb044a90..60be49ba3c00 100644
--- a/security/apparmor/include/lib.h
+++ b/security/apparmor/include/lib.h
@@ -161,6 +161,7 @@ struct aa_str_table {
 
 bool aa_resize_str_table(struct aa_str_table *t, int newsize, gfp_t gfp);
 void aa_destroy_str_table(struct aa_str_table *table);
+size_t aa_str_table_size(struct aa_str_table *table);
 
 struct counted_str {
        struct kref count;
diff --git a/security/apparmor/include/match.h 
b/security/apparmor/include/match.h
index 7accb1c39849..2e9e280dee84 100644
--- a/security/apparmor/include/match.h
+++ b/security/apparmor/include/match.h
@@ -138,6 +138,7 @@ aa_state_t aa_dfa_matchn_until(struct aa_dfa *dfa, 
aa_state_t start,
                               const char *str, int n, const char **retpos);
 
 void aa_dfa_free_kref(struct kref *kref);
+size_t aa_dfa_size(struct aa_dfa *dfa);
 
 /* This needs to be a power of 2 */
 #define WB_HISTORY_SIZE 32
diff --git a/security/apparmor/include/policy.h 
b/security/apparmor/include/policy.h
index 8fc515209ede..8beac1f9d6dc 100644
--- a/security/apparmor/include/policy.h
+++ b/security/apparmor/include/policy.h
@@ -273,6 +273,9 @@ struct aa_profile {
        struct rhashtable *data;
 
        int n_rules;
+
+       long resident_size;
+
        /* special - variable length must be last entry in profile */
        struct aa_label label;
 };
@@ -287,6 +290,9 @@ extern enum profile_mode aa_g_profile_mode;
 #define name_is_shared(A, B) ((A)->hname && (A)->hname == (B)->hname)
 
 struct aa_ruleset *aa_alloc_ruleset(gfp_t gfp);
+size_t aa_pdb_size(struct aa_policydb *pdb);
+size_t aa_ruleset_resident_size(struct aa_ruleset *rules);
+size_t aa_profile_resident_size(struct aa_profile *profile);
 struct aa_profile *aa_alloc_profile(const char *name, struct aa_proxy *proxy,
                                    gfp_t gfp);
 struct aa_profile *aa_alloc_null(struct aa_profile *parent, const char *name,
diff --git a/security/apparmor/include/policy_unpack.h 
b/security/apparmor/include/policy_unpack.h
index c01f6885dbe3..10e16560ed90 100644
--- a/security/apparmor/include/policy_unpack.h
+++ b/security/apparmor/include/policy_unpack.h
@@ -129,6 +129,16 @@ struct aa_loaddata {
        char *data;
 };
 
+/*
+ * Resident byte footprint of a retained rawdata blob, charged to the owning
+ * ns while the blob is on ns->rawdata_list.
+ */
+static inline size_t aa_loaddata_resident_size(struct aa_loaddata *data)
+{
+       return sizeof(*data) +
+              (data->compressed_size ? data->compressed_size : data->size);
+}
+
 int aa_unpack(struct aa_loaddata *udata, struct list_head *lh, const char **ns,
              char *compressed_data, size_t compressed_size);
 
diff --git a/security/apparmor/lib.c b/security/apparmor/lib.c
index e41ff57798b2..7d660779520a 100644
--- a/security/apparmor/lib.c
+++ b/security/apparmor/lib.c
@@ -161,6 +161,27 @@ void aa_destroy_str_table(struct aa_str_table *t)
        }
 }
 
+/**
+ * aa_str_table_size - resident byte footprint of a string table
+ * @t: the string table to measure  (MAYBE NULL)
+ *
+ * Returns: bytes resident for @t
+ */
+size_t aa_str_table_size(struct aa_str_table *t)
+{
+       size_t size;
+       int i;
+
+       if (!t || !t->table)
+               return 0;
+
+       size = (size_t)t->size * sizeof(struct aa_str_table_ent);
+       for (i = 0; i < t->size; i++)
+               size += t->table[i].size;
+
+       return size;
+}
+
 /**
  * skipn_spaces - Removes leading whitespace from @str.
  * @str: The string to be stripped.
diff --git a/security/apparmor/match.c b/security/apparmor/match.c
index d43ff34d705c..1310fc6fd838 100644
--- a/security/apparmor/match.c
+++ b/security/apparmor/match.c
@@ -264,6 +264,31 @@ void aa_dfa_free_kref(struct kref *kref)
        dfa_free(dfa);
 }
 
+/**
+ * aa_dfa_size - compute the resident byte footprint of a dfa
+ * @dfa: dfa to measure (MAYBE NULL)
+ *
+ * Returns: bytes resident for @dfa
+ */
+size_t aa_dfa_size(struct aa_dfa *dfa)
+{
+       size_t size;
+       int i;
+
+       if (!dfa)
+               return 0;
+
+       size = sizeof(*dfa);
+       for (i = 0; i < ARRAY_SIZE(dfa->tables); i++) {
+               struct table_header *t = dfa->tables[i];
+
+               if (t)
+                       size += table_size(t->td_lolen, t->td_flags);
+       }
+
+       return size;
+}
+
 
 
 /**
diff --git a/security/apparmor/policy.c b/security/apparmor/policy.c
index e9236e538b98..23dfb62e76b8 100644
--- a/security/apparmor/policy.c
+++ b/security/apparmor/policy.c
@@ -142,6 +142,84 @@ struct aa_policydb *aa_alloc_pdb(gfp_t gfp)
        return pdb;
 }
 
+/**
+ * aa_pdb_size - resident byte footprint of a policydb
+ * @pdb: policydb to measure  (MAYBE NULL)
+ *
+ * Returns: bytes resident for @pdb
+ */
+size_t aa_pdb_size(struct aa_policydb *pdb)
+{
+       size_t size;
+
+       if (!pdb || pdb == nullpdb)
+               return 0;
+
+       size = sizeof(*pdb);
+       size += aa_dfa_size(pdb->dfa);
+       size += (size_t)pdb->size * sizeof(struct aa_perms);
+       size += aa_str_table_size(&pdb->trans);
+       size += (size_t)pdb->tags.hdrs.size * sizeof(struct aa_tags_header);
+       size += (size_t)pdb->tags.sets.size * sizeof(u32);
+       size += aa_str_table_size(&pdb->tags.strs);
+
+       return size;
+}
+
+/**
+ * aa_ruleset_resident_size - resident byte footprint of a ruleset
+ * @rules: ruleset to measure  (MAYBE NULL)
+ *
+ * Returns: bytes resident for @rules
+ */
+size_t aa_ruleset_resident_size(struct aa_ruleset *rules)
+{
+       size_t size;
+       int i;
+
+       if (!rules)
+               return 0;
+
+       size = sizeof(*rules);
+       size += aa_pdb_size(rules->policy);
+       /* file aliases policy when file rules are embedded in the policydb */
+       if (rules->file != rules->policy)
+               size += aa_pdb_size(rules->file);
+       size += (size_t)rules->secmark_count * sizeof(struct aa_secmark);
+       for (i = 0; i < rules->secmark_count; i++) {
+               if (rules->secmark[i].label)
+                       size += strlen(rules->secmark[i].label) + 1;
+       }
+
+       return size;
+}
+
+/**
+ * aa_profile_resident_size - resident byte footprint of a profile's policy
+ * @profile: profile to measure  (MAYBE NULL)
+ *
+ * Returns: the standing (resident) byte cost of @profile's loaded policy
+ */
+size_t aa_profile_resident_size(struct aa_profile *profile)
+{
+       size_t size = 0;
+       int i;
+
+       if (!profile)
+               return 0;
+
+       for (i = 0; i < profile->n_rules; i++)
+               size += aa_ruleset_resident_size(profile->label.rules[i]);
+
+       size += aa_pdb_size(profile->attach.xmatch);
+       for (i = 0; i < profile->attach.xattr_count; i++) {
+               if (profile->attach.xattrs[i])
+                       size += strlen(profile->attach.xattrs[i]) + 1;
+       }
+
+       return size;
+}
+
 
 /**
  * __add_profile - add a profiles to list and label tree
@@ -706,6 +784,8 @@ struct aa_profile *aa_alloc_null(struct aa_profile *parent, 
const char *name,
        rules->file = aa_get_pdb(nullpdb);
        rules->policy = aa_get_pdb(nullpdb);
        aa_compute_profile_mediates(profile);
+       /* resident_size invariant: set at construction (as in unpack_profile) 
*/
+       profile->resident_size = (long)aa_profile_resident_size(profile);
 
        if (parent) {
                profile->path_flags = parent->path_flags;
diff --git a/security/apparmor/policy_unpack.c 
b/security/apparmor/policy_unpack.c
index 5969d78f16af..47360d4c5eb1 100644
--- a/security/apparmor/policy_unpack.c
+++ b/security/apparmor/policy_unpack.c
@@ -1405,6 +1405,12 @@ static struct aa_profile *unpack_profile(struct aa_ext 
*e, char **ns_name)
 
        aa_compute_profile_mediates(profile);
 
+       /*
+        * Precompute the resident byte footprint once here, to reuse it on load
+        * avoiding re-walking the whole profile.
+        */
+       profile->resident_size = (long)aa_profile_resident_size(profile);
+
        return profile;
 
 fail:
-- 
2.51.0


Reply via email to