/* * reproducer for Bug 887683 v2 * unable to handle kernel NULL in selinux_socket_unix_may_send * * This reproducer is based on systemd/journald sources, * it's possible there is way to make it even more simple, but * since I was able to trigger the Bug, I rather kept it as it is. * * jstancek@redhat.com * * gcc selinux_socket_unix_may_send.c; ./a.out */ #include #include #include #include #include #include #include #include #include #include #include #define offsetof(type, member) __builtin_offsetof (type, member) #define SNDBUF_SIZE (8*1024*1024) int pipefd[2]; union sockaddr_union { struct sockaddr sa; struct sockaddr_in in4; struct sockaddr_in6 in6; struct sockaddr_un un; struct sockaddr_nl nl; struct sockaddr_storage storage; }; int fd_inc_sndbuf(int fd, size_t n) { int r, value; socklen_t l = sizeof(value); r = getsockopt(fd, SOL_SOCKET, SO_SNDBUF, &value, &l); if (r >= 0 && l == sizeof(value) && (size_t) value >= n*2) return 0; value = (int) n; r = setsockopt(fd, SOL_SOCKET, SO_SNDBUF, &value, sizeof(value)); if (r < 0) return -errno; return 1; } int client(int id, int loops) { int fd; char data[1024] = "123456789"; struct iovec *w; int i; int j = 0; int n = 1; int ret; int dummy; struct sockaddr_un sa; struct msghdr mh; union { struct cmsghdr cmsghdr; uint8_t buf[CMSG_SPACE(sizeof(int))]; } control; memset(&sa, 0, sizeof(sa)); sa.sun_family = AF_UNIX; snprintf(sa.sun_path, sizeof(sa.sun_path), "/tmp/socket_test%d", id); w = malloc(sizeof(struct iovec) * n * 5 + 3); w[j].iov_base = data; w[j].iov_len = 10; j++; memset(&control, 0, sizeof(control)); mh.msg_control = &control; mh.msg_controllen = sizeof(control); memset(&mh, 0, sizeof(mh)); mh.msg_name = &sa; mh.msg_namelen = offsetof(struct sockaddr_un, sun_path) + strlen(sa.sun_path); mh.msg_iov = w; mh.msg_iovlen = 1; for (i = 0; i < loops; i++) { fd = socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC, 0); if (fd < 0) return -errno; ret = fd_inc_sndbuf(fd, SNDBUF_SIZE); if (ret < 0) perror("fd_inc_sndbuf"); write(pipefd[1], &dummy, 1); ret = sendmsg(fd, &mh, MSG_NOSIGNAL); /*if (ret < 0) perror("ret:"); else printf("ret: %d\n", ret);*/ close(fd); } } int server(int id, int loops) { int fd, ret, status; union sockaddr_union sa; int dummy; int i; sa.un.sun_family = AF_UNIX; snprintf(sa.un.sun_path, sizeof(sa.un.sun_path), "/tmp/socket_test%d", id); for (i = 0; i < loops; i++) { unlink(sa.un.sun_path); fd = socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0); if (fd < 0) perror("socket"); int one = 1; ret = setsockopt(fd, SOL_SOCKET, SO_PASSCRED, &one, sizeof(one)); if (ret < 0) perror("setsockopt 1"); ret = setsockopt(fd, SOL_SOCKET, SO_PASSSEC, &one, sizeof(one)); if (ret < 0) perror("setsockopt 2"); ret = setsockopt(fd, SOL_SOCKET, SO_TIMESTAMP, &one, sizeof(one)); if (ret < 0) perror("setsockopt 3"); ret = bind(fd, &sa.sa, offsetof(union sockaddr_union, un.sun_path) + strlen(sa.un.sun_path)); if (ret < 0) perror("bind"); read(pipefd[0], &dummy, 1); close(fd); } } void child(int loops) { int i; int mypid = getpid(); if (pipe(pipefd) < 0) { perror("pipe"); return; } if (fork() == 0) { client(mypid, loops); exit(0); } else { server(mypid, loops); } } int main() { int i = 0; int cpus = sysconf(_SC_NPROCESSORS_ONLN)*4; int status; for (i = 0; i < cpus; i++) { if (fork() == 0) { child(100000); exit(0); } } for (i = 0; i < cpus; i++) { wait(&status); } printf("Done.\n"); return 0; }