aboutsummaryrefslogtreecommitdiffstats
path: root/security
diff options
context:
space:
mode:
authorJohn Johansen <john.johansen@canonical.com>2013-07-11 00:06:43 -0400
committerJohn Johansen <john.johansen@canonical.com>2013-08-14 14:42:06 -0400
commit01e2b670aa898a39259bc85c78e3d74820f4d3b6 (patch)
treecd78cea5f92788c213f2eb8ef287535ac1bb3327 /security
parentdd51c84857630e77c139afe4d9bba65fc051dc3f (diff)
apparmor: convert profile lists to RCU based locking
Signed-off-by: John Johansen <john.johansen@canonical.com>
Diffstat (limited to 'security')
-rw-r--r--security/apparmor/domain.c14
-rw-r--r--security/apparmor/include/apparmor.h6
-rw-r--r--security/apparmor/include/policy.h45
-rw-r--r--security/apparmor/policy.c213
4 files changed, 167 insertions, 111 deletions
diff --git a/security/apparmor/domain.c b/security/apparmor/domain.c
index 01b7bd669a88..454bcd7f3452 100644
--- a/security/apparmor/domain.c
+++ b/security/apparmor/domain.c
@@ -144,7 +144,7 @@ static struct aa_profile *__attach_match(const char *name,
144 int len = 0; 144 int len = 0;
145 struct aa_profile *profile, *candidate = NULL; 145 struct aa_profile *profile, *candidate = NULL;
146 146
147 list_for_each_entry(profile, head, base.list) { 147 list_for_each_entry_rcu(profile, head, base.list) {
148 if (profile->flags & PFLAG_NULL) 148 if (profile->flags & PFLAG_NULL)
149 continue; 149 continue;
150 if (profile->xmatch && profile->xmatch_len > len) { 150 if (profile->xmatch && profile->xmatch_len > len) {
@@ -177,9 +177,9 @@ static struct aa_profile *find_attach(struct aa_namespace *ns,
177{ 177{
178 struct aa_profile *profile; 178 struct aa_profile *profile;
179 179
180 read_lock(&ns->lock); 180 rcu_read_lock();
181 profile = aa_get_profile(__attach_match(name, list)); 181 profile = aa_get_profile(__attach_match(name, list));
182 read_unlock(&ns->lock); 182 rcu_read_unlock();
183 183
184 return profile; 184 return profile;
185} 185}
@@ -641,7 +641,10 @@ int aa_change_hat(const char *hats[], int count, u64 token, bool permtest)
641 if (count) { 641 if (count) {
642 /* attempting to change into a new hat or switch to a sibling */ 642 /* attempting to change into a new hat or switch to a sibling */
643 struct aa_profile *root; 643 struct aa_profile *root;
644 root = PROFILE_IS_HAT(profile) ? profile->parent : profile; 644 if (PROFILE_IS_HAT(profile))
645 root = aa_get_profile_rcu(&profile->parent);
646 else
647 root = aa_get_profile(profile);
645 648
646 /* find first matching hat */ 649 /* find first matching hat */
647 for (i = 0; i < count && !hat; i++) 650 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 error = -ECHILD; 656 error = -ECHILD;
654 else 657 else
655 error = -ENOENT; 658 error = -ENOENT;
659 aa_put_profile(root);
656 goto out; 660 goto out;
657 } 661 }
658 662
@@ -667,6 +671,7 @@ int aa_change_hat(const char *hats[], int count, u64 token, bool permtest)
667 671
668 /* freed below */ 672 /* freed below */
669 name = new_compound_name(root->base.hname, hats[0]); 673 name = new_compound_name(root->base.hname, hats[0]);
674 aa_put_profile(root);
670 target = name; 675 target = name;
671 /* released below */ 676 /* released below */
672 hat = aa_new_null_profile(profile, 1); 677 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 goto audit; 681 goto audit;
677 } 682 }
678 } else { 683 } else {
684 aa_put_profile(root);
679 target = hat->base.hname; 685 target = hat->base.hname;
680 if (!PROFILE_IS_HAT(hat)) { 686 if (!PROFILE_IS_HAT(hat)) {
681 info = "target not hat"; 687 info = "target not hat";
diff --git a/security/apparmor/include/apparmor.h b/security/apparmor/include/apparmor.h
index 1ba2ca56a6ef..8fb1488a3cd4 100644
--- a/security/apparmor/include/apparmor.h
+++ b/security/apparmor/include/apparmor.h
@@ -78,6 +78,12 @@ static inline void *kvzalloc(size_t size)
78 return __aa_kvmalloc(size, __GFP_ZERO); 78 return __aa_kvmalloc(size, __GFP_ZERO);
79} 79}
80 80
81/* returns 0 if kref not incremented */
82static inline int kref_get_not0(struct kref *kref)
83{
84 return atomic_inc_not_zero(&kref->refcount);
85}
86
81/** 87/**
82 * aa_strneq - compare null terminated @str to a non null terminated substring 88 * aa_strneq - compare null terminated @str to a non null terminated substring
83 * @str: a null terminated string 89 * @str: a null terminated string
diff --git a/security/apparmor/include/policy.h b/security/apparmor/include/policy.h
index b25491a3046a..82487a853353 100644
--- a/security/apparmor/include/policy.h
+++ b/security/apparmor/include/policy.h
@@ -42,6 +42,8 @@ extern const char *const profile_mode_names[];
42 42
43#define PROFILE_IS_HAT(_profile) ((_profile)->flags & PFLAG_HAT) 43#define PROFILE_IS_HAT(_profile) ((_profile)->flags & PFLAG_HAT)
44 44
45#define on_list_rcu(X) (!list_empty(X) && (X)->prev != LIST_POISON2)
46
45/* 47/*
46 * FIXME: currently need a clean way to replace and remove profiles as a 48 * FIXME: currently need a clean way to replace and remove profiles as a
47 * set. It should be done at the namespace level. 49 * set. It should be done at the namespace level.
@@ -75,6 +77,7 @@ struct aa_profile;
75 * @hname - The hierarchical name 77 * @hname - The hierarchical name
76 * @count: reference count of the obj 78 * @count: reference count of the obj
77 * @list: list policy object is on 79 * @list: list policy object is on
80 * @rcu: rcu head used when removing from @list
78 * @profiles: head of the profiles list contained in the object 81 * @profiles: head of the profiles list contained in the object
79 */ 82 */
80struct aa_policy { 83struct aa_policy {
@@ -83,6 +86,7 @@ struct aa_policy {
83 struct kref count; 86 struct kref count;
84 struct list_head list; 87 struct list_head list;
85 struct list_head profiles; 88 struct list_head profiles;
89 struct rcu_head rcu;
86}; 90};
87 91
88/* struct aa_ns_acct - accounting of profiles in namespace 92/* struct aa_ns_acct - accounting of profiles in namespace
@@ -124,7 +128,7 @@ struct aa_ns_acct {
124struct aa_namespace { 128struct aa_namespace {
125 struct aa_policy base; 129 struct aa_policy base;
126 struct aa_namespace *parent; 130 struct aa_namespace *parent;
127 rwlock_t lock; 131 struct mutex lock;
128 struct aa_ns_acct acct; 132 struct aa_ns_acct acct;
129 struct aa_profile *unconfined; 133 struct aa_profile *unconfined;
130 struct list_head sub_ns; 134 struct list_head sub_ns;
@@ -166,7 +170,7 @@ struct aa_policydb {
166 * attachments are determined by profile X transition rules. 170 * attachments are determined by profile X transition rules.
167 * 171 *
168 * The @replacedby field is write protected by the profile lock. Reads 172 * The @replacedby field is write protected by the profile lock. Reads
169 * are assumed to be atomic, and are done without locking. 173 * are assumed to be atomic.
170 * 174 *
171 * Profiles have a hierarchy where hats and children profiles keep 175 * Profiles have a hierarchy where hats and children profiles keep
172 * a reference to their parent. 176 * a reference to their parent.
@@ -177,7 +181,7 @@ struct aa_policydb {
177 */ 181 */
178struct aa_profile { 182struct aa_profile {
179 struct aa_policy base; 183 struct aa_policy base;
180 struct aa_profile *parent; 184 struct aa_profile __rcu *parent;
181 185
182 struct aa_namespace *ns; 186 struct aa_namespace *ns;
183 struct aa_profile *replacedby; 187 struct aa_profile *replacedby;
@@ -296,6 +300,41 @@ static inline struct aa_profile *aa_get_profile(struct aa_profile *p)
296} 300}
297 301
298/** 302/**
303 * aa_get_profile_not0 - increment refcount on profile @p found via lookup
304 * @p: profile (MAYBE NULL)
305 *
306 * Returns: pointer to @p if @p is NULL will return NULL
307 * Requires: @p must be held with valid refcount when called
308 */
309static inline struct aa_profile *aa_get_profile_not0(struct aa_profile *p)
310{
311 if (p && kref_get_not0(&p->base.count))
312 return p;
313
314 return NULL;
315}
316
317/**
318 * aa_get_profile_rcu - increment a refcount profile that can be replaced
319 * @p: pointer to profile that can be replaced (NOT NULL)
320 *
321 * Returns: pointer to a refcounted profile.
322 * else NULL if no profile
323 */
324static inline struct aa_profile *aa_get_profile_rcu(struct aa_profile __rcu **p)
325{
326 struct aa_profile *c;
327
328 rcu_read_lock();
329 do {
330 c = rcu_dereference(*p);
331 } while (c && !kref_get_not0(&c->base.count));
332 rcu_read_unlock();
333
334 return c;
335}
336
337/**
299 * aa_put_profile - decrement refcount on profile @p 338 * aa_put_profile - decrement refcount on profile @p
300 * @p: profile (MAYBE NULL) 339 * @p: profile (MAYBE NULL)
301 */ 340 */
diff --git a/security/apparmor/policy.c b/security/apparmor/policy.c
index 407b442c0a2c..25bbbb482bb6 100644
--- a/security/apparmor/policy.c
+++ b/security/apparmor/policy.c
@@ -153,13 +153,13 @@ static bool policy_init(struct aa_policy *policy, const char *prefix,
153static void policy_destroy(struct aa_policy *policy) 153static void policy_destroy(struct aa_policy *policy)
154{ 154{
155 /* still contains profiles -- invalid */ 155 /* still contains profiles -- invalid */
156 if (!list_empty(&policy->profiles)) { 156 if (on_list_rcu(&policy->profiles)) {
157 AA_ERROR("%s: internal error, " 157 AA_ERROR("%s: internal error, "
158 "policy '%s' still contains profiles\n", 158 "policy '%s' still contains profiles\n",
159 __func__, policy->name); 159 __func__, policy->name);
160 BUG(); 160 BUG();
161 } 161 }
162 if (!list_empty(&policy->list)) { 162 if (on_list_rcu(&policy->list)) {
163 AA_ERROR("%s: internal error, policy '%s' still on list\n", 163 AA_ERROR("%s: internal error, policy '%s' still on list\n",
164 __func__, policy->name); 164 __func__, policy->name);
165 BUG(); 165 BUG();
@@ -174,7 +174,7 @@ static void policy_destroy(struct aa_policy *policy)
174 * @head: list to search (NOT NULL) 174 * @head: list to search (NOT NULL)
175 * @name: name to search for (NOT NULL) 175 * @name: name to search for (NOT NULL)
176 * 176 *
177 * Requires: correct locks for the @head list be held 177 * Requires: rcu_read_lock be held
178 * 178 *
179 * Returns: unrefcounted policy that match @name or NULL if not found 179 * Returns: unrefcounted policy that match @name or NULL if not found
180 */ 180 */
@@ -182,7 +182,7 @@ static struct aa_policy *__policy_find(struct list_head *head, const char *name)
182{ 182{
183 struct aa_policy *policy; 183 struct aa_policy *policy;
184 184
185 list_for_each_entry(policy, head, list) { 185 list_for_each_entry_rcu(policy, head, list) {
186 if (!strcmp(policy->name, name)) 186 if (!strcmp(policy->name, name))
187 return policy; 187 return policy;
188 } 188 }
@@ -195,7 +195,7 @@ static struct aa_policy *__policy_find(struct list_head *head, const char *name)
195 * @str: string to search for (NOT NULL) 195 * @str: string to search for (NOT NULL)
196 * @len: length of match required 196 * @len: length of match required
197 * 197 *
198 * Requires: correct locks for the @head list be held 198 * Requires: rcu_read_lock be held
199 * 199 *
200 * Returns: unrefcounted policy that match @str or NULL if not found 200 * Returns: unrefcounted policy that match @str or NULL if not found
201 * 201 *
@@ -207,7 +207,7 @@ static struct aa_policy *__policy_strn_find(struct list_head *head,
207{ 207{
208 struct aa_policy *policy; 208 struct aa_policy *policy;
209 209
210 list_for_each_entry(policy, head, list) { 210 list_for_each_entry_rcu(policy, head, list) {
211 if (aa_strneq(policy->name, str, len)) 211 if (aa_strneq(policy->name, str, len))
212 return policy; 212 return policy;
213 } 213 }
@@ -284,7 +284,7 @@ static struct aa_namespace *alloc_namespace(const char *prefix,
284 goto fail_ns; 284 goto fail_ns;
285 285
286 INIT_LIST_HEAD(&ns->sub_ns); 286 INIT_LIST_HEAD(&ns->sub_ns);
287 rwlock_init(&ns->lock); 287 mutex_init(&ns->lock);
288 288
289 /* released by free_namespace */ 289 /* released by free_namespace */
290 ns->unconfined = aa_alloc_profile("unconfined"); 290 ns->unconfined = aa_alloc_profile("unconfined");
@@ -350,7 +350,7 @@ void aa_free_namespace_kref(struct kref *kref)
350 * 350 *
351 * Returns: unrefcounted namespace 351 * Returns: unrefcounted namespace
352 * 352 *
353 * Requires: ns lock be held 353 * Requires: rcu_read_lock be held
354 */ 354 */
355static struct aa_namespace *__aa_find_namespace(struct list_head *head, 355static struct aa_namespace *__aa_find_namespace(struct list_head *head,
356 const char *name) 356 const char *name)
@@ -373,9 +373,9 @@ struct aa_namespace *aa_find_namespace(struct aa_namespace *root,
373{ 373{
374 struct aa_namespace *ns = NULL; 374 struct aa_namespace *ns = NULL;
375 375
376 read_lock(&root->lock); 376 rcu_read_lock();
377 ns = aa_get_namespace(__aa_find_namespace(&root->sub_ns, name)); 377 ns = aa_get_namespace(__aa_find_namespace(&root->sub_ns, name));
378 read_unlock(&root->lock); 378 rcu_read_unlock();
379 379
380 return ns; 380 return ns;
381} 381}
@@ -392,7 +392,7 @@ static struct aa_namespace *aa_prepare_namespace(const char *name)
392 392
393 root = aa_current_profile()->ns; 393 root = aa_current_profile()->ns;
394 394
395 write_lock(&root->lock); 395 mutex_lock(&root->lock);
396 396
397 /* if name isn't specified the profile is loaded to the current ns */ 397 /* if name isn't specified the profile is loaded to the current ns */
398 if (!name) { 398 if (!name) {
@@ -405,31 +405,17 @@ static struct aa_namespace *aa_prepare_namespace(const char *name)
405 /* released by caller */ 405 /* released by caller */
406 ns = aa_get_namespace(__aa_find_namespace(&root->sub_ns, name)); 406 ns = aa_get_namespace(__aa_find_namespace(&root->sub_ns, name));
407 if (!ns) { 407 if (!ns) {
408 /* namespace not found */ 408 ns = alloc_namespace(root->base.hname, name);
409 struct aa_namespace *new_ns; 409 if (!ns)
410 write_unlock(&root->lock); 410 goto out;
411 new_ns = alloc_namespace(root->base.hname, name); 411 /* add parent ref */
412 if (!new_ns) 412 ns->parent = aa_get_namespace(root);
413 return NULL; 413 list_add_rcu(&ns->base.list, &root->sub_ns);
414 write_lock(&root->lock); 414 /* add list ref */
415 /* test for race when new_ns was allocated */ 415 aa_get_namespace(ns);
416 ns = __aa_find_namespace(&root->sub_ns, name);
417 if (!ns) {
418 /* add parent ref */
419 new_ns->parent = aa_get_namespace(root);
420
421 list_add(&new_ns->base.list, &root->sub_ns);
422 /* add list ref */
423 ns = aa_get_namespace(new_ns);
424 } else {
425 /* raced so free the new one */
426 free_namespace(new_ns);
427 /* get reference on namespace */
428 aa_get_namespace(ns);
429 }
430 } 416 }
431out: 417out:
432 write_unlock(&root->lock); 418 mutex_unlock(&root->lock);
433 419
434 /* return ref */ 420 /* return ref */
435 return ns; 421 return ns;
@@ -447,7 +433,7 @@ out:
447static void __list_add_profile(struct list_head *list, 433static void __list_add_profile(struct list_head *list,
448 struct aa_profile *profile) 434 struct aa_profile *profile)
449{ 435{
450 list_add(&profile->base.list, list); 436 list_add_rcu(&profile->base.list, list);
451 /* get list reference */ 437 /* get list reference */
452 aa_get_profile(profile); 438 aa_get_profile(profile);
453} 439}
@@ -466,10 +452,8 @@ static void __list_add_profile(struct list_head *list,
466 */ 452 */
467static void __list_remove_profile(struct aa_profile *profile) 453static void __list_remove_profile(struct aa_profile *profile)
468{ 454{
469 list_del_init(&profile->base.list); 455 list_del_rcu(&profile->base.list);
470 if (!(profile->flags & PFLAG_NO_LIST_REF)) 456 aa_put_profile(profile);
471 /* release list reference */
472 aa_put_profile(profile);
473} 457}
474 458
475static void __profile_list_release(struct list_head *head); 459static void __profile_list_release(struct list_head *head);
@@ -510,17 +494,40 @@ static void __ns_list_release(struct list_head *head);
510 */ 494 */
511static void destroy_namespace(struct aa_namespace *ns) 495static void destroy_namespace(struct aa_namespace *ns)
512{ 496{
497 struct aa_profile *unconfined;
498
513 if (!ns) 499 if (!ns)
514 return; 500 return;
515 501
516 write_lock(&ns->lock); 502 mutex_lock(&ns->lock);
517 /* release all profiles in this namespace */ 503 /* release all profiles in this namespace */
518 __profile_list_release(&ns->base.profiles); 504 __profile_list_release(&ns->base.profiles);
519 505
520 /* release all sub namespaces */ 506 /* release all sub namespaces */
521 __ns_list_release(&ns->sub_ns); 507 __ns_list_release(&ns->sub_ns);
522 508
523 write_unlock(&ns->lock); 509 unconfined = ns->unconfined;
510 /*
511 * break the ns, unconfined profile cyclic reference and forward
512 * all new unconfined profiles requests to the parent namespace
513 * This will result in all confined tasks that have a profile
514 * being removed, inheriting the parent->unconfined profile.
515 */
516 if (ns->parent)
517 ns->unconfined = aa_get_profile(ns->parent->unconfined);
518
519 /* release original ns->unconfined ref */
520 aa_put_profile(unconfined);
521
522 mutex_unlock(&ns->lock);
523}
524
525static void aa_put_ns_rcu(struct rcu_head *head)
526{
527 struct aa_namespace *ns = container_of(head, struct aa_namespace,
528 base.rcu);
529 /* release ns->base.list ref */
530 aa_put_namespace(ns);
524} 531}
525 532
526/** 533/**
@@ -531,26 +538,12 @@ static void destroy_namespace(struct aa_namespace *ns)
531 */ 538 */
532static void __remove_namespace(struct aa_namespace *ns) 539static void __remove_namespace(struct aa_namespace *ns)
533{ 540{
534 struct aa_profile *unconfined = ns->unconfined;
535
536 /* remove ns from namespace list */ 541 /* remove ns from namespace list */
537 list_del_init(&ns->base.list); 542 list_del_rcu(&ns->base.list);
538
539 /*
540 * break the ns, unconfined profile cyclic reference and forward
541 * all new unconfined profiles requests to the parent namespace
542 * This will result in all confined tasks that have a profile
543 * being removed, inheriting the parent->unconfined profile.
544 */
545 if (ns->parent)
546 ns->unconfined = aa_get_profile(ns->parent->unconfined);
547 543
548 destroy_namespace(ns); 544 destroy_namespace(ns);
549 545
550 /* release original ns->unconfined ref */ 546 call_rcu(&ns->base.rcu, aa_put_ns_rcu);
551 aa_put_profile(unconfined);
552 /* release ns->base.list ref, from removal above */
553 aa_put_namespace(ns);
554} 547}
555 548
556/** 549/**
@@ -614,16 +607,9 @@ static void free_profile(struct aa_profile *profile)
614 if (!profile) 607 if (!profile)
615 return; 608 return;
616 609
617 if (!list_empty(&profile->base.list)) {
618 AA_ERROR("%s: internal error, "
619 "profile '%s' still on ns list\n",
620 __func__, profile->base.name);
621 BUG();
622 }
623
624 /* free children profiles */ 610 /* free children profiles */
625 policy_destroy(&profile->base); 611 policy_destroy(&profile->base);
626 aa_put_profile(profile->parent); 612 aa_put_profile(rcu_access_pointer(profile->parent));
627 613
628 aa_put_namespace(profile->ns); 614 aa_put_namespace(profile->ns);
629 kzfree(profile->rename); 615 kzfree(profile->rename);
@@ -661,6 +647,16 @@ static void free_profile(struct aa_profile *profile)
661} 647}
662 648
663/** 649/**
650 * aa_free_profile_rcu - free aa_profile by rcu (called by aa_free_profile_kref)
651 * @head: rcu_head callback for freeing of a profile (NOT NULL)
652 */
653static void aa_free_profile_rcu(struct rcu_head *head)
654{
655 struct aa_profile *p = container_of(head, struct aa_profile, base.rcu);
656 free_profile(p);
657}
658
659/**
664 * aa_free_profile_kref - free aa_profile by kref (called by aa_put_profile) 660 * aa_free_profile_kref - free aa_profile by kref (called by aa_put_profile)
665 * @kr: kref callback for freeing of a profile (NOT NULL) 661 * @kr: kref callback for freeing of a profile (NOT NULL)
666 */ 662 */
@@ -668,8 +664,7 @@ void aa_free_profile_kref(struct kref *kref)
668{ 664{
669 struct aa_profile *p = container_of(kref, struct aa_profile, 665 struct aa_profile *p = container_of(kref, struct aa_profile,
670 base.count); 666 base.count);
671 667 call_rcu(&p->base.rcu, aa_free_profile_rcu);
672 free_profile(p);
673} 668}
674 669
675/** 670/**
@@ -733,12 +728,12 @@ struct aa_profile *aa_new_null_profile(struct aa_profile *parent, int hat)
733 profile->flags |= PFLAG_HAT; 728 profile->flags |= PFLAG_HAT;
734 729
735 /* released on free_profile */ 730 /* released on free_profile */
736 profile->parent = aa_get_profile(parent); 731 rcu_assign_pointer(profile->parent, aa_get_profile(parent));
737 profile->ns = aa_get_namespace(parent->ns); 732 profile->ns = aa_get_namespace(parent->ns);
738 733
739 write_lock(&profile->ns->lock); 734 mutex_lock(&profile->ns->lock);
740 __list_add_profile(&parent->base.profiles, profile); 735 __list_add_profile(&parent->base.profiles, profile);
741 write_unlock(&profile->ns->lock); 736 mutex_unlock(&profile->ns->lock);
742 737
743 /* refcount released by caller */ 738 /* refcount released by caller */
744 return profile; 739 return profile;
@@ -754,7 +749,7 @@ fail:
754 * @head: list to search (NOT NULL) 749 * @head: list to search (NOT NULL)
755 * @name: name of profile (NOT NULL) 750 * @name: name of profile (NOT NULL)
756 * 751 *
757 * Requires: ns lock protecting list be held 752 * Requires: rcu_read_lock be held
758 * 753 *
759 * Returns: unrefcounted profile ptr, or NULL if not found 754 * Returns: unrefcounted profile ptr, or NULL if not found
760 */ 755 */
@@ -769,7 +764,7 @@ static struct aa_profile *__find_child(struct list_head *head, const char *name)
769 * @name: name of profile (NOT NULL) 764 * @name: name of profile (NOT NULL)
770 * @len: length of @name substring to match 765 * @len: length of @name substring to match
771 * 766 *
772 * Requires: ns lock protecting list be held 767 * Requires: rcu_read_lock be held
773 * 768 *
774 * Returns: unrefcounted profile ptr, or NULL if not found 769 * Returns: unrefcounted profile ptr, or NULL if not found
775 */ 770 */
@@ -790,9 +785,9 @@ struct aa_profile *aa_find_child(struct aa_profile *parent, const char *name)
790{ 785{
791 struct aa_profile *profile; 786 struct aa_profile *profile;
792 787
793 read_lock(&parent->ns->lock); 788 rcu_read_lock();
794 profile = aa_get_profile(__find_child(&parent->base.profiles, name)); 789 profile = aa_get_profile(__find_child(&parent->base.profiles, name));
795 read_unlock(&parent->ns->lock); 790 rcu_read_unlock();
796 791
797 /* refcount released by caller */ 792 /* refcount released by caller */
798 return profile; 793 return profile;
@@ -807,7 +802,7 @@ struct aa_profile *aa_find_child(struct aa_profile *parent, const char *name)
807 * that matches hname does not need to exist, in general this 802 * that matches hname does not need to exist, in general this
808 * is used to load a new profile. 803 * is used to load a new profile.
809 * 804 *
810 * Requires: ns->lock be held 805 * Requires: rcu_read_lock be held
811 * 806 *
812 * Returns: unrefcounted policy or NULL if not found 807 * Returns: unrefcounted policy or NULL if not found
813 */ 808 */
@@ -839,7 +834,7 @@ static struct aa_policy *__lookup_parent(struct aa_namespace *ns,
839 * @base: base list to start looking up profile name from (NOT NULL) 834 * @base: base list to start looking up profile name from (NOT NULL)
840 * @hname: hierarchical profile name (NOT NULL) 835 * @hname: hierarchical profile name (NOT NULL)
841 * 836 *
842 * Requires: ns->lock be held 837 * Requires: rcu_read_lock be held
843 * 838 *
844 * Returns: unrefcounted profile pointer or NULL if not found 839 * Returns: unrefcounted profile pointer or NULL if not found
845 * 840 *
@@ -878,9 +873,11 @@ struct aa_profile *aa_lookup_profile(struct aa_namespace *ns, const char *hname)
878{ 873{
879 struct aa_profile *profile; 874 struct aa_profile *profile;
880 875
881 read_lock(&ns->lock); 876 rcu_read_lock();
882 profile = aa_get_profile(__lookup_profile(&ns->base, hname)); 877 do {
883 read_unlock(&ns->lock); 878 profile = __lookup_profile(&ns->base, hname);
879 } while (profile && !aa_get_profile_not0(profile));
880 rcu_read_unlock();
884 881
885 /* the unconfined profile is not in the regular profile list */ 882 /* the unconfined profile is not in the regular profile list */
886 if (!profile && strcmp(hname, "unconfined") == 0) 883 if (!profile && strcmp(hname, "unconfined") == 0)
@@ -1002,7 +999,7 @@ static void __replace_profile(struct aa_profile *old, struct aa_profile *new)
1002 999
1003 if (!list_empty(&old->base.profiles)) { 1000 if (!list_empty(&old->base.profiles)) {
1004 LIST_HEAD(lh); 1001 LIST_HEAD(lh);
1005 list_splice_init(&old->base.profiles, &lh); 1002 list_splice_init_rcu(&old->base.profiles, &lh, synchronize_rcu);
1006 1003
1007 list_for_each_entry_safe(child, tmp, &lh, base.list) { 1004 list_for_each_entry_safe(child, tmp, &lh, base.list) {
1008 struct aa_profile *p; 1005 struct aa_profile *p;
@@ -1018,20 +1015,24 @@ static void __replace_profile(struct aa_profile *old, struct aa_profile *new)
1018 /* inherit @child and its children */ 1015 /* inherit @child and its children */
1019 /* TODO: update hname of inherited children */ 1016 /* TODO: update hname of inherited children */
1020 /* list refcount transferred to @new */ 1017 /* list refcount transferred to @new */
1021 list_add(&child->base.list, &new->base.profiles); 1018 p = rcu_dereference_protected(child->parent,
1022 aa_put_profile(child->parent); 1019 mutex_is_locked(&child->ns->lock));
1023 child->parent = aa_get_profile(new); 1020 rcu_assign_pointer(child->parent, aa_get_profile(new));
1021 list_add_rcu(&child->base.list, &new->base.profiles);
1022 aa_put_profile(p);
1024 } 1023 }
1025 } 1024 }
1026 1025
1027 if (!new->parent) 1026 if (!rcu_access_pointer(new->parent)) {
1028 new->parent = aa_get_profile(old->parent); 1027 struct aa_profile *parent = rcu_dereference(old->parent);
1028 rcu_assign_pointer(new->parent, aa_get_profile(parent));
1029 }
1029 /* released by free_profile */ 1030 /* released by free_profile */
1030 old->replacedby = aa_get_profile(new); 1031 old->replacedby = aa_get_profile(new);
1031 1032
1032 if (list_empty(&new->base.list)) { 1033 if (list_empty(&new->base.list)) {
1033 /* new is not on a list already */ 1034 /* new is not on a list already */
1034 list_replace_init(&old->base.list, &new->base.list); 1035 list_replace_rcu(&old->base.list, &new->base.list);
1035 aa_get_profile(new); 1036 aa_get_profile(new);
1036 aa_put_profile(old); 1037 aa_put_profile(old);
1037 } else 1038 } else
@@ -1099,7 +1100,7 @@ ssize_t aa_replace_profiles(void *udata, size_t size, bool noreplace)
1099 goto fail; 1100 goto fail;
1100 } 1101 }
1101 1102
1102 write_lock(&ns->lock); 1103 mutex_lock(&ns->lock);
1103 /* setup parent and ns info */ 1104 /* setup parent and ns info */
1104 list_for_each_entry(ent, &lh, list) { 1105 list_for_each_entry(ent, &lh, list) {
1105 struct aa_policy *policy; 1106 struct aa_policy *policy;
@@ -1135,11 +1136,12 @@ ssize_t aa_replace_profiles(void *udata, size_t size, bool noreplace)
1135 name = ent->new->base.hname; 1136 name = ent->new->base.hname;
1136 goto fail_lock; 1137 goto fail_lock;
1137 } 1138 }
1138 ent->new->parent = aa_get_profile(p); 1139 rcu_assign_pointer(ent->new->parent, aa_get_profile(p));
1139 } else if (policy != &ns->base) 1140 } else if (policy != &ns->base) {
1140 /* released on profile replacement or free_profile */ 1141 /* released on profile replacement or free_profile */
1141 ent->new->parent = aa_get_profile((struct aa_profile *) 1142 struct aa_profile *p = (struct aa_profile *) policy;
1142 policy); 1143 rcu_assign_pointer(ent->new->parent, aa_get_profile(p));
1144 }
1143 } 1145 }
1144 1146
1145 /* do actual replacement */ 1147 /* do actual replacement */
@@ -1156,13 +1158,16 @@ ssize_t aa_replace_profiles(void *udata, size_t size, bool noreplace)
1156 } else if (ent->rename) { 1158 } else if (ent->rename) {
1157 __replace_profile(ent->rename, ent->new); 1159 __replace_profile(ent->rename, ent->new);
1158 } else if (ent->new->parent) { 1160 } else if (ent->new->parent) {
1159 struct aa_profile *parent; 1161 struct aa_profile *parent, *newest;
1160 parent = aa_newest_version(ent->new->parent); 1162 parent = rcu_dereference_protected(ent->new->parent,
1163 mutex_is_locked(&ns->lock));
1164 newest = aa_newest_version(parent);
1165
1161 /* parent replaced in this atomic set? */ 1166 /* parent replaced in this atomic set? */
1162 if (parent != ent->new->parent) { 1167 if (newest != parent) {
1163 aa_get_profile(parent); 1168 aa_get_profile(newest);
1164 aa_put_profile(ent->new->parent); 1169 aa_put_profile(parent);
1165 ent->new->parent = parent; 1170 rcu_assign_pointer(ent->new->parent, newest);
1166 } 1171 }
1167 __list_add_profile(&parent->base.profiles, ent->new); 1172 __list_add_profile(&parent->base.profiles, ent->new);
1168 } else 1173 } else
@@ -1170,7 +1175,7 @@ ssize_t aa_replace_profiles(void *udata, size_t size, bool noreplace)
1170 1175
1171 aa_load_ent_free(ent); 1176 aa_load_ent_free(ent);
1172 } 1177 }
1173 write_unlock(&ns->lock); 1178 mutex_unlock(&ns->lock);
1174 1179
1175out: 1180out:
1176 aa_put_namespace(ns); 1181 aa_put_namespace(ns);
@@ -1180,7 +1185,7 @@ out:
1180 return size; 1185 return size;
1181 1186
1182fail_lock: 1187fail_lock:
1183 write_unlock(&ns->lock); 1188 mutex_unlock(&ns->lock);
1184fail: 1189fail:
1185 error = audit_policy(op, GFP_KERNEL, name, info, error); 1190 error = audit_policy(op, GFP_KERNEL, name, info, error);
1186 1191
@@ -1235,12 +1240,12 @@ ssize_t aa_remove_profiles(char *fqname, size_t size)
1235 1240
1236 if (!name) { 1241 if (!name) {
1237 /* remove namespace - can only happen if fqname[0] == ':' */ 1242 /* remove namespace - can only happen if fqname[0] == ':' */
1238 write_lock(&ns->parent->lock); 1243 mutex_lock(&ns->parent->lock);
1239 __remove_namespace(ns); 1244 __remove_namespace(ns);
1240 write_unlock(&ns->parent->lock); 1245 mutex_unlock(&ns->parent->lock);
1241 } else { 1246 } else {
1242 /* remove profile */ 1247 /* remove profile */
1243 write_lock(&ns->lock); 1248 mutex_lock(&ns->lock);
1244 profile = aa_get_profile(__lookup_profile(&ns->base, name)); 1249 profile = aa_get_profile(__lookup_profile(&ns->base, name));
1245 if (!profile) { 1250 if (!profile) {
1246 error = -ENOENT; 1251 error = -ENOENT;
@@ -1249,7 +1254,7 @@ ssize_t aa_remove_profiles(char *fqname, size_t size)
1249 } 1254 }
1250 name = profile->base.hname; 1255 name = profile->base.hname;
1251 __remove_profile(profile); 1256 __remove_profile(profile);
1252 write_unlock(&ns->lock); 1257 mutex_unlock(&ns->lock);
1253 } 1258 }
1254 1259
1255 /* don't fail removal if audit fails */ 1260 /* don't fail removal if audit fails */
@@ -1259,7 +1264,7 @@ ssize_t aa_remove_profiles(char *fqname, size_t size)
1259 return size; 1264 return size;
1260 1265
1261fail_ns_lock: 1266fail_ns_lock:
1262 write_unlock(&ns->lock); 1267 mutex_unlock(&ns->lock);
1263 aa_put_namespace(ns); 1268 aa_put_namespace(ns);
1264 1269
1265fail: 1270fail: