diff --git a/security/apparmor/audit.c b/security/apparmor/audit.c index 7bf7ae252db6..8b514d83c2bf 100644 --- a/security/apparmor/audit.c +++ b/security/apparmor/audit.c @@ -207,6 +207,7 @@ struct aa_audit_rule { }; /****************************** audit rules *******************************/ + void aa_audit_rule_free(void *vrule, int lsmid) { struct aa_audit_rule *rule = vrule; @@ -350,7 +351,7 @@ long aa_audit_data_cmp(struct apparmor_audit_data *lhs, return 0; } -void aa_audit_node_free(struct aa_audit_node *node) +static void audit_node_free(struct aa_audit_node *node) { if (!node) return; @@ -373,6 +374,13 @@ void aa_audit_node_free(struct aa_audit_node *node) kmem_cache_free(aa_audit_slab, node); } +void aa_audit_node_free_kref(struct kref *kref) +{ + struct aa_audit_node *node = container_of(kref, struct aa_audit_node, + count); + audit_node_free(node); +} + struct aa_audit_node *aa_dup_audit_data(struct apparmor_audit_data *orig, gfp_t gfp) { @@ -381,6 +389,7 @@ struct aa_audit_node *aa_dup_audit_data(struct apparmor_audit_data *orig, copy = kmem_cache_zalloc(aa_audit_slab, gfp); if (!copy) return NULL; + kref_init(©->count); copy->knotif.ad = ©->data; INIT_LIST_HEAD(©->list); @@ -457,7 +466,7 @@ struct aa_audit_node *aa_dup_audit_data(struct apparmor_audit_data *orig, return copy; fail: - aa_audit_node_free(copy); + audit_node_free(copy); return NULL; } @@ -473,6 +482,7 @@ __out_skip: \ __node; \ }) +// increments refcount on node struct aa_audit_node *aa_audit_cache_find(struct aa_audit_cache *cache, struct apparmor_audit_data *ad) { @@ -480,6 +490,7 @@ struct aa_audit_node *aa_audit_cache_find(struct aa_audit_cache *cache, rcu_read_lock(); node = __audit_cache_find(cache, ad); + aa_get_audit_node(node); rcu_read_unlock(); return node; @@ -490,9 +501,12 @@ struct aa_audit_node *aa_audit_cache_find(struct aa_audit_cache *cache, * @cache: the cache to insert into * @node: the audit node to insert into the cache * - * Returns: matching node in cache OR @node if @node was inserted. + * Returns: refcounted matching node in cache OR @node if @node was inserted. + * + * Increments refcount on node if successfully inserted. Assumes caller + * already has valid ref count. + * Increments refcount on existing node if returned */ - struct aa_audit_node *aa_audit_cache_insert(struct aa_audit_cache *cache, struct aa_audit_node *node) @@ -507,6 +521,8 @@ struct aa_audit_node *aa_audit_cache_insert(struct aa_audit_cache *cache, tmp = node; cache->size++; } + + aa_get_audit_node(tmp); /* else raced another insert */ spin_unlock(&cache->lock); @@ -531,7 +547,7 @@ void aa_audit_cache_destroy(struct aa_audit_cache *cache) list_for_each_entry_safe(node, tmp, &cache->head, list) { list_del_init(&node->list); - aa_audit_node_free(node); + aa_put_audit_node(node); } cache->size = 0; } diff --git a/security/apparmor/file.c b/security/apparmor/file.c index e4e7cf9fd418..5bec17a213a6 100644 --- a/security/apparmor/file.c +++ b/security/apparmor/file.c @@ -106,6 +106,7 @@ static int check_cache(struct aa_profile *profile, AA_DEBUG(DEBUG_UPCALL, "cache hit denied, request: 0x%x by cached deny 0x%x\n", ad->request, hit->data.denied); + aa_put_audit_node(hit); return ad->error; } else if (ad->request & ~hit->data.request) { /* asking for more perms than is cached */ @@ -116,9 +117,12 @@ static int check_cache(struct aa_profile *profile, } else { AA_DEBUG(DEBUG_UPCALL, "cache hit"); ad->error = 0; + aa_put_audit_node(hit); /* do audit */ return 0; } + aa_put_audit_node(hit); + hit = NULL; } else { AA_DEBUG(DEBUG_UPCALL, "cache miss"); } @@ -151,34 +155,31 @@ static int check_cache(struct aa_profile *profile, ad->error = node->data.error; if (cache_response) { - if (hit) { -hit: + /* TODO: shouldn't add until after auditing it, or at + * least having a refcount. Fix once removing entry is + * allowed + */ + AA_DEBUG(DEBUG_UPCALL, "inserting cache entry requ 0x%x denied 0x%x", + node->data.request, node->data.denied); + hit = aa_audit_cache_insert(&profile->learning_cache, + node); + AA_DEBUG(DEBUG_UPCALL, "cache insert %s: name %s node %s\n", + hit != node ? "lost race" : "", + hit->data.name, node->data.name); + if (hit != node) { AA_DEBUG(DEBUG_UPCALL, "updating existing cache entry"); aa_audit_cache_update_ent(&profile->learning_cache, hit, &node->data); + aa_put_audit_node(hit); } else { - /* TODO: shouldn't add until after auditing it, or at - * least having a refcount. Fix once removing entry is - * allowed - */ - AA_DEBUG(DEBUG_UPCALL, "inserting cache entry requ 0x%x denied 0x%x", - node->data.request, node->data.denied); - hit = aa_audit_cache_insert(&profile->learning_cache, - node); - AA_DEBUG(DEBUG_UPCALL, "cache insert %s: name %s node %s\n", - hit != node ? "lost race" : "", - hit->data.name, node->data.name); - if (hit != node) - goto hit; + AA_DEBUG(DEBUG_UPCALL, "inserted into cache"); - /* do not free node, it is now owned by the cache */ - node = NULL; } /* now to audit */ } /* cache_response */ return_to_audit: - aa_audit_node_free(node); + aa_put_audit_node(node); return 0; } diff --git a/security/apparmor/include/audit.h b/security/apparmor/include/audit.h index 588f2cafed01..a5a45c4a197e 100644 --- a/security/apparmor/include/audit.h +++ b/security/apparmor/include/audit.h @@ -174,6 +174,7 @@ struct apparmor_audit_data { }; struct aa_audit_node { + struct kref count; struct apparmor_audit_data data; struct list_head list; struct aa_knotif knotif; @@ -254,10 +255,26 @@ int aa_audit_rule_match(struct lsmblob *blob, u32 field, u32 op, void *vrule, int lsmid); -void aa_audit_node_free(struct aa_audit_node *node); +void aa_audit_node_free_kref(struct kref *kref); struct aa_audit_node *aa_dup_audit_data(struct apparmor_audit_data *orig, gfp_t gfp); long aa_audit_data_cmp(struct apparmor_audit_data *lhs, struct apparmor_audit_data *rhs); + +static inline struct aa_audit_node *aa_get_audit_node(struct aa_audit_node *node) +{ + if (node) + kref_get(&(node->count)); + + return node; +} + +static inline void aa_put_audit_node(struct aa_audit_node *node) +{ + if (node) + kref_put(&node->count, aa_audit_node_free_kref); +} + + #endif /* __AA_AUDIT_H */ diff --git a/security/apparmor/include/notify.h b/security/apparmor/include/notify.h index 40cd67589b99..938bb07be42f 100644 --- a/security/apparmor/include/notify.h +++ b/security/apparmor/include/notify.h @@ -91,5 +91,4 @@ static inline void aa_put_listener(struct aa_listener *listener) kref_put(&listener->count, aa_listener_kref); } - #endif /* __AA_NOTIFY_H */