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
