Skip to content

Commit

Permalink
apparmor: convert profile lists to RCU based locking
Browse files Browse the repository at this point in the history
Signed-off-by: John Johansen <john.johansen@canonical.com>
  • Loading branch information
John Johansen committed Aug 14, 2013
1 parent dd51c84 commit 01e2b67
Show file tree
Hide file tree
Showing 4 changed files with 167 additions and 111 deletions.
14 changes: 10 additions & 4 deletions security/apparmor/domain.c
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,7 @@ static struct aa_profile *__attach_match(const char *name,
int len = 0;
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)
continue;
if (profile->xmatch && profile->xmatch_len > len) {
Expand Down Expand Up @@ -177,9 +177,9 @@ static struct aa_profile *find_attach(struct aa_namespace *ns,
{
struct aa_profile *profile;

read_lock(&ns->lock);
rcu_read_lock();
profile = aa_get_profile(__attach_match(name, list));
read_unlock(&ns->lock);
rcu_read_unlock();

return profile;
}
Expand Down Expand Up @@ -641,7 +641,10 @@ int aa_change_hat(const char *hats[], int count, u64 token, bool permtest)
if (count) {
/* attempting to change into a new hat or switch to a sibling */
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 */
for (i = 0; i < count && !hat; i++)
Expand All @@ -653,6 +656,7 @@ int aa_change_hat(const char *hats[], int count, u64 token, bool permtest)
error = -ECHILD;
else
error = -ENOENT;
aa_put_profile(root);
goto out;
}

Expand All @@ -667,6 +671,7 @@ int aa_change_hat(const char *hats[], int count, u64 token, bool permtest)

/* freed below */
name = new_compound_name(root->base.hname, hats[0]);
aa_put_profile(root);
target = name;
/* released below */
hat = aa_new_null_profile(profile, 1);
Expand All @@ -676,6 +681,7 @@ int aa_change_hat(const char *hats[], int count, u64 token, bool permtest)
goto audit;
}
} else {
aa_put_profile(root);
target = hat->base.hname;
if (!PROFILE_IS_HAT(hat)) {
info = "target not hat";
Expand Down
6 changes: 6 additions & 0 deletions security/apparmor/include/apparmor.h
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,12 @@ static inline void *kvzalloc(size_t size)
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
* @str: a null terminated string
Expand Down
45 changes: 42 additions & 3 deletions security/apparmor/include/policy.h
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,8 @@ extern const char *const profile_mode_names[];

#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
* set. It should be done at the namespace level.
Expand Down Expand Up @@ -75,6 +77,7 @@ struct aa_profile;
* @hname - The hierarchical name
* @count: reference count of the obj
* @list: list policy object is on
* @rcu: rcu head used when removing from @list
* @profiles: head of the profiles list contained in the object
*/
struct aa_policy {
Expand All @@ -83,6 +86,7 @@ struct aa_policy {
struct kref count;
struct list_head list;
struct list_head profiles;
struct rcu_head rcu;
};

/* struct aa_ns_acct - accounting of profiles in namespace
Expand Down Expand Up @@ -124,7 +128,7 @@ struct aa_ns_acct {
struct aa_namespace {
struct aa_policy base;
struct aa_namespace *parent;
rwlock_t lock;
struct mutex lock;
struct aa_ns_acct acct;
struct aa_profile *unconfined;
struct list_head sub_ns;
Expand Down Expand Up @@ -166,7 +170,7 @@ struct aa_policydb {
* attachments are determined by profile X transition rules.
*
* 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
* a reference to their parent.
Expand All @@ -177,7 +181,7 @@ struct aa_policydb {
*/
struct aa_profile {
struct aa_policy base;
struct aa_profile *parent;
struct aa_profile __rcu *parent;

struct aa_namespace *ns;
struct aa_profile *replacedby;
Expand Down Expand Up @@ -295,6 +299,41 @@ static inline struct aa_profile *aa_get_profile(struct aa_profile *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
* @p: profile (MAYBE NULL)
Expand Down
Loading

0 comments on commit 01e2b67

Please sign in to comment.