Skip to content

Commit 8c62ed2

Browse files
committed
apparmor: fix aa_xattrs_match() may sleep while holding a RCU lock
aa_xattrs_match() is unfortunately calling vfs_getxattr_alloc() from a context protected by an rcu_read_lock. This can not be done as vfs_getxattr_alloc() may sleep regardles of the gfp_t value being passed to it. Fix this by breaking the rcu_read_lock on the policy search when the xattr match feature is requested and restarting the search if a policy changes occur. Fixes: 8e51f90 ("apparmor: Add support for attaching profiles via xattr, presence and value") Reported-by: Jia-Ju Bai <[email protected]> Reported-by: Al Viro <[email protected]> Signed-off-by: John Johansen <[email protected]>
1 parent 20d4e80 commit 8c62ed2

File tree

3 files changed

+46
-42
lines changed

3 files changed

+46
-42
lines changed

security/apparmor/apparmorfs.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -623,7 +623,7 @@ static __poll_t ns_revision_poll(struct file *file, poll_table *pt)
623623

624624
void __aa_bump_ns_revision(struct aa_ns *ns)
625625
{
626-
ns->revision++;
626+
WRITE_ONCE(ns->revision, ns->revision + 1);
627627
wake_up_interruptible(&ns->wait);
628628
}
629629

security/apparmor/domain.c

Lines changed: 43 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -317,6 +317,7 @@ static int aa_xattrs_match(const struct linux_binprm *bprm,
317317

318318
if (!bprm || !profile->xattr_count)
319319
return 0;
320+
might_sleep();
320321

321322
/* transition from exec match to xattr set */
322323
state = aa_dfa_null_transition(profile->xmatch, state);
@@ -361,10 +362,11 @@ static int aa_xattrs_match(const struct linux_binprm *bprm,
361362
}
362363

363364
/**
364-
* __attach_match_ - find an attachment match
365+
* find_attach - do attachment search for unconfined processes
365366
* @bprm - binprm structure of transitioning task
366-
* @name - to match against (NOT NULL)
367+
* @ns: the current namespace (NOT NULL)
367368
* @head - profile list to walk (NOT NULL)
369+
* @name - to match against (NOT NULL)
368370
* @info - info message if there was an error (NOT NULL)
369371
*
370372
* Do a linear search on the profiles in the list. There is a matching
@@ -374,12 +376,11 @@ static int aa_xattrs_match(const struct linux_binprm *bprm,
374376
*
375377
* Requires: @head not be shared or have appropriate locks held
376378
*
377-
* Returns: profile or NULL if no match found
379+
* Returns: label or NULL if no match found
378380
*/
379-
static struct aa_profile *__attach_match(const struct linux_binprm *bprm,
380-
const char *name,
381-
struct list_head *head,
382-
const char **info)
381+
static struct aa_label *find_attach(const struct linux_binprm *bprm,
382+
struct aa_ns *ns, struct list_head *head,
383+
const char *name, const char **info)
383384
{
384385
int candidate_len = 0, candidate_xattrs = 0;
385386
bool conflict = false;
@@ -388,6 +389,8 @@ static struct aa_profile *__attach_match(const struct linux_binprm *bprm,
388389
AA_BUG(!name);
389390
AA_BUG(!head);
390391

392+
rcu_read_lock();
393+
restart:
391394
list_for_each_entry_rcu(profile, head, base.list) {
392395
if (profile->label.flags & FLAG_NULL &&
393396
&profile->label == ns_unconfined(profile->ns))
@@ -413,16 +416,32 @@ static struct aa_profile *__attach_match(const struct linux_binprm *bprm,
413416
perm = dfa_user_allow(profile->xmatch, state);
414417
/* any accepting state means a valid match. */
415418
if (perm & MAY_EXEC) {
416-
int ret;
419+
int ret = 0;
417420

418421
if (count < candidate_len)
419422
continue;
420423

421-
ret = aa_xattrs_match(bprm, profile, state);
422-
/* Fail matching if the xattrs don't match */
423-
if (ret < 0)
424-
continue;
425-
424+
if (bprm && profile->xattr_count) {
425+
long rev = READ_ONCE(ns->revision);
426+
427+
if (!aa_get_profile_not0(profile))
428+
goto restart;
429+
rcu_read_unlock();
430+
ret = aa_xattrs_match(bprm, profile,
431+
state);
432+
rcu_read_lock();
433+
aa_put_profile(profile);
434+
if (rev !=
435+
READ_ONCE(ns->revision))
436+
/* policy changed */
437+
goto restart;
438+
/*
439+
* Fail matching if the xattrs don't
440+
* match
441+
*/
442+
if (ret < 0)
443+
continue;
444+
}
426445
/*
427446
* TODO: allow for more flexible best match
428447
*
@@ -445,43 +464,28 @@ static struct aa_profile *__attach_match(const struct linux_binprm *bprm,
445464
candidate_xattrs = ret;
446465
conflict = false;
447466
}
448-
} else if (!strcmp(profile->base.name, name))
467+
} else if (!strcmp(profile->base.name, name)) {
449468
/*
450469
* old exact non-re match, without conditionals such
451470
* as xattrs. no more searching required
452471
*/
453-
return profile;
472+
candidate = profile;
473+
goto out;
474+
}
454475
}
455476

456-
if (conflict) {
457-
*info = "conflicting profile attachments";
477+
if (!candidate || conflict) {
478+
if (conflict)
479+
*info = "conflicting profile attachments";
480+
rcu_read_unlock();
458481
return NULL;
459482
}
460483

461-
return candidate;
462-
}
463-
464-
/**
465-
* find_attach - do attachment search for unconfined processes
466-
* @bprm - binprm structure of transitioning task
467-
* @ns: the current namespace (NOT NULL)
468-
* @list: list to search (NOT NULL)
469-
* @name: the executable name to match against (NOT NULL)
470-
* @info: info message if there was an error
471-
*
472-
* Returns: label or NULL if no match found
473-
*/
474-
static struct aa_label *find_attach(const struct linux_binprm *bprm,
475-
struct aa_ns *ns, struct list_head *list,
476-
const char *name, const char **info)
477-
{
478-
struct aa_profile *profile;
479-
480-
rcu_read_lock();
481-
profile = aa_get_profile(__attach_match(bprm, name, list, info));
484+
out:
485+
candidate = aa_get_newest_profile(candidate);
482486
rcu_read_unlock();
483487

484-
return profile ? &profile->label : NULL;
488+
return &candidate->label;
485489
}
486490

487491
static const char *next_name(int xtype, const char *name)

security/apparmor/policy.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1125,8 +1125,8 @@ ssize_t aa_remove_profiles(struct aa_ns *policy_ns, struct aa_label *subj,
11251125
if (!name) {
11261126
/* remove namespace - can only happen if fqname[0] == ':' */
11271127
mutex_lock_nested(&ns->parent->lock, ns->level);
1128-
__aa_remove_ns(ns);
11291128
__aa_bump_ns_revision(ns);
1129+
__aa_remove_ns(ns);
11301130
mutex_unlock(&ns->parent->lock);
11311131
} else {
11321132
/* remove profile */
@@ -1138,9 +1138,9 @@ ssize_t aa_remove_profiles(struct aa_ns *policy_ns, struct aa_label *subj,
11381138
goto fail_ns_lock;
11391139
}
11401140
name = profile->base.hname;
1141+
__aa_bump_ns_revision(ns);
11411142
__remove_profile(profile);
11421143
__aa_labelset_update_subtree(ns);
1143-
__aa_bump_ns_revision(ns);
11441144
mutex_unlock(&ns->lock);
11451145
}
11461146

0 commit comments

Comments
 (0)