mirror of
				https://gitlab.freedesktop.org/pipewire/pipewire.git
				synced 2025-11-03 09:01:54 -05:00 
			
		
		
		
	pipewire: module-protocol-native: avoid file descriptor leaks
At the moment, file descriptors may be leaked due to a malicious/buggy client: 1. If the control messages have been truncated, some file descriptors may still have been successfully transferred. Currently, seeing the MSG_CTRUNC bit causes `refill_buffer()` to immediately return -EPROTO without doing anything with the control messages, which may contain file descriptors. 2. When there is no truncation, it is still possible that the current batch of file descriptors causes the total file descriptor count to go over the maximum number of fds for the given buffer (currently 1024). In this case, too, `refill_buffer()` immediately returns -EPROTO without closing the file descriptors that can not be saved. Fix both of these cases by closing all file descriptors in all remaining cmsgs when one of the mentioned conditions occur.
This commit is contained in:
		
							parent
							
								
									dec7f7a608
								
							
						
					
					
						commit
						1d4551a98d
					
				
					 1 changed files with 39 additions and 5 deletions
				
			
		| 
						 | 
				
			
			@ -175,10 +175,37 @@ static void handle_connection_error(struct pw_protocol_native_connection *conn,
 | 
			
		|||
		pw_log_error("connection %p: could not recvmsg on fd:%d: %s", conn, conn->fd, strerror(res));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static size_t cmsg_data_length(const struct cmsghdr *cmsg)
 | 
			
		||||
{
 | 
			
		||||
	const void *begin = CMSG_DATA(cmsg);
 | 
			
		||||
	const void *end = SPA_PTROFF(cmsg, cmsg->cmsg_len, void);
 | 
			
		||||
 | 
			
		||||
	spa_assert(begin <= end);
 | 
			
		||||
 | 
			
		||||
	return SPA_PTRDIFF(end, begin);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void close_all_fds(struct msghdr *msg, struct cmsghdr *from)
 | 
			
		||||
{
 | 
			
		||||
	for (; from != NULL; from = CMSG_NXTHDR(msg, from)) {
 | 
			
		||||
		if (from->cmsg_level != SOL_SOCKET || from->cmsg_type != SCM_RIGHTS)
 | 
			
		||||
			continue;
 | 
			
		||||
 | 
			
		||||
		size_t n_fds = cmsg_data_length(from) / sizeof(int);
 | 
			
		||||
		for (size_t i = 0; i < n_fds; i++) {
 | 
			
		||||
			const void *p = SPA_PTROFF(CMSG_DATA(from), sizeof(int) * i, void);
 | 
			
		||||
			int fd;
 | 
			
		||||
 | 
			
		||||
			memcpy(&fd, p, sizeof(fd));
 | 
			
		||||
			close(fd);
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int refill_buffer(struct pw_protocol_native_connection *conn, struct buffer *buf)
 | 
			
		||||
{
 | 
			
		||||
	ssize_t len;
 | 
			
		||||
	struct cmsghdr *cmsg;
 | 
			
		||||
	struct cmsghdr *cmsg = NULL;
 | 
			
		||||
	struct msghdr msg = { 0 };
 | 
			
		||||
	struct iovec iov[1];
 | 
			
		||||
	char cmsgbuf[CMSG_SPACE(MAX_FDS_MSG * sizeof(int))];
 | 
			
		||||
| 
						 | 
				
			
			@ -198,7 +225,7 @@ static int refill_buffer(struct pw_protocol_native_connection *conn, struct buff
 | 
			
		|||
	while (true) {
 | 
			
		||||
		len = recvmsg(conn->fd, &msg, msg.msg_flags);
 | 
			
		||||
		if (msg.msg_flags & MSG_CTRUNC)
 | 
			
		||||
			return -EPROTO;
 | 
			
		||||
			goto cmsgs_truncated;
 | 
			
		||||
		if (len == 0 && avail != 0)
 | 
			
		||||
			return -EPIPE;
 | 
			
		||||
		else if (len < 0) {
 | 
			
		||||
| 
						 | 
				
			
			@ -218,10 +245,9 @@ static int refill_buffer(struct pw_protocol_native_connection *conn, struct buff
 | 
			
		|||
		if (cmsg->cmsg_level != SOL_SOCKET || cmsg->cmsg_type != SCM_RIGHTS)
 | 
			
		||||
			continue;
 | 
			
		||||
 | 
			
		||||
		n_fds =
 | 
			
		||||
		    (cmsg->cmsg_len - ((char *) CMSG_DATA(cmsg) - (char *) cmsg)) / sizeof(int);
 | 
			
		||||
		n_fds = cmsg_data_length(cmsg) / sizeof(int);
 | 
			
		||||
		if (n_fds + buf->n_fds > MAX_FDS)
 | 
			
		||||
			return -EPROTO;
 | 
			
		||||
			goto too_many_fds;
 | 
			
		||||
		memcpy(&buf->fds[buf->n_fds], CMSG_DATA(cmsg), n_fds * sizeof(int));
 | 
			
		||||
		buf->n_fds += n_fds;
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			@ -234,6 +260,14 @@ static int refill_buffer(struct pw_protocol_native_connection *conn, struct buff
 | 
			
		|||
recv_error:
 | 
			
		||||
	handle_connection_error(conn, errno);
 | 
			
		||||
	return -errno;
 | 
			
		||||
 | 
			
		||||
cmsgs_truncated:
 | 
			
		||||
	close_all_fds(&msg, CMSG_FIRSTHDR(&msg));
 | 
			
		||||
	return -EPROTO;
 | 
			
		||||
 | 
			
		||||
too_many_fds:
 | 
			
		||||
	close_all_fds(&msg, cmsg);
 | 
			
		||||
	return -EPROTO;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void clear_buffer(struct buffer *buf, bool fds)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue