1 /* 2 * Check decoding of struct msghdr.msg_name* arguments of recvmsg syscall. 3 * 4 * Copyright (c) 2016 Dmitry V. Levin <ldv (at) altlinux.org> 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 3. The name of the author may not be used to endorse or promote products 16 * derived from this software without specific prior written permission. 17 * 18 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 19 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 20 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 21 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 22 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 23 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 24 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 25 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 27 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 */ 29 30 #include "tests.h" 31 #include <stddef.h> 32 #include <stdio.h> 33 #include <string.h> 34 #include <unistd.h> 35 #include <sys/socket.h> 36 #include <sys/un.h> 37 38 static int 39 send_recv(const int send_fd, const int recv_fd, 40 struct msghdr *const msg, const int flags) 41 { 42 if (send(send_fd, "A", 1, 0) != 1) 43 perror_msg_and_skip("send"); 44 return recvmsg(recv_fd, msg, flags); 45 } 46 47 static void 48 test_msg_name(const int send_fd, const int recv_fd) 49 { 50 char *const recv_buf = tail_alloc(sizeof(*recv_buf)); 51 struct iovec *const iov = tail_alloc(sizeof(*iov)); 52 iov->iov_base = recv_buf; 53 iov->iov_len = sizeof(*recv_buf); 54 55 struct sockaddr_un *const addr = tail_alloc(sizeof(*addr)); 56 struct msghdr *const msg = tail_alloc(sizeof(*msg)); 57 msg->msg_name = addr; 58 msg->msg_namelen = sizeof(*addr); 59 msg->msg_iov = iov; 60 msg->msg_iovlen = 1; 61 msg->msg_control = 0; 62 msg->msg_controllen = 0; 63 msg->msg_flags = 0; 64 65 int rc = send_recv(send_fd, recv_fd, msg, MSG_DONTWAIT); 66 if (rc < 0) 67 perror_msg_and_skip("recvmsg"); 68 printf("recvmsg(%d, {msg_name={sa_family=AF_UNIX, sun_path=\"%s\"}" 69 ", msg_namelen=%d->%d, msg_iov=[{iov_base=\"A\", iov_len=1}]" 70 ", msg_iovlen=1, msg_controllen=0, msg_flags=0}, MSG_DONTWAIT)" 71 " = %d\n", 72 recv_fd, addr->sun_path, (int) sizeof(struct sockaddr_un), 73 (int) msg->msg_namelen, rc); 74 75 memset(addr, 0, sizeof(*addr)); 76 rc = send_recv(send_fd, recv_fd, msg, MSG_DONTWAIT); 77 printf("recvmsg(%d, {msg_name={sa_family=AF_UNIX, sun_path=\"%s\"}" 78 ", msg_namelen=%d, msg_iov=[{iov_base=\"A\", iov_len=1}]" 79 ", msg_iovlen=1, msg_controllen=0, msg_flags=0}, MSG_DONTWAIT)" 80 " = %d\n", 81 recv_fd, addr->sun_path, (int) msg->msg_namelen, rc); 82 83 msg->msg_name = 0; 84 rc = send_recv(send_fd, recv_fd, msg, MSG_DONTWAIT); 85 printf("recvmsg(%d, {msg_name=NULL, msg_namelen=%d" 86 ", msg_iov=[{iov_base=\"A\", iov_len=1}], msg_iovlen=1" 87 ", msg_controllen=0, msg_flags=0}, MSG_DONTWAIT) = %d\n", 88 recv_fd, (int) msg->msg_namelen, rc); 89 90 const size_t offsetof_sun_path = offsetof(struct sockaddr_un, sun_path); 91 msg->msg_name = addr; 92 msg->msg_namelen = offsetof_sun_path; 93 memset(addr->sun_path, 'A', sizeof(addr->sun_path)); 94 95 rc = send_recv(send_fd, recv_fd, msg, MSG_DONTWAIT); 96 printf("recvmsg(%d, {msg_name={sa_family=AF_UNIX}, msg_namelen=%d->%d" 97 ", msg_iov=[{iov_base=\"A\", iov_len=1}], msg_iovlen=1" 98 ", msg_controllen=0, msg_flags=0}, MSG_DONTWAIT) = %d\n", 99 recv_fd, (int) offsetof_sun_path, (int) msg->msg_namelen, rc); 100 101 msg->msg_namelen = sizeof(struct sockaddr); 102 msg->msg_name = ((void *) (addr + 1)) - msg->msg_namelen; 103 rc = send_recv(send_fd, recv_fd, msg, MSG_DONTWAIT); 104 printf("recvmsg(%d, {msg_name={sa_family=AF_UNIX, sun_path=\"%.*s\"}" 105 ", msg_namelen=%d->%d, msg_iov=[{iov_base=\"A\", iov_len=1}]" 106 ", msg_iovlen=1, msg_controllen=0, msg_flags=0}, MSG_DONTWAIT)" 107 " = %d\n", 108 recv_fd, (int) (sizeof(struct sockaddr) - offsetof_sun_path), 109 ((struct sockaddr_un *) msg->msg_name)->sun_path, 110 (int) sizeof(struct sockaddr), (int) msg->msg_namelen, rc); 111 112 rc = send_recv(send_fd, recv_fd, msg, MSG_DONTWAIT); 113 printf("recvmsg(%d, {msg_namelen=%d}, MSG_DONTWAIT) = %d %s (%m)\n", 114 recv_fd, (int) msg->msg_namelen, rc, errno2name()); 115 116 /* 117 * When recvmsg is called with a valid descriptor 118 * but inaccessible memory, it causes segfaults on some architectures. 119 * As in these cases we test decoding of failed recvmsg calls, 120 * it's ok to fail recvmsg with any reason as long as 121 * it doesn't read that inaccessible memory. 122 */ 123 124 /* 125 * Sadly, musl recvmsg wrapper blindly dereferences 2nd argument, 126 * so limit this test to glibc that doesn't. 127 */ 128 #ifdef __GLIBC__ 129 rc = send_recv(send_fd, -1, msg + 1, 0); 130 printf("recvmsg(-1, %p, 0) = %d %s (%m)\n", 131 msg + 1, rc, errno2name()); 132 #endif 133 134 rc = send_recv(send_fd, -1, 0, 0); 135 printf("recvmsg(-1, NULL, 0) = %d %s (%m)\n", 136 rc, errno2name()); 137 } 138 139 int 140 main(void) 141 { 142 int fds[2]; 143 if (socketpair(AF_UNIX, SOCK_STREAM, 0, fds)) 144 perror_msg_and_skip("socketpair"); 145 146 const struct sockaddr_un un = { 147 .sun_family = AF_UNIX, 148 .sun_path = "msg_name-recvmsg.test.send.socket" 149 }; 150 151 (void) unlink(un.sun_path); 152 if (bind(fds[1], (const void *) &un, sizeof(un))) 153 perror_msg_and_skip("bind"); 154 (void) unlink(un.sun_path); 155 156 test_msg_name(fds[1], fds[0]); 157 158 puts("+++ exited with 0 +++"); 159 return 0; 160 } 161