diff --git a/include/ssp/Makefile b/include/ssp/Makefile --- a/include/ssp/Makefile +++ b/include/ssp/Makefile @@ -1,4 +1,4 @@ -INCS= poll.h random.h ssp.h stdio.h stdlib.h string.h strings.h unistd.h +INCS= poll.h random.h ssp.h stdio.h stdlib.h string.h strings.h uio.h unistd.h INCS+= wchar.h INCSDIR= ${INCLUDEDIR}/ssp diff --git a/include/ssp/ssp.h b/include/ssp/ssp.h --- a/include/ssp/ssp.h +++ b/include/ssp/ssp.h @@ -94,9 +94,25 @@ #define __ssp_overlap(a, b, l) \ (((a) <= (b) && (b) < (a) + (l)) || ((b) <= (a) && (a) < (b) + (l))) +#include + __BEGIN_DECLS void __stack_chk_fail(void) __dead2; void __chk_fail(void) __dead2; __END_DECLS +__ssp_inline void +__ssp_check_iovec(const struct iovec *iov, int iovcnt) +{ + const size_t iovsz = __ssp_bos(iov); + + if (iovsz != (size_t)-1 && iovsz / sizeof(*iov) < (size_t)iovcnt) + __chk_fail(); + + for (int i = 0; i < iovcnt; i++) { + if (__ssp_bos(iov[i].iov_base) < iov[i].iov_len) + __chk_fail(); + } +} + #endif /* _SSP_SSP_H_ */ diff --git a/include/ssp/uio.h b/include/ssp/uio.h new file mode 100644 --- /dev/null +++ b/include/ssp/uio.h @@ -0,0 +1,56 @@ +/*- + * + * 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_UIO_H_ +#define _SSP_UIO_H_ + +#include + +#if __SSP_FORTIFY_LEVEL > 0 + +__BEGIN_DECLS + +__ssp_redirect_raw_impl(ssize_t, readv, readv, + (int fd, const struct iovec *iov, int iovcnt)) +{ + + __ssp_check_iovec(iov, iovcnt); + return (__ssp_real(readv)(fd, iov, iovcnt)); +} + +__ssp_redirect_raw_impl(ssize_t, preadv, preadv, + (int fd, const struct iovec *iov, int iovcnt, off_t offset)) +{ + + __ssp_check_iovec(iov, iovcnt); + return (__ssp_real(preadv)(fd, iov, iovcnt, offset)); +} + +__END_DECLS + +#endif /* __SSP_FORTIFY_LEVEL > 0 */ +#endif /* _SSP_UIO_H_ */ diff --git a/lib/libc/sys/readv.c b/lib/libc/sys/readv.c --- a/lib/libc/sys/readv.c +++ b/lib/libc/sys/readv.c @@ -33,13 +33,14 @@ #include #include #include +#include #include "libc_private.h" __weak_reference(__sys_readv, __readv); #pragma weak readv ssize_t -readv(int fd, const struct iovec *iov, int iovcnt) +__ssp_real(readv)(int fd, const struct iovec *iov, int iovcnt) { return (INTERPOS_SYS(readv, fd, iov, iovcnt)); } 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 @@ -5,6 +5,7 @@ # sys/ headers FORTIFY_TCATS+= random +FORTIFY_TCATS+= uio # non-sys/ headers FORTIFY_TCATS+= poll 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 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 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 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 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 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 diff --git a/lib/libc/tests/secure/fortify_uio_test.c b/lib/libc/tests/secure/fortify_uio_test.c new file mode 100644 --- /dev/null +++ b/lib/libc/tests/secure/fortify_uio_test.c @@ -0,0 +1,724 @@ +/* @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 + +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); +} + +/* + * 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(readv_before_end); +ATF_TC_BODY(readv_before_end, tc) +{ +#define BUF &__stack.__buf + struct { + uint8_t padding_l; + struct iovec __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; + + readv(STDIN_FILENO, __stack.__buf, __len); +#undef BUF + +} + +ATF_TC_WITHOUT_HEAD(readv_end); +ATF_TC_BODY(readv_end, tc) +{ +#define BUF &__stack.__buf + struct { + uint8_t padding_l; + struct iovec __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; + + readv(STDIN_FILENO, __stack.__buf, __len); +#undef BUF + +} + +ATF_TC_WITHOUT_HEAD(readv_after_end); +ATF_TC_BODY(readv_after_end, tc) +{ +#define BUF &__stack.__buf + struct { + uint8_t padding_l; + struct iovec __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; + + __child = fork(); + ATF_REQUIRE(__child >= 0); + if (__child > 0) + goto monitor; + + /* Child */ + disable_coredumps(); + readv(STDIN_FILENO, __stack.__buf, __len); + _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(readv_heap_before_end); +ATF_TC_BODY(readv_heap_before_end, tc) +{ +#define BUF __stack.__buf + struct { + uint8_t padding_l; + struct iovec * __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; + + __stack.__buf = malloc(__bufsz); + + readv(STDIN_FILENO, __stack.__buf, __len); +#undef BUF + +} + +ATF_TC_WITHOUT_HEAD(readv_heap_end); +ATF_TC_BODY(readv_heap_end, tc) +{ +#define BUF __stack.__buf + struct { + uint8_t padding_l; + struct iovec * __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; + + __stack.__buf = malloc(__bufsz); + + readv(STDIN_FILENO, __stack.__buf, __len); +#undef BUF + +} + +ATF_TC_WITHOUT_HEAD(readv_heap_after_end); +ATF_TC_BODY(readv_heap_after_end, tc) +{ +#define BUF __stack.__buf + struct { + uint8_t padding_l; + struct iovec * __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; + + __child = fork(); + ATF_REQUIRE(__child >= 0); + if (__child > 0) + goto monitor; + + /* Child */ + disable_coredumps(); + __stack.__buf = malloc(__bufsz); + + readv(STDIN_FILENO, __stack.__buf, __len); + _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(readv_iov_before_end); +ATF_TC_BODY(readv_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; + struct iovec iov[1]; + + iov[0].iov_base = __stack.__buf; + iov[0].iov_len = __len; + + replace_stdin(); + + readv(STDIN_FILENO, iov, nitems(iov)); +#undef BUF + +} + +ATF_TC_WITHOUT_HEAD(readv_iov_end); +ATF_TC_BODY(readv_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; + struct iovec iov[1]; + + iov[0].iov_base = __stack.__buf; + iov[0].iov_len = __len; + + replace_stdin(); + + readv(STDIN_FILENO, iov, nitems(iov)); +#undef BUF + +} + +ATF_TC_WITHOUT_HEAD(readv_iov_heap_before_end); +ATF_TC_BODY(readv_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; + struct iovec iov[1]; + + __stack.__buf = malloc(__bufsz); + iov[0].iov_base = __stack.__buf; + iov[0].iov_len = __len; + + replace_stdin(); + + readv(STDIN_FILENO, iov, nitems(iov)); +#undef BUF + +} + +ATF_TC_WITHOUT_HEAD(readv_iov_heap_end); +ATF_TC_BODY(readv_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; + struct iovec iov[1]; + + __stack.__buf = malloc(__bufsz); + iov[0].iov_base = __stack.__buf; + iov[0].iov_len = __len; + + replace_stdin(); + + readv(STDIN_FILENO, iov, nitems(iov)); +#undef BUF + +} + +ATF_TC_WITHOUT_HEAD(readv_iov_heap_after_end); +ATF_TC_BODY(readv_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; + struct iovec iov[1]; + + __child = fork(); + ATF_REQUIRE(__child >= 0); + if (__child > 0) + goto monitor; + + /* Child */ + disable_coredumps(); + __stack.__buf = malloc(__bufsz); + iov[0].iov_base = __stack.__buf; + iov[0].iov_len = __len; + + replace_stdin(); + + readv(STDIN_FILENO, iov, nitems(iov)); + _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(preadv_before_end); +ATF_TC_BODY(preadv_before_end, tc) +{ +#define BUF &__stack.__buf + struct { + uint8_t padding_l; + struct iovec __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; + + preadv(STDIN_FILENO, __stack.__buf, __len, 0); +#undef BUF + +} + +ATF_TC_WITHOUT_HEAD(preadv_end); +ATF_TC_BODY(preadv_end, tc) +{ +#define BUF &__stack.__buf + struct { + uint8_t padding_l; + struct iovec __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; + + preadv(STDIN_FILENO, __stack.__buf, __len, 0); +#undef BUF + +} + +ATF_TC_WITHOUT_HEAD(preadv_after_end); +ATF_TC_BODY(preadv_after_end, tc) +{ +#define BUF &__stack.__buf + struct { + uint8_t padding_l; + struct iovec __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; + + __child = fork(); + ATF_REQUIRE(__child >= 0); + if (__child > 0) + goto monitor; + + /* Child */ + disable_coredumps(); + preadv(STDIN_FILENO, __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(preadv_heap_before_end); +ATF_TC_BODY(preadv_heap_before_end, tc) +{ +#define BUF __stack.__buf + struct { + uint8_t padding_l; + struct iovec * __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; + + __stack.__buf = malloc(__bufsz); + + preadv(STDIN_FILENO, __stack.__buf, __len, 0); +#undef BUF + +} + +ATF_TC_WITHOUT_HEAD(preadv_heap_end); +ATF_TC_BODY(preadv_heap_end, tc) +{ +#define BUF __stack.__buf + struct { + uint8_t padding_l; + struct iovec * __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; + + __stack.__buf = malloc(__bufsz); + + preadv(STDIN_FILENO, __stack.__buf, __len, 0); +#undef BUF + +} + +ATF_TC_WITHOUT_HEAD(preadv_heap_after_end); +ATF_TC_BODY(preadv_heap_after_end, tc) +{ +#define BUF __stack.__buf + struct { + uint8_t padding_l; + struct iovec * __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; + + __child = fork(); + ATF_REQUIRE(__child >= 0); + if (__child > 0) + goto monitor; + + /* Child */ + disable_coredumps(); + __stack.__buf = malloc(__bufsz); + + preadv(STDIN_FILENO, __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(preadv_iov_before_end); +ATF_TC_BODY(preadv_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; + struct iovec iov[1]; + + iov[0].iov_base = __stack.__buf; + iov[0].iov_len = __len; + + replace_stdin(); + + preadv(STDIN_FILENO, iov, nitems(iov), 0); +#undef BUF + +} + +ATF_TC_WITHOUT_HEAD(preadv_iov_end); +ATF_TC_BODY(preadv_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; + struct iovec iov[1]; + + iov[0].iov_base = __stack.__buf; + iov[0].iov_len = __len; + + replace_stdin(); + + preadv(STDIN_FILENO, iov, nitems(iov), 0); +#undef BUF + +} + +ATF_TC_WITHOUT_HEAD(preadv_iov_heap_before_end); +ATF_TC_BODY(preadv_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; + struct iovec iov[1]; + + __stack.__buf = malloc(__bufsz); + iov[0].iov_base = __stack.__buf; + iov[0].iov_len = __len; + + replace_stdin(); + + preadv(STDIN_FILENO, iov, nitems(iov), 0); +#undef BUF + +} + +ATF_TC_WITHOUT_HEAD(preadv_iov_heap_end); +ATF_TC_BODY(preadv_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; + struct iovec iov[1]; + + __stack.__buf = malloc(__bufsz); + iov[0].iov_base = __stack.__buf; + iov[0].iov_len = __len; + + replace_stdin(); + + preadv(STDIN_FILENO, iov, nitems(iov), 0); +#undef BUF + +} + +ATF_TC_WITHOUT_HEAD(preadv_iov_heap_after_end); +ATF_TC_BODY(preadv_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; + struct iovec iov[1]; + + __child = fork(); + ATF_REQUIRE(__child >= 0); + if (__child > 0) + goto monitor; + + /* Child */ + disable_coredumps(); + __stack.__buf = malloc(__bufsz); + iov[0].iov_base = __stack.__buf; + iov[0].iov_len = __len; + + replace_stdin(); + + preadv(STDIN_FILENO, iov, nitems(iov), 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_TP_ADD_TCS(tp) +{ + ATF_TP_ADD_TC(tp, readv_before_end); + ATF_TP_ADD_TC(tp, readv_end); + ATF_TP_ADD_TC(tp, readv_after_end); + ATF_TP_ADD_TC(tp, readv_heap_before_end); + ATF_TP_ADD_TC(tp, readv_heap_end); + ATF_TP_ADD_TC(tp, readv_heap_after_end); + ATF_TP_ADD_TC(tp, readv_iov_before_end); + ATF_TP_ADD_TC(tp, readv_iov_end); + ATF_TP_ADD_TC(tp, readv_iov_heap_before_end); + ATF_TP_ADD_TC(tp, readv_iov_heap_end); + ATF_TP_ADD_TC(tp, readv_iov_heap_after_end); + ATF_TP_ADD_TC(tp, preadv_before_end); + ATF_TP_ADD_TC(tp, preadv_end); + ATF_TP_ADD_TC(tp, preadv_after_end); + ATF_TP_ADD_TC(tp, preadv_heap_before_end); + ATF_TP_ADD_TC(tp, preadv_heap_end); + ATF_TP_ADD_TC(tp, preadv_heap_after_end); + ATF_TP_ADD_TC(tp, preadv_iov_before_end); + ATF_TP_ADD_TC(tp, preadv_iov_end); + ATF_TP_ADD_TC(tp, preadv_iov_heap_before_end); + ATF_TP_ADD_TC(tp, preadv_iov_heap_end); + ATF_TP_ADD_TC(tp, preadv_iov_heap_after_end); + return (atf_no_error()); +} 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 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 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/time.h", + "sys/uio.h", "sys/wait.h", "dirent.h", "errno.h", @@ -70,6 +71,14 @@ srcvar[sizeof(srcvar) - 1] = '\0'; ]] +local readv_stackvars = "\tstruct iovec iov[1];\n" +local readv_init = [[ + iov[0].iov_base = __stack.__buf; + iov[0].iov_len = __len; + + replace_stdin(); +]] + local stdio_init = [[ replace_stdin(); ]] @@ -125,6 +134,57 @@ exclude = excludes_stack_overflow, }, }, + uio = { + -- + { + func = "readv", + buftype = "struct iovec[]", + bufsize = 2, + arguments = { + "STDIN_FILENO", + "__buf", + "__len", + }, + }, + { + func = "readv", + variant = "iov", + arguments = { + "STDIN_FILENO", + "iov", + "nitems(iov)", + }, + exclude = excludes_stack_overflow, + stackvars = readv_stackvars, + init = readv_init, + uses_len = true, + }, + { + func = "preadv", + buftype = "struct iovec[]", + bufsize = 2, + arguments = { + "STDIN_FILENO", + "__buf", + "__len", + "0", + }, + }, + { + func = "preadv", + variant = "iov", + arguments = { + "STDIN_FILENO", + "iov", + "nitems(iov)", + "0", + }, + exclude = excludes_stack_overflow, + stackvars = readv_stackvars, + init = readv_init, + uses_len = true, + }, + }, poll = { -- { diff --git a/sys/sys/uio.h b/sys/sys/uio.h --- a/sys/sys/uio.h +++ b/sys/sys/uio.h @@ -99,6 +99,10 @@ #else /* !_KERNEL */ +#if defined(_FORTIFY_SOURCE) && _FORTIFY_SOURCE > 0 +#include +#endif + __BEGIN_DECLS ssize_t readv(int, const struct iovec *, int); ssize_t writev(int, const struct iovec *, int);