stable.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
From: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
To: linux-kernel@vger.kernel.org
Cc: Greg Kroah-Hartman <gregkh@linuxfoundation.org>,
	stable@vger.kernel.org, Xinjie Zheng <xinjie@google.com>,
	Sujithra Periasamy <sujithra@google.com>,
	Ondrej Mosnacek <omosnace@redhat.com>,
	Paul Moore <paul@paul-moore.com>,
	Vijay Balakrishna <vijayb@linux.microsoft.com>
Subject: [PATCH 5.4 11/18] selinux: fix race condition when computing ocontext SIDs
Date: Wed, 15 Dec 2021 18:21:32 +0100	[thread overview]
Message-ID: <20211215172023.192907977@linuxfoundation.org> (raw)
In-Reply-To: <20211215172022.795825673@linuxfoundation.org>

From: Ondrej Mosnacek <omosnace@redhat.com>

commit cbfcd13be5cb2a07868afe67520ed181956579a7 upstream.

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 24ed7fdae669 ("selinux: use separate table for initial
SID lookup") and 66f8e2f03c02 ("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 66f8e2f03c02, 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] https://github.com/containers/container-selinux/issues/89
[2] https://lists.fedoraproject.org/archives/list/selinux@lists.fedoraproject.org/thread/6DMTAMHIOAOEMUAVTULJD45JZU7IBAFM/
[3] https://selinuxproject.org/page/NetworkStatements#netifcon

Cc: stable@vger.kernel.org
Cc: Xinjie Zheng <xinjie@google.com>
Reported-by: Sujithra Periasamy <sujithra@google.com>
Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2")
Signed-off-by: Ondrej Mosnacek <omosnace@redhat.com>
Signed-off-by: Paul Moore <paul@paul-moore.com>
[vijayb: Backport contextual differences are due to v5.10 RCU related
 changes are not in 5.4]
Signed-off-by: Vijay Balakrishna <vijayb@linux.microsoft.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
---
 security/selinux/ss/services.c |  159 ++++++++++++++++++++++-------------------
 1 file changed, 87 insertions(+), 72 deletions(-)

