Skip to content

Commit bc6e5f6

Browse files
committed
apparmor: Remove use of the double lock
The use of the double lock is not necessary and problematic. Instead pull the bits that need locks into their own sections and grab the needed references. Fixes: c05e705 ("apparmor: add fine grained af_unix mediation") Signed-off-by: John Johansen <[email protected]>
1 parent 6afb0a7 commit bc6e5f6

File tree

5 files changed

+104
-102
lines changed

5 files changed

+104
-102
lines changed

security/apparmor/af_unix.c

Lines changed: 101 additions & 96 deletions
Original file line numberDiff line numberDiff line change
@@ -30,11 +30,10 @@ static inline struct sock *aa_unix_sk(struct unix_sock *u)
3030
}
3131

3232
static int unix_fs_perm(const char *op, u32 mask, const struct cred *subj_cred,
33-
struct aa_label *label, struct unix_sock *u)
33+
struct aa_label *label, struct path *path)
3434
{
3535
AA_BUG(!label);
36-
AA_BUG(!u);
37-
AA_BUG(!is_unix_fs(aa_unix_sk(u)));
36+
AA_BUG(!path);
3837

3938
if (unconfined(label) || !label_mediates(label, AA_CLASS_FILE))
4039
return 0;
@@ -43,13 +42,13 @@ static int unix_fs_perm(const char *op, u32 mask, const struct cred *subj_cred,
4342
/* if !u->path.dentry socket is being shutdown - implicit delegation
4443
* until obj delegation is supported
4544
*/
46-
if (u->path.dentry) {
45+
if (path->dentry) {
4746
/* the sunpath may not be valid for this ns so use the path */
48-
struct path_cond cond = { u->path.dentry->d_inode->i_uid,
49-
u->path.dentry->d_inode->i_mode
47+
struct path_cond cond = { path->dentry->d_inode->i_uid,
48+
path->dentry->d_inode->i_mode
5049
};
5150

52-
return aa_path_perm(op, subj_cred, label, &u->path,
51+
return aa_path_perm(op, subj_cred, label, path,
5352
PATH_SOCK_COND, mask, &cond);
5453
} /* else implicitly delegated */
5554

@@ -102,18 +101,27 @@ static aa_state_t match_to_local(struct aa_policydb *policy,
102101
return state;
103102
}
104103

104+
struct sockaddr_un *aa_sunaddr(const struct unix_sock *u, int *addrlen)
105+
{
106+
struct unix_address *addr;
107+
108+
/* memory barrier is sufficient see note in net/unix/af_unix.c */
109+
addr = smp_load_acquire(&u->addr);
110+
if (addr) {
111+
*addrlen = addr->len;
112+
return addr->name;
113+
}
114+
*addrlen = 0;
115+
return NULL;
116+
}
117+
105118
static aa_state_t match_to_sk(struct aa_policydb *policy,
106119
aa_state_t state, u32 request,
107120
struct unix_sock *u, struct aa_perms **p,
108121
const char **info)
109122
{
110-
struct sockaddr_un *addr = NULL;
111-
int addrlen = 0;
112-
113-
if (u->addr) {
114-
addr = u->addr->name;
115-
addrlen = u->addr->len;
116-
}
123+
int addrlen;
124+
struct sockaddr_un *addr = aa_sunaddr(u, &addrlen);
117125

118126
return match_to_local(policy, state, request, u->sk.sk_type,
119127
u->sk.sk_protocol, addr, addrlen, p, info);
@@ -363,7 +371,8 @@ static int profile_opt_perm(struct aa_profile *profile, u32 request,
363371

364372
/* null peer_label is allowed, in which case the peer_sk label is used */
365373
static int profile_peer_perm(struct aa_profile *profile, u32 request,
366-
struct sock *sk, struct sock *peer_sk,
374+
struct sock *sk, struct sockaddr_un *peer_addr,
375+
int peer_addrlen,
367376
struct aa_label *peer_label,
368377
struct apparmor_audit_data *ad)
369378
{
@@ -375,26 +384,16 @@ static int profile_peer_perm(struct aa_profile *profile, u32 request,
375384
AA_BUG(!profile);
376385
AA_BUG(profile_unconfined(profile));
377386
AA_BUG(!sk);
378-
AA_BUG(!peer_sk);
387+
AA_BUG(!peer_label);
379388
AA_BUG(!ad);
380-
AA_BUG(is_unix_fs(peer_sk)); /* currently always calls unix_fs_perm */
381389

382390
state = RULE_MEDIATES_v9NET(rules);
383391
if (state) {
384-
struct aa_sk_ctx *peer_ctx = aa_sock(peer_sk);
385392
struct aa_profile *peerp;
386-
struct sockaddr_un *addr = NULL;
387-
int len = 0;
388393

389-
if (unix_sk(peer_sk)->addr) {
390-
addr = unix_sk(peer_sk)->addr->name;
391-
len = unix_sk(peer_sk)->addr->len;
392-
}
393394
state = match_to_peer(rules->policy, state, request,
394395
unix_sk(sk),
395-
addr, len, &p, &ad->info);
396-
if (!peer_label)
397-
peer_label = peer_ctx->label;
396+
peer_addr, peer_addrlen, &p, &ad->info);
398397

399398
return fn_for_each_in_ns(peer_label, peerp,
400399
match_label(profile, rules, state, request,
@@ -422,9 +421,8 @@ int aa_unix_create_perm(struct aa_label *label, int family, int type,
422421
return 0;
423422
}
424423

425-
int aa_unix_label_sk_perm(const struct cred *subj_cred,
426-
struct aa_label *label, const char *op, u32 request,
427-
struct sock *sk)
424+
int aa_unix_label_sk_perm(const struct cred *subj_cred, struct aa_label *label,
425+
const char *op, u32 request, struct sock *sk)
428426
{
429427
if (!unconfined(label)) {
430428
struct aa_profile *profile;
@@ -436,35 +434,27 @@ int aa_unix_label_sk_perm(const struct cred *subj_cred,
436434
return 0;
437435
}
438436

439-
static int unix_label_sock_perm(const struct cred *subj_cred,
440-
struct aa_label *label, const char *op,
441-
u32 request, struct socket *sock)
442-
{
443-
if (unconfined(label))
444-
return 0;
445-
if (is_unix_fs(sock->sk))
446-
return unix_fs_perm(op, request, subj_cred, label,
447-
unix_sk(sock->sk));
448-
449-
return aa_unix_label_sk_perm(subj_cred, label, op, request, sock->sk);
450-
}
451-
452437
/* revalidation, get/set attr, shutdown */
453438
int aa_unix_sock_perm(const char *op, u32 request, struct socket *sock)
454439
{
455440
struct aa_label *label;
456441
int error;
457442

458443
label = begin_current_label_crit_section();
459-
error = unix_label_sock_perm(current_cred(), label, op, request, sock);
444+
if (is_unix_fs(sock->sk))
445+
error = unix_fs_perm(op, request, current_cred(), label,
446+
&unix_sk(sock->sk)->path);
447+
else
448+
error = aa_unix_label_sk_perm(current_cred(), label, op,
449+
request, sock->sk);
460450
end_current_label_crit_section(label);
461451

462452
return error;
463453
}
464454

465455
static int valid_addr(struct sockaddr *addr, int addr_len)
466456
{
467-
struct sockaddr_un *sunaddr = (struct sockaddr_un *)addr;
457+
struct sockaddr_un *sunaddr = unix_addr(addr);
468458

469459
/* addr_len == offsetof(struct sockaddr_un, sun_path) is autobind */
470460
if (addr_len < offsetof(struct sockaddr_un, sun_path) ||
@@ -586,6 +576,22 @@ int aa_unix_opt_perm(const char *op, u32 request, struct socket *sock,
586576
return error;
587577
}
588578

579+
static int unix_peer_perm(const struct cred *subj_cred,
580+
struct aa_label *label, const char *op, u32 request,
581+
struct sock *sk, struct sockaddr_un *peer_addr,
582+
int peer_addrlen, struct aa_label *peer_label)
583+
{
584+
struct aa_profile *profile;
585+
DEFINE_AUDIT_SK(ad, op, subj_cred, sk);
586+
587+
ad.net.addr = peer_addr;
588+
ad.net.addrlen = peer_addrlen;
589+
590+
return fn_for_each_confined(label, profile,
591+
profile_peer_perm(profile, request, sk,
592+
peer_addr, peer_addrlen, peer_label, &ad));
593+
}
594+
589595
/**
590596
*
591597
* Requires: lock held on both @sk and @peer_sk
@@ -602,58 +608,37 @@ int aa_unix_peer_perm(const struct cred *subj_cred,
602608
AA_BUG(!label);
603609
AA_BUG(!sk);
604610
AA_BUG(!peer_sk);
611+
AA_BUG(!peer_label);
605612

606613
if (is_unix_fs(aa_unix_sk(peeru))) {
607-
return unix_fs_perm(op, request, subj_cred, label, peeru);
614+
return unix_fs_perm(op, request, subj_cred, label,
615+
&peeru->path);
608616
} else if (is_unix_fs(aa_unix_sk(u))) {
609-
return unix_fs_perm(op, request, subj_cred, label, u);
617+
return unix_fs_perm(op, request, subj_cred, label, &u->path);
610618
} else if (!unconfined(label)) {
611-
struct aa_profile *profile;
612-
DEFINE_AUDIT_SK(ad, op, subj_cred, sk);
613-
614-
ad.net.peer_sk = peer_sk;
619+
int plen;
620+
struct sockaddr_un *paddr = aa_sunaddr(unix_sk(peer_sk),
621+
&plen);
615622

616-
return fn_for_each_confined(label, profile,
617-
profile_peer_perm(profile, request, sk,
618-
peer_sk, peer_label, &ad));
623+
return unix_peer_perm(subj_cred, label, op, request,
624+
sk, paddr, plen, peer_label);
619625
}
620626

621627
return 0;
622628
}
623629

624-
static void unix_state_double_lock(struct sock *sk1, struct sock *sk2)
625-
{
626-
if (unlikely(sk1 == sk2) || !sk2) {
627-
unix_state_lock(sk1);
628-
return;
629-
}
630-
if (sk1 < sk2) {
631-
unix_state_lock(sk1);
632-
unix_state_lock(sk2);
633-
} else {
634-
unix_state_lock(sk2);
635-
unix_state_lock(sk1);
636-
}
637-
}
638-
639-
static void unix_state_double_unlock(struct sock *sk1, struct sock *sk2)
640-
{
641-
if (unlikely(sk1 == sk2) || !sk2) {
642-
unix_state_unlock(sk1);
643-
return;
644-
}
645-
unix_state_unlock(sk1);
646-
unix_state_unlock(sk2);
647-
}
648-
649-
/* TODO: examine replacing double lock with cached addr */
650-
630+
/* This fn is only checked if something has changed in the security
631+
* boundaries. Otherwise cached info off file is sufficient
632+
*/
651633
int aa_unix_file_perm(const struct cred *subj_cred, struct aa_label *label,
652634
const char *op, u32 request, struct file *file)
653635
{
654636
struct socket *sock = (struct socket *) file->private_data;
637+
struct sockaddr_un *addr, *peer_addr;
638+
int addrlen, peer_addrlen;
655639
struct sock *peer_sk = NULL;
656640
u32 sk_req = request & ~NET_PEER_MASK;
641+
struct path path;
657642
bool is_sk_fs;
658643
int error = 0;
659644

@@ -663,40 +648,60 @@ int aa_unix_file_perm(const struct cred *subj_cred, struct aa_label *label,
663648
AA_BUG(sock->sk->sk_family != PF_UNIX);
664649

665650
/* TODO: update sock label with new task label */
651+
/* investigate only using lock via unix_peer_get()
652+
* addr only needs the memory barrier, but need to investigate
653+
* path
654+
*/
666655
unix_state_lock(sock->sk);
667656
peer_sk = unix_peer(sock->sk);
668657
if (peer_sk)
669658
sock_hold(peer_sk);
670659

671660
is_sk_fs = is_unix_fs(sock->sk);
661+
addr = aa_sunaddr(unix_sk(sock->sk), &addrlen);
662+
path = unix_sk(sock->sk)->path;
663+
unix_state_unlock(sock->sk);
664+
672665
if (is_sk_fs && peer_sk)
673666
sk_req = request;
674-
if (sk_req)
675-
error = unix_label_sock_perm(subj_cred, label, op, sk_req,
676-
sock);
677-
unix_state_unlock(sock->sk);
667+
if (sk_req) {
668+
if (is_sk_fs)
669+
error = unix_fs_perm(op, sk_req, subj_cred, label,
670+
&path);
671+
else
672+
error = aa_unix_label_sk_perm(subj_cred, label, op,
673+
sk_req, sock->sk);
674+
}
678675
if (!peer_sk)
679-
return error;
676+
goto out;
677+
678+
peer_addr = aa_sunaddr(unix_sk(peer_sk), &peer_addrlen);
680679

681-
unix_state_double_lock(sock->sk, peer_sk);
680+
struct path peer_path;
681+
682+
peer_path = unix_sk(peer_sk)->path;
682683
if (!is_sk_fs && is_unix_fs(peer_sk)) {
683684
last_error(error,
684685
unix_fs_perm(op, request, subj_cred, label,
685-
unix_sk(peer_sk)));
686+
&peer_path));
686687
} else if (!is_sk_fs) {
687688
struct aa_sk_ctx *pctx = aa_sock(peer_sk);
688689

690+
/* no fs check of aa_unix_peer_perm because conditions above
691+
* ensure they will never be done
692+
*/
689693
last_error(error,
690-
xcheck(aa_unix_peer_perm(subj_cred, label, op,
691-
MAY_READ | MAY_WRITE,
692-
sock->sk, peer_sk, NULL),
693-
aa_unix_peer_perm(file->f_cred, pctx->label, op,
694-
MAY_READ | MAY_WRITE,
695-
peer_sk, sock->sk, label)));
694+
xcheck(unix_peer_perm(subj_cred, label, op,
695+
MAY_READ | MAY_WRITE, sock->sk,
696+
peer_addr, peer_addrlen,
697+
pctx->label),
698+
unix_peer_perm(file->f_cred, pctx->label, op,
699+
MAY_READ | MAY_WRITE, peer_sk,
700+
addr, addrlen, label)));
696701
}
697-
unix_state_double_unlock(sock->sk, peer_sk);
698-
699702
sock_put(peer_sk);
700703

704+
out:
705+
701706
return error;
702707
}

security/apparmor/include/af_unix.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
#define is_unix_connected(S) ((S)->state == SS_CONNECTED)
3232

3333

34+
struct sockaddr_un *aa_sunaddr(const struct unix_sock *u, int *addrlen);
3435
int aa_unix_peer_perm(const struct cred *subj_cred,
3536
struct aa_label *label, const char *op, u32 request,
3637
struct sock *sk, struct sock *peer_sk,

security/apparmor/include/audit.h

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -138,7 +138,6 @@ struct apparmor_audit_data {
138138
};
139139
struct {
140140
int type, protocol;
141-
struct sock *peer_sk;
142141
void *addr;
143142
int addrlen;
144143
} net;

security/apparmor/lsm.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1112,7 +1112,7 @@ static int unix_connect_perm(const struct cred *cred, struct aa_label *label,
11121112

11131113
error = aa_unix_peer_perm(cred, label, OP_CONNECT,
11141114
(AA_MAY_CONNECT | AA_MAY_SEND | AA_MAY_RECEIVE),
1115-
sk, peer_sk, NULL);
1115+
sk, peer_sk, peer_ctx->label);
11161116
if (!is_unix_fs(peer_sk)) {
11171117
last_error(error,
11181118
aa_unix_peer_perm(cred,
@@ -1184,7 +1184,7 @@ static int apparmor_unix_may_send(struct socket *sock, struct socket *peer)
11841184
label = __begin_current_label_crit_section(&needput);
11851185
error = xcheck(aa_unix_peer_perm(current_cred(),
11861186
label, OP_SENDMSG, AA_MAY_SEND,
1187-
sock->sk, peer->sk, NULL),
1187+
sock->sk, peer->sk, peer_ctx->label),
11881188
aa_unix_peer_perm(peer->file ? peer->file->f_cred : NULL,
11891189
peer_ctx->label, OP_SENDMSG,
11901190
AA_MAY_RECEIVE,

security/apparmor/net.c

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -148,9 +148,6 @@ void audit_net_cb(struct audit_buffer *ab, void *va)
148148
audit_unix_addr(ab, "peer_addr",
149149
unix_addr(ad->net.addr),
150150
ad->net.addrlen);
151-
else
152-
audit_unix_sk_addr(ab, "peer_addr",
153-
ad->net.peer_sk);
154151
}
155152
}
156153
if (ad->peer) {

0 commit comments

Comments
 (0)