Hi, I've now isolated the problems and have a reproducer (attached) and it has _nothing_ to do with SCM_CREDS, but with much more basic functionality.
Problem 1 (causing SIGLOST): When msg_name and msg_namelen are filled for a SOCK_STREAM AF_LOCAL socket (maybe also other AF_LOCAL, didn't check) upon calling recvmsg, SIGLOST is generated. After reading up on this a bit, it appears to be that Hurd doesn't have peer names for anoymous AF_LOCAL sockets (in contrast to Linux and kFreeBSD) - but even in that case, I would've expected errno = EINVAL and not SIGLOST, so this is definitely a bug. Problem 2: MSG_DONTWAIT is ignored for recvmsg, it always blocks (at least when using AF_LOCK sockets). I've even tried to pre-fill the msg_flags member with it (which you shouldn't need to do according to POSIX, the flags argument should be enough), but that also doesn't help. I've attached a simple reproducer: gcc -Wall -o hurd_recvmsg hurd_recvmsg.c [shell 1] ./hurd_recvmsg server [shell 2] ./hurd_recvmsg => works on Hurd gcc -Wall -o hurd_recvmsg hurd_recvmsg.c -DMSG_NAME [shell 1] ./hurd_recvmsg server [shell 2] ./hurd_recvmsg => SIGLOST on server (even if you don't want to support peer names in AF_LOCAL, I'd expect recvmsg to either ignore msg_name or at the very least return -EINVAL - but not cause the program to crash with a signal that's Hurd-specific and hence most programs don't catch gcc -Wall -o hurd_recvmsg hurd_recvmsg.c -DSECOND_RECV [shell 1] ./hurd_recvmsg server [shell 2] ./hurd_recvmsg => hangs on recv in server (even though MSG_DONTWAIT is passed to the second call) All three variants (+ the combined variant with both defines) work on kFreeBSD, so this is a Hurd issue. Tricky thing is: I can easily work around Problem 1, but I don't see any way of working around problem 2 in the code, because having working non-blocking I/O is absolutely essential. (I also wonder why nobody ever found this bug before? This appears to be really easy to trigger...) Anyway, would be great if someone could look at Bug #2 here, because I don't see any way to work around it. Thank you! Regards, Christian
#include <sys/socket.h> #include <sys/poll.h> #include <sys/time.h> #include <sys/un.h> #include <sys/uio.h> #include <string.h> #include <stdlib.h> #include <signal.h> #include <stdio.h> #include <stdarg.h> #include <time.h> #include <unistd.h> #include <errno.h> #include <netdb.h> #include <fcntl.h> #include <stddef.h> #define SOCKET "test-socket" static int do_recv(int sock, char *buffer, size_t count, struct cmsgcred **cred, int dontwait) { struct cmsgcred *cred_buf; unsigned char control[CMSG_SPACE(sizeof(*cred_buf)) + 1024]; struct cmsghdr *cmsg; struct msghdr msg; struct iovec iov; int len; char name_buf[256]; if (dontwait) dontwait = MSG_DONTWAIT; iov.iov_base = buffer; iov.iov_len = count; memset(&msg, 0, sizeof(msg)); #ifdef MSG_NAME msg.msg_name = name_buf; msg.msg_namelen = sizeof(name_buf); #endif msg.msg_iov = &iov; msg.msg_iovlen = 1; msg.msg_control = control; msg.msg_controllen = sizeof(control); msg.msg_flags = dontwait; len = recvmsg(sock, &msg, dontwait); if (len < 0) return len; cmsg = CMSG_FIRSTHDR(&msg); while (cmsg) { if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_CREDS) { cred_buf = malloc(sizeof(*cred_buf)); memcpy(cred_buf, CMSG_DATA(cmsg), sizeof(*cred_buf)); *cred = cred_buf; break; } cmsg = CMSG_NXTHDR(&msg, cmsg); } return len; } static int do_send(int sock, const char *buffer, size_t count) { unsigned char control[CMSG_SPACE(sizeof(struct cmsgcred))]; struct cmsghdr *cmsg; struct msghdr msg; struct iovec iov; iov.iov_base = (void *)buffer; iov.iov_len = count; memset(&msg, 0, sizeof(msg)); msg.msg_iov = &iov; msg.msg_iovlen = 1; memset(&control, 0, sizeof(control)); msg.msg_control = control; msg.msg_controllen = sizeof(control); cmsg = CMSG_FIRSTHDR(&msg); cmsg->cmsg_len = CMSG_LEN(sizeof(struct cmsgcred)); cmsg->cmsg_level = SOL_SOCKET; cmsg->cmsg_type = SCM_CREDS; return sendmsg(sock, &msg, 0); } #define DO(_a) \ do { \ int r = (_a); \ if (r < 0) { \ perror(#_a); \ exit(1); \ } \ } while(0) int main(int argc, char **argv) { int sock; int server = 0; struct sockaddr_storage addr_buf; struct sockaddr_un *addr_un = (struct sockaddr_un *)&addr_buf; struct sockaddr *addr = (struct sockaddr *)&addr_buf; socklen_t addr_len; int v; struct pollfd f; if (argc > 1 && !strcmp(argv[1], "server")) server = 1; addr_un->sun_family = AF_UNIX; strcpy(addr_un->sun_path, SOCKET); /* kids, don't do this in real code, this is a buffer overflow in waiting */ addr_len = offsetof(struct sockaddr_un, sun_path) + strlen(SOCKET); DO(sock = socket(AF_LOCAL, SOCK_STREAM, 0)); if (server) { int csock; struct cmsgcred *creds, *creds2; int v2, r; unlink(SOCKET); DO(bind(sock, addr, addr_len)); DO(listen(sock, 1)); DO(csock = accept(sock, NULL, NULL)); close(sock); DO(do_recv(csock, (char *)&v, sizeof(int), &creds, 0)); #ifdef SECOND_RECV r = do_recv(csock, (char *)&v2, sizeof(int), &creds2, 1); /* set MSG_DONTWAIT on second message */ if (r == 0 || errno != EAGAIN) { printf("Warning: second receive didn't give us -EAGAIN, got rv=%d, errno=%d (%m).\n", r, errno); } #endif if (!creds) { printf("no creds, v = %d\n", v); return 0; } printf("creds->uid = %d, v = %d\n", (int)creds->cmcred_uid, v); return 0; } DO(connect(sock, addr, addr_len)); v = 42; DO(do_send(sock, (const char *)&v, sizeof(int))); f.fd = sock; f.events = POLLIN; f.revents = 0; /* wait for server to exit and close socket */ poll(&f, 1, -1); printf("Send successful.\n"); return 0; }