diff options
| author | Michael Kerrisk <mtk.manpages@gmail.com> | 2018-04-12 10:53:21 +0200 |
|---|---|---|
| committer | Michael Kerrisk <mtk.manpages@gmail.com> | 2018-04-12 10:55:29 +0200 |
| commit | 7934bcdfdcc2ad2581d977a7a5e9d41e46e3068a (patch) | |
| tree | 2a9dfc684e0e0c35448b9b53f110d4ef391b12b7 /man7/unix.7 | |
| parent | d3e7786deff75403aafe0a444d22a1f4dfbc3964 (diff) | |
| download | man-pages-7934bcdfdcc2ad2581d977a7a5e9d41e46e3068a.tar.gz | |
unix.7: ERRORS: add EBADF for sending closed file descriptor with SCM_RIGHTS
As noted by Rusty Russell:
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)).
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;
}
Reported-by: Rusty Russell <rusty@rustcorp.com.au>
Signed-off-by: Michael Kerrisk <mtk.manpages@gmail.com>
Diffstat (limited to 'man7/unix.7')
| -rw-r--r-- | man7/unix.7 | 9 |
1 files changed, 9 insertions, 0 deletions
diff --git a/man7/unix.7 b/man7/unix.7 index 79f93899c6..a8efc8940e 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) |
