/* * reproducer for Bug 887683 * 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 child() { int fd = socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC, 0); char data[1024] = "123456789"; struct iovec *w; 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; if (fd < 0) return -errno; ret = fd_inc_sndbuf(fd, SNDBUF_SIZE); if (ret < 0) perror("fd_inc_sndbuf"); memset(&sa, 0, sizeof(sa)); sa.sun_family = AF_UNIX; strncpy(sa.sun_path, "/run/systemd/journal/socket_test", sizeof(sa.sun_path)); 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; 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 parent() { int fd, ret, status; union sockaddr_union sa; int dummy; fd = socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0); if (fd < 0) perror("socket"); sa.un.sun_family = AF_UNIX; strncpy(sa.un.sun_path, "/run/systemd/journal/socket_test", sizeof(sa.un.sun_path)); unlink(sa.un.sun_path); 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); wait(&status); } int main() { int busypids[8192]; int childpid; int i = 0; int cpus = sysconf(_SC_NPROCESSORS_ONLN)*2; if (pipe(pipefd) < 0) { perror("pipe"); return 1; } for (i = 0; i < cpus; i++) { childpid = fork(); if (childpid == 0) { while(1); } busypids[i] = childpid; } i = 0; while (i < 100000) { childpid = fork(); if (childpid == 0) { child(); exit(0); } parent(); i++; if (i % 10 == 0) { printf("loop: %d\n", i); } } for (i = 0; i < cpus; i++) { kill(busypids[i], SIGKILL); } printf("Done.\n"); return 0; }