Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

apparmor: update how unconfined is handled

ns->unconfined is being used read side without locking, nor rcu but is
being updated when a namespace is removed. This works for the root ns
which is never removed but has a race window and can cause failures when
children namespaces are removed.

Also ns and ns->unconfined have a circular refcounting dependency that
is problematic and must be broken. Currently this is done incorrectly
when the namespace is destroyed.

Fix this by forward referencing unconfined via the replacedby infrastructure
instead of directly updating the ns->unconfined pointer.

Remove the circular refcount dependency by making the ns and its unconfined
profile share the same refcount.

Signed-off-by: John Johansen <john.johansen@canonical.com>
Acked-by: Seth Arnold <seth.arnold@canonical.com>

+67 -83
+1 -1
security/apparmor/domain.c
··· 434 434 new_profile = aa_get_profile(profile); 435 435 goto x_clear; 436 436 } else if (perms.xindex & AA_X_UNCONFINED) { 437 - new_profile = aa_get_profile(ns->unconfined); 437 + new_profile = aa_get_newest_profile(ns->unconfined); 438 438 info = "ux fallback"; 439 439 } else { 440 440 error = -ENOENT;
+39 -41
security/apparmor/include/policy.h
··· 68 68 PFLAG_NO_LIST_REF = 0x40, /* list doesn't keep profile ref */ 69 69 PFLAG_OLD_NULL_TRANS = 0x100, /* use // as the null transition */ 70 70 PFLAG_INVALID = 0x200, /* profile replaced/removed */ 71 + PFLAG_NS_COUNT = 0x400, /* carries NS ref count */ 71 72 72 73 /* These flags must correspond with PATH_flags */ 73 74 PFLAG_MEDIATE_DELETED = 0x10000, /* mediate instead delegate deleted */ ··· 79 78 /* struct aa_policy - common part of both namespaces and profiles 80 79 * @name: name of the object 81 80 * @hname - The hierarchical name 82 - * @count: reference count of the obj 83 81 * @list: list policy object is on 84 82 * @rcu: rcu head used when removing from @list 85 83 * @profiles: head of the profiles list contained in the object ··· 86 86 struct aa_policy { 87 87 char *name; 88 88 char *hname; 89 - struct kref count; 90 89 struct list_head list; 91 90 struct list_head profiles; 92 91 struct rcu_head rcu; ··· 156 157 157 158 /* struct aa_profile - basic confinement data 158 159 * @base - base components of the profile (name, refcount, lists, lock ...) 160 + * @count: reference count of the obj 159 161 * @parent: parent of profile 160 162 * @ns: namespace the profile is in 161 163 * @replacedby: is set to the profile that replaced this profile ··· 189 189 */ 190 190 struct aa_profile { 191 191 struct aa_policy base; 192 + struct kref count; 192 193 struct aa_profile __rcu *parent; 193 194 194 195 struct aa_namespace *ns; ··· 224 223 struct aa_namespace *aa_find_namespace(struct aa_namespace *root, 225 224 const char *name); 226 225 227 - static inline struct aa_policy *aa_get_common(struct aa_policy *c) 228 - { 229 - if (c) 230 - kref_get(&c->count); 231 - 232 - return c; 233 - } 234 - 235 - /** 236 - * aa_get_namespace - increment references count on @ns 237 - * @ns: namespace to increment reference count of (MAYBE NULL) 238 - * 239 - * Returns: pointer to @ns, if @ns is NULL returns NULL 240 - * Requires: @ns must be held with valid refcount when called 241 - */ 242 - static inline struct aa_namespace *aa_get_namespace(struct aa_namespace *ns) 243 - { 244 - if (ns) 245 - kref_get(&(ns->base.count)); 246 - 247 - return ns; 248 - } 249 - 250 - /** 251 - * aa_put_namespace - decrement refcount on @ns 252 - * @ns: namespace to put reference of 253 - * 254 - * Decrement reference count of @ns and if no longer in use free it 255 - */ 256 - static inline void aa_put_namespace(struct aa_namespace *ns) 257 - { 258 - if (ns) 259 - kref_put(&ns->base.count, aa_free_namespace_kref); 260 - } 261 226 262 227 void aa_free_replacedby_kref(struct kref *kref); 263 228 struct aa_profile *aa_alloc_profile(const char *name); ··· 252 285 static inline struct aa_profile *aa_get_profile(struct aa_profile *p) 253 286 { 254 287 if (p) 255 - kref_get(&(p->base.count)); 288 + kref_get(&(p->count)); 256 289 257 290 return p; 258 291 } ··· 266 299 */ 267 300 static inline struct aa_profile *aa_get_profile_not0(struct aa_profile *p) 268 301 { 269 - if (p && kref_get_not0(&p->base.count)) 302 + if (p && kref_get_not0(&p->count)) 270 303 return p; 271 304 272 305 return NULL; ··· 286 319 rcu_read_lock(); 287 320 do { 288 321 c = rcu_dereference(*p); 289 - } while (c && !kref_get_not0(&c->base.count)); 322 + } while (c && !kref_get_not0(&c->count)); 290 323 rcu_read_unlock(); 291 324 292 325 return c; ··· 317 350 */ 318 351 static inline void aa_put_profile(struct aa_profile *p) 319 352 { 320 - if (p) 321 - kref_put(&p->base.count, aa_free_profile_kref); 353 + if (p) { 354 + if (p->flags & PFLAG_NS_COUNT) 355 + kref_put(&p->count, aa_free_namespace_kref); 356 + else 357 + kref_put(&p->count, aa_free_profile_kref); 358 + } 322 359 } 323 360 324 361 static inline struct aa_replacedby *aa_get_replacedby(struct aa_replacedby *p) ··· 347 376 rcu_assign_pointer(orig->replacedby->profile, aa_get_profile(new)); 348 377 orig->flags |= PFLAG_INVALID; 349 378 aa_put_profile(tmp); 379 + } 380 + 381 + /** 382 + * aa_get_namespace - increment references count on @ns 383 + * @ns: namespace to increment reference count of (MAYBE NULL) 384 + * 385 + * Returns: pointer to @ns, if @ns is NULL returns NULL 386 + * Requires: @ns must be held with valid refcount when called 387 + */ 388 + static inline struct aa_namespace *aa_get_namespace(struct aa_namespace *ns) 389 + { 390 + if (ns) 391 + aa_get_profile(ns->unconfined); 392 + 393 + return ns; 394 + } 395 + 396 + /** 397 + * aa_put_namespace - decrement refcount on @ns 398 + * @ns: namespace to put reference of 399 + * 400 + * Decrement reference count of @ns and if no longer in use free it 401 + */ 402 + static inline void aa_put_namespace(struct aa_namespace *ns) 403 + { 404 + if (ns) 405 + aa_put_profile(ns->unconfined); 350 406 } 351 407 352 408 static inline int AUDIT_MODE(struct aa_profile *profile)
+27 -41
security/apparmor/policy.c
··· 141 141 policy->name = (char *)hname_tail(policy->hname); 142 142 INIT_LIST_HEAD(&policy->list); 143 143 INIT_LIST_HEAD(&policy->profiles); 144 - kref_init(&policy->count); 145 144 146 145 return 1; 147 146 } ··· 291 292 goto fail_unconfined; 292 293 293 294 ns->unconfined->flags = PFLAG_UNCONFINED | PFLAG_IX_ON_NAME_ERROR | 294 - PFLAG_IMMUTABLE; 295 + PFLAG_IMMUTABLE | PFLAG_NS_COUNT; 295 296 296 - /* 297 - * released by free_namespace, however __remove_namespace breaks 298 - * the cyclic references (ns->unconfined, and unconfined->ns) and 299 - * replaces with refs to parent namespace unconfined 300 - */ 301 - ns->unconfined->ns = aa_get_namespace(ns); 297 + /* ns and ns->unconfined share ns->unconfined refcount */ 298 + ns->unconfined->ns = ns; 302 299 303 300 atomic_set(&ns->uniq_null, 0); 304 301 ··· 307 312 return NULL; 308 313 } 309 314 315 + static void free_profile(struct aa_profile *profile); 310 316 /** 311 317 * free_namespace - free a profile namespace 312 318 * @ns: the namespace to free (MAYBE NULL) ··· 323 327 policy_destroy(&ns->base); 324 328 aa_put_namespace(ns->parent); 325 329 326 - if (ns->unconfined && ns->unconfined->ns == ns) 327 - ns->unconfined->ns = NULL; 328 - 329 - aa_put_profile(ns->unconfined); 330 + ns->unconfined->ns = NULL; 331 + free_profile(ns->unconfined); 330 332 kzfree(ns); 333 + } 334 + 335 + /** 336 + * aa_free_namespace_rcu - free aa_namespace by rcu 337 + * @head: rcu_head callback for freeing of a profile (NOT NULL) 338 + * 339 + * rcu_head is to the unconfined profile associated with the namespace 340 + */ 341 + static void aa_free_namespace_rcu(struct rcu_head *head) 342 + { 343 + struct aa_profile *p = container_of(head, struct aa_profile, base.rcu); 344 + free_namespace(p->ns); 331 345 } 332 346 333 347 /** 334 348 * aa_free_namespace_kref - free aa_namespace by kref (see aa_put_namespace) 335 349 * @kr: kref callback for freeing of a namespace (NOT NULL) 350 + * 351 + * kref is to the unconfined profile associated with the namespace 336 352 */ 337 353 void aa_free_namespace_kref(struct kref *kref) 338 354 { 339 - free_namespace(container_of(kref, struct aa_namespace, base.count)); 355 + struct aa_profile *p = container_of(kref, struct aa_profile, count); 356 + call_rcu(&p->base.rcu, aa_free_namespace_rcu); 340 357 } 341 358 342 359 /** ··· 503 494 */ 504 495 static void destroy_namespace(struct aa_namespace *ns) 505 496 { 506 - struct aa_profile *unconfined; 507 - 508 497 if (!ns) 509 498 return; 510 499 ··· 513 506 /* release all sub namespaces */ 514 507 __ns_list_release(&ns->sub_ns); 515 508 516 - unconfined = ns->unconfined; 517 - /* 518 - * break the ns, unconfined profile cyclic reference and forward 519 - * all new unconfined profiles requests to the parent namespace 520 - * This will result in all confined tasks that have a profile 521 - * being removed, inheriting the parent->unconfined profile. 522 - */ 523 509 if (ns->parent) 524 - ns->unconfined = aa_get_profile(ns->parent->unconfined); 525 - 526 - /* release original ns->unconfined ref */ 527 - aa_put_profile(unconfined); 528 - 510 + __aa_update_replacedby(ns->unconfined, ns->parent->unconfined); 529 511 mutex_unlock(&ns->lock); 530 - } 531 - 532 - static void aa_put_ns_rcu(struct rcu_head *head) 533 - { 534 - struct aa_namespace *ns = container_of(head, struct aa_namespace, 535 - base.rcu); 536 - /* release ns->base.list ref */ 537 - aa_put_namespace(ns); 538 512 } 539 513 540 514 /** ··· 528 540 { 529 541 /* remove ns from namespace list */ 530 542 list_del_rcu(&ns->base.list); 531 - 532 543 destroy_namespace(ns); 533 - 534 - call_rcu(&ns->base.rcu, aa_put_ns_rcu); 544 + aa_put_namespace(ns); 535 545 } 536 546 537 547 /** ··· 642 656 */ 643 657 void aa_free_profile_kref(struct kref *kref) 644 658 { 645 - struct aa_profile *p = container_of(kref, struct aa_profile, 646 - base.count); 659 + struct aa_profile *p = container_of(kref, struct aa_profile, count); 647 660 call_rcu(&p->base.rcu, aa_free_profile_rcu); 648 661 } 649 662 ··· 668 683 669 684 if (!policy_init(&profile->base, NULL, hname)) 670 685 goto fail; 686 + kref_init(&profile->count); 671 687 672 688 /* refcount released by caller */ 673 689 return profile; ··· 870 884 871 885 /* the unconfined profile is not in the regular profile list */ 872 886 if (!profile && strcmp(hname, "unconfined") == 0) 873 - profile = aa_get_profile(ns->unconfined); 887 + profile = aa_get_newest_profile(ns->unconfined); 874 888 875 889 /* refcount released by caller */ 876 890 return profile;