diff options
Diffstat (limited to 'security')
-rw-r--r-- | security/apparmor/context.c | 16 | ||||
-rw-r--r-- | security/apparmor/domain.c | 4 | ||||
-rw-r--r-- | security/apparmor/include/context.h | 15 | ||||
-rw-r--r-- | security/apparmor/include/policy.h | 78 | ||||
-rw-r--r-- | security/apparmor/lsm.c | 14 | ||||
-rw-r--r-- | security/apparmor/policy.c | 85 |
6 files changed, 125 insertions, 87 deletions
diff --git a/security/apparmor/context.c b/security/apparmor/context.c index d5af1d15f26d..3064c6ced87c 100644 --- a/security/apparmor/context.c +++ b/security/apparmor/context.c | |||
@@ -112,9 +112,9 @@ int aa_replace_current_profile(struct aa_profile *profile) | |||
112 | aa_clear_task_cxt_trans(cxt); | 112 | aa_clear_task_cxt_trans(cxt); |
113 | 113 | ||
114 | /* be careful switching cxt->profile, when racing replacement it | 114 | /* be careful switching cxt->profile, when racing replacement it |
115 | * is possible that cxt->profile->replacedby is the reference keeping | 115 | * is possible that cxt->profile->replacedby->profile is the reference |
116 | * @profile valid, so make sure to get its reference before dropping | 116 | * keeping @profile valid, so make sure to get its reference before |
117 | * the reference on cxt->profile */ | 117 | * dropping the reference on cxt->profile */ |
118 | aa_get_profile(profile); | 118 | aa_get_profile(profile); |
119 | aa_put_profile(cxt->profile); | 119 | aa_put_profile(cxt->profile); |
120 | cxt->profile = profile; | 120 | cxt->profile = profile; |
@@ -175,7 +175,7 @@ int aa_set_current_hat(struct aa_profile *profile, u64 token) | |||
175 | abort_creds(new); | 175 | abort_creds(new); |
176 | return -EACCES; | 176 | return -EACCES; |
177 | } | 177 | } |
178 | cxt->profile = aa_get_profile(aa_newest_version(profile)); | 178 | cxt->profile = aa_get_newest_profile(profile); |
179 | /* clear exec on switching context */ | 179 | /* clear exec on switching context */ |
180 | aa_put_profile(cxt->onexec); | 180 | aa_put_profile(cxt->onexec); |
181 | cxt->onexec = NULL; | 181 | cxt->onexec = NULL; |
@@ -212,14 +212,8 @@ int aa_restore_previous_profile(u64 token) | |||
212 | } | 212 | } |
213 | 213 | ||
214 | aa_put_profile(cxt->profile); | 214 | aa_put_profile(cxt->profile); |
215 | cxt->profile = aa_newest_version(cxt->previous); | 215 | cxt->profile = aa_get_newest_profile(cxt->previous); |
216 | BUG_ON(!cxt->profile); | 216 | BUG_ON(!cxt->profile); |
217 | if (unlikely(cxt->profile != cxt->previous)) { | ||
218 | aa_get_profile(cxt->profile); | ||
219 | aa_put_profile(cxt->previous); | ||
220 | } | ||
221 | /* ref has been transfered so avoid putting ref in clear_task_cxt */ | ||
222 | cxt->previous = NULL; | ||
223 | /* clear exec && prev information when restoring to previous context */ | 217 | /* clear exec && prev information when restoring to previous context */ |
224 | aa_clear_task_cxt_trans(cxt); | 218 | aa_clear_task_cxt_trans(cxt); |
225 | 219 | ||
diff --git a/security/apparmor/domain.c b/security/apparmor/domain.c index 454bcd7f3452..5488d095af6f 100644 --- a/security/apparmor/domain.c +++ b/security/apparmor/domain.c | |||
@@ -359,7 +359,7 @@ int apparmor_bprm_set_creds(struct linux_binprm *bprm) | |||
359 | cxt = cred_cxt(bprm->cred); | 359 | cxt = cred_cxt(bprm->cred); |
360 | BUG_ON(!cxt); | 360 | BUG_ON(!cxt); |
361 | 361 | ||
362 | profile = aa_get_profile(aa_newest_version(cxt->profile)); | 362 | profile = aa_get_newest_profile(cxt->profile); |
363 | /* | 363 | /* |
364 | * get the namespace from the replacement profile as replacement | 364 | * get the namespace from the replacement profile as replacement |
365 | * can change the namespace | 365 | * can change the namespace |
@@ -417,7 +417,7 @@ int apparmor_bprm_set_creds(struct linux_binprm *bprm) | |||
417 | 417 | ||
418 | if (!(cp.allow & AA_MAY_ONEXEC)) | 418 | if (!(cp.allow & AA_MAY_ONEXEC)) |
419 | goto audit; | 419 | goto audit; |
420 | new_profile = aa_get_profile(aa_newest_version(cxt->onexec)); | 420 | new_profile = aa_get_newest_profile(cxt->onexec); |
421 | goto apply; | 421 | goto apply; |
422 | } | 422 | } |
423 | 423 | ||
diff --git a/security/apparmor/include/context.h b/security/apparmor/include/context.h index d44ba5802e3d..6bf65798e5d1 100644 --- a/security/apparmor/include/context.h +++ b/security/apparmor/include/context.h | |||
@@ -98,7 +98,7 @@ static inline struct aa_profile *aa_cred_profile(const struct cred *cred) | |||
98 | { | 98 | { |
99 | struct aa_task_cxt *cxt = cred_cxt(cred); | 99 | struct aa_task_cxt *cxt = cred_cxt(cred); |
100 | BUG_ON(!cxt || !cxt->profile); | 100 | BUG_ON(!cxt || !cxt->profile); |
101 | return aa_newest_version(cxt->profile); | 101 | return cxt->profile; |
102 | } | 102 | } |
103 | 103 | ||
104 | /** | 104 | /** |
@@ -152,15 +152,14 @@ static inline struct aa_profile *aa_current_profile(void) | |||
152 | struct aa_profile *profile; | 152 | struct aa_profile *profile; |
153 | BUG_ON(!cxt || !cxt->profile); | 153 | BUG_ON(!cxt || !cxt->profile); |
154 | 154 | ||
155 | profile = aa_newest_version(cxt->profile); | 155 | if (PROFILE_INVALID(cxt->profile)) { |
156 | /* | 156 | profile = aa_get_newest_profile(cxt->profile); |
157 | * Whether or not replacement succeeds, use newest profile so | ||
158 | * there is no need to update it after replacement. | ||
159 | */ | ||
160 | if (unlikely((cxt->profile != profile))) | ||
161 | aa_replace_current_profile(profile); | 157 | aa_replace_current_profile(profile); |
158 | aa_put_profile(profile); | ||
159 | cxt = current_cxt(); | ||
160 | } | ||
162 | 161 | ||
163 | return profile; | 162 | return cxt->profile; |
164 | } | 163 | } |
165 | 164 | ||
166 | /** | 165 | /** |
diff --git a/security/apparmor/include/policy.h b/security/apparmor/include/policy.h index 82487a853353..e9f2baf4467e 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 PROFILE_INVALID(_profile) ((_profile)->flags & PFLAG_INVALID) | ||
46 | |||
45 | #define on_list_rcu(X) (!list_empty(X) && (X)->prev != LIST_POISON2) | 47 | #define on_list_rcu(X) (!list_empty(X) && (X)->prev != LIST_POISON2) |
46 | 48 | ||
47 | /* | 49 | /* |
@@ -65,6 +67,7 @@ enum profile_flags { | |||
65 | PFLAG_USER_DEFINED = 0x20, /* user based profile - lower privs */ | 67 | PFLAG_USER_DEFINED = 0x20, /* user based profile - lower privs */ |
66 | PFLAG_NO_LIST_REF = 0x40, /* list doesn't keep profile ref */ | 68 | PFLAG_NO_LIST_REF = 0x40, /* list doesn't keep profile ref */ |
67 | PFLAG_OLD_NULL_TRANS = 0x100, /* use // as the null transition */ | 69 | PFLAG_OLD_NULL_TRANS = 0x100, /* use // as the null transition */ |
70 | PFLAG_INVALID = 0x200, /* profile replaced/removed */ | ||
68 | 71 | ||
69 | /* These flags must correspond with PATH_flags */ | 72 | /* These flags must correspond with PATH_flags */ |
70 | PFLAG_MEDIATE_DELETED = 0x10000, /* mediate instead delegate deleted */ | 73 | PFLAG_MEDIATE_DELETED = 0x10000, /* mediate instead delegate deleted */ |
@@ -146,6 +149,12 @@ struct aa_policydb { | |||
146 | 149 | ||
147 | }; | 150 | }; |
148 | 151 | ||
152 | struct aa_replacedby { | ||
153 | struct kref count; | ||
154 | struct aa_profile __rcu *profile; | ||
155 | }; | ||
156 | |||
157 | |||
149 | /* struct aa_profile - basic confinement data | 158 | /* struct aa_profile - basic confinement data |
150 | * @base - base components of the profile (name, refcount, lists, lock ...) | 159 | * @base - base components of the profile (name, refcount, lists, lock ...) |
151 | * @parent: parent of profile | 160 | * @parent: parent of profile |
@@ -169,8 +178,7 @@ struct aa_policydb { | |||
169 | * used to determine profile attachment against unconfined tasks. All other | 178 | * used to determine profile attachment against unconfined tasks. All other |
170 | * attachments are determined by profile X transition rules. | 179 | * attachments are determined by profile X transition rules. |
171 | * | 180 | * |
172 | * The @replacedby field is write protected by the profile lock. Reads | 181 | * The @replacedby struct is write protected by the profile lock. |
173 | * are assumed to be atomic. | ||
174 | * | 182 | * |
175 | * Profiles have a hierarchy where hats and children profiles keep | 183 | * Profiles have a hierarchy where hats and children profiles keep |
176 | * a reference to their parent. | 184 | * a reference to their parent. |
@@ -184,14 +192,14 @@ struct aa_profile { | |||
184 | struct aa_profile __rcu *parent; | 192 | struct aa_profile __rcu *parent; |
185 | 193 | ||
186 | struct aa_namespace *ns; | 194 | struct aa_namespace *ns; |
187 | struct aa_profile *replacedby; | 195 | struct aa_replacedby *replacedby; |
188 | const char *rename; | 196 | const char *rename; |
189 | 197 | ||
190 | struct aa_dfa *xmatch; | 198 | struct aa_dfa *xmatch; |
191 | int xmatch_len; | 199 | int xmatch_len; |
192 | enum audit_mode audit; | 200 | enum audit_mode audit; |
193 | enum profile_mode mode; | 201 | enum profile_mode mode; |
194 | u32 flags; | 202 | long flags; |
195 | u32 path_flags; | 203 | u32 path_flags; |
196 | int size; | 204 | int size; |
197 | 205 | ||
@@ -250,6 +258,7 @@ static inline void aa_put_namespace(struct aa_namespace *ns) | |||
250 | kref_put(&ns->base.count, aa_free_namespace_kref); | 258 | kref_put(&ns->base.count, aa_free_namespace_kref); |
251 | } | 259 | } |
252 | 260 | ||
261 | void aa_free_replacedby_kref(struct kref *kref); | ||
253 | struct aa_profile *aa_alloc_profile(const char *name); | 262 | struct aa_profile *aa_alloc_profile(const char *name); |
254 | struct aa_profile *aa_new_null_profile(struct aa_profile *parent, int hat); | 263 | struct aa_profile *aa_new_null_profile(struct aa_profile *parent, int hat); |
255 | void aa_free_profile_kref(struct kref *kref); | 264 | void aa_free_profile_kref(struct kref *kref); |
@@ -265,24 +274,6 @@ ssize_t aa_remove_profiles(char *name, size_t size); | |||
265 | 274 | ||
266 | #define unconfined(X) ((X)->flags & PFLAG_UNCONFINED) | 275 | #define unconfined(X) ((X)->flags & PFLAG_UNCONFINED) |
267 | 276 | ||
268 | /** | ||
269 | * aa_newest_version - find the newest version of @profile | ||
270 | * @profile: the profile to check for newer versions of (NOT NULL) | ||
271 | * | ||
272 | * Returns: newest version of @profile, if @profile is the newest version | ||
273 | * return @profile. | ||
274 | * | ||
275 | * NOTE: the profile returned is not refcounted, The refcount on @profile | ||
276 | * must be held until the caller decides what to do with the returned newest | ||
277 | * version. | ||
278 | */ | ||
279 | static inline struct aa_profile *aa_newest_version(struct aa_profile *profile) | ||
280 | { | ||
281 | while (profile->replacedby) | ||
282 | profile = profile->replacedby; | ||
283 | |||
284 | return profile; | ||
285 | } | ||
286 | 277 | ||
287 | /** | 278 | /** |
288 | * aa_get_profile - increment refcount on profile @p | 279 | * aa_get_profile - increment refcount on profile @p |
@@ -335,6 +326,25 @@ static inline struct aa_profile *aa_get_profile_rcu(struct aa_profile __rcu **p) | |||
335 | } | 326 | } |
336 | 327 | ||
337 | /** | 328 | /** |
329 | * aa_get_newest_profile - find the newest version of @profile | ||
330 | * @profile: the profile to check for newer versions of | ||
331 | * | ||
332 | * Returns: refcounted newest version of @profile taking into account | ||
333 | * replacement, renames and removals | ||
334 | * return @profile. | ||
335 | */ | ||
336 | static inline struct aa_profile *aa_get_newest_profile(struct aa_profile *p) | ||
337 | { | ||
338 | if (!p) | ||
339 | return NULL; | ||
340 | |||
341 | if (PROFILE_INVALID(p)) | ||
342 | return aa_get_profile_rcu(&p->replacedby->profile); | ||
343 | |||
344 | return aa_get_profile(p); | ||
345 | } | ||
346 | |||
347 | /** | ||
338 | * aa_put_profile - decrement refcount on profile @p | 348 | * aa_put_profile - decrement refcount on profile @p |
339 | * @p: profile (MAYBE NULL) | 349 | * @p: profile (MAYBE NULL) |
340 | */ | 350 | */ |
@@ -344,6 +354,30 @@ static inline void aa_put_profile(struct aa_profile *p) | |||
344 | kref_put(&p->base.count, aa_free_profile_kref); | 354 | kref_put(&p->base.count, aa_free_profile_kref); |
345 | } | 355 | } |
346 | 356 | ||
357 | static inline struct aa_replacedby *aa_get_replacedby(struct aa_replacedby *p) | ||
358 | { | ||
359 | if (p) | ||
360 | kref_get(&(p->count)); | ||
361 | |||
362 | return p; | ||
363 | } | ||
364 | |||
365 | static inline void aa_put_replacedby(struct aa_replacedby *p) | ||
366 | { | ||
367 | if (p) | ||
368 | kref_put(&p->count, aa_free_replacedby_kref); | ||
369 | } | ||
370 | |||
371 | /* requires profile list write lock held */ | ||
372 | static inline void __aa_update_replacedby(struct aa_profile *orig, | ||
373 | struct aa_profile *new) | ||
374 | { | ||
375 | struct aa_profile *tmp = rcu_dereference(orig->replacedby->profile); | ||
376 | rcu_assign_pointer(orig->replacedby->profile, aa_get_profile(new)); | ||
377 | orig->flags |= PFLAG_INVALID; | ||
378 | aa_put_profile(tmp); | ||
379 | } | ||
380 | |||
347 | static inline int AUDIT_MODE(struct aa_profile *profile) | 381 | static inline int AUDIT_MODE(struct aa_profile *profile) |
348 | { | 382 | { |
349 | if (aa_g_audit != AUDIT_NORMAL) | 383 | if (aa_g_audit != AUDIT_NORMAL) |
diff --git a/security/apparmor/lsm.c b/security/apparmor/lsm.c index 96506dfe51ec..c8c148a738f7 100644 --- a/security/apparmor/lsm.c +++ b/security/apparmor/lsm.c | |||
@@ -508,19 +508,21 @@ static int apparmor_getprocattr(struct task_struct *task, char *name, | |||
508 | /* released below */ | 508 | /* released below */ |
509 | const struct cred *cred = get_task_cred(task); | 509 | const struct cred *cred = get_task_cred(task); |
510 | struct aa_task_cxt *cxt = cred_cxt(cred); | 510 | struct aa_task_cxt *cxt = cred_cxt(cred); |
511 | struct aa_profile *profile = NULL; | ||
511 | 512 | ||
512 | if (strcmp(name, "current") == 0) | 513 | if (strcmp(name, "current") == 0) |
513 | error = aa_getprocattr(aa_newest_version(cxt->profile), | 514 | profile = aa_get_newest_profile(cxt->profile); |
514 | value); | ||
515 | else if (strcmp(name, "prev") == 0 && cxt->previous) | 515 | else if (strcmp(name, "prev") == 0 && cxt->previous) |
516 | error = aa_getprocattr(aa_newest_version(cxt->previous), | 516 | profile = aa_get_newest_profile(cxt->previous); |
517 | value); | ||
518 | else if (strcmp(name, "exec") == 0 && cxt->onexec) | 517 | else if (strcmp(name, "exec") == 0 && cxt->onexec) |
519 | error = aa_getprocattr(aa_newest_version(cxt->onexec), | 518 | profile = aa_get_newest_profile(cxt->onexec); |
520 | value); | ||
521 | else | 519 | else |
522 | error = -EINVAL; | 520 | error = -EINVAL; |
523 | 521 | ||
522 | if (profile) | ||
523 | error = aa_getprocattr(profile, value); | ||
524 | |||
525 | aa_put_profile(profile); | ||
524 | put_cred(cred); | 526 | put_cred(cred); |
525 | 527 | ||
526 | return error; | 528 | return error; |
diff --git a/security/apparmor/policy.c b/security/apparmor/policy.c index 25bbbb482bb6..41b8f275c626 100644 --- a/security/apparmor/policy.c +++ b/security/apparmor/policy.c | |||
@@ -469,7 +469,7 @@ static void __remove_profile(struct aa_profile *profile) | |||
469 | /* release any children lists first */ | 469 | /* release any children lists first */ |
470 | __profile_list_release(&profile->base.profiles); | 470 | __profile_list_release(&profile->base.profiles); |
471 | /* released by free_profile */ | 471 | /* released by free_profile */ |
472 | profile->replacedby = aa_get_profile(profile->ns->unconfined); | 472 | __aa_update_replacedby(profile, profile->ns->unconfined); |
473 | __list_remove_profile(profile); | 473 | __list_remove_profile(profile); |
474 | } | 474 | } |
475 | 475 | ||
@@ -588,6 +588,23 @@ void __init aa_free_root_ns(void) | |||
588 | aa_put_namespace(ns); | 588 | aa_put_namespace(ns); |
589 | } | 589 | } |
590 | 590 | ||
591 | |||
592 | static void free_replacedby(struct aa_replacedby *r) | ||
593 | { | ||
594 | if (r) { | ||
595 | aa_put_profile(rcu_dereference(r->profile)); | ||
596 | kzfree(r); | ||
597 | } | ||
598 | } | ||
599 | |||
600 | |||
601 | void aa_free_replacedby_kref(struct kref *kref) | ||
602 | { | ||
603 | struct aa_replacedby *r = container_of(kref, struct aa_replacedby, | ||
604 | count); | ||
605 | free_replacedby(r); | ||
606 | } | ||
607 | |||
591 | /** | 608 | /** |
592 | * free_profile - free a profile | 609 | * free_profile - free a profile |
593 | * @profile: the profile to free (MAYBE NULL) | 610 | * @profile: the profile to free (MAYBE NULL) |
@@ -600,8 +617,6 @@ void __init aa_free_root_ns(void) | |||
600 | */ | 617 | */ |
601 | static void free_profile(struct aa_profile *profile) | 618 | static void free_profile(struct aa_profile *profile) |
602 | { | 619 | { |
603 | struct aa_profile *p; | ||
604 | |||
605 | AA_DEBUG("%s(%p)\n", __func__, profile); | 620 | AA_DEBUG("%s(%p)\n", __func__, profile); |
606 | 621 | ||
607 | if (!profile) | 622 | if (!profile) |
@@ -620,28 +635,7 @@ static void free_profile(struct aa_profile *profile) | |||
620 | 635 | ||
621 | aa_put_dfa(profile->xmatch); | 636 | aa_put_dfa(profile->xmatch); |
622 | aa_put_dfa(profile->policy.dfa); | 637 | aa_put_dfa(profile->policy.dfa); |
623 | 638 | aa_put_replacedby(profile->replacedby); | |
624 | /* put the profile reference for replacedby, but not via | ||
625 | * put_profile(kref_put). | ||
626 | * replacedby can form a long chain that can result in cascading | ||
627 | * frees that blows the stack because kref_put makes a nested fn | ||
628 | * call (it looks like recursion, with free_profile calling | ||
629 | * free_profile) for each profile in the chain lp#1056078. | ||
630 | */ | ||
631 | for (p = profile->replacedby; p; ) { | ||
632 | if (atomic_dec_and_test(&p->base.count.refcount)) { | ||
633 | /* no more refs on p, grab its replacedby */ | ||
634 | struct aa_profile *next = p->replacedby; | ||
635 | /* break the chain */ | ||
636 | p->replacedby = NULL; | ||
637 | /* now free p, chain is broken */ | ||
638 | free_profile(p); | ||
639 | |||
640 | /* follow up with next profile in the chain */ | ||
641 | p = next; | ||
642 | } else | ||
643 | break; | ||
644 | } | ||
645 | 639 | ||
646 | kzfree(profile); | 640 | kzfree(profile); |
647 | } | 641 | } |
@@ -682,13 +676,22 @@ struct aa_profile *aa_alloc_profile(const char *hname) | |||
682 | if (!profile) | 676 | if (!profile) |
683 | return NULL; | 677 | return NULL; |
684 | 678 | ||
685 | if (!policy_init(&profile->base, NULL, hname)) { | 679 | profile->replacedby = kzalloc(sizeof(struct aa_replacedby), GFP_KERNEL); |
686 | kzfree(profile); | 680 | if (!profile->replacedby) |
687 | return NULL; | 681 | goto fail; |
688 | } | 682 | kref_init(&profile->replacedby->count); |
683 | |||
684 | if (!policy_init(&profile->base, NULL, hname)) | ||
685 | goto fail; | ||
689 | 686 | ||
690 | /* refcount released by caller */ | 687 | /* refcount released by caller */ |
691 | return profile; | 688 | return profile; |
689 | |||
690 | fail: | ||
691 | kzfree(profile->replacedby); | ||
692 | kzfree(profile); | ||
693 | |||
694 | return NULL; | ||
692 | } | 695 | } |
693 | 696 | ||
694 | /** | 697 | /** |
@@ -985,6 +988,7 @@ static struct aa_profile *__list_lookup_parent(struct list_head *lh, | |||
985 | * __replace_profile - replace @old with @new on a list | 988 | * __replace_profile - replace @old with @new on a list |
986 | * @old: profile to be replaced (NOT NULL) | 989 | * @old: profile to be replaced (NOT NULL) |
987 | * @new: profile to replace @old with (NOT NULL) | 990 | * @new: profile to replace @old with (NOT NULL) |
991 | * @share_replacedby: transfer @old->replacedby to @new | ||
988 | * | 992 | * |
989 | * Will duplicate and refcount elements that @new inherits from @old | 993 | * Will duplicate and refcount elements that @new inherits from @old |
990 | * and will inherit @old children. | 994 | * and will inherit @old children. |
@@ -993,7 +997,8 @@ static struct aa_profile *__list_lookup_parent(struct list_head *lh, | |||
993 | * | 997 | * |
994 | * Requires: namespace list lock be held, or list not be shared | 998 | * Requires: namespace list lock be held, or list not be shared |
995 | */ | 999 | */ |
996 | static void __replace_profile(struct aa_profile *old, struct aa_profile *new) | 1000 | static void __replace_profile(struct aa_profile *old, struct aa_profile *new, |
1001 | bool share_replacedby) | ||
997 | { | 1002 | { |
998 | struct aa_profile *child, *tmp; | 1003 | struct aa_profile *child, *tmp; |
999 | 1004 | ||
@@ -1008,7 +1013,7 @@ static void __replace_profile(struct aa_profile *old, struct aa_profile *new) | |||
1008 | p = __find_child(&new->base.profiles, child->base.name); | 1013 | p = __find_child(&new->base.profiles, child->base.name); |
1009 | if (p) { | 1014 | if (p) { |
1010 | /* @p replaces @child */ | 1015 | /* @p replaces @child */ |
1011 | __replace_profile(child, p); | 1016 | __replace_profile(child, p, share_replacedby); |
1012 | continue; | 1017 | continue; |
1013 | } | 1018 | } |
1014 | 1019 | ||
@@ -1027,8 +1032,11 @@ static void __replace_profile(struct aa_profile *old, struct aa_profile *new) | |||
1027 | struct aa_profile *parent = rcu_dereference(old->parent); | 1032 | struct aa_profile *parent = rcu_dereference(old->parent); |
1028 | rcu_assign_pointer(new->parent, aa_get_profile(parent)); | 1033 | rcu_assign_pointer(new->parent, aa_get_profile(parent)); |
1029 | } | 1034 | } |
1030 | /* released by free_profile */ | 1035 | __aa_update_replacedby(old, new); |
1031 | old->replacedby = aa_get_profile(new); | 1036 | if (share_replacedby) { |
1037 | aa_put_replacedby(new->replacedby); | ||
1038 | new->replacedby = aa_get_replacedby(old->replacedby); | ||
1039 | } | ||
1032 | 1040 | ||
1033 | if (list_empty(&new->base.list)) { | 1041 | if (list_empty(&new->base.list)) { |
1034 | /* new is not on a list already */ | 1042 | /* new is not on a list already */ |
@@ -1152,23 +1160,24 @@ ssize_t aa_replace_profiles(void *udata, size_t size, bool noreplace) | |||
1152 | audit_policy(op, GFP_ATOMIC, ent->new->base.name, NULL, error); | 1160 | audit_policy(op, GFP_ATOMIC, ent->new->base.name, NULL, error); |
1153 | 1161 | ||
1154 | if (ent->old) { | 1162 | if (ent->old) { |
1155 | __replace_profile(ent->old, ent->new); | 1163 | __replace_profile(ent->old, ent->new, 1); |
1156 | if (ent->rename) | 1164 | if (ent->rename) |
1157 | __replace_profile(ent->rename, ent->new); | 1165 | __replace_profile(ent->rename, ent->new, 0); |
1158 | } else if (ent->rename) { | 1166 | } else if (ent->rename) { |
1159 | __replace_profile(ent->rename, ent->new); | 1167 | __replace_profile(ent->rename, ent->new, 0); |
1160 | } else if (ent->new->parent) { | 1168 | } else if (ent->new->parent) { |
1161 | struct aa_profile *parent, *newest; | 1169 | struct aa_profile *parent, *newest; |
1162 | parent = rcu_dereference_protected(ent->new->parent, | 1170 | parent = rcu_dereference_protected(ent->new->parent, |
1163 | mutex_is_locked(&ns->lock)); | 1171 | mutex_is_locked(&ns->lock)); |
1164 | newest = aa_newest_version(parent); | 1172 | newest = aa_get_newest_profile(parent); |
1165 | 1173 | ||
1166 | /* parent replaced in this atomic set? */ | 1174 | /* parent replaced in this atomic set? */ |
1167 | if (newest != parent) { | 1175 | if (newest != parent) { |
1168 | aa_get_profile(newest); | 1176 | aa_get_profile(newest); |
1169 | aa_put_profile(parent); | 1177 | aa_put_profile(parent); |
1170 | rcu_assign_pointer(ent->new->parent, newest); | 1178 | rcu_assign_pointer(ent->new->parent, newest); |
1171 | } | 1179 | } else |
1180 | aa_put_profile(newest); | ||
1172 | __list_add_profile(&parent->base.profiles, ent->new); | 1181 | __list_add_profile(&parent->base.profiles, ent->new); |
1173 | } else | 1182 | } else |
1174 | __list_add_profile(&ns->base.profiles, ent->new); | 1183 | __list_add_profile(&ns->base.profiles, ent->new); |