Skip to content

Commit 5acb878

Browse files
canonical-rlee287gregkh
authored andcommitted
apparmor: grab ns lock and refresh when looking up changehat child profiles
[ Upstream commit 32e9276 ] 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> Signed-off-by: Sasha Levin <sashal@kernel.org>
1 parent 6a26de6 commit 5acb878

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
@@ -13,6 +13,7 @@
1313
#include <linux/fs.h>
1414
#include <linux/file.h>
1515
#include <linux/mount.h>
16+
#include <linux/mutex.h>
1617
#include <linux/syscalls.h>
1718
#include <linux/personality.h>
1819
#include <linux/xattr.h>
@@ -1061,6 +1062,7 @@ static struct aa_label *change_hat(const struct cred *subj_cred,
10611062
int count, int flags)
10621063
{
10631064
struct aa_profile *profile, *root, *hat = NULL;
1065+
struct aa_ns *ns, *new_ns;
10641066
struct aa_label *new;
10651067
struct label_it it;
10661068
bool sibling = false;
@@ -1071,6 +1073,32 @@ static struct aa_label *change_hat(const struct cred *subj_cred,
10711073
AA_BUG(!hats);
10721074
AA_BUG(count < 1);
10731075

1076+
/*
1077+
* Acquire the newest label and then hold the lock until we choose a
1078+
* hat, so that profile replacement doesn't atomically truncate the
1079+
* list of potential hats. Because we are getting the namespaces from
1080+
* the profiles and label, we can rely on the namespaces being live
1081+
* and avoid incrementing their refcounts while grabbing the lock.
1082+
*/
1083+
label = aa_get_label(label);
1084+
ns = labels_ns(label);
1085+
1086+
retry:
1087+
mutex_lock_nested(&ns->lock, ns->level);
1088+
if (label_is_stale(label)) {
1089+
new = aa_get_newest_label(label);
1090+
new_ns = labels_ns(new);
1091+
if (new_ns != ns) {
1092+
aa_put_label(new);
1093+
mutex_unlock(&ns->lock);
1094+
ns = new_ns;
1095+
label = new;
1096+
goto retry;
1097+
}
1098+
aa_put_label(label);
1099+
label = new;
1100+
}
1101+
10741102
if (PROFILE_IS_HAT(labels_profile(label)))
10751103
sibling = true;
10761104

@@ -1079,7 +1107,7 @@ static struct aa_label *change_hat(const struct cred *subj_cred,
10791107
name = hats[i];
10801108
label_for_each_in_ns(it, labels_ns(label), label, profile) {
10811109
if (sibling && PROFILE_IS_HAT(profile)) {
1082-
root = aa_get_profile_rcu(&profile->parent);
1110+
root = aa_get_profile(profile->parent);
10831111
} else if (!sibling && !PROFILE_IS_HAT(profile)) {
10841112
root = aa_get_profile(profile);
10851113
} else { /* conflicting change type */
@@ -1139,6 +1167,7 @@ static struct aa_label *change_hat(const struct cred *subj_cred,
11391167
GLOBAL_ROOT_UID, info, error);
11401168
}
11411169
}
1170+
mutex_unlock(&ns->lock);
11421171
return ERR_PTR(error);
11431172

11441173
build:
@@ -1151,7 +1180,7 @@ static struct aa_label *change_hat(const struct cred *subj_cred,
11511180
error = -ENOMEM;
11521181
goto fail;
11531182
} /* else if (IS_ERR) build_change_hat has logged error so return new */
1154-
1183+
mutex_unlock(&ns->lock);
11551184
return new;
11561185
}
11571186

0 commit comments

Comments
 (0)