'avc_compute_av()' can block, so we carefully exit the RCU read-side
critical section before calling it in 'avc_has_perm_noaudit()'.
Unfortunately, if we're calling from the VFS layer on the RCU path walk
via 'selinux_inode_permission()' then we're still actually in an RCU
read-side critical section and must not block.
'avc_denied()' already handles this by simply returning success and
postponing the auditing until we're called again on the slowpath, so
follow the same approach here and return early if the node lookup fails
on the RCU walk path.
Signed-off-by: Will Deacon <will@xxxxxxxxxx>
---
security/selinux/avc.c | 9 ++++++---
1 file changed, 6 insertions(+), 3 deletions(-)
diff --git a/security/selinux/avc.c b/security/selinux/avc.c
index ecd3829996aa..9c183c899e92 100644
--- a/security/selinux/avc.c
+++ b/security/selinux/avc.c
@@ -1159,16 +1159,19 @@ inline int avc_has_perm_noaudit(struct selinux_state *state,
rcu_read_lock();
node = avc_lookup(state->avc, ssid, tsid, tclass);
- if (unlikely(!node))
+ if (unlikely(!node)) {
+ if (flags & AVC_NONBLOCKING)
+ goto out;
node = avc_compute_av(state, ssid, tsid, tclass, avd, &xp_node);
- else
+ } else {
memcpy(avd, &node->ae.avd, sizeof(*avd));
+ }
denied = requested & ~(avd->allowed);
if (unlikely(denied))
rc = avc_denied(state, ssid, tsid, tclass, requested, 0, 0,
flags, avd);
-
+out:
rcu_read_unlock();
return rc;
}