--- a/security/selinux/ss/services.c
+++ b/security/selinux/ss/services.c
@@ -2251,6 +2251,43 @@ size_t security_policydb_len(struct seli
 }
 
 /**
+ * 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.
  * @protocol: protocol number
  * @port: port number
@@ -2262,10 +2299,12 @@ int security_port_sid(struct selinux_sta
 	struct policydb *policydb;
 	struct sidtab *sidtab;
 	struct ocontext *c;
-	int rc = 0;
+	int rc;
 
 	read_lock(&state->ss->policy_rwlock);
 
+retry:
+	rc = 0;
 	policydb = &state->ss->policydb;
 	sidtab = state->ss->sidtab;
 
@@ -2279,14 +2318,11 @@ int security_port_sid(struct selinux_sta
 	}
 
 	if (c) {
-		if (!c->sid[0]) {
-			rc = sidtab_context_to_sid(sidtab,
-						   &c->context[0],
-						   &c->sid[0]);
-			if (rc)
-				goto out;
-		}
-		*out_sid = c->sid[0];
+		rc = ocontext_to_sid(sidtab, c, 0, out_sid);
+		if (rc == -ESTALE)
+			goto retry;
+		if (rc)
+			goto out;
 	} else {
 		*out_sid = SECINITSID_PORT;
 	}
@@ -2308,10 +2344,12 @@ int security_ib_pkey_sid(struct selinux_
 	struct policydb *policydb;
 	struct sidtab *sidtab;
 	struct ocontext *c;
-	int rc = 0;
+	int rc;
 
 	read_lock(&state->ss->policy_rwlock);
 
+retry:
+	rc = 0;
 	policydb = &state->ss->policydb;
 	sidtab = state->ss->sidtab;
 
@@ -2326,14 +2364,11 @@ int security_ib_pkey_sid(struct selinux_
 	}
 
 	if (c) {
-		if (!c->sid[0]) {
-			rc = sidtab_context_to_sid(sidtab,
-						   &c->context[0],
-						   &c->sid[0]);
-			if (rc)
-				goto out;
-		}
-		*out_sid = c->sid[0];
+		rc = ocontext_to_sid(sidtab, c, 0, out_sid);
+		if (rc == -ESTALE)
+			goto retry;
+		if (rc)
+			goto out;
 	} else
 		*out_sid = SECINITSID_UNLABELED;
 
@@ -2354,10 +2389,12 @@ int security_ib_endport_sid(struct selin
 	struct policydb *policydb;
 	struct sidtab *sidtab;
 	struct ocontext *c;
-	int rc = 0;
+	int rc;
 
 	read_lock(&state->ss->policy_rwlock);
 
+retry:
+	rc = 0;
 	policydb = &state->ss->policydb;
 	sidtab = state->ss->sidtab;
 
@@ -2373,14 +2410,11 @@ int security_ib_endport_sid(struct selin
 	}
 
 	if (c) {
-		if (!c->sid[0]) {
-			rc = sidtab_context_to_sid(sidtab,
-						   &c->context[0],
-						   &c->sid[0]);
-			if (rc)
-				goto out;
-		}
-		*out_sid = c->sid[0];
+		rc = ocontext_to_sid(sidtab, c, 0, out_sid);
+		if (rc == -ESTALE)
+			goto retry;
+		if (rc)
+			goto out;
 	} else
 		*out_sid = SECINITSID_UNLABELED;
 
@@ -2399,11 +2433,13 @@ int security_netif_sid(struct selinux_st
 {
 	struct policydb *policydb;
 	struct sidtab *sidtab;
-	int rc = 0;
+	int rc;
 	struct ocontext *c;
 
 	read_lock(&state->ss->policy_rwlock);
 
+retry:
+	rc = 0;
 	policydb = &state->ss->policydb;
 	sidtab = state->ss->sidtab;
 
@@ -2415,19 +2451,11 @@ int security_netif_sid(struct selinux_st
 	}
 
 	if (c) {
-		if (!c->sid[0] || !c->sid[1]) {
-			rc = sidtab_context_to_sid(sidtab,
-						  &c->context[0],
-						  &c->sid[0]);
-			if (rc)
-				goto out;
-			rc = sidtab_context_to_sid(sidtab,
-						   &c->context[1],
-						   &c->sid[1]);
-			if (rc)
-				goto out;
-		}
-		*if_sid = c->sid[0];
+		rc = ocontext_to_sid(sidtab, c, 0, if_sid);
+		if (rc == -ESTALE)
+			goto retry;
+		if (rc)
+			goto out;
 	} else
 		*if_sid = SECINITSID_NETIF;
 
@@ -2469,6 +2497,7 @@ int security_node_sid(struct selinux_sta
 
 	read_lock(&state->ss->policy_rwlock);
 
+retry:
 	policydb = &state->ss->policydb;
 	sidtab = state->ss->sidtab;
 
@@ -2511,14 +2540,11 @@ int security_node_sid(struct selinux_sta
 	}
 
 	if (c) {
-		if (!c->sid[0]) {
-			rc = sidtab_context_to_sid(sidtab,
-						   &c->context[0],
-						   &c->sid[0]);
-			if (rc)
-				goto out;
-		}
-		*out_sid = c->sid[0];
+		rc = ocontext_to_sid(sidtab, c, 0, out_sid);
+		if (rc == -ESTALE)
+			goto retry;
+		if (rc)
+			goto out;
 	} else {
 		*out_sid = SECINITSID_NODE;
 	}
@@ -2677,7 +2703,7 @@ static inline int __security_genfs_sid(s
 	u16 sclass;
 	struct genfs *genfs;
 	struct ocontext *c;
-	int rc, cmp = 0;
+	int cmp = 0;
 
 	while (path[0] == '/' && path[1] == '/')
 		path++;
@@ -2691,9 +2717,8 @@ static inline int __security_genfs_sid(s
 			break;
 	}
 
-	rc = -ENOENT;
 	if (!genfs || cmp)
-		goto out;
+		return -ENOENT;
 
 	for (c = genfs->head; c; c = c->next) {
 		len = strlen(c->u.name);
@@ -2702,20 +2727,10 @@ static inline int __security_genfs_sid(s
 			break;
 	}
 
-	rc = -ENOENT;
 	if (!c)
-		goto out;
+		return -ENOENT;
 
-	if (!c->sid[0]) {
-		rc = sidtab_context_to_sid(sidtab, &c->context[0], &c->sid[0]);
-		if (rc)
-			goto out;
-	}
-
-	*sid = c->sid[0];
-	rc = 0;
-out:
-	return rc;
+	return ocontext_to_sid(sidtab, c, 0, sid);
 }
 
 /**
@@ -2750,13 +2765,15 @@ int security_fs_use(struct selinux_state
 {
 	struct policydb *policydb;
 	struct sidtab *sidtab;
-	int rc = 0;
+	int rc;
 	struct ocontext *c;
 	struct superblock_security_struct *sbsec = sb->s_security;
 	const char *fstype = sb->s_type->name;
 
 	read_lock(&state->ss->policy_rwlock);
 
+retry:
+	rc = 0;
 	policydb = &state->ss->policydb;
 	sidtab = state->ss->sidtab;
 
@@ -2769,13 +2786,11 @@ int security_fs_use(struct selinux_state
 
 	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)
-				goto out;
-		}
-		sbsec->sid = c->sid[0];
+		rc = ocontext_to_sid(sidtab, c, 0, &sbsec->sid);
+		if (rc == -ESTALE)
+			goto retry;
+		if (rc)
+			goto out;
 	} else {
 		rc = __security_genfs_sid(state, fstype, "/", SECCLASS_DIR,
 					  &sbsec->sid);



  parent reply	other threads:[~2021-12-15 17:28 UTC|newest]

Thread overview: 25+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2021-12-15 17:21 [PATCH 5.4 00/18] 5.4.166-rc1 review Greg Kroah-Hartman
2021-12-15 17:21 ` [PATCH 5.4 01/18] nfc: fix segfault in nfc_genl_dump_devices_done Greg Kroah-Hartman
2021-12-15 17:21 ` [PATCH 5.4 02/18] drm/msm/dsi: set default num_data_lanes Greg Kroah-Hartman
2021-12-15 17:21 ` [PATCH 5.4 03/18] net/mlx4_en: Update reported link modes for 1/10G Greg Kroah-Hartman
2021-12-15 17:21 ` [PATCH 5.4 04/18] parisc/agp: Annotate parisc agp init functions with __init Greg Kroah-Hartman
2021-12-15 17:21 ` [PATCH 5.4 05/18] i2c: rk3x: Handle a spurious start completion interrupt flag Greg Kroah-Hartman
2021-12-15 17:21 ` [PATCH 5.4 06/18] net: netlink: af_netlink: Prevent empty skb by adding a check on len Greg Kroah-Hartman
2021-12-15 17:21 ` [PATCH 5.4 07/18] drm/amd/display: Fix for the no Audio bug with Tiled Displays Greg Kroah-Hartman
2021-12-15 17:21 ` [PATCH 5.4 08/18] drm/amd/display: add connector type check for CRC source set Greg Kroah-Hartman
2021-12-15 17:21 ` [PATCH 5.4 09/18] tracing: Fix a kmemleak false positive in tracing_map Greg Kroah-Hartman
2021-12-15 17:21 ` [PATCH 5.4 10/18] KVM: x86: Ignore sparse banks size for an "all CPUs", non-sparse IPI req Greg Kroah-Hartman
2021-12-15 17:21 ` Greg Kroah-Hartman [this message]
2021-12-15 17:21 ` [PATCH 5.4 12/18] bpf: Fix integer overflow in argument calculation for bpf_map_area_alloc Greg Kroah-Hartman
2021-12-15 17:21 ` [PATCH 5.4 13/18] hwmon: (dell-smm) Fix warning on /proc/i8k creation error Greg Kroah-Hartman
2021-12-15 17:21 ` [PATCH 5.4 14/18] memblock: free_unused_memmap: use pageblock units instead of MAX_ORDER Greg Kroah-Hartman
2021-12-15 17:21 ` [PATCH 5.4 15/18] memblock: align freed memory map on pageblock boundaries with SPARSEMEM Greg Kroah-Hartman
2021-12-15 17:21 ` [PATCH 5.4 16/18] memblock: ensure there is no overflow in memblock_overlaps_region() Greg Kroah-Hartman
2021-12-15 17:21 ` [PATCH 5.4 17/18] arm: extend pfn_valid to take into account freed memory map alignment Greg Kroah-Hartman
2021-12-15 17:21 ` [PATCH 5.4 18/18] arm: ioremap: dont abuse pfn_valid() to check if pfn is in RAM Greg Kroah-Hartman
2021-12-15 20:00 ` [PATCH 5.4 00/18] 5.4.166-rc1 review Jon Hunter
2021-12-15 21:52 ` Shuah Khan
2021-12-15 22:47 ` Florian Fainelli
2021-12-16 11:47 ` Naresh Kamboju
2021-12-16 18:07 ` Guenter Roeck
2021-12-17  0:53 ` Samuel Zou

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20211215172023.192907977@linuxfoundation.org \
    --to=gregkh@linuxfoundation.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=omosnace@redhat.com \
    --cc=paul@paul-moore.com \
    --cc=stable@vger.kernel.org \
    --cc=sujithra@google.com \
    --cc=vijayb@linux.microsoft.com \
    --cc=xinjie@google.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).