Maintain per-namespace running totals of the resident bytes and non-null profile count of the namespace plus all its descendants, walking the parent chain at the existing charge/uncharge sites.
Expose them as read-only .subtree_size and .subtree_count. Signed-off-by: Maxime Bélair <[email protected]> --- security/apparmor/apparmorfs.c | 7 +++++ security/apparmor/include/apparmorfs.h | 2 ++ security/apparmor/include/policy_ns.h | 4 +++ security/apparmor/policy_ns.c | 38 +++++++++++++++++++++++--- 4 files changed, 47 insertions(+), 4 deletions(-) diff --git a/security/apparmor/apparmorfs.c b/security/apparmor/apparmorfs.c index 98029e7f9d29..d5aa7a23e4ab 100644 --- a/security/apparmor/apparmorfs.c +++ b/security/apparmor/apparmorfs.c @@ -1494,6 +1494,9 @@ 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_subtree_count, + atomic_long_read(&ns->acct.subtree_profile_count)); +SEQ_NS_ACCT(acct_subtree_size, atomic_long_read(&ns->acct.subtree_resident)); 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); @@ -2262,6 +2265,10 @@ static const struct aa_ns_acct_file { { ".max_count", &seq_ns_acct_max_count_fops, AAFS_NS_MAX_COUNT }, { ".size", &seq_ns_acct_size_fops, AAFS_NS_SIZE }, { ".max_size", &seq_ns_acct_max_size_fops, AAFS_NS_MAX_SIZE }, + { ".subtree_count", &seq_ns_acct_subtree_count_fops, + AAFS_NS_SUBTREE_COUNT }, + { ".subtree_size", &seq_ns_acct_subtree_size_fops, + AAFS_NS_SUBTREE_SIZE }, { ".max_profile", &seq_ns_acct_max_profile_fops, AAFS_NS_MAX_PROFILE }, { ".namespaces", &seq_ns_acct_namespaces_fops, AAFS_NS_NAMESPACES }, { ".depth", &seq_ns_acct_depth_fops, AAFS_NS_DEPTH }, diff --git a/security/apparmor/include/apparmorfs.h b/security/apparmor/include/apparmorfs.h index faf37b48df72..b8e198b529b4 100644 --- a/security/apparmor/include/apparmorfs.h +++ b/security/apparmor/include/apparmorfs.h @@ -83,6 +83,8 @@ enum aafs_ns_type { AAFS_NS_NAMESPACES, AAFS_NS_DEPTH, AAFS_NS_CRIU, + 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 dce753feb91f..7398e9ab7f1e 100644 --- a/security/apparmor/include/policy_ns.h +++ b/security/apparmor/include/policy_ns.h @@ -51,6 +51,8 @@ static inline void aa_ns_capset_init_unset(struct aa_ns_capset *caps) * @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 + * @subtree_resident: resident policy bytes of this ns plus all descendants + * @subtree_profile_count: non-null profiles of this ns plus all descendants * @ratelimit: bounds OP_NS_QUOTA audit emission */ struct aa_ns_acct { @@ -58,6 +60,8 @@ struct aa_ns_acct { atomic_long_t resident; atomic_long_t profile_count; atomic_long_t ns_count; + atomic_long_t subtree_resident; + atomic_long_t subtree_profile_count; struct ratelimit_state ratelimit; }; diff --git a/security/apparmor/policy_ns.c b/security/apparmor/policy_ns.c index fb67557548b8..38234a21c570 100644 --- a/security/apparmor/policy_ns.c +++ b/security/apparmor/policy_ns.c @@ -210,6 +210,8 @@ void aa_ns_acct_init(struct aa_ns *ns) atomic_long_set(&acct->resident, 0); atomic_long_set(&acct->profile_count, 0); atomic_long_set(&acct->ns_count, 0); + atomic_long_set(&acct->subtree_resident, 0); + atomic_long_set(&acct->subtree_profile_count, 0); ratelimit_state_init(&acct->ratelimit, AA_NS_QUOTA_RATELIMIT_INTERVAL, AA_NS_QUOTA_RATELIMIT_BURST); @@ -449,6 +451,22 @@ int aa_ns_admit_load_set(struct aa_ns *ns, struct list_head *lh, return 0; } +/* + * acct_rollup - add a usage delta to @ns's and every ancestor's subtree totals + * @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) + * + * The parent chain is stable for the life of @ns. + */ +static void acct_rollup(struct aa_ns *ns, long bytes, long profiles) +{ + for (; ns; ns = ns->parent) { + atomic_long_add(bytes, &ns->acct.subtree_resident); + atomic_long_add(profiles, &ns->acct.subtree_profile_count); + } +} + /** * aa_ns_charge_profile - charge a profile's resident policy to its ns * @profile: the profile being made live (NOT NULL) @@ -458,6 +476,7 @@ int aa_ns_admit_load_set(struct aa_ns *ns, struct list_head *lh, void aa_ns_charge_profile(struct aa_profile *profile) { struct aa_ns *ns = profile->ns; + bool counted; long bytes; if (!ns || profile->acct_resident) @@ -465,9 +484,11 @@ void aa_ns_charge_profile(struct aa_profile *profile) bytes = profile->resident_size; profile->acct_resident = bytes; + counted = !(profile->label.flags & FLAG_NULL); atomic_long_add(bytes, &ns->acct.resident); - if (!(profile->label.flags & FLAG_NULL)) + if (counted) atomic_long_inc(&ns->acct.profile_count); + acct_rollup(ns, bytes, counted ? 1 : 0); } /** @@ -479,7 +500,10 @@ void aa_ns_charge_profile(struct aa_profile *profile) */ void aa_ns_charge_rawdata(struct aa_ns *ns, struct aa_loaddata *data) { - atomic_long_add(aa_loaddata_resident_size(data), &ns->acct.resident); + long bytes = aa_loaddata_resident_size(data); + + atomic_long_add(bytes, &ns->acct.resident); + acct_rollup(ns, bytes, 0); } /** @@ -491,7 +515,10 @@ void aa_ns_charge_rawdata(struct aa_ns *ns, struct aa_loaddata *data) */ void aa_ns_uncharge_rawdata(struct aa_ns *ns, struct aa_loaddata *data) { - atomic_long_sub(aa_loaddata_resident_size(data), &ns->acct.resident); + long bytes = aa_loaddata_resident_size(data); + + atomic_long_sub(bytes, &ns->acct.resident); + acct_rollup(ns, -bytes, 0); } /** @@ -503,13 +530,16 @@ void aa_ns_uncharge_rawdata(struct aa_ns *ns, struct aa_loaddata *data) void aa_ns_uncharge_profile(struct aa_profile *profile) { struct aa_ns *ns = profile->ns; + bool counted; if (!ns || !profile->acct_resident) return; + counted = !(profile->label.flags & FLAG_NULL); atomic_long_sub(profile->acct_resident, &ns->acct.resident); - if (!(profile->label.flags & FLAG_NULL)) + if (counted) atomic_long_dec(&ns->acct.profile_count); + acct_rollup(ns, -profile->acct_resident, counted ? -1 : 0); profile->acct_resident = 0; } -- 2.51.0
