diff --git a/include/ssp/Makefile b/include/ssp/Makefile --- a/include/ssp/Makefile +++ b/include/ssp/Makefile @@ -1,5 +1,5 @@ -INCS= poll.h random.h ssp.h stdio.h stdlib.h string.h strings.h uio.h unistd.h -INCS+= wchar.h +INCS= poll.h random.h socket.h ssp.h stdio.h stdlib.h string.h strings.h +INCS+= uio.h unistd.h wchar.h INCSDIR= ${INCLUDEDIR}/ssp .include diff --git a/include/ssp/socket.h b/include/ssp/socket.h new file mode 100644 --- /dev/null +++ b/include/ssp/socket.h @@ -0,0 +1,123 @@ +/*- + * + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2024, Klara, Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +#ifndef _SSP_SOCKET_H_ +#define _SSP_SOCKET_H_ + +#include + +#if __SSP_FORTIFY_LEVEL > 0 + +#include + +__BEGIN_DECLS + +__ssp_inline void +__ssp_check_msghdr(struct msghdr *hdr) +{ + + if (__ssp_bos(hdr->msg_name) < hdr->msg_namelen) + __chk_fail(); + + __ssp_check_iovec(hdr->msg_iov, hdr->msg_iovlen); + + if (__ssp_bos(hdr->msg_control) < hdr->msg_controllen) + __chk_fail(); +} + +__ssp_redirect_raw_impl(int, getpeername, getpeername, + (int fdes, struct sockaddr *__restrict name, socklen_t *__restrict namelen)) +{ + size_t namesz = __ssp_bos(name); + + if (namesz != (size_t)-1 && namesz < *namelen) + __chk_fail(); + + return (__ssp_real(getpeername)(fdes, name, namelen)); +} + +__ssp_redirect_raw_impl(int, getsockname, getsockname, + (int fdes, struct sockaddr *__restrict name, + socklen_t *__restrict namelen)) +{ + size_t namesz = __ssp_bos(name); + + if (namesz != (size_t)-1 && namesz < *namelen) + __chk_fail(); + + return (__ssp_real(getsockname)(fdes, name, namelen)); +} + +__ssp_redirect(ssize_t, recv, (int __sock, void *__buf, size_t __len, + int __flags), (__sock, __buf, __len, __flags)); + +__ssp_redirect_raw_impl(ssize_t, recvfrom, recvfrom, + (int s, void *buf, size_t len, int flags, + struct sockaddr *__restrict from, + socklen_t *__restrict fromlen)) +{ + + if (__ssp_bos(buf) < len) + __chk_fail(); + if (from != NULL && __ssp_bos(from) < *fromlen) + __chk_fail(); + + return (__ssp_real(recvfrom)(s, buf, len, flags, from, fromlen)); +} + +__ssp_redirect_raw_impl(ssize_t, recvmsg, recvmsg, + (int s, struct msghdr *hdr, int flags)) +{ + + __ssp_check_msghdr(hdr); + return (__ssp_real(recvmsg)(s, hdr, flags)); +} + +#if __BSD_VISIBLE +struct timespec; + +__ssp_redirect_raw_impl(ssize_t, recvmmsg, recvmmsg, + (int s, struct mmsghdr *__restrict hdrvec, size_t vlen, int flags, + const struct timespec *__restrict timeout)) +{ + const size_t vecsz = __ssp_bos(hdrvec); + + if (vecsz != (size_t)-1 && vecsz / sizeof(*hdrvec) < vlen) + __chk_fail(); + + for (size_t i = 0; i < vlen; i++) { + __ssp_check_msghdr(&hdrvec[i].msg_hdr); + } + + return (__ssp_real(recvmmsg)(s, hdrvec, vlen, flags, timeout)); +} +#endif + +__END_DECLS + +#endif /* __SSP_FORTIFY_LEVEL > 0 */ +#endif /* _SSP_SOCKET_H_ */ diff --git a/lib/libc/sys/recv.c b/lib/libc/sys/recv.c --- a/lib/libc/sys/recv.c +++ b/lib/libc/sys/recv.c @@ -31,12 +31,13 @@ #include #include +#include #include "libc_private.h" #include ssize_t -recv(int s, void *buf, size_t len, int flags) +__ssp_real(recv)(int s, void *buf, size_t len, int flags) { /* * POSIX says recv() shall be a cancellation point, so call the diff --git a/lib/libc/sys/recvfrom.c b/lib/libc/sys/recvfrom.c --- a/lib/libc/sys/recvfrom.c +++ b/lib/libc/sys/recvfrom.c @@ -32,13 +32,14 @@ #include #include #include +#include #include "libc_private.h" __weak_reference(__sys_recvfrom, __recvfrom); #pragma weak recvfrom ssize_t -recvfrom(int s, void *buf, size_t len, int flags, +__ssp_real(recvfrom)(int s, void *buf, size_t len, int flags, struct sockaddr * __restrict from, socklen_t * __restrict fromlen) { return (INTERPOS_SYS(recvfrom, s, buf, len, flags, from, fromlen)); diff --git a/lib/libc/sys/recvmsg.c b/lib/libc/sys/recvmsg.c --- a/lib/libc/sys/recvmsg.c +++ b/lib/libc/sys/recvmsg.c @@ -32,13 +32,14 @@ #include #include #include +#include #include "libc_private.h" __weak_reference(__sys_recvmsg, __recvmsg); #pragma weak recvmsg ssize_t -recvmsg(int s, struct msghdr *msg, int flags) +__ssp_real(recvmsg)(int s, struct msghdr *msg, int flags) { return (INTERPOS_SYS(recvmsg, s, msg, flags)); } diff --git a/lib/libc/tests/secure/Makefile b/lib/libc/tests/secure/Makefile --- a/lib/libc/tests/secure/Makefile +++ b/lib/libc/tests/secure/Makefile @@ -6,6 +6,7 @@ # sys/ headers FORTIFY_TCATS+= random FORTIFY_TCATS+= select +FORTIFY_TCATS+= socket FORTIFY_TCATS+= uio # non-sys/ headers diff --git a/lib/libc/tests/secure/fortify_poll_test.c b/lib/libc/tests/secure/fortify_poll_test.c --- a/lib/libc/tests/secure/fortify_poll_test.c +++ b/lib/libc/tests/secure/fortify_poll_test.c @@ -7,6 +7,7 @@ #include #include #include +#include #include #include #include @@ -65,6 +66,50 @@ return (linkname); } +/* + * For our purposes, first descriptor will be the reader; we'll send both + * raw data and a control message over it so that the result can be used for + * any of our recv*() tests. + */ +static void __unused +new_socket(int sock[2]) +{ + unsigned char ctrl[CMSG_SPACE(sizeof(int))] = { 0 }; + static char sockbuf[256]; + ssize_t rv; + size_t total = 0; + struct msghdr hdr = { 0 }; + struct cmsghdr *cmsg; + int error, fd; + + error = socketpair(AF_UNIX, SOCK_STREAM, 0, sock); + ATF_REQUIRE(error == 0); + + while (total != sizeof(sockbuf)) { + rv = send(sock[1], &sockbuf[total], sizeof(sockbuf) - total, 0); + + ATF_REQUIRE_MSG(rv > 0, + "expected bytes sent, got %zd with %zu left (size %zu, total %zu)", + rv, sizeof(sockbuf) - total, sizeof(sockbuf), total); + ATF_REQUIRE_MSG(total + (size_t)rv <= sizeof(sockbuf), + "%zd exceeds total %zu", rv, sizeof(sockbuf)); + total += rv; + } + + hdr.msg_control = ctrl; + hdr.msg_controllen = sizeof(ctrl); + + cmsg = CMSG_FIRSTHDR(&hdr); + cmsg->cmsg_level = SOL_SOCKET; + cmsg->cmsg_type = SCM_RIGHTS; + cmsg->cmsg_len = CMSG_LEN(sizeof(fd)); + fd = STDIN_FILENO; + memcpy(CMSG_DATA(cmsg), &fd, sizeof(fd)); + + error = sendmsg(sock[1], &hdr, 0); + ATF_REQUIRE(error != -1); +} + /* * Constructs a tmpfile that we can use for testing read(2) and friends. */ diff --git a/lib/libc/tests/secure/fortify_random_test.c b/lib/libc/tests/secure/fortify_random_test.c --- a/lib/libc/tests/secure/fortify_random_test.c +++ b/lib/libc/tests/secure/fortify_random_test.c @@ -7,6 +7,7 @@ #include #include #include +#include #include #include #include @@ -65,6 +66,50 @@ return (linkname); } +/* + * For our purposes, first descriptor will be the reader; we'll send both + * raw data and a control message over it so that the result can be used for + * any of our recv*() tests. + */ +static void __unused +new_socket(int sock[2]) +{ + unsigned char ctrl[CMSG_SPACE(sizeof(int))] = { 0 }; + static char sockbuf[256]; + ssize_t rv; + size_t total = 0; + struct msghdr hdr = { 0 }; + struct cmsghdr *cmsg; + int error, fd; + + error = socketpair(AF_UNIX, SOCK_STREAM, 0, sock); + ATF_REQUIRE(error == 0); + + while (total != sizeof(sockbuf)) { + rv = send(sock[1], &sockbuf[total], sizeof(sockbuf) - total, 0); + + ATF_REQUIRE_MSG(rv > 0, + "expected bytes sent, got %zd with %zu left (size %zu, total %zu)", + rv, sizeof(sockbuf) - total, sizeof(sockbuf), total); + ATF_REQUIRE_MSG(total + (size_t)rv <= sizeof(sockbuf), + "%zd exceeds total %zu", rv, sizeof(sockbuf)); + total += rv; + } + + hdr.msg_control = ctrl; + hdr.msg_controllen = sizeof(ctrl); + + cmsg = CMSG_FIRSTHDR(&hdr); + cmsg->cmsg_level = SOL_SOCKET; + cmsg->cmsg_type = SCM_RIGHTS; + cmsg->cmsg_len = CMSG_LEN(sizeof(fd)); + fd = STDIN_FILENO; + memcpy(CMSG_DATA(cmsg), &fd, sizeof(fd)); + + error = sendmsg(sock[1], &hdr, 0); + ATF_REQUIRE(error != -1); +} + /* * Constructs a tmpfile that we can use for testing read(2) and friends. */ diff --git a/lib/libc/tests/secure/fortify_select_test.c b/lib/libc/tests/secure/fortify_select_test.c --- a/lib/libc/tests/secure/fortify_select_test.c +++ b/lib/libc/tests/secure/fortify_select_test.c @@ -7,6 +7,7 @@ #include #include #include +#include #include #include #include @@ -65,6 +66,50 @@ return (linkname); } +/* + * For our purposes, first descriptor will be the reader; we'll send both + * raw data and a control message over it so that the result can be used for + * any of our recv*() tests. + */ +static void __unused +new_socket(int sock[2]) +{ + unsigned char ctrl[CMSG_SPACE(sizeof(int))] = { 0 }; + static char sockbuf[256]; + ssize_t rv; + size_t total = 0; + struct msghdr hdr = { 0 }; + struct cmsghdr *cmsg; + int error, fd; + + error = socketpair(AF_UNIX, SOCK_STREAM, 0, sock); + ATF_REQUIRE(error == 0); + + while (total != sizeof(sockbuf)) { + rv = send(sock[1], &sockbuf[total], sizeof(sockbuf) - total, 0); + + ATF_REQUIRE_MSG(rv > 0, + "expected bytes sent, got %zd with %zu left (size %zu, total %zu)", + rv, sizeof(sockbuf) - total, sizeof(sockbuf), total); + ATF_REQUIRE_MSG(total + (size_t)rv <= sizeof(sockbuf), + "%zd exceeds total %zu", rv, sizeof(sockbuf)); + total += rv; + } + + hdr.msg_control = ctrl; + hdr.msg_controllen = sizeof(ctrl); + + cmsg = CMSG_FIRSTHDR(&hdr); + cmsg->cmsg_level = SOL_SOCKET; + cmsg->cmsg_type = SCM_RIGHTS; + cmsg->cmsg_len = CMSG_LEN(sizeof(fd)); + fd = STDIN_FILENO; + memcpy(CMSG_DATA(cmsg), &fd, sizeof(fd)); + + error = sendmsg(sock[1], &hdr, 0); + ATF_REQUIRE(error != -1); +} + /* * Constructs a tmpfile that we can use for testing read(2) and friends. */ diff --git a/lib/libc/tests/secure/fortify_socket_test.c b/lib/libc/tests/secure/fortify_socket_test.c new file mode 100644 --- /dev/null +++ b/lib/libc/tests/secure/fortify_socket_test.c @@ -0,0 +1,1697 @@ +/* @generated by generate-fortify_test.lua */ + +#define _FORTIFY_SOURCE 2 +#define TMPFILE_SIZE (1024 * 32) + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static FILE * __unused +new_fp(size_t __len) +{ + static char fpbuf[LINE_MAX]; + FILE *fp; + + ATF_REQUIRE(__len <= sizeof(fpbuf)); + + memset(fpbuf, 'A', sizeof(fpbuf) - 1); + fpbuf[sizeof(fpbuf) - 1] = '\0'; + + fp = fmemopen(fpbuf, sizeof(fpbuf), "rb"); + ATF_REQUIRE(fp != NULL); + + return (fp); +} + +/* + * Create a new symlink to use for readlink(2) style tests, we'll just use a + * random target name to have something interesting to look at. + */ +static const char * __unused +new_link(size_t __len) +{ + static const char linkname[] = "link"; + char target[MAXNAMLEN]; + int error; + + ATF_REQUIRE(__len <= sizeof(target)); + + arc4random_buf(target, sizeof(target)); + + error = unlink(linkname); + ATF_REQUIRE(error == 0 || errno == ENOENT); + + error = symlink(target, linkname); + ATF_REQUIRE(error == 0); + + return (linkname); +} + +/* + * For our purposes, first descriptor will be the reader; we'll send both + * raw data and a control message over it so that the result can be used for + * any of our recv*() tests. + */ +static void __unused +new_socket(int sock[2]) +{ + unsigned char ctrl[CMSG_SPACE(sizeof(int))] = { 0 }; + static char sockbuf[256]; + ssize_t rv; + size_t total = 0; + struct msghdr hdr = { 0 }; + struct cmsghdr *cmsg; + int error, fd; + + error = socketpair(AF_UNIX, SOCK_STREAM, 0, sock); + ATF_REQUIRE(error == 0); + + while (total != sizeof(sockbuf)) { + rv = send(sock[1], &sockbuf[total], sizeof(sockbuf) - total, 0); + + ATF_REQUIRE_MSG(rv > 0, + "expected bytes sent, got %zd with %zu left (size %zu, total %zu)", + rv, sizeof(sockbuf) - total, sizeof(sockbuf), total); + ATF_REQUIRE_MSG(total + (size_t)rv <= sizeof(sockbuf), + "%zd exceeds total %zu", rv, sizeof(sockbuf)); + total += rv; + } + + hdr.msg_control = ctrl; + hdr.msg_controllen = sizeof(ctrl); + + cmsg = CMSG_FIRSTHDR(&hdr); + cmsg->cmsg_level = SOL_SOCKET; + cmsg->cmsg_type = SCM_RIGHTS; + cmsg->cmsg_len = CMSG_LEN(sizeof(fd)); + fd = STDIN_FILENO; + memcpy(CMSG_DATA(cmsg), &fd, sizeof(fd)); + + error = sendmsg(sock[1], &hdr, 0); + ATF_REQUIRE(error != -1); +} + +/* + * Constructs a tmpfile that we can use for testing read(2) and friends. + */ +static int __unused +new_tmpfile(void) +{ + char buf[1024]; + ssize_t rv; + size_t written; + int fd; + + fd = open("tmpfile", O_RDWR | O_CREAT | O_TRUNC, 0644); + ATF_REQUIRE(fd >= 0); + + written = 0; + while (written < TMPFILE_SIZE) { + rv = write(fd, buf, sizeof(buf)); + ATF_REQUIRE(rv > 0); + + written += rv; + } + + lseek(fd, 0, SEEK_SET); + return (fd); +} + +static void +disable_coredumps(void) +{ + struct rlimit rl = { 0 }; + + setrlimit(RLIMIT_CORE, &rl); +} + +/* + * Replaces stdin with a file that we can actually read from, for tests where + * we want a FILE * or fd that we can get data from. + */ +static void __unused +replace_stdin(void) +{ + int fd; + + fd = new_tmpfile(); + + (void)dup2(fd, STDIN_FILENO); + if (fd != STDIN_FILENO) + close(fd); +} + +ATF_TC_WITHOUT_HEAD(getpeername_before_end); +ATF_TC_BODY(getpeername_before_end, tc) +{ +#define BUF &__stack.__buf + struct { + uint8_t padding_l; + struct sockaddr __buf; + uint8_t padding_r; + } __stack; + const size_t __bufsz __unused = sizeof(__stack.__buf); + const size_t __len = sizeof(struct sockaddr) - 1; + const size_t __idx __unused = __len - 1; + int sock[2] = { -1, -1 }; + socklen_t socklen; + new_socket(sock); + ATF_REQUIRE(sock[0] >= 0); + socklen = __len; + + getpeername(sock[0], &__stack.__buf, &socklen); +#undef BUF + +} + +ATF_TC_WITHOUT_HEAD(getpeername_end); +ATF_TC_BODY(getpeername_end, tc) +{ +#define BUF &__stack.__buf + struct { + uint8_t padding_l; + struct sockaddr __buf; + uint8_t padding_r; + } __stack; + const size_t __bufsz __unused = sizeof(__stack.__buf); + const size_t __len = sizeof(struct sockaddr); + const size_t __idx __unused = __len - 1; + int sock[2] = { -1, -1 }; + socklen_t socklen; + new_socket(sock); + ATF_REQUIRE(sock[0] >= 0); + socklen = __len; + + getpeername(sock[0], &__stack.__buf, &socklen); +#undef BUF + +} + +ATF_TC_WITHOUT_HEAD(getpeername_heap_before_end); +ATF_TC_BODY(getpeername_heap_before_end, tc) +{ +#define BUF __stack.__buf + struct { + uint8_t padding_l; + struct sockaddr * __buf; + uint8_t padding_r; + } __stack; + const size_t __bufsz __unused = sizeof(*__stack.__buf) * (1); + const size_t __len = sizeof(struct sockaddr) - 1; + const size_t __idx __unused = __len - 1; + int sock[2] = { -1, -1 }; + socklen_t socklen; + __stack.__buf = malloc(__bufsz); + new_socket(sock); + ATF_REQUIRE(sock[0] >= 0); + socklen = __len; + + getpeername(sock[0], __stack.__buf, &socklen); +#undef BUF + +} + +ATF_TC_WITHOUT_HEAD(getpeername_heap_end); +ATF_TC_BODY(getpeername_heap_end, tc) +{ +#define BUF __stack.__buf + struct { + uint8_t padding_l; + struct sockaddr * __buf; + uint8_t padding_r; + } __stack; + const size_t __bufsz __unused = sizeof(*__stack.__buf) * (1); + const size_t __len = sizeof(struct sockaddr); + const size_t __idx __unused = __len - 1; + int sock[2] = { -1, -1 }; + socklen_t socklen; + __stack.__buf = malloc(__bufsz); + new_socket(sock); + ATF_REQUIRE(sock[0] >= 0); + socklen = __len; + + getpeername(sock[0], __stack.__buf, &socklen); +#undef BUF + +} + +ATF_TC_WITHOUT_HEAD(getpeername_heap_after_end); +ATF_TC_BODY(getpeername_heap_after_end, tc) +{ +#define BUF __stack.__buf + struct { + uint8_t padding_l; + struct sockaddr * __buf; + uint8_t padding_r; + } __stack; + const size_t __bufsz __unused = sizeof(*__stack.__buf) * (1); + const size_t __len = sizeof(struct sockaddr) + 1; + const size_t __idx __unused = __len - 1; + pid_t __child; + int __status; + int sock[2] = { -1, -1 }; + socklen_t socklen; + __child = fork(); + ATF_REQUIRE(__child >= 0); + if (__child > 0) + goto monitor; + + /* Child */ + disable_coredumps(); + __stack.__buf = malloc(__bufsz); + new_socket(sock); + ATF_REQUIRE(sock[0] >= 0); + socklen = __len; + + getpeername(sock[0], __stack.__buf, &socklen); + _exit(1); /* Should have aborted. */ + +monitor: + while (waitpid(__child, &__status, 0) != __child) { + ATF_REQUIRE_EQ(EINTR, errno); + } + + ATF_REQUIRE_MSG(WIFSIGNALED(__status), "not signaled, status %x", __status); + ATF_REQUIRE_EQ(SIGABRT, WTERMSIG(__status)); +#undef BUF + +} + +ATF_TC_WITHOUT_HEAD(getsockname_before_end); +ATF_TC_BODY(getsockname_before_end, tc) +{ +#define BUF &__stack.__buf + struct { + uint8_t padding_l; + struct sockaddr __buf; + uint8_t padding_r; + } __stack; + const size_t __bufsz __unused = sizeof(__stack.__buf); + const size_t __len = sizeof(struct sockaddr) - 1; + const size_t __idx __unused = __len - 1; + int sock[2] = { -1, -1 }; + socklen_t socklen; + new_socket(sock); + ATF_REQUIRE(sock[0] >= 0); + socklen = __len; + + getsockname(sock[0], &__stack.__buf, &socklen); +#undef BUF + +} + +ATF_TC_WITHOUT_HEAD(getsockname_end); +ATF_TC_BODY(getsockname_end, tc) +{ +#define BUF &__stack.__buf + struct { + uint8_t padding_l; + struct sockaddr __buf; + uint8_t padding_r; + } __stack; + const size_t __bufsz __unused = sizeof(__stack.__buf); + const size_t __len = sizeof(struct sockaddr); + const size_t __idx __unused = __len - 1; + int sock[2] = { -1, -1 }; + socklen_t socklen; + new_socket(sock); + ATF_REQUIRE(sock[0] >= 0); + socklen = __len; + + getsockname(sock[0], &__stack.__buf, &socklen); +#undef BUF + +} + +ATF_TC_WITHOUT_HEAD(getsockname_heap_before_end); +ATF_TC_BODY(getsockname_heap_before_end, tc) +{ +#define BUF __stack.__buf + struct { + uint8_t padding_l; + struct sockaddr * __buf; + uint8_t padding_r; + } __stack; + const size_t __bufsz __unused = sizeof(*__stack.__buf) * (1); + const size_t __len = sizeof(struct sockaddr) - 1; + const size_t __idx __unused = __len - 1; + int sock[2] = { -1, -1 }; + socklen_t socklen; + __stack.__buf = malloc(__bufsz); + new_socket(sock); + ATF_REQUIRE(sock[0] >= 0); + socklen = __len; + + getsockname(sock[0], __stack.__buf, &socklen); +#undef BUF + +} + +ATF_TC_WITHOUT_HEAD(getsockname_heap_end); +ATF_TC_BODY(getsockname_heap_end, tc) +{ +#define BUF __stack.__buf + struct { + uint8_t padding_l; + struct sockaddr * __buf; + uint8_t padding_r; + } __stack; + const size_t __bufsz __unused = sizeof(*__stack.__buf) * (1); + const size_t __len = sizeof(struct sockaddr); + const size_t __idx __unused = __len - 1; + int sock[2] = { -1, -1 }; + socklen_t socklen; + __stack.__buf = malloc(__bufsz); + new_socket(sock); + ATF_REQUIRE(sock[0] >= 0); + socklen = __len; + + getsockname(sock[0], __stack.__buf, &socklen); +#undef BUF + +} + +ATF_TC_WITHOUT_HEAD(getsockname_heap_after_end); +ATF_TC_BODY(getsockname_heap_after_end, tc) +{ +#define BUF __stack.__buf + struct { + uint8_t padding_l; + struct sockaddr * __buf; + uint8_t padding_r; + } __stack; + const size_t __bufsz __unused = sizeof(*__stack.__buf) * (1); + const size_t __len = sizeof(struct sockaddr) + 1; + const size_t __idx __unused = __len - 1; + pid_t __child; + int __status; + int sock[2] = { -1, -1 }; + socklen_t socklen; + __child = fork(); + ATF_REQUIRE(__child >= 0); + if (__child > 0) + goto monitor; + + /* Child */ + disable_coredumps(); + __stack.__buf = malloc(__bufsz); + new_socket(sock); + ATF_REQUIRE(sock[0] >= 0); + socklen = __len; + + getsockname(sock[0], __stack.__buf, &socklen); + _exit(1); /* Should have aborted. */ + +monitor: + while (waitpid(__child, &__status, 0) != __child) { + ATF_REQUIRE_EQ(EINTR, errno); + } + + ATF_REQUIRE_MSG(WIFSIGNALED(__status), "not signaled, status %x", __status); + ATF_REQUIRE_EQ(SIGABRT, WTERMSIG(__status)); +#undef BUF + +} + +ATF_TC_WITHOUT_HEAD(recv_before_end); +ATF_TC_BODY(recv_before_end, tc) +{ +#define BUF &__stack.__buf + struct { + uint8_t padding_l; + unsigned char __buf[42]; + uint8_t padding_r; + } __stack; + const size_t __bufsz __unused = sizeof(__stack.__buf); + const size_t __len = 42 - 1; + const size_t __idx __unused = __len - 1; + int sock[2] = { -1, -1 }; + + new_socket(sock); + ATF_REQUIRE(sock[0] >= 0); + + recv(sock[0], __stack.__buf, __len, 0); +#undef BUF + +} + +ATF_TC_WITHOUT_HEAD(recv_end); +ATF_TC_BODY(recv_end, tc) +{ +#define BUF &__stack.__buf + struct { + uint8_t padding_l; + unsigned char __buf[42]; + uint8_t padding_r; + } __stack; + const size_t __bufsz __unused = sizeof(__stack.__buf); + const size_t __len = 42; + const size_t __idx __unused = __len - 1; + int sock[2] = { -1, -1 }; + + new_socket(sock); + ATF_REQUIRE(sock[0] >= 0); + + recv(sock[0], __stack.__buf, __len, 0); +#undef BUF + +} + +ATF_TC_WITHOUT_HEAD(recv_heap_before_end); +ATF_TC_BODY(recv_heap_before_end, tc) +{ +#define BUF __stack.__buf + struct { + uint8_t padding_l; + unsigned char * __buf; + uint8_t padding_r; + } __stack; + const size_t __bufsz __unused = sizeof(*__stack.__buf) * (42); + const size_t __len = 42 - 1; + const size_t __idx __unused = __len - 1; + int sock[2] = { -1, -1 }; + + __stack.__buf = malloc(__bufsz); + new_socket(sock); + ATF_REQUIRE(sock[0] >= 0); + + recv(sock[0], __stack.__buf, __len, 0); +#undef BUF + +} + +ATF_TC_WITHOUT_HEAD(recv_heap_end); +ATF_TC_BODY(recv_heap_end, tc) +{ +#define BUF __stack.__buf + struct { + uint8_t padding_l; + unsigned char * __buf; + uint8_t padding_r; + } __stack; + const size_t __bufsz __unused = sizeof(*__stack.__buf) * (42); + const size_t __len = 42; + const size_t __idx __unused = __len - 1; + int sock[2] = { -1, -1 }; + + __stack.__buf = malloc(__bufsz); + new_socket(sock); + ATF_REQUIRE(sock[0] >= 0); + + recv(sock[0], __stack.__buf, __len, 0); +#undef BUF + +} + +ATF_TC_WITHOUT_HEAD(recv_heap_after_end); +ATF_TC_BODY(recv_heap_after_end, tc) +{ +#define BUF __stack.__buf + struct { + uint8_t padding_l; + unsigned char * __buf; + uint8_t padding_r; + } __stack; + const size_t __bufsz __unused = sizeof(*__stack.__buf) * (42); + const size_t __len = 42 + 1; + const size_t __idx __unused = __len - 1; + pid_t __child; + int __status; + int sock[2] = { -1, -1 }; + + __child = fork(); + ATF_REQUIRE(__child >= 0); + if (__child > 0) + goto monitor; + + /* Child */ + disable_coredumps(); + __stack.__buf = malloc(__bufsz); + new_socket(sock); + ATF_REQUIRE(sock[0] >= 0); + + recv(sock[0], __stack.__buf, __len, 0); + _exit(1); /* Should have aborted. */ + +monitor: + while (waitpid(__child, &__status, 0) != __child) { + ATF_REQUIRE_EQ(EINTR, errno); + } + + ATF_REQUIRE_MSG(WIFSIGNALED(__status), "not signaled, status %x", __status); + ATF_REQUIRE_EQ(SIGABRT, WTERMSIG(__status)); +#undef BUF + +} + +ATF_TC_WITHOUT_HEAD(recvfrom_before_end); +ATF_TC_BODY(recvfrom_before_end, tc) +{ +#define BUF &__stack.__buf + struct { + uint8_t padding_l; + unsigned char __buf[42]; + uint8_t padding_r; + } __stack; + const size_t __bufsz __unused = sizeof(__stack.__buf); + const size_t __len = 42 - 1; + const size_t __idx __unused = __len - 1; + int sock[2] = { -1, -1 }; + + new_socket(sock); + ATF_REQUIRE(sock[0] >= 0); + + recvfrom(sock[0], __stack.__buf, __len, 0, NULL, NULL); +#undef BUF + +} + +ATF_TC_WITHOUT_HEAD(recvfrom_end); +ATF_TC_BODY(recvfrom_end, tc) +{ +#define BUF &__stack.__buf + struct { + uint8_t padding_l; + unsigned char __buf[42]; + uint8_t padding_r; + } __stack; + const size_t __bufsz __unused = sizeof(__stack.__buf); + const size_t __len = 42; + const size_t __idx __unused = __len - 1; + int sock[2] = { -1, -1 }; + + new_socket(sock); + ATF_REQUIRE(sock[0] >= 0); + + recvfrom(sock[0], __stack.__buf, __len, 0, NULL, NULL); +#undef BUF + +} + +ATF_TC_WITHOUT_HEAD(recvfrom_heap_before_end); +ATF_TC_BODY(recvfrom_heap_before_end, tc) +{ +#define BUF __stack.__buf + struct { + uint8_t padding_l; + unsigned char * __buf; + uint8_t padding_r; + } __stack; + const size_t __bufsz __unused = sizeof(*__stack.__buf) * (42); + const size_t __len = 42 - 1; + const size_t __idx __unused = __len - 1; + int sock[2] = { -1, -1 }; + + __stack.__buf = malloc(__bufsz); + new_socket(sock); + ATF_REQUIRE(sock[0] >= 0); + + recvfrom(sock[0], __stack.__buf, __len, 0, NULL, NULL); +#undef BUF + +} + +ATF_TC_WITHOUT_HEAD(recvfrom_heap_end); +ATF_TC_BODY(recvfrom_heap_end, tc) +{ +#define BUF __stack.__buf + struct { + uint8_t padding_l; + unsigned char * __buf; + uint8_t padding_r; + } __stack; + const size_t __bufsz __unused = sizeof(*__stack.__buf) * (42); + const size_t __len = 42; + const size_t __idx __unused = __len - 1; + int sock[2] = { -1, -1 }; + + __stack.__buf = malloc(__bufsz); + new_socket(sock); + ATF_REQUIRE(sock[0] >= 0); + + recvfrom(sock[0], __stack.__buf, __len, 0, NULL, NULL); +#undef BUF + +} + +ATF_TC_WITHOUT_HEAD(recvfrom_heap_after_end); +ATF_TC_BODY(recvfrom_heap_after_end, tc) +{ +#define BUF __stack.__buf + struct { + uint8_t padding_l; + unsigned char * __buf; + uint8_t padding_r; + } __stack; + const size_t __bufsz __unused = sizeof(*__stack.__buf) * (42); + const size_t __len = 42 + 1; + const size_t __idx __unused = __len - 1; + pid_t __child; + int __status; + int sock[2] = { -1, -1 }; + + __child = fork(); + ATF_REQUIRE(__child >= 0); + if (__child > 0) + goto monitor; + + /* Child */ + disable_coredumps(); + __stack.__buf = malloc(__bufsz); + new_socket(sock); + ATF_REQUIRE(sock[0] >= 0); + + recvfrom(sock[0], __stack.__buf, __len, 0, NULL, NULL); + _exit(1); /* Should have aborted. */ + +monitor: + while (waitpid(__child, &__status, 0) != __child) { + ATF_REQUIRE_EQ(EINTR, errno); + } + + ATF_REQUIRE_MSG(WIFSIGNALED(__status), "not signaled, status %x", __status); + ATF_REQUIRE_EQ(SIGABRT, WTERMSIG(__status)); +#undef BUF + +} + +ATF_TC_WITHOUT_HEAD(recvfrom_sockaddr_before_end); +ATF_TC_BODY(recvfrom_sockaddr_before_end, tc) +{ +#define BUF &__stack.__buf + struct { + uint8_t padding_l; + struct sockaddr __buf; + uint8_t padding_r; + } __stack; + const size_t __bufsz __unused = sizeof(__stack.__buf); + const size_t __len = sizeof(struct sockaddr) - 1; + const size_t __idx __unused = __len - 1; + int sock[2] = { -1, -1 }; + char data[16]; + socklen_t socklen; + + new_socket(sock); + ATF_REQUIRE(sock[0] >= 0); + socklen = __len; + + recvfrom(sock[0], data, sizeof(data), 0, &__stack.__buf, &socklen); +#undef BUF + +} + +ATF_TC_WITHOUT_HEAD(recvfrom_sockaddr_end); +ATF_TC_BODY(recvfrom_sockaddr_end, tc) +{ +#define BUF &__stack.__buf + struct { + uint8_t padding_l; + struct sockaddr __buf; + uint8_t padding_r; + } __stack; + const size_t __bufsz __unused = sizeof(__stack.__buf); + const size_t __len = sizeof(struct sockaddr); + const size_t __idx __unused = __len - 1; + int sock[2] = { -1, -1 }; + char data[16]; + socklen_t socklen; + + new_socket(sock); + ATF_REQUIRE(sock[0] >= 0); + socklen = __len; + + recvfrom(sock[0], data, sizeof(data), 0, &__stack.__buf, &socklen); +#undef BUF + +} + +ATF_TC_WITHOUT_HEAD(recvfrom_sockaddr_heap_before_end); +ATF_TC_BODY(recvfrom_sockaddr_heap_before_end, tc) +{ +#define BUF __stack.__buf + struct { + uint8_t padding_l; + struct sockaddr * __buf; + uint8_t padding_r; + } __stack; + const size_t __bufsz __unused = sizeof(*__stack.__buf) * (1); + const size_t __len = sizeof(struct sockaddr) - 1; + const size_t __idx __unused = __len - 1; + int sock[2] = { -1, -1 }; + char data[16]; + socklen_t socklen; + + __stack.__buf = malloc(__bufsz); + new_socket(sock); + ATF_REQUIRE(sock[0] >= 0); + socklen = __len; + + recvfrom(sock[0], data, sizeof(data), 0, __stack.__buf, &socklen); +#undef BUF + +} + +ATF_TC_WITHOUT_HEAD(recvfrom_sockaddr_heap_end); +ATF_TC_BODY(recvfrom_sockaddr_heap_end, tc) +{ +#define BUF __stack.__buf + struct { + uint8_t padding_l; + struct sockaddr * __buf; + uint8_t padding_r; + } __stack; + const size_t __bufsz __unused = sizeof(*__stack.__buf) * (1); + const size_t __len = sizeof(struct sockaddr); + const size_t __idx __unused = __len - 1; + int sock[2] = { -1, -1 }; + char data[16]; + socklen_t socklen; + + __stack.__buf = malloc(__bufsz); + new_socket(sock); + ATF_REQUIRE(sock[0] >= 0); + socklen = __len; + + recvfrom(sock[0], data, sizeof(data), 0, __stack.__buf, &socklen); +#undef BUF + +} + +ATF_TC_WITHOUT_HEAD(recvfrom_sockaddr_heap_after_end); +ATF_TC_BODY(recvfrom_sockaddr_heap_after_end, tc) +{ +#define BUF __stack.__buf + struct { + uint8_t padding_l; + struct sockaddr * __buf; + uint8_t padding_r; + } __stack; + const size_t __bufsz __unused = sizeof(*__stack.__buf) * (1); + const size_t __len = sizeof(struct sockaddr) + 1; + const size_t __idx __unused = __len - 1; + pid_t __child; + int __status; + int sock[2] = { -1, -1 }; + char data[16]; + socklen_t socklen; + + __child = fork(); + ATF_REQUIRE(__child >= 0); + if (__child > 0) + goto monitor; + + /* Child */ + disable_coredumps(); + __stack.__buf = malloc(__bufsz); + new_socket(sock); + ATF_REQUIRE(sock[0] >= 0); + socklen = __len; + + recvfrom(sock[0], data, sizeof(data), 0, __stack.__buf, &socklen); + _exit(1); /* Should have aborted. */ + +monitor: + while (waitpid(__child, &__status, 0) != __child) { + ATF_REQUIRE_EQ(EINTR, errno); + } + + ATF_REQUIRE_MSG(WIFSIGNALED(__status), "not signaled, status %x", __status); + ATF_REQUIRE_EQ(SIGABRT, WTERMSIG(__status)); +#undef BUF + +} + +ATF_TC_WITHOUT_HEAD(recvmsg_msg_name_before_end); +ATF_TC_BODY(recvmsg_msg_name_before_end, tc) +{ +#define BUF &__stack.__buf + struct { + uint8_t padding_l; + struct sockaddr __buf; + uint8_t padding_r; + } __stack; + const size_t __bufsz __unused = sizeof(__stack.__buf); + const size_t __len = sizeof(struct sockaddr) - 1; + const size_t __idx __unused = __len - 1; + int sock[2] = { -1, -1 }; + struct msghdr msg; + + memset(&msg, 0, sizeof(msg)); + msg.msg_name = BUF; + msg.msg_namelen = __len; + + recvmsg(sock[0], &msg, 0); +#undef BUF + +} + +ATF_TC_WITHOUT_HEAD(recvmsg_msg_name_end); +ATF_TC_BODY(recvmsg_msg_name_end, tc) +{ +#define BUF &__stack.__buf + struct { + uint8_t padding_l; + struct sockaddr __buf; + uint8_t padding_r; + } __stack; + const size_t __bufsz __unused = sizeof(__stack.__buf); + const size_t __len = sizeof(struct sockaddr); + const size_t __idx __unused = __len - 1; + int sock[2] = { -1, -1 }; + struct msghdr msg; + + memset(&msg, 0, sizeof(msg)); + msg.msg_name = BUF; + msg.msg_namelen = __len; + + recvmsg(sock[0], &msg, 0); +#undef BUF + +} + +ATF_TC_WITHOUT_HEAD(recvmsg_msg_name_heap_before_end); +ATF_TC_BODY(recvmsg_msg_name_heap_before_end, tc) +{ +#define BUF __stack.__buf + struct { + uint8_t padding_l; + struct sockaddr * __buf; + uint8_t padding_r; + } __stack; + const size_t __bufsz __unused = sizeof(*__stack.__buf) * (1); + const size_t __len = sizeof(struct sockaddr) - 1; + const size_t __idx __unused = __len - 1; + int sock[2] = { -1, -1 }; + struct msghdr msg; + + __stack.__buf = malloc(__bufsz); + memset(&msg, 0, sizeof(msg)); + msg.msg_name = BUF; + msg.msg_namelen = __len; + + recvmsg(sock[0], &msg, 0); +#undef BUF + +} + +ATF_TC_WITHOUT_HEAD(recvmsg_msg_name_heap_end); +ATF_TC_BODY(recvmsg_msg_name_heap_end, tc) +{ +#define BUF __stack.__buf + struct { + uint8_t padding_l; + struct sockaddr * __buf; + uint8_t padding_r; + } __stack; + const size_t __bufsz __unused = sizeof(*__stack.__buf) * (1); + const size_t __len = sizeof(struct sockaddr); + const size_t __idx __unused = __len - 1; + int sock[2] = { -1, -1 }; + struct msghdr msg; + + __stack.__buf = malloc(__bufsz); + memset(&msg, 0, sizeof(msg)); + msg.msg_name = BUF; + msg.msg_namelen = __len; + + recvmsg(sock[0], &msg, 0); +#undef BUF + +} + +ATF_TC_WITHOUT_HEAD(recvmsg_msg_name_heap_after_end); +ATF_TC_BODY(recvmsg_msg_name_heap_after_end, tc) +{ +#define BUF __stack.__buf + struct { + uint8_t padding_l; + struct sockaddr * __buf; + uint8_t padding_r; + } __stack; + const size_t __bufsz __unused = sizeof(*__stack.__buf) * (1); + const size_t __len = sizeof(struct sockaddr) + 1; + const size_t __idx __unused = __len - 1; + pid_t __child; + int __status; + int sock[2] = { -1, -1 }; + struct msghdr msg; + + __child = fork(); + ATF_REQUIRE(__child >= 0); + if (__child > 0) + goto monitor; + + /* Child */ + disable_coredumps(); + __stack.__buf = malloc(__bufsz); + memset(&msg, 0, sizeof(msg)); + msg.msg_name = BUF; + msg.msg_namelen = __len; + + recvmsg(sock[0], &msg, 0); + _exit(1); /* Should have aborted. */ + +monitor: + while (waitpid(__child, &__status, 0) != __child) { + ATF_REQUIRE_EQ(EINTR, errno); + } + + ATF_REQUIRE_MSG(WIFSIGNALED(__status), "not signaled, status %x", __status); + ATF_REQUIRE_EQ(SIGABRT, WTERMSIG(__status)); +#undef BUF + +} + +ATF_TC_WITHOUT_HEAD(recvmsg_msg_iov_before_end); +ATF_TC_BODY(recvmsg_msg_iov_before_end, tc) +{ +#define BUF &__stack.__buf + struct { + uint8_t padding_l; + unsigned char __buf[42]; + uint8_t padding_r; + } __stack; + const size_t __bufsz __unused = sizeof(__stack.__buf); + const size_t __len = 42 - 1; + const size_t __idx __unused = __len - 1; + int sock[2] = { -1, -1 }; + struct msghdr msg; + struct iovec iov[2]; + + memset(&msg, 0, sizeof(msg)); + memset(&iov[0], 0, sizeof(iov)); + + /* + * We position the buffer second just so that we can confirm that the + * fortification bits are traversing the iovec correctly. + */ + iov[1].iov_base = BUF; + iov[1].iov_len = __len; + + msg.msg_iov = &iov[0]; + msg.msg_iovlen = nitems(iov); + + recvmsg(sock[0], &msg, 0); +#undef BUF + +} + +ATF_TC_WITHOUT_HEAD(recvmsg_msg_iov_end); +ATF_TC_BODY(recvmsg_msg_iov_end, tc) +{ +#define BUF &__stack.__buf + struct { + uint8_t padding_l; + unsigned char __buf[42]; + uint8_t padding_r; + } __stack; + const size_t __bufsz __unused = sizeof(__stack.__buf); + const size_t __len = 42; + const size_t __idx __unused = __len - 1; + int sock[2] = { -1, -1 }; + struct msghdr msg; + struct iovec iov[2]; + + memset(&msg, 0, sizeof(msg)); + memset(&iov[0], 0, sizeof(iov)); + + /* + * We position the buffer second just so that we can confirm that the + * fortification bits are traversing the iovec correctly. + */ + iov[1].iov_base = BUF; + iov[1].iov_len = __len; + + msg.msg_iov = &iov[0]; + msg.msg_iovlen = nitems(iov); + + recvmsg(sock[0], &msg, 0); +#undef BUF + +} + +ATF_TC_WITHOUT_HEAD(recvmsg_msg_iov_heap_before_end); +ATF_TC_BODY(recvmsg_msg_iov_heap_before_end, tc) +{ +#define BUF __stack.__buf + struct { + uint8_t padding_l; + unsigned char * __buf; + uint8_t padding_r; + } __stack; + const size_t __bufsz __unused = sizeof(*__stack.__buf) * (42); + const size_t __len = 42 - 1; + const size_t __idx __unused = __len - 1; + int sock[2] = { -1, -1 }; + struct msghdr msg; + struct iovec iov[2]; + + __stack.__buf = malloc(__bufsz); + memset(&msg, 0, sizeof(msg)); + memset(&iov[0], 0, sizeof(iov)); + + /* + * We position the buffer second just so that we can confirm that the + * fortification bits are traversing the iovec correctly. + */ + iov[1].iov_base = BUF; + iov[1].iov_len = __len; + + msg.msg_iov = &iov[0]; + msg.msg_iovlen = nitems(iov); + + recvmsg(sock[0], &msg, 0); +#undef BUF + +} + +ATF_TC_WITHOUT_HEAD(recvmsg_msg_iov_heap_end); +ATF_TC_BODY(recvmsg_msg_iov_heap_end, tc) +{ +#define BUF __stack.__buf + struct { + uint8_t padding_l; + unsigned char * __buf; + uint8_t padding_r; + } __stack; + const size_t __bufsz __unused = sizeof(*__stack.__buf) * (42); + const size_t __len = 42; + const size_t __idx __unused = __len - 1; + int sock[2] = { -1, -1 }; + struct msghdr msg; + struct iovec iov[2]; + + __stack.__buf = malloc(__bufsz); + memset(&msg, 0, sizeof(msg)); + memset(&iov[0], 0, sizeof(iov)); + + /* + * We position the buffer second just so that we can confirm that the + * fortification bits are traversing the iovec correctly. + */ + iov[1].iov_base = BUF; + iov[1].iov_len = __len; + + msg.msg_iov = &iov[0]; + msg.msg_iovlen = nitems(iov); + + recvmsg(sock[0], &msg, 0); +#undef BUF + +} + +ATF_TC_WITHOUT_HEAD(recvmsg_msg_iov_heap_after_end); +ATF_TC_BODY(recvmsg_msg_iov_heap_after_end, tc) +{ +#define BUF __stack.__buf + struct { + uint8_t padding_l; + unsigned char * __buf; + uint8_t padding_r; + } __stack; + const size_t __bufsz __unused = sizeof(*__stack.__buf) * (42); + const size_t __len = 42 + 1; + const size_t __idx __unused = __len - 1; + pid_t __child; + int __status; + int sock[2] = { -1, -1 }; + struct msghdr msg; + struct iovec iov[2]; + + __child = fork(); + ATF_REQUIRE(__child >= 0); + if (__child > 0) + goto monitor; + + /* Child */ + disable_coredumps(); + __stack.__buf = malloc(__bufsz); + memset(&msg, 0, sizeof(msg)); + memset(&iov[0], 0, sizeof(iov)); + + /* + * We position the buffer second just so that we can confirm that the + * fortification bits are traversing the iovec correctly. + */ + iov[1].iov_base = BUF; + iov[1].iov_len = __len; + + msg.msg_iov = &iov[0]; + msg.msg_iovlen = nitems(iov); + + recvmsg(sock[0], &msg, 0); + _exit(1); /* Should have aborted. */ + +monitor: + while (waitpid(__child, &__status, 0) != __child) { + ATF_REQUIRE_EQ(EINTR, errno); + } + + ATF_REQUIRE_MSG(WIFSIGNALED(__status), "not signaled, status %x", __status); + ATF_REQUIRE_EQ(SIGABRT, WTERMSIG(__status)); +#undef BUF + +} + +ATF_TC_WITHOUT_HEAD(recvmsg_msg_control_before_end); +ATF_TC_BODY(recvmsg_msg_control_before_end, tc) +{ +#define BUF &__stack.__buf + struct { + uint8_t padding_l; + unsigned char __buf[CMSG_SPACE(sizeof(int))]; + uint8_t padding_r; + } __stack; + const size_t __bufsz __unused = sizeof(__stack.__buf); + const size_t __len = CMSG_SPACE(sizeof(int)) - 1; + const size_t __idx __unused = __len - 1; + int sock[2] = { -1, -1 }; + struct msghdr msg; + + memset(&msg, 0, sizeof(msg)); + + msg.msg_control = BUF; + msg.msg_controllen = __len; + + recvmsg(sock[0], &msg, 0); +#undef BUF + +} + +ATF_TC_WITHOUT_HEAD(recvmsg_msg_control_end); +ATF_TC_BODY(recvmsg_msg_control_end, tc) +{ +#define BUF &__stack.__buf + struct { + uint8_t padding_l; + unsigned char __buf[CMSG_SPACE(sizeof(int))]; + uint8_t padding_r; + } __stack; + const size_t __bufsz __unused = sizeof(__stack.__buf); + const size_t __len = CMSG_SPACE(sizeof(int)); + const size_t __idx __unused = __len - 1; + int sock[2] = { -1, -1 }; + struct msghdr msg; + + memset(&msg, 0, sizeof(msg)); + + msg.msg_control = BUF; + msg.msg_controllen = __len; + + recvmsg(sock[0], &msg, 0); +#undef BUF + +} + +ATF_TC_WITHOUT_HEAD(recvmsg_msg_control_heap_before_end); +ATF_TC_BODY(recvmsg_msg_control_heap_before_end, tc) +{ +#define BUF __stack.__buf + struct { + uint8_t padding_l; + unsigned char * __buf; + uint8_t padding_r; + } __stack; + const size_t __bufsz __unused = sizeof(*__stack.__buf) * (CMSG_SPACE(sizeof(int))); + const size_t __len = CMSG_SPACE(sizeof(int)) - 1; + const size_t __idx __unused = __len - 1; + int sock[2] = { -1, -1 }; + struct msghdr msg; + + __stack.__buf = malloc(__bufsz); + memset(&msg, 0, sizeof(msg)); + + msg.msg_control = BUF; + msg.msg_controllen = __len; + + recvmsg(sock[0], &msg, 0); +#undef BUF + +} + +ATF_TC_WITHOUT_HEAD(recvmsg_msg_control_heap_end); +ATF_TC_BODY(recvmsg_msg_control_heap_end, tc) +{ +#define BUF __stack.__buf + struct { + uint8_t padding_l; + unsigned char * __buf; + uint8_t padding_r; + } __stack; + const size_t __bufsz __unused = sizeof(*__stack.__buf) * (CMSG_SPACE(sizeof(int))); + const size_t __len = CMSG_SPACE(sizeof(int)); + const size_t __idx __unused = __len - 1; + int sock[2] = { -1, -1 }; + struct msghdr msg; + + __stack.__buf = malloc(__bufsz); + memset(&msg, 0, sizeof(msg)); + + msg.msg_control = BUF; + msg.msg_controllen = __len; + + recvmsg(sock[0], &msg, 0); +#undef BUF + +} + +ATF_TC_WITHOUT_HEAD(recvmsg_msg_control_heap_after_end); +ATF_TC_BODY(recvmsg_msg_control_heap_after_end, tc) +{ +#define BUF __stack.__buf + struct { + uint8_t padding_l; + unsigned char * __buf; + uint8_t padding_r; + } __stack; + const size_t __bufsz __unused = sizeof(*__stack.__buf) * (CMSG_SPACE(sizeof(int))); + const size_t __len = CMSG_SPACE(sizeof(int)) + 1; + const size_t __idx __unused = __len - 1; + pid_t __child; + int __status; + int sock[2] = { -1, -1 }; + struct msghdr msg; + + __child = fork(); + ATF_REQUIRE(__child >= 0); + if (__child > 0) + goto monitor; + + /* Child */ + disable_coredumps(); + __stack.__buf = malloc(__bufsz); + memset(&msg, 0, sizeof(msg)); + + msg.msg_control = BUF; + msg.msg_controllen = __len; + + recvmsg(sock[0], &msg, 0); + _exit(1); /* Should have aborted. */ + +monitor: + while (waitpid(__child, &__status, 0) != __child) { + ATF_REQUIRE_EQ(EINTR, errno); + } + + ATF_REQUIRE_MSG(WIFSIGNALED(__status), "not signaled, status %x", __status); + ATF_REQUIRE_EQ(SIGABRT, WTERMSIG(__status)); +#undef BUF + +} + +ATF_TC_WITHOUT_HEAD(recvmmsg_msgvec_before_end); +ATF_TC_BODY(recvmmsg_msgvec_before_end, tc) +{ +#define BUF &__stack.__buf + struct { + uint8_t padding_l; + struct mmsghdr __buf[2]; + uint8_t padding_r; + } __stack; + const size_t __bufsz __unused = sizeof(__stack.__buf); + const size_t __len = 2 - 1; + const size_t __idx __unused = __len - 1; + int sock[2] = { -1, -1 }; + + recvmmsg(sock[0], __stack.__buf, __len, 0, NULL); +#undef BUF + +} + +ATF_TC_WITHOUT_HEAD(recvmmsg_msgvec_end); +ATF_TC_BODY(recvmmsg_msgvec_end, tc) +{ +#define BUF &__stack.__buf + struct { + uint8_t padding_l; + struct mmsghdr __buf[2]; + uint8_t padding_r; + } __stack; + const size_t __bufsz __unused = sizeof(__stack.__buf); + const size_t __len = 2; + const size_t __idx __unused = __len - 1; + int sock[2] = { -1, -1 }; + + recvmmsg(sock[0], __stack.__buf, __len, 0, NULL); +#undef BUF + +} + +ATF_TC_WITHOUT_HEAD(recvmmsg_msgvec_after_end); +ATF_TC_BODY(recvmmsg_msgvec_after_end, tc) +{ +#define BUF &__stack.__buf + struct { + uint8_t padding_l; + struct mmsghdr __buf[2]; + uint8_t padding_r; + } __stack; + const size_t __bufsz __unused = sizeof(__stack.__buf); + const size_t __len = 2 + 1; + const size_t __idx __unused = __len - 1; + pid_t __child; + int __status; + int sock[2] = { -1, -1 }; + + __child = fork(); + ATF_REQUIRE(__child >= 0); + if (__child > 0) + goto monitor; + + /* Child */ + disable_coredumps(); + recvmmsg(sock[0], __stack.__buf, __len, 0, NULL); + _exit(1); /* Should have aborted. */ + +monitor: + while (waitpid(__child, &__status, 0) != __child) { + ATF_REQUIRE_EQ(EINTR, errno); + } + + ATF_REQUIRE_MSG(WIFSIGNALED(__status), "not signaled, status %x", __status); + ATF_REQUIRE_EQ(SIGABRT, WTERMSIG(__status)); +#undef BUF + +} + +ATF_TC_WITHOUT_HEAD(recvmmsg_msgvec_heap_before_end); +ATF_TC_BODY(recvmmsg_msgvec_heap_before_end, tc) +{ +#define BUF __stack.__buf + struct { + uint8_t padding_l; + struct mmsghdr * __buf; + uint8_t padding_r; + } __stack; + const size_t __bufsz __unused = sizeof(*__stack.__buf) * (2); + const size_t __len = 2 - 1; + const size_t __idx __unused = __len - 1; + int sock[2] = { -1, -1 }; + + __stack.__buf = malloc(__bufsz); + + recvmmsg(sock[0], __stack.__buf, __len, 0, NULL); +#undef BUF + +} + +ATF_TC_WITHOUT_HEAD(recvmmsg_msgvec_heap_end); +ATF_TC_BODY(recvmmsg_msgvec_heap_end, tc) +{ +#define BUF __stack.__buf + struct { + uint8_t padding_l; + struct mmsghdr * __buf; + uint8_t padding_r; + } __stack; + const size_t __bufsz __unused = sizeof(*__stack.__buf) * (2); + const size_t __len = 2; + const size_t __idx __unused = __len - 1; + int sock[2] = { -1, -1 }; + + __stack.__buf = malloc(__bufsz); + + recvmmsg(sock[0], __stack.__buf, __len, 0, NULL); +#undef BUF + +} + +ATF_TC_WITHOUT_HEAD(recvmmsg_msgvec_heap_after_end); +ATF_TC_BODY(recvmmsg_msgvec_heap_after_end, tc) +{ +#define BUF __stack.__buf + struct { + uint8_t padding_l; + struct mmsghdr * __buf; + uint8_t padding_r; + } __stack; + const size_t __bufsz __unused = sizeof(*__stack.__buf) * (2); + const size_t __len = 2 + 1; + const size_t __idx __unused = __len - 1; + pid_t __child; + int __status; + int sock[2] = { -1, -1 }; + + __child = fork(); + ATF_REQUIRE(__child >= 0); + if (__child > 0) + goto monitor; + + /* Child */ + disable_coredumps(); + __stack.__buf = malloc(__bufsz); + + recvmmsg(sock[0], __stack.__buf, __len, 0, NULL); + _exit(1); /* Should have aborted. */ + +monitor: + while (waitpid(__child, &__status, 0) != __child) { + ATF_REQUIRE_EQ(EINTR, errno); + } + + ATF_REQUIRE_MSG(WIFSIGNALED(__status), "not signaled, status %x", __status); + ATF_REQUIRE_EQ(SIGABRT, WTERMSIG(__status)); +#undef BUF + +} + +ATF_TC_WITHOUT_HEAD(recvmmsg_msghdr_before_end); +ATF_TC_BODY(recvmmsg_msghdr_before_end, tc) +{ +#define BUF &__stack.__buf + struct { + uint8_t padding_l; + unsigned char __buf[42]; + uint8_t padding_r; + } __stack; + const size_t __bufsz __unused = sizeof(__stack.__buf); + const size_t __len = 42 - 1; + const size_t __idx __unused = __len - 1; + int sock[2] = { -1, -1 }; + struct mmsghdr msgvec[2]; + + memset(&msgvec[0], 0, sizeof(msgvec)); + + /* + * Same as above, make sure fortification isn't ignoring n > 1 elements + * of the msgvec. + */ + msgvec[1].msg_hdr.msg_control = BUF; + msgvec[1].msg_hdr.msg_controllen = __len; + + recvmmsg(sock[0], &msgvec[0], nitems(msgvec), 0, NULL); +#undef BUF + +} + +ATF_TC_WITHOUT_HEAD(recvmmsg_msghdr_end); +ATF_TC_BODY(recvmmsg_msghdr_end, tc) +{ +#define BUF &__stack.__buf + struct { + uint8_t padding_l; + unsigned char __buf[42]; + uint8_t padding_r; + } __stack; + const size_t __bufsz __unused = sizeof(__stack.__buf); + const size_t __len = 42; + const size_t __idx __unused = __len - 1; + int sock[2] = { -1, -1 }; + struct mmsghdr msgvec[2]; + + memset(&msgvec[0], 0, sizeof(msgvec)); + + /* + * Same as above, make sure fortification isn't ignoring n > 1 elements + * of the msgvec. + */ + msgvec[1].msg_hdr.msg_control = BUF; + msgvec[1].msg_hdr.msg_controllen = __len; + + recvmmsg(sock[0], &msgvec[0], nitems(msgvec), 0, NULL); +#undef BUF + +} + +ATF_TC_WITHOUT_HEAD(recvmmsg_msghdr_heap_before_end); +ATF_TC_BODY(recvmmsg_msghdr_heap_before_end, tc) +{ +#define BUF __stack.__buf + struct { + uint8_t padding_l; + unsigned char * __buf; + uint8_t padding_r; + } __stack; + const size_t __bufsz __unused = sizeof(*__stack.__buf) * (42); + const size_t __len = 42 - 1; + const size_t __idx __unused = __len - 1; + int sock[2] = { -1, -1 }; + struct mmsghdr msgvec[2]; + + __stack.__buf = malloc(__bufsz); + memset(&msgvec[0], 0, sizeof(msgvec)); + + /* + * Same as above, make sure fortification isn't ignoring n > 1 elements + * of the msgvec. + */ + msgvec[1].msg_hdr.msg_control = BUF; + msgvec[1].msg_hdr.msg_controllen = __len; + + recvmmsg(sock[0], &msgvec[0], nitems(msgvec), 0, NULL); +#undef BUF + +} + +ATF_TC_WITHOUT_HEAD(recvmmsg_msghdr_heap_end); +ATF_TC_BODY(recvmmsg_msghdr_heap_end, tc) +{ +#define BUF __stack.__buf + struct { + uint8_t padding_l; + unsigned char * __buf; + uint8_t padding_r; + } __stack; + const size_t __bufsz __unused = sizeof(*__stack.__buf) * (42); + const size_t __len = 42; + const size_t __idx __unused = __len - 1; + int sock[2] = { -1, -1 }; + struct mmsghdr msgvec[2]; + + __stack.__buf = malloc(__bufsz); + memset(&msgvec[0], 0, sizeof(msgvec)); + + /* + * Same as above, make sure fortification isn't ignoring n > 1 elements + * of the msgvec. + */ + msgvec[1].msg_hdr.msg_control = BUF; + msgvec[1].msg_hdr.msg_controllen = __len; + + recvmmsg(sock[0], &msgvec[0], nitems(msgvec), 0, NULL); +#undef BUF + +} + +ATF_TC_WITHOUT_HEAD(recvmmsg_msghdr_heap_after_end); +ATF_TC_BODY(recvmmsg_msghdr_heap_after_end, tc) +{ +#define BUF __stack.__buf + struct { + uint8_t padding_l; + unsigned char * __buf; + uint8_t padding_r; + } __stack; + const size_t __bufsz __unused = sizeof(*__stack.__buf) * (42); + const size_t __len = 42 + 1; + const size_t __idx __unused = __len - 1; + pid_t __child; + int __status; + int sock[2] = { -1, -1 }; + struct mmsghdr msgvec[2]; + + __child = fork(); + ATF_REQUIRE(__child >= 0); + if (__child > 0) + goto monitor; + + /* Child */ + disable_coredumps(); + __stack.__buf = malloc(__bufsz); + memset(&msgvec[0], 0, sizeof(msgvec)); + + /* + * Same as above, make sure fortification isn't ignoring n > 1 elements + * of the msgvec. + */ + msgvec[1].msg_hdr.msg_control = BUF; + msgvec[1].msg_hdr.msg_controllen = __len; + + recvmmsg(sock[0], &msgvec[0], nitems(msgvec), 0, NULL); + _exit(1); /* Should have aborted. */ + +monitor: + while (waitpid(__child, &__status, 0) != __child) { + ATF_REQUIRE_EQ(EINTR, errno); + } + + ATF_REQUIRE_MSG(WIFSIGNALED(__status), "not signaled, status %x", __status); + ATF_REQUIRE_EQ(SIGABRT, WTERMSIG(__status)); +#undef BUF + +} + +ATF_TP_ADD_TCS(tp) +{ + ATF_TP_ADD_TC(tp, getpeername_before_end); + ATF_TP_ADD_TC(tp, getpeername_end); + ATF_TP_ADD_TC(tp, getpeername_heap_before_end); + ATF_TP_ADD_TC(tp, getpeername_heap_end); + ATF_TP_ADD_TC(tp, getpeername_heap_after_end); + ATF_TP_ADD_TC(tp, getsockname_before_end); + ATF_TP_ADD_TC(tp, getsockname_end); + ATF_TP_ADD_TC(tp, getsockname_heap_before_end); + ATF_TP_ADD_TC(tp, getsockname_heap_end); + ATF_TP_ADD_TC(tp, getsockname_heap_after_end); + ATF_TP_ADD_TC(tp, recv_before_end); + ATF_TP_ADD_TC(tp, recv_end); + ATF_TP_ADD_TC(tp, recv_heap_before_end); + ATF_TP_ADD_TC(tp, recv_heap_end); + ATF_TP_ADD_TC(tp, recv_heap_after_end); + ATF_TP_ADD_TC(tp, recvfrom_before_end); + ATF_TP_ADD_TC(tp, recvfrom_end); + ATF_TP_ADD_TC(tp, recvfrom_heap_before_end); + ATF_TP_ADD_TC(tp, recvfrom_heap_end); + ATF_TP_ADD_TC(tp, recvfrom_heap_after_end); + ATF_TP_ADD_TC(tp, recvfrom_sockaddr_before_end); + ATF_TP_ADD_TC(tp, recvfrom_sockaddr_end); + ATF_TP_ADD_TC(tp, recvfrom_sockaddr_heap_before_end); + ATF_TP_ADD_TC(tp, recvfrom_sockaddr_heap_end); + ATF_TP_ADD_TC(tp, recvfrom_sockaddr_heap_after_end); + ATF_TP_ADD_TC(tp, recvmsg_msg_name_before_end); + ATF_TP_ADD_TC(tp, recvmsg_msg_name_end); + ATF_TP_ADD_TC(tp, recvmsg_msg_name_heap_before_end); + ATF_TP_ADD_TC(tp, recvmsg_msg_name_heap_end); + ATF_TP_ADD_TC(tp, recvmsg_msg_name_heap_after_end); + ATF_TP_ADD_TC(tp, recvmsg_msg_iov_before_end); + ATF_TP_ADD_TC(tp, recvmsg_msg_iov_end); + ATF_TP_ADD_TC(tp, recvmsg_msg_iov_heap_before_end); + ATF_TP_ADD_TC(tp, recvmsg_msg_iov_heap_end); + ATF_TP_ADD_TC(tp, recvmsg_msg_iov_heap_after_end); + ATF_TP_ADD_TC(tp, recvmsg_msg_control_before_end); + ATF_TP_ADD_TC(tp, recvmsg_msg_control_end); + ATF_TP_ADD_TC(tp, recvmsg_msg_control_heap_before_end); + ATF_TP_ADD_TC(tp, recvmsg_msg_control_heap_end); + ATF_TP_ADD_TC(tp, recvmsg_msg_control_heap_after_end); + ATF_TP_ADD_TC(tp, recvmmsg_msgvec_before_end); + ATF_TP_ADD_TC(tp, recvmmsg_msgvec_end); + ATF_TP_ADD_TC(tp, recvmmsg_msgvec_after_end); + ATF_TP_ADD_TC(tp, recvmmsg_msgvec_heap_before_end); + ATF_TP_ADD_TC(tp, recvmmsg_msgvec_heap_end); + ATF_TP_ADD_TC(tp, recvmmsg_msgvec_heap_after_end); + ATF_TP_ADD_TC(tp, recvmmsg_msghdr_before_end); + ATF_TP_ADD_TC(tp, recvmmsg_msghdr_end); + ATF_TP_ADD_TC(tp, recvmmsg_msghdr_heap_before_end); + ATF_TP_ADD_TC(tp, recvmmsg_msghdr_heap_end); + ATF_TP_ADD_TC(tp, recvmmsg_msghdr_heap_after_end); + return (atf_no_error()); +} diff --git a/lib/libc/tests/secure/fortify_stdio_test.c b/lib/libc/tests/secure/fortify_stdio_test.c --- a/lib/libc/tests/secure/fortify_stdio_test.c +++ b/lib/libc/tests/secure/fortify_stdio_test.c @@ -7,6 +7,7 @@ #include #include #include +#include #include #include #include @@ -65,6 +66,50 @@ return (linkname); } +/* + * For our purposes, first descriptor will be the reader; we'll send both + * raw data and a control message over it so that the result can be used for + * any of our recv*() tests. + */ +static void __unused +new_socket(int sock[2]) +{ + unsigned char ctrl[CMSG_SPACE(sizeof(int))] = { 0 }; + static char sockbuf[256]; + ssize_t rv; + size_t total = 0; + struct msghdr hdr = { 0 }; + struct cmsghdr *cmsg; + int error, fd; + + error = socketpair(AF_UNIX, SOCK_STREAM, 0, sock); + ATF_REQUIRE(error == 0); + + while (total != sizeof(sockbuf)) { + rv = send(sock[1], &sockbuf[total], sizeof(sockbuf) - total, 0); + + ATF_REQUIRE_MSG(rv > 0, + "expected bytes sent, got %zd with %zu left (size %zu, total %zu)", + rv, sizeof(sockbuf) - total, sizeof(sockbuf), total); + ATF_REQUIRE_MSG(total + (size_t)rv <= sizeof(sockbuf), + "%zd exceeds total %zu", rv, sizeof(sockbuf)); + total += rv; + } + + hdr.msg_control = ctrl; + hdr.msg_controllen = sizeof(ctrl); + + cmsg = CMSG_FIRSTHDR(&hdr); + cmsg->cmsg_level = SOL_SOCKET; + cmsg->cmsg_type = SCM_RIGHTS; + cmsg->cmsg_len = CMSG_LEN(sizeof(fd)); + fd = STDIN_FILENO; + memcpy(CMSG_DATA(cmsg), &fd, sizeof(fd)); + + error = sendmsg(sock[1], &hdr, 0); + ATF_REQUIRE(error != -1); +} + /* * Constructs a tmpfile that we can use for testing read(2) and friends. */ diff --git a/lib/libc/tests/secure/fortify_stdlib_test.c b/lib/libc/tests/secure/fortify_stdlib_test.c --- a/lib/libc/tests/secure/fortify_stdlib_test.c +++ b/lib/libc/tests/secure/fortify_stdlib_test.c @@ -7,6 +7,7 @@ #include #include #include +#include #include #include #include @@ -65,6 +66,50 @@ return (linkname); } +/* + * For our purposes, first descriptor will be the reader; we'll send both + * raw data and a control message over it so that the result can be used for + * any of our recv*() tests. + */ +static void __unused +new_socket(int sock[2]) +{ + unsigned char ctrl[CMSG_SPACE(sizeof(int))] = { 0 }; + static char sockbuf[256]; + ssize_t rv; + size_t total = 0; + struct msghdr hdr = { 0 }; + struct cmsghdr *cmsg; + int error, fd; + + error = socketpair(AF_UNIX, SOCK_STREAM, 0, sock); + ATF_REQUIRE(error == 0); + + while (total != sizeof(sockbuf)) { + rv = send(sock[1], &sockbuf[total], sizeof(sockbuf) - total, 0); + + ATF_REQUIRE_MSG(rv > 0, + "expected bytes sent, got %zd with %zu left (size %zu, total %zu)", + rv, sizeof(sockbuf) - total, sizeof(sockbuf), total); + ATF_REQUIRE_MSG(total + (size_t)rv <= sizeof(sockbuf), + "%zd exceeds total %zu", rv, sizeof(sockbuf)); + total += rv; + } + + hdr.msg_control = ctrl; + hdr.msg_controllen = sizeof(ctrl); + + cmsg = CMSG_FIRSTHDR(&hdr); + cmsg->cmsg_level = SOL_SOCKET; + cmsg->cmsg_type = SCM_RIGHTS; + cmsg->cmsg_len = CMSG_LEN(sizeof(fd)); + fd = STDIN_FILENO; + memcpy(CMSG_DATA(cmsg), &fd, sizeof(fd)); + + error = sendmsg(sock[1], &hdr, 0); + ATF_REQUIRE(error != -1); +} + /* * Constructs a tmpfile that we can use for testing read(2) and friends. */ diff --git a/lib/libc/tests/secure/fortify_string_test.c b/lib/libc/tests/secure/fortify_string_test.c --- a/lib/libc/tests/secure/fortify_string_test.c +++ b/lib/libc/tests/secure/fortify_string_test.c @@ -7,6 +7,7 @@ #include #include #include +#include #include #include #include @@ -65,6 +66,50 @@ return (linkname); } +/* + * For our purposes, first descriptor will be the reader; we'll send both + * raw data and a control message over it so that the result can be used for + * any of our recv*() tests. + */ +static void __unused +new_socket(int sock[2]) +{ + unsigned char ctrl[CMSG_SPACE(sizeof(int))] = { 0 }; + static char sockbuf[256]; + ssize_t rv; + size_t total = 0; + struct msghdr hdr = { 0 }; + struct cmsghdr *cmsg; + int error, fd; + + error = socketpair(AF_UNIX, SOCK_STREAM, 0, sock); + ATF_REQUIRE(error == 0); + + while (total != sizeof(sockbuf)) { + rv = send(sock[1], &sockbuf[total], sizeof(sockbuf) - total, 0); + + ATF_REQUIRE_MSG(rv > 0, + "expected bytes sent, got %zd with %zu left (size %zu, total %zu)", + rv, sizeof(sockbuf) - total, sizeof(sockbuf), total); + ATF_REQUIRE_MSG(total + (size_t)rv <= sizeof(sockbuf), + "%zd exceeds total %zu", rv, sizeof(sockbuf)); + total += rv; + } + + hdr.msg_control = ctrl; + hdr.msg_controllen = sizeof(ctrl); + + cmsg = CMSG_FIRSTHDR(&hdr); + cmsg->cmsg_level = SOL_SOCKET; + cmsg->cmsg_type = SCM_RIGHTS; + cmsg->cmsg_len = CMSG_LEN(sizeof(fd)); + fd = STDIN_FILENO; + memcpy(CMSG_DATA(cmsg), &fd, sizeof(fd)); + + error = sendmsg(sock[1], &hdr, 0); + ATF_REQUIRE(error != -1); +} + /* * Constructs a tmpfile that we can use for testing read(2) and friends. */ diff --git a/lib/libc/tests/secure/fortify_strings_test.c b/lib/libc/tests/secure/fortify_strings_test.c --- a/lib/libc/tests/secure/fortify_strings_test.c +++ b/lib/libc/tests/secure/fortify_strings_test.c @@ -7,6 +7,7 @@ #include #include #include +#include #include #include #include @@ -65,6 +66,50 @@ return (linkname); } +/* + * For our purposes, first descriptor will be the reader; we'll send both + * raw data and a control message over it so that the result can be used for + * any of our recv*() tests. + */ +static void __unused +new_socket(int sock[2]) +{ + unsigned char ctrl[CMSG_SPACE(sizeof(int))] = { 0 }; + static char sockbuf[256]; + ssize_t rv; + size_t total = 0; + struct msghdr hdr = { 0 }; + struct cmsghdr *cmsg; + int error, fd; + + error = socketpair(AF_UNIX, SOCK_STREAM, 0, sock); + ATF_REQUIRE(error == 0); + + while (total != sizeof(sockbuf)) { + rv = send(sock[1], &sockbuf[total], sizeof(sockbuf) - total, 0); + + ATF_REQUIRE_MSG(rv > 0, + "expected bytes sent, got %zd with %zu left (size %zu, total %zu)", + rv, sizeof(sockbuf) - total, sizeof(sockbuf), total); + ATF_REQUIRE_MSG(total + (size_t)rv <= sizeof(sockbuf), + "%zd exceeds total %zu", rv, sizeof(sockbuf)); + total += rv; + } + + hdr.msg_control = ctrl; + hdr.msg_controllen = sizeof(ctrl); + + cmsg = CMSG_FIRSTHDR(&hdr); + cmsg->cmsg_level = SOL_SOCKET; + cmsg->cmsg_type = SCM_RIGHTS; + cmsg->cmsg_len = CMSG_LEN(sizeof(fd)); + fd = STDIN_FILENO; + memcpy(CMSG_DATA(cmsg), &fd, sizeof(fd)); + + error = sendmsg(sock[1], &hdr, 0); + ATF_REQUIRE(error != -1); +} + /* * Constructs a tmpfile that we can use for testing read(2) and friends. */ diff --git a/lib/libc/tests/secure/fortify_uio_test.c b/lib/libc/tests/secure/fortify_uio_test.c --- a/lib/libc/tests/secure/fortify_uio_test.c +++ b/lib/libc/tests/secure/fortify_uio_test.c @@ -7,6 +7,7 @@ #include #include #include +#include #include #include #include @@ -65,6 +66,50 @@ return (linkname); } +/* + * For our purposes, first descriptor will be the reader; we'll send both + * raw data and a control message over it so that the result can be used for + * any of our recv*() tests. + */ +static void __unused +new_socket(int sock[2]) +{ + unsigned char ctrl[CMSG_SPACE(sizeof(int))] = { 0 }; + static char sockbuf[256]; + ssize_t rv; + size_t total = 0; + struct msghdr hdr = { 0 }; + struct cmsghdr *cmsg; + int error, fd; + + error = socketpair(AF_UNIX, SOCK_STREAM, 0, sock); + ATF_REQUIRE(error == 0); + + while (total != sizeof(sockbuf)) { + rv = send(sock[1], &sockbuf[total], sizeof(sockbuf) - total, 0); + + ATF_REQUIRE_MSG(rv > 0, + "expected bytes sent, got %zd with %zu left (size %zu, total %zu)", + rv, sizeof(sockbuf) - total, sizeof(sockbuf), total); + ATF_REQUIRE_MSG(total + (size_t)rv <= sizeof(sockbuf), + "%zd exceeds total %zu", rv, sizeof(sockbuf)); + total += rv; + } + + hdr.msg_control = ctrl; + hdr.msg_controllen = sizeof(ctrl); + + cmsg = CMSG_FIRSTHDR(&hdr); + cmsg->cmsg_level = SOL_SOCKET; + cmsg->cmsg_type = SCM_RIGHTS; + cmsg->cmsg_len = CMSG_LEN(sizeof(fd)); + fd = STDIN_FILENO; + memcpy(CMSG_DATA(cmsg), &fd, sizeof(fd)); + + error = sendmsg(sock[1], &hdr, 0); + ATF_REQUIRE(error != -1); +} + /* * Constructs a tmpfile that we can use for testing read(2) and friends. */ diff --git a/lib/libc/tests/secure/fortify_unistd_test.c b/lib/libc/tests/secure/fortify_unistd_test.c --- a/lib/libc/tests/secure/fortify_unistd_test.c +++ b/lib/libc/tests/secure/fortify_unistd_test.c @@ -7,6 +7,7 @@ #include #include #include +#include #include #include #include @@ -65,6 +66,50 @@ return (linkname); } +/* + * For our purposes, first descriptor will be the reader; we'll send both + * raw data and a control message over it so that the result can be used for + * any of our recv*() tests. + */ +static void __unused +new_socket(int sock[2]) +{ + unsigned char ctrl[CMSG_SPACE(sizeof(int))] = { 0 }; + static char sockbuf[256]; + ssize_t rv; + size_t total = 0; + struct msghdr hdr = { 0 }; + struct cmsghdr *cmsg; + int error, fd; + + error = socketpair(AF_UNIX, SOCK_STREAM, 0, sock); + ATF_REQUIRE(error == 0); + + while (total != sizeof(sockbuf)) { + rv = send(sock[1], &sockbuf[total], sizeof(sockbuf) - total, 0); + + ATF_REQUIRE_MSG(rv > 0, + "expected bytes sent, got %zd with %zu left (size %zu, total %zu)", + rv, sizeof(sockbuf) - total, sizeof(sockbuf), total); + ATF_REQUIRE_MSG(total + (size_t)rv <= sizeof(sockbuf), + "%zd exceeds total %zu", rv, sizeof(sockbuf)); + total += rv; + } + + hdr.msg_control = ctrl; + hdr.msg_controllen = sizeof(ctrl); + + cmsg = CMSG_FIRSTHDR(&hdr); + cmsg->cmsg_level = SOL_SOCKET; + cmsg->cmsg_type = SCM_RIGHTS; + cmsg->cmsg_len = CMSG_LEN(sizeof(fd)); + fd = STDIN_FILENO; + memcpy(CMSG_DATA(cmsg), &fd, sizeof(fd)); + + error = sendmsg(sock[1], &hdr, 0); + ATF_REQUIRE(error != -1); +} + /* * Constructs a tmpfile that we can use for testing read(2) and friends. */ diff --git a/lib/libc/tests/secure/fortify_wchar_test.c b/lib/libc/tests/secure/fortify_wchar_test.c --- a/lib/libc/tests/secure/fortify_wchar_test.c +++ b/lib/libc/tests/secure/fortify_wchar_test.c @@ -7,6 +7,7 @@ #include #include #include +#include #include #include #include @@ -65,6 +66,50 @@ return (linkname); } +/* + * For our purposes, first descriptor will be the reader; we'll send both + * raw data and a control message over it so that the result can be used for + * any of our recv*() tests. + */ +static void __unused +new_socket(int sock[2]) +{ + unsigned char ctrl[CMSG_SPACE(sizeof(int))] = { 0 }; + static char sockbuf[256]; + ssize_t rv; + size_t total = 0; + struct msghdr hdr = { 0 }; + struct cmsghdr *cmsg; + int error, fd; + + error = socketpair(AF_UNIX, SOCK_STREAM, 0, sock); + ATF_REQUIRE(error == 0); + + while (total != sizeof(sockbuf)) { + rv = send(sock[1], &sockbuf[total], sizeof(sockbuf) - total, 0); + + ATF_REQUIRE_MSG(rv > 0, + "expected bytes sent, got %zd with %zu left (size %zu, total %zu)", + rv, sizeof(sockbuf) - total, sizeof(sockbuf), total); + ATF_REQUIRE_MSG(total + (size_t)rv <= sizeof(sockbuf), + "%zd exceeds total %zu", rv, sizeof(sockbuf)); + total += rv; + } + + hdr.msg_control = ctrl; + hdr.msg_controllen = sizeof(ctrl); + + cmsg = CMSG_FIRSTHDR(&hdr); + cmsg->cmsg_level = SOL_SOCKET; + cmsg->cmsg_type = SCM_RIGHTS; + cmsg->cmsg_len = CMSG_LEN(sizeof(fd)); + fd = STDIN_FILENO; + memcpy(CMSG_DATA(cmsg), &fd, sizeof(fd)); + + error = sendmsg(sock[1], &hdr, 0); + ATF_REQUIRE(error != -1); +} + /* * Constructs a tmpfile that we can use for testing read(2) and friends. */ diff --git a/lib/libc/tests/secure/generate-fortify-tests.lua b/lib/libc/tests/secure/generate-fortify-tests.lua --- a/lib/libc/tests/secure/generate-fortify-tests.lua +++ b/lib/libc/tests/secure/generate-fortify-tests.lua @@ -31,6 +31,7 @@ "sys/random.h", "sys/resource.h", "sys/select.h", + "sys/socket.h", "sys/time.h", "sys/uio.h", "sys/wait.h", @@ -80,6 +81,20 @@ replace_stdin(); ]] +local socket_stackvars = "\tint sock[2] = { -1, -1 };\n" +local recvfrom_sockaddr_stackvars = socket_stackvars .. [[ + char data[16]; + socklen_t socklen; +]] +local recvmsg_stackvars = socket_stackvars .. "\tstruct msghdr msg;\n" +local socket_init = [[ + new_socket(sock); + ATF_REQUIRE(sock[0] >= 0); +]] +local socket_socklen_init = socket_init .. [[ + socklen = __len; +]] + local stdio_init = [[ replace_stdin(); ]] @@ -165,6 +180,187 @@ }, }, }, + socket = { + -- + { + func = "getpeername", + buftype = "struct sockaddr", + bufsize = "sizeof(struct sockaddr)", + arguments = { + "sock[0]", + "__buf", + "&socklen", + }, + exclude = excludes_stack_overflow, + stackvars = socket_stackvars .. "\tsocklen_t socklen;", + init = socket_socklen_init, + uses_len = true, + }, + { + func = "getsockname", + buftype = "struct sockaddr", + bufsize = "sizeof(struct sockaddr)", + arguments = { + "sock[0]", + "__buf", + "&socklen", + }, + exclude = excludes_stack_overflow, + stackvars = socket_stackvars .. "\tsocklen_t socklen;", + init = socket_socklen_init, + uses_len = true, + }, + { + func = "recv", + arguments = { + "sock[0]", + "__buf", + "__len", + "0", + }, + exclude = excludes_stack_overflow, + stackvars = socket_stackvars, + init = socket_init, + }, + { + func = "recvfrom", + arguments = { + "sock[0]", + "__buf", + "__len", + "0", + "NULL", + "NULL", + }, + exclude = excludes_stack_overflow, + stackvars = socket_stackvars, + init = socket_init, + }, + { + func = "recvfrom", + variant = "sockaddr", + buftype = "struct sockaddr", + bufsize = "sizeof(struct sockaddr)", + arguments = { + "sock[0]", + "data", + "sizeof(data)", + "0", + "__buf", + "&socklen", + }, + exclude = excludes_stack_overflow, + stackvars = recvfrom_sockaddr_stackvars, + init = socket_socklen_init, + uses_len = true, + }, + { + func = "recvmsg", + variant = "msg_name", + buftype = "struct sockaddr", + bufsize = "sizeof(struct sockaddr)", + arguments = { + "sock[0]", + "&msg", + "0", + }, + exclude = excludes_stack_overflow, + stackvars = recvmsg_stackvars, + init = [[ + memset(&msg, 0, sizeof(msg)); + msg.msg_name = BUF; + msg.msg_namelen = __len; +]], + uses_len = true, + }, + { + func = "recvmsg", + variant = "msg_iov", + arguments = { + "sock[0]", + "&msg", + "0", + }, + exclude = excludes_stack_overflow, + stackvars = recvmsg_stackvars .. "\tstruct iovec iov[2];\n", + init = [[ + memset(&msg, 0, sizeof(msg)); + memset(&iov[0], 0, sizeof(iov)); + + /* + * We position the buffer second just so that we can confirm that the + * fortification bits are traversing the iovec correctly. + */ + iov[1].iov_base = BUF; + iov[1].iov_len = __len; + + msg.msg_iov = &iov[0]; + msg.msg_iovlen = nitems(iov); +]], + uses_len = true, + }, + { + func = "recvmsg", + variant = "msg_control", + bufsize = "CMSG_SPACE(sizeof(int))", + arguments = { + "sock[0]", + "&msg", + "0", + }, + exclude = excludes_stack_overflow, + stackvars = recvmsg_stackvars, + init = [[ + memset(&msg, 0, sizeof(msg)); + + msg.msg_control = BUF; + msg.msg_controllen = __len; +]], + uses_len = true, + }, + { + func = "recvmmsg", + variant = "msgvec", + buftype = "struct mmsghdr[]", + bufsize = "2", + arguments = { + "sock[0]", + "__buf", + "__len", + "0", + "NULL", + }, + stackvars = socket_stackvars, + }, + { + -- We'll assume that recvmsg is covering msghdr + -- validation thoroughly enough, we'll just try tossing + -- an error in the second element of a msgvec to try and + -- make sure that each one is being validated. + func = "recvmmsg", + variant = "msghdr", + arguments = { + "sock[0]", + "&msgvec[0]", + "nitems(msgvec)", + "0", + "NULL", + }, + exclude = excludes_stack_overflow, + stackvars = socket_stackvars .. "\tstruct mmsghdr msgvec[2];\n", + init = [[ + memset(&msgvec[0], 0, sizeof(msgvec)); + + /* + * Same as above, make sure fortification isn't ignoring n > 1 elements + * of the msgvec. + */ + msgvec[1].msg_hdr.msg_control = BUF; + msgvec[1].msg_hdr.msg_controllen = __len; +]], + uses_len = true, + }, + }, uio = { -- { @@ -1138,6 +1334,50 @@ return (linkname); } +/* + * For our purposes, first descriptor will be the reader; we'll send both + * raw data and a control message over it so that the result can be used for + * any of our recv*() tests. + */ +static void __unused +new_socket(int sock[2]) +{ + unsigned char ctrl[CMSG_SPACE(sizeof(int))] = { 0 }; + static char sockbuf[256]; + ssize_t rv; + size_t total = 0; + struct msghdr hdr = { 0 }; + struct cmsghdr *cmsg; + int error, fd; + + error = socketpair(AF_UNIX, SOCK_STREAM, 0, sock); + ATF_REQUIRE(error == 0); + + while (total != sizeof(sockbuf)) { + rv = send(sock[1], &sockbuf[total], sizeof(sockbuf) - total, 0); + + ATF_REQUIRE_MSG(rv > 0, + "expected bytes sent, got %zd with %zu left (size %zu, total %zu)", + rv, sizeof(sockbuf) - total, sizeof(sockbuf), total); + ATF_REQUIRE_MSG(total + (size_t)rv <= sizeof(sockbuf), + "%zd exceeds total %zu", rv, sizeof(sockbuf)); + total += rv; + } + + hdr.msg_control = ctrl; + hdr.msg_controllen = sizeof(ctrl); + + cmsg = CMSG_FIRSTHDR(&hdr); + cmsg->cmsg_level = SOL_SOCKET; + cmsg->cmsg_type = SCM_RIGHTS; + cmsg->cmsg_len = CMSG_LEN(sizeof(fd)); + fd = STDIN_FILENO; + memcpy(CMSG_DATA(cmsg), &fd, sizeof(fd)); + + error = sendmsg(sock[1], &hdr, 0); + ATF_REQUIRE(error != -1); +} + /* * Constructs a tmpfile that we can use for testing read(2) and friends. */ diff --git a/lib/libsys/recvmmsg.c b/lib/libsys/recvmmsg.c --- a/lib/libsys/recvmmsg.c +++ b/lib/libsys/recvmmsg.c @@ -32,11 +32,12 @@ #include #include #include +#include #include "libc_private.h" ssize_t -recvmmsg(int s, struct mmsghdr *__restrict msgvec, size_t vlen, int flags, - const struct timespec *__restrict timeout) +__ssp_real(recvmmsg)(int s, struct mmsghdr *__restrict msgvec, size_t vlen, + int flags, const struct timespec *__restrict timeout) { struct pollfd pfd[1]; size_t i, rcvd; diff --git a/sys/sys/socket.h b/sys/sys/socket.h --- a/sys/sys/socket.h +++ b/sys/sys/socket.h @@ -670,6 +670,10 @@ }; #endif /* __BSD_VISIBLE */ +#if defined(_FORTIFY_SOURCE) && _FORTIFY_SOURCE > 0 +#include +#endif + #ifndef _KERNEL #include