Skip to content

Commit

Permalink
selinux: fix race condition when computing ocontext SIDs
Browse files Browse the repository at this point in the history
Current code contains a lot of racy patterns when converting an
ocontext's context structure to an SID. This is being done in a "lazy"
fashion, such that the SID is looked up in the SID table only when it's
first needed and then cached in the "sid" field of the ocontext
structure. However, this is done without any locking or memory barriers
and is thus unsafe.

Between commits 24ed7fd ("selinux: use separate table for initial
SID lookup") and 66f8e2f ("selinux: sidtab reverse lookup hash
table"), this race condition lead to an actual observable bug, because a
pointer to the shared sid field was passed directly to
sidtab_context_to_sid(), which was using this location to also store an
intermediate value, which could have been read by other threads and
interpreted as an SID. In practice this caused e.g. new mounts to get a
wrong (seemingly random) filesystem context, leading to strange denials.
This bug has been spotted in the wild at least twice, see [1] and [2].

Fix the race condition by making all the racy functions use a common
helper that ensures the ocontext::sid accesses are made safely using the
appropriate SMP constructs.

Note that security_netif_sid() was populating the sid field of both
contexts stored in the ocontext, but only the first one was actually
used. The SELinux wiki's documentation on the "netifcon" policy
statement [3] suggests that using only the first context is intentional.
I kept only the handling of the first context here, as there is really
no point in doing the SID lookup for the unused one.

I wasn't able to reproduce the bug mentioned above on any kernel that
includes commit 66f8e2f, even though it has been reported that the
issue occurs with that commit, too, just less frequently. Thus, I wasn't
able to verify that this patch fixes the issue, but it makes sense to
avoid the race condition regardless.

[1] containers/container-selinux#89
[2] https://lists.fedoraproject.org/archives/list/[email protected]/thread/6DMTAMHIOAOEMUAVTULJD45JZU7IBAFM/
[3] https://selinuxproject.org/page/NetworkStatements#netifcon

Cc: [email protected]
Cc: Xinjie Zheng <[email protected]>
Reported-by: Sujithra Periasamy <[email protected]>
Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2")
Signed-off-by: Ondrej Mosnacek <[email protected]>
Signed-off-by: Paul Moore <[email protected]>
  • Loading branch information
WOnder93 authored and pcmoore committed Oct 11, 2021
1 parent 4342f70 commit cbfcd13
Showing 1 changed file with 77 additions and 85 deletions.
162 changes: 77 additions & 85 deletions security/selinux/ss/services.c
Original file line number Diff line number Diff line change
Expand Up @@ -2376,6 +2376,43 @@ int security_load_policy(struct selinux_state *state, void *data, size_t len,
return rc;
}

/**
* ocontext_to_sid - Helper to safely get sid for an ocontext
* @sidtab: SID table
* @c: ocontext structure
* @index: index of the context entry (0 or 1)
* @out_sid: pointer to the resulting SID value
*
* For all ocontexts except OCON_ISID the SID fields are populated
* on-demand when needed. Since updating the SID value is an SMP-sensitive
* operation, this helper must be used to do that safely.
*
* WARNING: This function may return -ESTALE, indicating that the caller
* must retry the operation after re-acquiring the policy pointer!
*/
static int ocontext_to_sid(struct sidtab *sidtab, struct ocontext *c,
size_t index, u32 *out_sid)
{
int rc;
u32 sid;

/* Ensure the associated sidtab entry is visible to this thread. */
sid = smp_load_acquire(&c->sid[index]);
if (!sid) {
rc = sidtab_context_to_sid(sidtab, &c->context[index], &sid);
if (rc)
return rc;

/*
* Ensure the new sidtab entry is visible to other threads
* when they see the SID.
*/
smp_store_release(&c->sid[index], sid);
}
*out_sid = sid;
return 0;
}

/**
* security_port_sid - Obtain the SID for a port.
* @state: SELinux state
Expand Down Expand Up @@ -2414,17 +2451,13 @@ int security_port_sid(struct selinux_state *state,
}

if (c) {
if (!c->sid[0]) {
rc = sidtab_context_to_sid(sidtab, &c->context[0],
&c->sid[0]);
if (rc == -ESTALE) {
rcu_read_unlock();
goto retry;
}
if (rc)
goto out;
rc = ocontext_to_sid(sidtab, c, 0, out_sid);
if (rc == -ESTALE) {
rcu_read_unlock();
goto retry;
}
*out_sid = c->sid[0];
if (rc)
goto out;
} else {
*out_sid = SECINITSID_PORT;
}
Expand Down Expand Up @@ -2473,18 +2506,13 @@ int security_ib_pkey_sid(struct selinux_state *state,
}

if (c) {
if (!c->sid[0]) {
rc = sidtab_context_to_sid(sidtab,
&c->context[0],
&c->sid[0]);
if (rc == -ESTALE) {
rcu_read_unlock();
goto retry;
}
if (rc)
goto out;
rc = ocontext_to_sid(sidtab, c, 0, out_sid);
if (rc == -ESTALE) {
rcu_read_unlock();
goto retry;
}
*out_sid = c->sid[0];
if (rc)
goto out;
} else
*out_sid = SECINITSID_UNLABELED;

Expand Down Expand Up @@ -2533,17 +2561,13 @@ int security_ib_endport_sid(struct selinux_state *state,
}

if (c) {
if (!c->sid[0]) {
rc = sidtab_context_to_sid(sidtab, &c->context[0],
&c->sid[0]);
if (rc == -ESTALE) {
rcu_read_unlock();
goto retry;
}
if (rc)
goto out;
rc = ocontext_to_sid(sidtab, c, 0, out_sid);
if (rc == -ESTALE) {
rcu_read_unlock();
goto retry;
}
*out_sid = c->sid[0];
if (rc)
goto out;
} else
*out_sid = SECINITSID_UNLABELED;

Expand Down Expand Up @@ -2587,25 +2611,13 @@ int security_netif_sid(struct selinux_state *state,
}

if (c) {
if (!c->sid[0] || !c->sid[1]) {
rc = sidtab_context_to_sid(sidtab, &c->context[0],
&c->sid[0]);
if (rc == -ESTALE) {
rcu_read_unlock();
goto retry;
}
if (rc)
goto out;
rc = sidtab_context_to_sid(sidtab, &c->context[1],
&c->sid[1]);
if (rc == -ESTALE) {
rcu_read_unlock();
goto retry;
}
if (rc)
goto out;
rc = ocontext_to_sid(sidtab, c, 0, if_sid);
if (rc == -ESTALE) {
rcu_read_unlock();
goto retry;
}
*if_sid = c->sid[0];
if (rc)
goto out;
} else
*if_sid = SECINITSID_NETIF;

Expand Down Expand Up @@ -2697,18 +2709,13 @@ int security_node_sid(struct selinux_state *state,
}

if (c) {
if (!c->sid[0]) {
rc = sidtab_context_to_sid(sidtab,
&c->context[0],
&c->sid[0]);
if (rc == -ESTALE) {
rcu_read_unlock();
goto retry;
}
if (rc)
goto out;
rc = ocontext_to_sid(sidtab, c, 0, out_sid);
if (rc == -ESTALE) {
rcu_read_unlock();
goto retry;
}
*out_sid = c->sid[0];
if (rc)
goto out;
} else {
*out_sid = SECINITSID_NODE;
}
Expand Down Expand Up @@ -2873,7 +2880,7 @@ static inline int __security_genfs_sid(struct selinux_policy *policy,
u16 sclass;
struct genfs *genfs;
struct ocontext *c;
int rc, cmp = 0;
int cmp = 0;

while (path[0] == '/' && path[1] == '/')
path++;
Expand All @@ -2887,9 +2894,8 @@ static inline int __security_genfs_sid(struct selinux_policy *policy,
break;
}

rc = -ENOENT;
if (!genfs || cmp)
goto out;
return -ENOENT;

for (c = genfs->head; c; c = c->next) {
len = strlen(c->u.name);
Expand All @@ -2898,20 +2904,10 @@ static inline int __security_genfs_sid(struct selinux_policy *policy,
break;
}

rc = -ENOENT;
if (!c)
goto out;

if (!c->sid[0]) {
rc = sidtab_context_to_sid(sidtab, &c->context[0], &c->sid[0]);
if (rc)
goto out;
}
return -ENOENT;

*sid = c->sid[0];
rc = 0;
out:
return rc;
return ocontext_to_sid(sidtab, c, 0, sid);
}

/**
Expand Down Expand Up @@ -2996,17 +2992,13 @@ int security_fs_use(struct selinux_state *state, struct super_block *sb)

if (c) {
sbsec->behavior = c->v.behavior;
if (!c->sid[0]) {
rc = sidtab_context_to_sid(sidtab, &c->context[0],
&c->sid[0]);
if (rc == -ESTALE) {
rcu_read_unlock();
goto retry;
}
if (rc)
goto out;
rc = ocontext_to_sid(sidtab, c, 0, &sbsec->sid);
if (rc == -ESTALE) {
rcu_read_unlock();
goto retry;
}
sbsec->sid = c->sid[0];
if (rc)
goto out;
} else {
rc = __security_genfs_sid(policy, fstype, "/",
SECCLASS_DIR, &sbsec->sid);
Expand Down

0 comments on commit cbfcd13

Please sign in to comment.