Re: Missing EBADF return value for sendmsg(2)/unix(7)

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On 04/06/2018 07:11 AM, Rusty Russell wrote:
> I was really surprised that sendmsg() returned EBADF on a valid fd;
> turns out I was using sendmsg with SCM_RIGHTS to send a closed fd, which
> gives EBADF (see test program below).
> 
> But this is only obliquely referenced in unix(7):
> 
>        SCM_RIGHTS
>               Send  or  receive  a  set of open file descriptors from another process.  The data portion contains an integer
>               array of the file descriptors.  The passed file descriptors behave as  though  they  have  been  created  with
>               dup(2).
> 
> EBADF is not mentioned in the unix(7) ERRORS (it's mentioned in dup(2)).

Thanks, Rusty. I applied the patch below.

Cheers,

Michael


diff --git a/man7/unix.7 b/man7/unix.7
index 79f93899c..a8efc8940 100644
--- a/man7/unix.7
+++ b/man7/unix.7
@@ -467,6 +467,15 @@ see
 The specified local address is already in use or the filesystem socket
 object already exists.
 .TP
+.B EBADF
+This error can occur for
+.BR sendmsg (2)
+when sending a file descriptor as ancillary data over
+a UNIX domain socket (see the description of
+.BR SCM_RIGHTS ,
+above), and indicates that the file descriptor number that
+is being sent is not valid (e.g., it is not an open file descripror).
+.TP
 .B ECONNREFUSED
 The remote address specified by
 .BR connect (2)


> Thanks,
> Rusty.
> 
> #include <assert.h>
> #include <errno.h>
> #include <stdbool.h>
> #include <stdio.h>
> #include <stdlib.h>
> #include <string.h>
> #include <sys/socket.h>
> #include <unistd.h>
> 
> int fdpass_send(int sockout, int fd)
> {
> 	/* From the cmsg(3) manpage: */
> 	struct msghdr msg = { 0 };
> 	struct cmsghdr *cmsg;
> 	struct iovec iov;
> 	char c = 0;
> 	union {         /* Ancillary data buffer, wrapped in a union
> 			   in order to ensure it is suitably aligned */
> 		char buf[CMSG_SPACE(sizeof(fd))];
> 		struct cmsghdr align;
> 	} u;
> 
> 	msg.msg_control = u.buf;
> 	msg.msg_controllen = sizeof(u.buf);
> 	memset(&u, 0, sizeof(u));
> 	cmsg = CMSG_FIRSTHDR(&msg);
> 	cmsg->cmsg_level = SOL_SOCKET;
> 	cmsg->cmsg_type = SCM_RIGHTS;
> 	cmsg->cmsg_len = CMSG_LEN(sizeof(fd));
> 	memcpy(CMSG_DATA(cmsg), &fd, sizeof(fd));
> 
> 	msg.msg_name = NULL;
> 	msg.msg_namelen = 0;
> 	msg.msg_iov = &iov;
> 	msg.msg_iovlen = 1;
> 	msg.msg_flags = 0;
> 
> 	/* Keith Packard reports that 0-length sends don't work, so we
> 	 * always send 1 byte. */
> 	iov.iov_base = &c;
> 	iov.iov_len = 1;
> 
> 	return sendmsg(sockout, &msg, 0);
> }
> 
> int fdpass_recv(int sockin)
> {
> 	/* From the cmsg(3) manpage: */
> 	struct msghdr msg = { 0 };
> 	struct cmsghdr *cmsg;
> 	struct iovec iov;
> 	int fd;
> 	char c;
> 	union {         /* Ancillary data buffer, wrapped in a union
> 			   in order to ensure it is suitably aligned */
> 		char buf[CMSG_SPACE(sizeof(fd))];
> 		struct cmsghdr align;
> 	} u;
> 
> 	msg.msg_control = u.buf;
> 	msg.msg_controllen = sizeof(u.buf);
> 
> 	msg.msg_name = NULL;
> 	msg.msg_namelen = 0;
> 	msg.msg_iov = &iov;
> 	msg.msg_iovlen = 1;
> 	msg.msg_flags = 0;
> 
> 	iov.iov_base = &c;
> 	iov.iov_len = 1;
> 
> 	if (recvmsg(sockin, &msg, 0) < 0)
> 		return -1;
> 
> 	cmsg = CMSG_FIRSTHDR(&msg);
>         if (!cmsg
> 	    || cmsg->cmsg_len != CMSG_LEN(sizeof(fd))
> 	    || cmsg->cmsg_level != SOL_SOCKET
> 	    || cmsg->cmsg_type != SCM_RIGHTS) {
> 		errno = -EINVAL;
> 		return -1;
> 	}
> 
> 	memcpy(&fd, CMSG_DATA(cmsg), sizeof(fd));
> 	return fd;
> }
> 
> static void child(int sockfd)
> {
> 	int newfd = fdpass_recv(sockfd);
> 	assert(newfd < 0);
> 	exit(0);
> }
> 
> int main(void)
> {
> 	int sv[2];
> 	int pid, ret;
> 
> 	assert(socketpair(AF_UNIX, SOCK_STREAM, 0, sv) == 0);
> 
> 	pid = fork();
> 	if (pid == 0) {
> 		close(sv[1]);
> 		child(sv[0]);
> 	}
> 
> 	close(sv[0]);
> 	ret = fdpass_send(sv[1], sv[0]);
> 	printf("fdpass of bad fd return %i (%s)\n", ret, strerror(errno));
> 	return 0;
> }
> --
> To unsubscribe from this list: send the line "unsubscribe linux-man" in
> the body of a message to majordomo@xxxxxxxxxxxxxxx
> More majordomo info at  http://vger.kernel.org/majordomo-info.html




-- 
Michael Kerrisk
Linux man-pages maintainer; http://www.kernel.org/doc/man-pages/
Linux/UNIX System Programming Training: http://man7.org/training/
--
To unsubscribe from this list: send the line "unsubscribe linux-man" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html



[Index of Archives]     [Kernel Documentation]     [Netdev]     [Linux Ethernet Bridging]     [Linux Wireless]     [Kernel Newbies]     [Security]     [Linux for Hams]     [Netfilter]     [Bugtraq]     [Yosemite News]     [MIPS Linux]     [ARM Linux]     [Linux RAID]     [Linux Admin]     [Samba]

  Powered by Linux