Commit 01e2b670 authored by John Johansen's avatar John Johansen

apparmor: convert profile lists to RCU based locking

Signed-off-by: default avatarJohn Johansen <john.johansen@canonical.com>
parent dd51c848
...@@ -144,7 +144,7 @@ static struct aa_profile *__attach_match(const char *name, ...@@ -144,7 +144,7 @@ static struct aa_profile *__attach_match(const char *name,
int len = 0; int len = 0;
struct aa_profile *profile, *candidate = NULL; struct aa_profile *profile, *candidate = NULL;
list_for_each_entry(profile, head, base.list) { list_for_each_entry_rcu(profile, head, base.list) {
if (profile->flags & PFLAG_NULL) if (profile->flags & PFLAG_NULL)
continue; continue;
if (profile->xmatch && profile->xmatch_len > len) { if (profile->xmatch && profile->xmatch_len > len) {
...@@ -177,9 +177,9 @@ static struct aa_profile *find_attach(struct aa_namespace *ns, ...@@ -177,9 +177,9 @@ static struct aa_profile *find_attach(struct aa_namespace *ns,
{ {
struct aa_profile *profile; struct aa_profile *profile;
read_lock(&ns->lock); rcu_read_lock();
profile = aa_get_profile(__attach_match(name, list)); profile = aa_get_profile(__attach_match(name, list));
read_unlock(&ns->lock); rcu_read_unlock();
return profile; return profile;
} }
...@@ -641,7 +641,10 @@ int aa_change_hat(const char *hats[], int count, u64 token, bool permtest) ...@@ -641,7 +641,10 @@ int aa_change_hat(const char *hats[], int count, u64 token, bool permtest)
if (count) { if (count) {
/* attempting to change into a new hat or switch to a sibling */ /* attempting to change into a new hat or switch to a sibling */
struct aa_profile *root; struct aa_profile *root;
root = PROFILE_IS_HAT(profile) ? profile->parent : profile; if (PROFILE_IS_HAT(profile))
root = aa_get_profile_rcu(&profile->parent);
else
root = aa_get_profile(profile);
/* find first matching hat */ /* find first matching hat */
for (i = 0; i < count && !hat; i++) for (i = 0; i < count && !hat; i++)
...@@ -653,6 +656,7 @@ int aa_change_hat(const char *hats[], int count, u64 token, bool permtest) ...@@ -653,6 +656,7 @@ int aa_change_hat(const char *hats[], int count, u64 token, bool permtest)
error = -ECHILD; error = -ECHILD;
else else
error = -ENOENT; error = -ENOENT;
aa_put_profile(root);
goto out; goto out;
} }
...@@ -667,6 +671,7 @@ int aa_change_hat(const char *hats[], int count, u64 token, bool permtest) ...@@ -667,6 +671,7 @@ int aa_change_hat(const char *hats[], int count, u64 token, bool permtest)
/* freed below */ /* freed below */
name = new_compound_name(root->base.hname, hats[0]); name = new_compound_name(root->base.hname, hats[0]);
aa_put_profile(root);
target = name; target = name;
/* released below */ /* released below */
hat = aa_new_null_profile(profile, 1); hat = aa_new_null_profile(profile, 1);
...@@ -676,6 +681,7 @@ int aa_change_hat(const char *hats[], int count, u64 token, bool permtest) ...@@ -676,6 +681,7 @@ int aa_change_hat(const char *hats[], int count, u64 token, bool permtest)
goto audit; goto audit;
} }
} else { } else {
aa_put_profile(root);
target = hat->base.hname; target = hat->base.hname;
if (!PROFILE_IS_HAT(hat)) { if (!PROFILE_IS_HAT(hat)) {
info = "target not hat"; info = "target not hat";
......
...@@ -78,6 +78,12 @@ static inline void *kvzalloc(size_t size) ...@@ -78,6 +78,12 @@ static inline void *kvzalloc(size_t size)
return __aa_kvmalloc(size, __GFP_ZERO); return __aa_kvmalloc(size, __GFP_ZERO);
} }
/* returns 0 if kref not incremented */
static inline int kref_get_not0(struct kref *kref)
{
return atomic_inc_not_zero(&kref->refcount);
}
/** /**
* aa_strneq - compare null terminated @str to a non null terminated substring * aa_strneq - compare null terminated @str to a non null terminated substring
* @str: a null terminated string * @str: a null terminated string
......
...@@ -42,6 +42,8 @@ extern const char *const profile_mode_names[]; ...@@ -42,6 +42,8 @@ extern const char *const profile_mode_names[];
#define PROFILE_IS_HAT(_profile) ((_profile)->flags & PFLAG_HAT) #define PROFILE_IS_HAT(_profile) ((_profile)->flags & PFLAG_HAT)
#define on_list_rcu(X) (!list_empty(X) && (X)->prev != LIST_POISON2)
/* /*
* FIXME: currently need a clean way to replace and remove profiles as a * FIXME: currently need a clean way to replace and remove profiles as a
* set. It should be done at the namespace level. * set. It should be done at the namespace level.
...@@ -75,6 +77,7 @@ struct aa_profile; ...@@ -75,6 +77,7 @@ struct aa_profile;
* @hname - The hierarchical name * @hname - The hierarchical name
* @count: reference count of the obj * @count: reference count of the obj
* @list: list policy object is on * @list: list policy object is on
* @rcu: rcu head used when removing from @list
* @profiles: head of the profiles list contained in the object * @profiles: head of the profiles list contained in the object
*/ */
struct aa_policy { struct aa_policy {
...@@ -83,6 +86,7 @@ struct aa_policy { ...@@ -83,6 +86,7 @@ struct aa_policy {
struct kref count; struct kref count;
struct list_head list; struct list_head list;
struct list_head profiles; struct list_head profiles;
struct rcu_head rcu;
}; };
/* struct aa_ns_acct - accounting of profiles in namespace /* struct aa_ns_acct - accounting of profiles in namespace
...@@ -124,7 +128,7 @@ struct aa_ns_acct { ...@@ -124,7 +128,7 @@ struct aa_ns_acct {
struct aa_namespace { struct aa_namespace {
struct aa_policy base; struct aa_policy base;
struct aa_namespace *parent; struct aa_namespace *parent;
rwlock_t lock; struct mutex lock;
struct aa_ns_acct acct; struct aa_ns_acct acct;
struct aa_profile *unconfined; struct aa_profile *unconfined;
struct list_head sub_ns; struct list_head sub_ns;
...@@ -166,7 +170,7 @@ struct aa_policydb { ...@@ -166,7 +170,7 @@ struct aa_policydb {
* attachments are determined by profile X transition rules. * attachments are determined by profile X transition rules.
* *
* The @replacedby field is write protected by the profile lock. Reads * The @replacedby field is write protected by the profile lock. Reads
* are assumed to be atomic, and are done without locking. * are assumed to be atomic.
* *
* Profiles have a hierarchy where hats and children profiles keep * Profiles have a hierarchy where hats and children profiles keep
* a reference to their parent. * a reference to their parent.
...@@ -177,7 +181,7 @@ struct aa_policydb { ...@@ -177,7 +181,7 @@ struct aa_policydb {
*/ */
struct aa_profile { struct aa_profile {
struct aa_policy base; struct aa_policy base;
struct aa_profile *parent; struct aa_profile __rcu *parent;
struct aa_namespace *ns; struct aa_namespace *ns;
struct aa_profile *replacedby; struct aa_profile *replacedby;
...@@ -295,6 +299,41 @@ static inline struct aa_profile *aa_get_profile(struct aa_profile *p) ...@@ -295,6 +299,41 @@ static inline struct aa_profile *aa_get_profile(struct aa_profile *p)
return p; return p;
} }
/**
* aa_get_profile_not0 - increment refcount on profile @p found via lookup
* @p: profile (MAYBE NULL)
*
* Returns: pointer to @p if @p is NULL will return NULL
* Requires: @p must be held with valid refcount when called
*/
static inline struct aa_profile *aa_get_profile_not0(struct aa_profile *p)
{
if (p && kref_get_not0(&p->base.count))
return p;
return NULL;
}
/**
* aa_get_profile_rcu - increment a refcount profile that can be replaced
* @p: pointer to profile that can be replaced (NOT NULL)
*
* Returns: pointer to a refcounted profile.
* else NULL if no profile
*/
static inline struct aa_profile *aa_get_profile_rcu(struct aa_profile __rcu **p)
{
struct aa_profile *c;
rcu_read_lock();
do {
c = rcu_dereference(*p);
} while (c && !kref_get_not0(&c->base.count));
rcu_read_unlock();
return c;
}
/** /**
* aa_put_profile - decrement refcount on profile @p * aa_put_profile - decrement refcount on profile @p
* @p: profile (MAYBE NULL) * @p: profile (MAYBE NULL)
......
This diff is collapsed.
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment