Skip to content

Commit 32e9276

Browse files
canonical-rlee287jrjohansen
authored andcommitted
apparmor: grab ns lock and refresh when looking up changehat child profiles
There was a race condition involving change_hat and profile replacement in which replacement of the parent profile during a changehat operation could result in the list of children becoming empty and the changehat operation failing. To prevent this: - grab the namespace lock until we've built the hat transition, and - use aa_get_newest_profile to avoid using stale profile objects. Link: https://bugs.launchpad.net/bugs/2139664 Fixes: 89dbf19 ("apparmor: move change_hat mediation to using labels") Reviewed-by: Georgia Garcia <georgia.garcia@canonical.com> Signed-off-by: Ryan Lee <ryan.lee@canonical.com> Signed-off-by: John Johansen <john.johansen@canonical.com>
1 parent ad213bb commit 32e9276

1 file changed

Lines changed: 31 additions & 2 deletions

File tree

security/apparmor/domain.c

Lines changed: 31 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
#include <linux/fs.h>
1313
#include <linux/file.h>
1414
#include <linux/mount.h>
15+
#include <linux/mutex.h>
1516
#include <linux/syscalls.h>
1617
#include <linux/personality.h>
1718
#include <linux/xattr.h>
@@ -1109,6 +1110,7 @@ static struct aa_label *change_hat(const struct cred *subj_cred,
11091110
int count, int flags)
11101111
{
11111112
struct aa_profile *profile, *root, *hat = NULL;
1113+
struct aa_ns *ns, *new_ns;
11121114
struct aa_label *new;
11131115
struct label_it it;
11141116
bool sibling = false;
@@ -1119,6 +1121,32 @@ static struct aa_label *change_hat(const struct cred *subj_cred,
11191121
AA_BUG(!hats);
11201122
AA_BUG(count < 1);
11211123

1124+
/*
1125+
* Acquire the newest label and then hold the lock until we choose a
1126+
* hat, so that profile replacement doesn't atomically truncate the
1127+
* list of potential hats. Because we are getting the namespaces from
1128+
* the profiles and label, we can rely on the namespaces being live
1129+
* and avoid incrementing their refcounts while grabbing the lock.
1130+
*/
1131+
label = aa_get_label(label);
1132+
ns = labels_ns(label);
1133+
1134+
retry:
1135+
mutex_lock_nested(&ns->lock, ns->level);
1136+
if (label_is_stale(label)) {
1137+
new = aa_get_newest_label(label);
1138+
new_ns = labels_ns(new);
1139+
if (new_ns != ns) {
1140+
aa_put_label(new);
1141+
mutex_unlock(&ns->lock);
1142+
ns = new_ns;
1143+
label = new;
1144+
goto retry;
1145+
}
1146+
aa_put_label(label);
1147+
label = new;
1148+
}
1149+
11221150
if (PROFILE_IS_HAT(labels_profile(label)))
11231151
sibling = true;
11241152

@@ -1127,7 +1155,7 @@ static struct aa_label *change_hat(const struct cred *subj_cred,
11271155
name = hats[i];
11281156
label_for_each_in_scope(it, labels_ns(label), label, profile) {
11291157
if (sibling && PROFILE_IS_HAT(profile)) {
1130-
root = aa_get_profile_rcu(&profile->parent);
1158+
root = aa_get_profile(profile->parent);
11311159
} else if (!sibling && !PROFILE_IS_HAT(profile)) {
11321160
root = aa_get_profile(profile);
11331161
} else { /* conflicting change type */
@@ -1187,6 +1215,7 @@ static struct aa_label *change_hat(const struct cred *subj_cred,
11871215
GLOBAL_ROOT_UID, info, error);
11881216
}
11891217
}
1218+
mutex_unlock(&ns->lock);
11901219
return ERR_PTR(error);
11911220

11921221
build:
@@ -1199,7 +1228,7 @@ static struct aa_label *change_hat(const struct cred *subj_cred,
11991228
error = -ENOMEM;
12001229
goto fail;
12011230
} /* else if (IS_ERR) build_change_hat has logged error so return new */
1202-
1231+
mutex_unlock(&ns->lock);
12031232
return new;
12041233
}
12051234

0 commit comments

Comments
 (0)