Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 3610cda5 authored by David S. Miller's avatar David S. Miller
Browse files

af_unix: Avoid socket->sk NULL OOPS in stream connect security hooks.



unix_release() can asynchornously set socket->sk to NULL, and
it does so without holding the unix_state_lock() on "other"
during stream connects.

However, the reverse mapping, sk->sk_socket, is only transitioned
to NULL under the unix_state_lock().

Therefore make the security hooks follow the reverse mapping instead
of the forward mapping.

Reported-by: default avatarJeremy Fitzhardinge <jeremy@goop.org>
Reported-by: default avatarLinus Torvalds <torvalds@linux-foundation.org>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 44b82883
Loading
Loading
Loading
Loading
+7 −8
Original line number Diff line number Diff line
@@ -796,8 +796,9 @@ static inline void security_free_mnt_opts(struct security_mnt_opts *opts)
 * @unix_stream_connect:
 *	Check permissions before establishing a Unix domain stream connection
 *	between @sock and @other.
 *	@sock contains the socket structure.
 *	@other contains the peer socket structure.
 *	@sock contains the sock structure.
 *	@other contains the peer sock structure.
 *	@newsk contains the new sock structure.
 *	Return 0 if permission is granted.
 * @unix_may_send:
 *	Check permissions before connecting or sending datagrams from @sock to
@@ -1568,8 +1569,7 @@ struct security_operations {
	int (*inode_getsecctx)(struct inode *inode, void **ctx, u32 *ctxlen);

#ifdef CONFIG_SECURITY_NETWORK
	int (*unix_stream_connect) (struct socket *sock,
				    struct socket *other, struct sock *newsk);
	int (*unix_stream_connect) (struct sock *sock, struct sock *other, struct sock *newsk);
	int (*unix_may_send) (struct socket *sock, struct socket *other);

	int (*socket_create) (int family, int type, int protocol, int kern);
@@ -2525,8 +2525,7 @@ static inline int security_inode_getsecctx(struct inode *inode, void **ctx, u32

#ifdef CONFIG_SECURITY_NETWORK

int security_unix_stream_connect(struct socket *sock, struct socket *other,
				 struct sock *newsk);
int security_unix_stream_connect(struct sock *sock, struct sock *other, struct sock *newsk);
int security_unix_may_send(struct socket *sock,  struct socket *other);
int security_socket_create(int family, int type, int protocol, int kern);
int security_socket_post_create(struct socket *sock, int family,
@@ -2567,8 +2566,8 @@ void security_tun_dev_post_create(struct sock *sk);
int security_tun_dev_attach(struct sock *sk);

#else	/* CONFIG_SECURITY_NETWORK */
static inline int security_unix_stream_connect(struct socket *sock,
					       struct socket *other,
static inline int security_unix_stream_connect(struct sock *sock,
					       struct sock *other,
					       struct sock *newsk)
{
	return 0;
+1 −1
Original line number Diff line number Diff line
@@ -1157,7 +1157,7 @@ static int unix_stream_connect(struct socket *sock, struct sockaddr *uaddr,
		goto restart;
	}

	err = security_unix_stream_connect(sock, other->sk_socket, newsk);
	err = security_unix_stream_connect(sk, other, newsk);
	if (err) {
		unix_state_unlock(sk);
		goto out_unlock;
+1 −1
Original line number Diff line number Diff line
@@ -548,7 +548,7 @@ static int cap_sem_semop(struct sem_array *sma, struct sembuf *sops,
}

#ifdef CONFIG_SECURITY_NETWORK
static int cap_unix_stream_connect(struct socket *sock, struct socket *other,
static int cap_unix_stream_connect(struct sock *sock, struct sock *other,
				   struct sock *newsk)
{
	return 0;
+1 −2
Original line number Diff line number Diff line
@@ -977,8 +977,7 @@ EXPORT_SYMBOL(security_inode_getsecctx);

#ifdef CONFIG_SECURITY_NETWORK

int security_unix_stream_connect(struct socket *sock, struct socket *other,
				 struct sock *newsk)
int security_unix_stream_connect(struct sock *sock, struct sock *other, struct sock *newsk)
{
	return security_ops->unix_stream_connect(sock, other, newsk);
}
+5 −5
Original line number Diff line number Diff line
@@ -3921,18 +3921,18 @@ static int selinux_socket_shutdown(struct socket *sock, int how)
	return sock_has_perm(current, sock->sk, SOCKET__SHUTDOWN);
}

static int selinux_socket_unix_stream_connect(struct socket *sock,
					      struct socket *other,
static int selinux_socket_unix_stream_connect(struct sock *sock,
					      struct sock *other,
					      struct sock *newsk)
{
	struct sk_security_struct *sksec_sock = sock->sk->sk_security;
	struct sk_security_struct *sksec_other = other->sk->sk_security;
	struct sk_security_struct *sksec_sock = sock->sk_security;
	struct sk_security_struct *sksec_other = other->sk_security;
	struct sk_security_struct *sksec_new = newsk->sk_security;
	struct common_audit_data ad;
	int err;

	COMMON_AUDIT_DATA_INIT(&ad, NET);
	ad.u.net.sk = other->sk;
	ad.u.net.sk = other;

	err = avc_has_perm(sksec_sock->sid, sksec_other->sid,
			   sksec_other->sclass,
Loading