diff options
| author | Jani Nikula <jani.nikula@intel.com> | 2025-06-09 12:40:46 +0300 | 
|---|---|---|
| committer | Jani Nikula <jani.nikula@intel.com> | 2025-06-09 12:40:46 +0300 | 
| commit | 34c55367af96f62e89221444f04487440ebc6487 (patch) | |
| tree | fdb36ba67d7dea09455b55037e26043b7e051ef9 /net/unix/af_unix.c | |
| parent | 7247efca0dcbc8ac6147db9200ed1549c0662465 (diff) | |
| parent | 19272b37aa4f83ca52bdf9c16d5d81bdd1354494 (diff) | |
| download | linux-34c55367af96f62e89221444f04487440ebc6487.tar.xz | |
Merge drm/drm-next into drm-intel-next
Sync to v6.16-rc1, among other things to get the fixed size GENMASK_U*()
and BIT_U*() macros.
Signed-off-by: Jani Nikula <jani.nikula@intel.com>
Diffstat (limited to 'net/unix/af_unix.c')
| -rw-r--r-- | net/unix/af_unix.c | 241 | 
1 files changed, 161 insertions, 80 deletions
diff --git a/net/unix/af_unix.c b/net/unix/af_unix.c index f78a2492826f..2e2e9997a68e 100644 --- a/net/unix/af_unix.c +++ b/net/unix/af_unix.c @@ -85,10 +85,13 @@  #include <linux/file.h>  #include <linux/filter.h>  #include <linux/fs.h> +#include <linux/fs_struct.h>  #include <linux/init.h>  #include <linux/kernel.h>  #include <linux/mount.h>  #include <linux/namei.h> +#include <linux/net.h> +#include <linux/pidfs.h>  #include <linux/poll.h>  #include <linux/proc_fs.h>  #include <linux/sched/signal.h> @@ -643,6 +646,9 @@ static void unix_sock_destructor(struct sock *sk)  		return;  	} +	if (sk->sk_peer_pid) +		pidfs_put_pid(sk->sk_peer_pid); +  	if (u->addr)  		unix_release_addr(u->addr); @@ -734,13 +740,48 @@ static void unix_release_sock(struct sock *sk, int embrion)  		unix_gc();		/* Garbage collect fds */  } -static void init_peercred(struct sock *sk) +struct unix_peercred { +	struct pid *peer_pid; +	const struct cred *peer_cred; +}; + +static inline int prepare_peercred(struct unix_peercred *peercred)  { -	sk->sk_peer_pid = get_pid(task_tgid(current)); -	sk->sk_peer_cred = get_current_cred(); +	struct pid *pid; +	int err; + +	pid = task_tgid(current); +	err = pidfs_register_pid(pid); +	if (likely(!err)) { +		peercred->peer_pid = get_pid(pid); +		peercred->peer_cred = get_current_cred(); +	} +	return err;  } -static void update_peercred(struct sock *sk) +static void drop_peercred(struct unix_peercred *peercred) +{ +	const struct cred *cred = NULL; +	struct pid *pid = NULL; + +	might_sleep(); + +	swap(peercred->peer_pid, pid); +	swap(peercred->peer_cred, cred); + +	pidfs_put_pid(pid); +	put_pid(pid); +	put_cred(cred); +} + +static inline void init_peercred(struct sock *sk, +				 const struct unix_peercred *peercred) +{ +	sk->sk_peer_pid = peercred->peer_pid; +	sk->sk_peer_cred = peercred->peer_cred; +} + +static void update_peercred(struct sock *sk, struct unix_peercred *peercred)  {  	const struct cred *old_cred;  	struct pid *old_pid; @@ -748,11 +789,11 @@ static void update_peercred(struct sock *sk)  	spin_lock(&sk->sk_peer_lock);  	old_pid = sk->sk_peer_pid;  	old_cred = sk->sk_peer_cred; -	init_peercred(sk); +	init_peercred(sk, peercred);  	spin_unlock(&sk->sk_peer_lock); -	put_pid(old_pid); -	put_cred(old_cred); +	peercred->peer_pid = old_pid; +	peercred->peer_cred = old_cred;  }  static void copy_peercred(struct sock *sk, struct sock *peersk) @@ -761,15 +802,22 @@ static void copy_peercred(struct sock *sk, struct sock *peersk)  	spin_lock(&sk->sk_peer_lock);  	sk->sk_peer_pid = get_pid(peersk->sk_peer_pid); +	pidfs_get_pid(sk->sk_peer_pid);  	sk->sk_peer_cred = get_cred(peersk->sk_peer_cred);  	spin_unlock(&sk->sk_peer_lock);  } +static bool unix_may_passcred(const struct sock *sk) +{ +	return sk->sk_scm_credentials || sk->sk_scm_pidfd; +} +  static int unix_listen(struct socket *sock, int backlog)  {  	int err;  	struct sock *sk = sock->sk;  	struct unix_sock *u = unix_sk(sk); +	struct unix_peercred peercred = {};  	err = -EOPNOTSUPP;  	if (sock->type != SOCK_STREAM && sock->type != SOCK_SEQPACKET) @@ -777,6 +825,9 @@ static int unix_listen(struct socket *sock, int backlog)  	err = -EINVAL;  	if (!READ_ONCE(u->addr))  		goto out;	/* No listens on an unbound socket */ +	err = prepare_peercred(&peercred); +	if (err) +		goto out;  	unix_state_lock(sk);  	if (sk->sk_state != TCP_CLOSE && sk->sk_state != TCP_LISTEN)  		goto out_unlock; @@ -786,11 +837,12 @@ static int unix_listen(struct socket *sock, int backlog)  	WRITE_ONCE(sk->sk_state, TCP_LISTEN);  	/* set credentials so connect can copy them */ -	update_peercred(sk); +	update_peercred(sk, &peercred);  	err = 0;  out_unlock:  	unix_state_unlock(sk); +	drop_peercred(&peercred);  out:  	return err;  } @@ -950,13 +1002,6 @@ static void unix_close(struct sock *sk, long timeout)  	 */  } -static void unix_unhash(struct sock *sk) -{ -	/* Nothing to do here, unix socket does not need a ->unhash(). -	 * This is merely for sockmap. -	 */ -} -  static bool unix_bpf_bypass_getsockopt(int level, int optname)  {  	if (level == SOL_SOCKET) { @@ -987,7 +1032,6 @@ struct proto unix_stream_proto = {  	.owner			= THIS_MODULE,  	.obj_size		= sizeof(struct unix_sock),  	.close			= unix_close, -	.unhash			= unix_unhash,  	.bpf_bypass_getsockopt	= unix_bpf_bypass_getsockopt,  #ifdef CONFIG_BPF_SYSCALL  	.psock_update_sk_prot	= unix_stream_bpf_update_proto, @@ -1018,6 +1062,7 @@ static struct sock *unix_create1(struct net *net, struct socket *sock, int kern,  	sock_init_data(sock, sk); +	sk->sk_scm_rights	= 1;  	sk->sk_hash		= unix_unbound_hash(sk);  	sk->sk_allocation	= GFP_KERNEL_ACCOUNT;  	sk->sk_write_space	= unix_write_space; @@ -1101,7 +1146,7 @@ static int unix_release(struct socket *sock)  }  static struct sock *unix_find_bsd(struct sockaddr_un *sunaddr, int addr_len, -				  int type) +				  int type, int flags)  {  	struct inode *inode;  	struct path path; @@ -1109,13 +1154,39 @@ static struct sock *unix_find_bsd(struct sockaddr_un *sunaddr, int addr_len,  	int err;  	unix_mkname_bsd(sunaddr, addr_len); -	err = kern_path(sunaddr->sun_path, LOOKUP_FOLLOW, &path); -	if (err) -		goto fail; -	err = path_permission(&path, MAY_WRITE); -	if (err) -		goto path_put; +	if (flags & SOCK_COREDUMP) { +		const struct cred *cred; +		struct cred *kcred; +		struct path root; + +		kcred = prepare_kernel_cred(&init_task); +		if (!kcred) { +			err = -ENOMEM; +			goto fail; +		} + +		task_lock(&init_task); +		get_fs_root(init_task.fs, &root); +		task_unlock(&init_task); + +		cred = override_creds(kcred); +		err = vfs_path_lookup(root.dentry, root.mnt, sunaddr->sun_path, +				      LOOKUP_BENEATH | LOOKUP_NO_SYMLINKS | +				      LOOKUP_NO_MAGICLINKS, &path); +		put_cred(revert_creds(cred)); +		path_put(&root); +		if (err) +			goto fail; +	} else { +		err = kern_path(sunaddr->sun_path, LOOKUP_FOLLOW, &path); +		if (err) +			goto fail; + +		err = path_permission(&path, MAY_WRITE); +		if (err) +			goto path_put; +	}  	err = -ECONNREFUSED;  	inode = d_backing_inode(path.dentry); @@ -1165,12 +1236,12 @@ static struct sock *unix_find_abstract(struct net *net,  static struct sock *unix_find_other(struct net *net,  				    struct sockaddr_un *sunaddr, -				    int addr_len, int type) +				    int addr_len, int type, int flags)  {  	struct sock *sk;  	if (sunaddr->sun_path[0]) -		sk = unix_find_bsd(sunaddr, addr_len, type); +		sk = unix_find_bsd(sunaddr, addr_len, type, flags);  	else  		sk = unix_find_abstract(net, sunaddr, addr_len, type); @@ -1419,16 +1490,14 @@ static int unix_dgram_connect(struct socket *sock, struct sockaddr *addr,  		if (err)  			goto out; -		if ((test_bit(SOCK_PASSCRED, &sock->flags) || -		     test_bit(SOCK_PASSPIDFD, &sock->flags)) && -		    !READ_ONCE(unix_sk(sk)->addr)) { +		if (unix_may_passcred(sk) && !READ_ONCE(unix_sk(sk)->addr)) {  			err = unix_autobind(sk);  			if (err)  				goto out;  		}  restart: -		other = unix_find_other(sock_net(sk), sunaddr, alen, sock->type); +		other = unix_find_other(sock_net(sk), sunaddr, alen, sock->type, 0);  		if (IS_ERR(other)) {  			err = PTR_ERR(other);  			goto out; @@ -1525,6 +1594,7 @@ static int unix_stream_connect(struct socket *sock, struct sockaddr *uaddr,  	struct sockaddr_un *sunaddr = (struct sockaddr_un *)uaddr;  	struct sock *sk = sock->sk, *newsk = NULL, *other = NULL;  	struct unix_sock *u = unix_sk(sk), *newu, *otheru; +	struct unix_peercred peercred = {};  	struct net *net = sock_net(sk);  	struct sk_buff *skb = NULL;  	unsigned char state; @@ -1539,9 +1609,7 @@ static int unix_stream_connect(struct socket *sock, struct sockaddr *uaddr,  	if (err)  		goto out; -	if ((test_bit(SOCK_PASSCRED, &sock->flags) || -	     test_bit(SOCK_PASSPIDFD, &sock->flags)) && -	    !READ_ONCE(u->addr)) { +	if (unix_may_passcred(sk) && !READ_ONCE(u->addr)) {  		err = unix_autobind(sk);  		if (err)  			goto out; @@ -1561,6 +1629,10 @@ static int unix_stream_connect(struct socket *sock, struct sockaddr *uaddr,  		goto out;  	} +	err = prepare_peercred(&peercred); +	if (err) +		goto out; +  	/* Allocate skb for sending to listening sock */  	skb = sock_wmalloc(newsk, 1, 0, GFP_KERNEL);  	if (!skb) { @@ -1570,7 +1642,7 @@ static int unix_stream_connect(struct socket *sock, struct sockaddr *uaddr,  restart:  	/*  Find listening sock. */ -	other = unix_find_other(net, sunaddr, addr_len, sk->sk_type); +	other = unix_find_other(net, sunaddr, addr_len, sk->sk_type, flags);  	if (IS_ERR(other)) {  		err = PTR_ERR(other);  		goto out_free_skb; @@ -1633,10 +1705,12 @@ restart:  	/* The way is open! Fastly set all the necessary fields... */  	sock_hold(sk); -	unix_peer(newsk)	= sk; -	newsk->sk_state		= TCP_ESTABLISHED; -	newsk->sk_type		= sk->sk_type; -	init_peercred(newsk); +	unix_peer(newsk) = sk; +	newsk->sk_state = TCP_ESTABLISHED; +	newsk->sk_type = sk->sk_type; +	newsk->sk_scm_recv_flags = other->sk_scm_recv_flags; +	init_peercred(newsk, &peercred); +  	newu = unix_sk(newsk);  	newu->listener = other;  	RCU_INIT_POINTER(newsk->sk_wq, &newu->peer_wq); @@ -1695,20 +1769,33 @@ out_free_skb:  out_free_sk:  	unix_release_sock(newsk, 0);  out: +	drop_peercred(&peercred);  	return err;  }  static int unix_socketpair(struct socket *socka, struct socket *sockb)  { +	struct unix_peercred ska_peercred = {}, skb_peercred = {};  	struct sock *ska = socka->sk, *skb = sockb->sk; +	int err; + +	err = prepare_peercred(&ska_peercred); +	if (err) +		return err; + +	err = prepare_peercred(&skb_peercred); +	if (err) { +		drop_peercred(&ska_peercred); +		return err; +	}  	/* Join our sockets back to back */  	sock_hold(ska);  	sock_hold(skb);  	unix_peer(ska) = skb;  	unix_peer(skb) = ska; -	init_peercred(ska); -	init_peercred(skb); +	init_peercred(ska, &ska_peercred); +	init_peercred(skb, &skb_peercred);  	ska->sk_state = TCP_ESTABLISHED;  	skb->sk_state = TCP_ESTABLISHED; @@ -1717,17 +1804,6 @@ static int unix_socketpair(struct socket *socka, struct socket *sockb)  	return 0;  } -static void unix_sock_inherit_flags(const struct socket *old, -				    struct socket *new) -{ -	if (test_bit(SOCK_PASSCRED, &old->flags)) -		set_bit(SOCK_PASSCRED, &new->flags); -	if (test_bit(SOCK_PASSPIDFD, &old->flags)) -		set_bit(SOCK_PASSPIDFD, &new->flags); -	if (test_bit(SOCK_PASSSEC, &old->flags)) -		set_bit(SOCK_PASSSEC, &new->flags); -} -  static int unix_accept(struct socket *sock, struct socket *newsock,  		       struct proto_accept_arg *arg)  { @@ -1764,7 +1840,6 @@ static int unix_accept(struct socket *sock, struct socket *newsock,  	unix_state_lock(tsk);  	unix_update_edges(unix_sk(tsk));  	newsock->state = SS_CONNECTED; -	unix_sock_inherit_flags(sock, newsock);  	sock_graft(tsk, newsock);  	unix_state_unlock(tsk);  	return 0; @@ -1873,7 +1948,7 @@ static int unix_scm_to_skb(struct scm_cookie *scm, struct sk_buff *skb, bool sen  {  	int err = 0; -	UNIXCB(skb).pid  = get_pid(scm->pid); +	UNIXCB(skb).pid = get_pid(scm->pid);  	UNIXCB(skb).uid = scm->creds.uid;  	UNIXCB(skb).gid = scm->creds.gid;  	UNIXCB(skb).fp = NULL; @@ -1885,28 +1960,19 @@ static int unix_scm_to_skb(struct scm_cookie *scm, struct sk_buff *skb, bool sen  	return err;  } -static bool unix_passcred_enabled(const struct socket *sock, -				  const struct sock *other) -{ -	return test_bit(SOCK_PASSCRED, &sock->flags) || -	       test_bit(SOCK_PASSPIDFD, &sock->flags) || -	       !other->sk_socket || -	       test_bit(SOCK_PASSCRED, &other->sk_socket->flags) || -	       test_bit(SOCK_PASSPIDFD, &other->sk_socket->flags); -} -  /*   * Some apps rely on write() giving SCM_CREDENTIALS   * We include credentials if source or destination socket   * asserted SOCK_PASSCRED.   */ -static void maybe_add_creds(struct sk_buff *skb, const struct socket *sock, -			    const struct sock *other) +static void unix_maybe_add_creds(struct sk_buff *skb, const struct sock *sk, +				 const struct sock *other)  {  	if (UNIXCB(skb).pid)  		return; -	if (unix_passcred_enabled(sock, other)) { -		UNIXCB(skb).pid  = get_pid(task_tgid(current)); + +	if (unix_may_passcred(sk) || unix_may_passcred(other)) { +		UNIXCB(skb).pid = get_pid(task_tgid(current));  		current_uid_gid(&UNIXCB(skb).uid, &UNIXCB(skb).gid);  	}  } @@ -1982,9 +2048,7 @@ static int unix_dgram_sendmsg(struct socket *sock, struct msghdr *msg,  			goto out;  	} -	if ((test_bit(SOCK_PASSCRED, &sock->flags) || -	     test_bit(SOCK_PASSPIDFD, &sock->flags)) && -	    !READ_ONCE(u->addr)) { +	if (unix_may_passcred(sk) && !READ_ONCE(u->addr)) {  		err = unix_autobind(sk);  		if (err)  			goto out; @@ -2026,7 +2090,7 @@ static int unix_dgram_sendmsg(struct socket *sock, struct msghdr *msg,  	if (msg->msg_namelen) {  lookup:  		other = unix_find_other(sock_net(sk), msg->msg_name, -					msg->msg_namelen, sk->sk_type); +					msg->msg_namelen, sk->sk_type, 0);  		if (IS_ERR(other)) {  			err = PTR_ERR(other);  			goto out_free; @@ -2101,6 +2165,11 @@ restart_locked:  		goto out_unlock;  	} +	if (UNIXCB(skb).fp && !other->sk_scm_rights) { +		err = -EPERM; +		goto out_unlock; +	} +  	if (sk->sk_type != SOCK_SEQPACKET) {  		err = security_unix_may_send(sk->sk_socket, other->sk_socket);  		if (err) @@ -2147,7 +2216,8 @@ restart_locked:  	if (sock_flag(other, SOCK_RCVTSTAMP))  		__net_timestamp(skb); -	maybe_add_creds(skb, sock, other); + +	unix_maybe_add_creds(skb, sk, other);  	scm_stat_add(other, skb);  	skb_queue_tail(&other->sk_receive_queue, skb);  	unix_state_unlock(other); @@ -2175,14 +2245,14 @@ out:  #define UNIX_SKB_FRAGS_SZ (PAGE_SIZE << get_order(32768))  #if IS_ENABLED(CONFIG_AF_UNIX_OOB) -static int queue_oob(struct socket *sock, struct msghdr *msg, struct sock *other, +static int queue_oob(struct sock *sk, struct msghdr *msg, struct sock *other,  		     struct scm_cookie *scm, bool fds_sent)  {  	struct unix_sock *ousk = unix_sk(other);  	struct sk_buff *skb;  	int err; -	skb = sock_alloc_send_skb(sock->sk, 1, msg->msg_flags & MSG_DONTWAIT, &err); +	skb = sock_alloc_send_skb(sk, 1, msg->msg_flags & MSG_DONTWAIT, &err);  	if (!skb)  		return err; @@ -2201,12 +2271,16 @@ static int queue_oob(struct socket *sock, struct msghdr *msg, struct sock *other  	if (sock_flag(other, SOCK_DEAD) ||  	    (other->sk_shutdown & RCV_SHUTDOWN)) { -		unix_state_unlock(other);  		err = -EPIPE; -		goto out; +		goto out_unlock;  	} -	maybe_add_creds(skb, sock, other); +	if (UNIXCB(skb).fp && !other->sk_scm_rights) { +		err = -EPERM; +		goto out_unlock; +	} + +	unix_maybe_add_creds(skb, sk, other);  	scm_stat_add(other, skb);  	spin_lock(&other->sk_receive_queue.lock); @@ -2219,6 +2293,8 @@ static int queue_oob(struct socket *sock, struct msghdr *msg, struct sock *other  	other->sk_data_ready(other);  	return 0; +out_unlock: +	unix_state_unlock(other);  out:  	consume_skb(skb);  	return err; @@ -2322,7 +2398,13 @@ static int unix_stream_sendmsg(struct socket *sock, struct msghdr *msg,  		    (other->sk_shutdown & RCV_SHUTDOWN))  			goto out_pipe_unlock; -		maybe_add_creds(skb, sock, other); +		if (UNIXCB(skb).fp && !other->sk_scm_rights) { +			unix_state_unlock(other); +			err = -EPERM; +			goto out_free; +		} + +		unix_maybe_add_creds(skb, sk, other);  		scm_stat_add(other, skb);  		skb_queue_tail(&other->sk_receive_queue, skb);  		unix_state_unlock(other); @@ -2332,7 +2414,7 @@ static int unix_stream_sendmsg(struct socket *sock, struct msghdr *msg,  #if IS_ENABLED(CONFIG_AF_UNIX_OOB)  	if (msg->msg_flags & MSG_OOB) { -		err = queue_oob(sock, msg, other, &scm, fds_sent); +		err = queue_oob(sk, msg, other, &scm, fds_sent);  		if (err)  			goto out_err;  		sent++; @@ -2854,8 +2936,7 @@ unlock:  			/* Never glue messages from different writers */  			if (!unix_skb_scm_eq(skb, &scm))  				break; -		} else if (test_bit(SOCK_PASSCRED, &sock->flags) || -			   test_bit(SOCK_PASSPIDFD, &sock->flags)) { +		} else if (unix_may_passcred(sk)) {  			/* Copy credentials */  			scm_set_cred(&scm, UNIXCB(skb).pid, UNIXCB(skb).uid, UNIXCB(skb).gid);  			unix_set_secdata(&scm, skb);  | 
