Skip to content

Commit cbfcd13

Browse files
WOnder93pcmoore
authored andcommitted
selinux: fix race condition when computing ocontext SIDs
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: 1da177e ("Linux-2.6.12-rc2") Signed-off-by: Ondrej Mosnacek <[email protected]> Signed-off-by: Paul Moore <[email protected]>
1 parent 4342f70 commit cbfcd13

File tree

1 file changed

+77
-85
lines changed

1 file changed

+77
-85
lines changed

security/selinux/ss/services.c

Lines changed: 77 additions & 85 deletions
Original file line numberDiff line numberDiff line change
@@ -2376,6 +2376,43 @@ int security_load_policy(struct selinux_state *state, void *data, size_t len,
23762376
return rc;
23772377
}
23782378

2379+
/**
2380+
* ocontext_to_sid - Helper to safely get sid for an ocontext
2381+
* @sidtab: SID table
2382+
* @c: ocontext structure
2383+
* @index: index of the context entry (0 or 1)
2384+
* @out_sid: pointer to the resulting SID value
2385+
*
2386+
* For all ocontexts except OCON_ISID the SID fields are populated
2387+
* on-demand when needed. Since updating the SID value is an SMP-sensitive
2388+
* operation, this helper must be used to do that safely.
2389+
*
2390+
* WARNING: This function may return -ESTALE, indicating that the caller
2391+
* must retry the operation after re-acquiring the policy pointer!
2392+
*/
2393+
static int ocontext_to_sid(struct sidtab *sidtab, struct ocontext *c,
2394+
size_t index, u32 *out_sid)
2395+
{
2396+
int rc;
2397+
u32 sid;
2398+
2399+
/* Ensure the associated sidtab entry is visible to this thread. */
2400+
sid = smp_load_acquire(&c->sid[index]);
2401+
if (!sid) {
2402+
rc = sidtab_context_to_sid(sidtab, &c->context[index], &sid);
2403+
if (rc)
2404+
return rc;
2405+
2406+
/*
2407+
* Ensure the new sidtab entry is visible to other threads
2408+
* when they see the SID.
2409+
*/
2410+
smp_store_release(&c->sid[index], sid);
2411+
}
2412+
*out_sid = sid;
2413+
return 0;
2414+
}
2415+
23792416
/**
23802417
* security_port_sid - Obtain the SID for a port.
23812418
* @state: SELinux state
@@ -2414,17 +2451,13 @@ int security_port_sid(struct selinux_state *state,
24142451
}
24152452

24162453
if (c) {
2417-
if (!c->sid[0]) {
2418-
rc = sidtab_context_to_sid(sidtab, &c->context[0],
2419-
&c->sid[0]);
2420-
if (rc == -ESTALE) {
2421-
rcu_read_unlock();
2422-
goto retry;
2423-
}
2424-
if (rc)
2425-
goto out;
2454+
rc = ocontext_to_sid(sidtab, c, 0, out_sid);
2455+
if (rc == -ESTALE) {
2456+
rcu_read_unlock();
2457+
goto retry;
24262458
}
2427-
*out_sid = c->sid[0];
2459+
if (rc)
2460+
goto out;
24282461
} else {
24292462
*out_sid = SECINITSID_PORT;
24302463
}
@@ -2473,18 +2506,13 @@ int security_ib_pkey_sid(struct selinux_state *state,
24732506
}
24742507

24752508
if (c) {
2476-
if (!c->sid[0]) {
2477-
rc = sidtab_context_to_sid(sidtab,
2478-
&c->context[0],
2479-
&c->sid[0]);
2480-
if (rc == -ESTALE) {
2481-
rcu_read_unlock();
2482-
goto retry;
2483-
}
2484-
if (rc)
2485-
goto out;
2509+
rc = ocontext_to_sid(sidtab, c, 0, out_sid);
2510+
if (rc == -ESTALE) {
2511+
rcu_read_unlock();
2512+
goto retry;
24862513
}
2487-
*out_sid = c->sid[0];
2514+
if (rc)
2515+
goto out;
24882516
} else
24892517
*out_sid = SECINITSID_UNLABELED;
24902518

@@ -2533,17 +2561,13 @@ int security_ib_endport_sid(struct selinux_state *state,
25332561
}
25342562

25352563
if (c) {
2536-
if (!c->sid[0]) {
2537-
rc = sidtab_context_to_sid(sidtab, &c->context[0],
2538-
&c->sid[0]);
2539-
if (rc == -ESTALE) {
2540-
rcu_read_unlock();
2541-
goto retry;
2542-
}
2543-
if (rc)
2544-
goto out;
2564+
rc = ocontext_to_sid(sidtab, c, 0, out_sid);
2565+
if (rc == -ESTALE) {
2566+
rcu_read_unlock();
2567+
goto retry;
25452568
}
2546-
*out_sid = c->sid[0];
2569+
if (rc)
2570+
goto out;
25472571
} else
25482572
*out_sid = SECINITSID_UNLABELED;
25492573

@@ -2587,25 +2611,13 @@ int security_netif_sid(struct selinux_state *state,
25872611
}
25882612

25892613
if (c) {
2590-
if (!c->sid[0] || !c->sid[1]) {
2591-
rc = sidtab_context_to_sid(sidtab, &c->context[0],
2592-
&c->sid[0]);
2593-
if (rc == -ESTALE) {
2594-
rcu_read_unlock();
2595-
goto retry;
2596-
}
2597-
if (rc)
2598-
goto out;
2599-
rc = sidtab_context_to_sid(sidtab, &c->context[1],
2600-
&c->sid[1]);
2601-
if (rc == -ESTALE) {
2602-
rcu_read_unlock();
2603-
goto retry;
2604-
}
2605-
if (rc)
2606-
goto out;
2614+
rc = ocontext_to_sid(sidtab, c, 0, if_sid);
2615+
if (rc == -ESTALE) {
2616+
rcu_read_unlock();
2617+
goto retry;
26072618
}
2608-
*if_sid = c->sid[0];
2619+
if (rc)
2620+
goto out;
26092621
} else
26102622
*if_sid = SECINITSID_NETIF;
26112623

@@ -2697,18 +2709,13 @@ int security_node_sid(struct selinux_state *state,
26972709
}
26982710

26992711
if (c) {
2700-
if (!c->sid[0]) {
2701-
rc = sidtab_context_to_sid(sidtab,
2702-
&c->context[0],
2703-
&c->sid[0]);
2704-
if (rc == -ESTALE) {
2705-
rcu_read_unlock();
2706-
goto retry;
2707-
}
2708-
if (rc)
2709-
goto out;
2712+
rc = ocontext_to_sid(sidtab, c, 0, out_sid);
2713+
if (rc == -ESTALE) {
2714+
rcu_read_unlock();
2715+
goto retry;
27102716
}
2711-
*out_sid = c->sid[0];
2717+
if (rc)
2718+
goto out;
27122719
} else {
27132720
*out_sid = SECINITSID_NODE;
27142721
}
@@ -2873,7 +2880,7 @@ static inline int __security_genfs_sid(struct selinux_policy *policy,
28732880
u16 sclass;
28742881
struct genfs *genfs;
28752882
struct ocontext *c;
2876-
int rc, cmp = 0;
2883+
int cmp = 0;
28772884

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

2890-
rc = -ENOENT;
28912897
if (!genfs || cmp)
2892-
goto out;
2898+
return -ENOENT;
28932899

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

2901-
rc = -ENOENT;
29022907
if (!c)
2903-
goto out;
2904-
2905-
if (!c->sid[0]) {
2906-
rc = sidtab_context_to_sid(sidtab, &c->context[0], &c->sid[0]);
2907-
if (rc)
2908-
goto out;
2909-
}
2908+
return -ENOENT;
29102909

2911-
*sid = c->sid[0];
2912-
rc = 0;
2913-
out:
2914-
return rc;
2910+
return ocontext_to_sid(sidtab, c, 0, sid);
29152911
}
29162912

29172913
/**
@@ -2996,17 +2992,13 @@ int security_fs_use(struct selinux_state *state, struct super_block *sb)
29962992

29972993
if (c) {
29982994
sbsec->behavior = c->v.behavior;
2999-
if (!c->sid[0]) {
3000-
rc = sidtab_context_to_sid(sidtab, &c->context[0],
3001-
&c->sid[0]);
3002-
if (rc == -ESTALE) {
3003-
rcu_read_unlock();
3004-
goto retry;
3005-
}
3006-
if (rc)
3007-
goto out;
2995+
rc = ocontext_to_sid(sidtab, c, 0, &sbsec->sid);
2996+
if (rc == -ESTALE) {
2997+
rcu_read_unlock();
2998+
goto retry;
30082999
}
3009-
sbsec->sid = c->sid[0];
3000+
if (rc)
3001+
goto out;
30103002
} else {
30113003
rc = __security_genfs_sid(policy, fstype, "/",
30123004
SECCLASS_DIR, &sbsec->sid);

0 commit comments

Comments
 (0)