From: Zhang Cen <rollkingzzc@gmail.com>
To: John Johansen <john.johansen@canonical.com>,
Paul Moore <paul@paul-moore.com>,
James Morris <jmorris@namei.org>,
"Serge E. Hallyn" <serge@hallyn.com>
Cc: apparmor@lists.ubuntu.com, linux-security-module@vger.kernel.org,
linux-kernel@vger.kernel.org, zerocling0077@gmail.com,
2045gemini@gmail.com, Zhang Cen <rollkingzzc@gmail.com>
Subject: [PATCH] apparmor: hold peer path references in aa_unix_file_perm()
Date: Fri, 15 May 2026 13:01:16 +0800 [thread overview]
Message-ID: <20260515050116.95754-1-rollkingzzc@gmail.com> (raw)
aa_unix_file_perm() keeps the connected peer alive with sock_hold(peer_sk),
but it then carries unix_sk(peer_sk)->path outside the peer socket state
lock without taking a path reference. That copied peer_path can race with
unix_release_sock(), which clears u->path under unix_state_lock(peer_sk)
and drops the socket-owned path reference with path_put() before the final
sock_put(peer_sk).
Take peer_sk's unix_state_lock() long enough to snapshot peer_path,
cache whether the peer is filesystem-bound, and path_get() a non-NULL
path before dropping the lock. Drop that path reference after the last
AppArmor peer path check. This restores the ownership invariant for
peer_path without changing AF_UNIX shutdown semantics once the peer path
has already been cleared.
The buggy scenario involves two paths, with each column showing the
order within that path:
aa_unix_file_perm() [borrower]: unix_release_sock() [peer close]:
1. unix_state_lock(sock->sk) 1. unix_state_lock(peer_sk)
2. peer_sk = unix_peer(sock->sk) 2. Save path = u->path
3. sock_hold(peer_sk) 3. Clear u->path.dentry/mnt
4. unix_state_unlock(sock->sk) 4. unix_state_unlock(peer_sk)
5. peer_path = unix_sk(peer_sk)->path 5. path_put(&path)
6. unix_fs_perm(&peer_path) 6. sock_put(peer_sk)
KASAN reported a slab-use-after-free in unix_fs_perm() at
security/apparmor/af_unix.c:46, with the free side in
unix_release_sock() -> path_put() at net/unix/af_unix.c:730.
Signed-off-by: Zhang Cen <rollkingzzc@gmail.com>
---
security/apparmor/af_unix.c | 31 ++++++++++++++++++-------------
1 file changed, 18 insertions(+), 13 deletions(-)
diff --git a/security/apparmor/af_unix.c b/security/apparmor/af_unix.c
index fdb4a9f21..7a1562f6f 100644
--- a/security/apparmor/af_unix.c
+++ b/security/apparmor/af_unix.c
@@ -716,7 +716,8 @@ int aa_unix_file_perm(const struct cred *subj_cred, struct aa_label *label,
struct sock *peer_sk = NULL;
u32 sk_req = request & ~NET_PEER_MASK;
struct path path;
- bool is_sk_fs;
+ struct path peer_path = {};
+ bool is_sk_fs, is_peer_fs = false;
int error = 0;
AA_BUG(!label);
@@ -724,9 +725,8 @@ int aa_unix_file_perm(const struct cred *subj_cred, struct aa_label *label,
AA_BUG(!sock->sk);
AA_BUG(sock->sk->sk_family != PF_UNIX);
- /* investigate only using lock via unix_peer_get()
- * addr only needs the memory barrier, but need to investigate
- * path
+ /* addr only needs the memory barrier; hold a peer path reference
+ * under peer_sk's state lock after sock_hold(peer_sk)
*/
unix_state_lock(sock->sk);
peer_sk = unix_peer(sock->sk);
@@ -749,14 +749,18 @@ int aa_unix_file_perm(const struct cred *subj_cred, struct aa_label *label,
goto out;
peer_addr = aa_sunaddr(unix_sk(peer_sk), &peer_addrlen);
-
- struct path peer_path;
-
- peer_path = unix_sk(peer_sk)->path;
- if (!is_sk_fs && is_unix_fs(peer_sk)) {
+ if (!is_sk_fs) {
+ unix_state_lock(peer_sk);
+ is_peer_fs = is_unix_fs(peer_sk);
+ peer_path = unix_sk(peer_sk)->path;
+ if (peer_path.dentry)
+ path_get(&peer_path);
+ unix_state_unlock(peer_sk);
+ }
+ if (!is_sk_fs && is_peer_fs) {
last_error(error,
unix_fs_perm(op, request, subj_cred, label,
- is_unix_fs(peer_sk) ? &peer_path : NULL));
+ &peer_path));
} else if (!is_sk_fs) {
struct aa_label *plabel;
struct aa_sk_ctx *pctx = aa_sock(peer_sk);
@@ -772,12 +776,12 @@ int aa_unix_file_perm(const struct cred *subj_cred, struct aa_label *label,
MAY_READ | MAY_WRITE, sock->sk,
is_sk_fs ? &path : NULL,
peer_addr, peer_addrlen,
- is_unix_fs(peer_sk) ?
+ is_peer_fs ?
&peer_path : NULL,
plabel),
unix_peer_perm(file->f_cred, plabel, op,
MAY_READ | MAY_WRITE, peer_sk,
- is_unix_fs(peer_sk) ?
+ is_peer_fs ?
&peer_path : NULL,
addr, addrlen,
is_sk_fs ? &path : NULL,
@@ -785,6 +789,8 @@ int aa_unix_file_perm(const struct cred *subj_cred, struct aa_label *label,
if (!error && !__aa_subj_label_is_cached(plabel, label))
update_peer_ctx(peer_sk, pctx, label);
}
+ if (peer_path.dentry)
+ path_put(&peer_path);
sock_put(peer_sk);
out:
@@ -796,4 +802,3 @@ int aa_unix_file_perm(const struct cred *subj_cred, struct aa_label *label,
return error;
}
-
--
2.43.0
reply other threads:[~2026-05-15 5:01 UTC|newest]
Thread overview: [no followups] expand[flat|nested] mbox.gz Atom feed
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=20260515050116.95754-1-rollkingzzc@gmail.com \
--to=rollkingzzc@gmail.com \
--cc=2045gemini@gmail.com \
--cc=apparmor@lists.ubuntu.com \
--cc=jmorris@namei.org \
--cc=john.johansen@canonical.com \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-security-module@vger.kernel.org \
--cc=paul@paul-moore.com \
--cc=serge@hallyn.com \
--cc=zerocling0077@gmail.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