From nobody Sat Jul 13 05:23:02 2024 X-Original-To: dev-commits-src-main@mlmmj.nyi.freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2610:1c1:1:606c::19:1]) by mlmmj.nyi.freebsd.org (Postfix) with ESMTP id 4WLcLG3y91z5PvZ8; Sat, 13 Jul 2024 05:23:02 +0000 (UTC) (envelope-from git@FreeBSD.org) Received: from mxrelay.nyi.freebsd.org (mxrelay.nyi.freebsd.org [IPv6:2610:1c1:1:606c::19:3]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256 client-signature RSA-PSS (4096 bits) client-digest SHA256) (Client CN "mxrelay.nyi.freebsd.org", Issuer "R11" (verified OK)) by mx1.freebsd.org (Postfix) with ESMTPS id 4WLcLG1l8Wz4nrN; Sat, 13 Jul 2024 05:23:02 +0000 (UTC) (envelope-from git@FreeBSD.org) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=freebsd.org; s=dkim; t=1720848182; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding; bh=+n8Vwc4L71G+OhY3c/kdnlmutHQ8VI3T3AfbKRpPN2w=; b=B9JV6sGNgzdlbb8X2/0AJ8gbIXiXj6Q9fCJ8OOLNwGnR9GDlrC0v+pqIsR2HMWSZ8aloUg dDgCKak4R5UbPb6W+ypK39vX/uAYiBTpRzfD3PO+dDGRfPEX/ua/jye7BkpcqhLJxdBdH1 p+GDfkLL9lP6VZdbiRLFdeC4nwBnBSXQeaFaYDx3NT1p1wcG/9STHW+CR78bjekmM4G4L2 9fCA8ZLsY8KAxf5dE8rSrhNiz62DLlZAVzL7EWJowJ6XTWn7gihBowk/HhLsN+CbUJmdkH Y5W2RBVPOLG4g8+qx8xRl5npOblGoJP93Tm3KV6nN5BfOEZWWT1Jj4KMB4kpmg== ARC-Seal: i=1; s=dkim; d=freebsd.org; t=1720848182; a=rsa-sha256; cv=none; b=GR8SOH9Rli+KDi8PEGb9gpf5ljKOtH1NCE2tIuvOTjXsYNTyX6tmsTUe0sPDQcrqLfDFO7 QRs0oWAshGyh0/Y2LlGUuT9iwGeyOCpQ25hCUuGsYGg4I6IrWbirli9pMBEI2Gz+DAQdEZ yEeNUWWh70ZWXPMul34JtYNNuD7xNu2oTiHBpw2slftklze4fjkDMElK2GASQigpji0IAB FfPq4rFzhYlDuEI3gFKdBgMgR9nlpDHjJlFYfS0APBU58ack+fXoXjMI56hwXbcQjq0mJ0 yaz5jG86dxhmPWMUC/Jma1BZ1HRMGKQOSKGe2sKMUgPauFtkbT+oYe5+rXdOUg== ARC-Authentication-Results: i=1; mx1.freebsd.org; none ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=freebsd.org; s=dkim; t=1720848182; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding; bh=+n8Vwc4L71G+OhY3c/kdnlmutHQ8VI3T3AfbKRpPN2w=; b=PxgynoY/T9CGCwFx87i7sjv5ukBO8sMlbrlivc+w6uHbqdt/B4C+2nK0h2LD2lzRBHMr2/ wLSdBwCo01TvVCv8UM6PdowEmWW+NTZH89ghhcQKj4kbdF5+n+F0dOYGVD6LrrAcS1Tq1T Yc66wBPaotFcvYG5Yg9ErQKyvGQUC4b1SVK1218uTLD6GG74A8+XIiCuhuwmoteM+ihrJS lN6mykCPZqOBU1CBijkUjQyl/iW3aIQSQfo7JcDb1xzGF1qEVpJFY184rt1zXCRYc8h3O2 r39DLqwIK8QJHboIBV2j9auQ3inPXV5U9e/eFyjo1nvCjCSIQUKoTpOIhp3A1A== Received: from gitrepo.freebsd.org (gitrepo.freebsd.org [IPv6:2610:1c1:1:6068::e6a:5]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256) (Client did not present a certificate) by mxrelay.nyi.freebsd.org (Postfix) with ESMTPS id 4WLcLG1Kq8z1Cj7; Sat, 13 Jul 2024 05:23:02 +0000 (UTC) (envelope-from git@FreeBSD.org) Received: from gitrepo.freebsd.org ([127.0.1.44]) by gitrepo.freebsd.org (8.18.1/8.18.1) with ESMTP id 46D5N2dX032583; Sat, 13 Jul 2024 05:23:02 GMT (envelope-from git@gitrepo.freebsd.org) Received: (from git@localhost) by gitrepo.freebsd.org (8.18.1/8.18.1/Submit) id 46D5N2qn032580; Sat, 13 Jul 2024 05:23:02 GMT (envelope-from git) Date: Sat, 13 Jul 2024 05:23:02 GMT Message-Id: <202407130523.46D5N2qn032580@gitrepo.freebsd.org> To: src-committers@FreeBSD.org, dev-commits-src-all@FreeBSD.org, dev-commits-src-main@FreeBSD.org From: Kyle Evans Subject: git: 88276dfbf19e - main - include: ssp: fortify poll/ppoll from List-Id: Commit messages for the main branch of the src repository List-Archive: https://lists.freebsd.org/archives/dev-commits-src-main List-Help: List-Post: List-Subscribe: List-Unsubscribe: X-BeenThere: dev-commits-src-main@freebsd.org Sender: owner-dev-commits-src-main@FreeBSD.org MIME-Version: 1.0 Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: 8bit X-Git-Committer: kevans X-Git-Repository: src X-Git-Refname: refs/heads/main X-Git-Reftype: branch X-Git-Commit: 88276dfbf19e3c0dbd1abee5c2e18c8a4a8c5559 Auto-Submitted: auto-generated The branch main has been updated by kevans: URL: https://cgit.FreeBSD.org/src/commit/?id=88276dfbf19e3c0dbd1abee5c2e18c8a4a8c5559 commit 88276dfbf19e3c0dbd1abee5c2e18c8a4a8c5559 Author: Kyle Evans AuthorDate: 2024-07-13 05:16:11 +0000 Commit: Kyle Evans CommitDate: 2024-07-13 05:16:24 +0000 include: ssp: fortify poll/ppoll from For poll/ppoll we just need to bounds-check the poll array that we're about to write out to. Reviewed by: kib, markj (earlier version) Sponsored by: Klara, Inc. Sponsored by: Stormshield Differential Revision: https://reviews.freebsd.org/D45680 --- include/ssp/Makefile | 2 +- include/ssp/poll.h | 60 +++ lib/libc/sys/poll.c | 5 +- lib/libc/sys/ppoll.c | 8 +- lib/libc/tests/secure/Makefile | 1 + lib/libc/tests/secure/fortify_poll_test.c | 531 +++++++++++++++++++++++ lib/libc/tests/secure/fortify_stdio_test.c | 1 + lib/libc/tests/secure/fortify_string_test.c | 1 + lib/libc/tests/secure/fortify_strings_test.c | 1 + lib/libc/tests/secure/fortify_unistd_test.c | 1 + lib/libc/tests/secure/generate-fortify-tests.lua | 34 ++ sys/sys/poll.h | 4 + 12 files changed, 643 insertions(+), 6 deletions(-) diff --git a/include/ssp/Makefile b/include/ssp/Makefile index 69e7df68bb47..a97139e75c7b 100644 --- a/include/ssp/Makefile +++ b/include/ssp/Makefile @@ -1,4 +1,4 @@ -INCS= ssp.h stdio.h string.h strings.h unistd.h +INCS= poll.h ssp.h stdio.h string.h strings.h unistd.h INCSDIR= ${INCLUDEDIR}/ssp .include diff --git a/include/ssp/poll.h b/include/ssp/poll.h new file mode 100644 index 000000000000..66992009e928 --- /dev/null +++ b/include/ssp/poll.h @@ -0,0 +1,60 @@ +/*- + * 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_POLL_H_ +#define _SSP_POLL_H_ + +#include + +#if __SSP_FORTIFY_LEVEL > 0 + +__BEGIN_DECLS + +__ssp_redirect_raw_impl(int, poll, poll, + (struct pollfd fds[], nfds_t nfds, int timeout)) +{ + if (__ssp_bos(fds) / sizeof(fds[0]) < nfds) + __chk_fail(); + + return (__ssp_real(poll)(fds, nfds, timeout)); +} + + +__ssp_redirect_raw_impl(int, ppoll, ppoll, + (struct pollfd fds[], nfds_t nfds, + const struct timespec *__restrict timeout, + const sigset_t *__restrict newsigmask)) +{ + if (__ssp_bos(fds) / sizeof(fds[0]) < nfds) + __chk_fail(); + + return (__ssp_real(ppoll)(fds, nfds, timeout, newsigmask)); +} + +__END_DECLS + +#endif /* __SSP_FORTIFY_LEVEL > 0 */ +#endif /* _SSP_POLL_H_ */ diff --git a/lib/libc/sys/poll.c b/lib/libc/sys/poll.c index 6528603e7603..7a418afbbf49 100644 --- a/lib/libc/sys/poll.c +++ b/lib/libc/sys/poll.c @@ -31,13 +31,14 @@ #include #include +#include #include "libc_private.h" __weak_reference(__sys_poll, __poll); -#pragma weak poll +#pragma weak __ssp_real(poll) int -poll(struct pollfd pfd[], nfds_t nfds, int timeout) +__ssp_real(poll)(struct pollfd pfd[], nfds_t nfds, int timeout) { return (INTERPOS_SYS(poll, pfd, nfds, timeout)); } diff --git a/lib/libc/sys/ppoll.c b/lib/libc/sys/ppoll.c index 52c4c177cf0a..ef72a902548b 100644 --- a/lib/libc/sys/ppoll.c +++ b/lib/libc/sys/ppoll.c @@ -31,14 +31,16 @@ #include #include +#include #include "libc_private.h" __weak_reference(__sys_ppoll, __ppoll); -#pragma weak ppoll +#pragma weak __ssp_real(ppoll) int -ppoll(struct pollfd pfd[], nfds_t nfds, const struct timespec *__restrict - timeout, const sigset_t *__restrict newsigmask) +__ssp_real(ppoll)(struct pollfd pfd[], nfds_t nfds, + const struct timespec *__restrict timeout, + const sigset_t *__restrict newsigmask) { return (INTERPOS_SYS(ppoll, pfd, nfds, timeout, newsigmask)); } diff --git a/lib/libc/tests/secure/Makefile b/lib/libc/tests/secure/Makefile index d809f7cadd74..2e98b5f2ed35 100644 --- a/lib/libc/tests/secure/Makefile +++ b/lib/libc/tests/secure/Makefile @@ -2,6 +2,7 @@ TESTSDIR:= ${TESTSBASE}/${RELDIR:C/libc\/tests/libc/} +FORTIFY_TCATS+= poll FORTIFY_TCATS+= stdio FORTIFY_TCATS+= string FORTIFY_TCATS+= strings diff --git a/lib/libc/tests/secure/fortify_poll_test.c b/lib/libc/tests/secure/fortify_poll_test.c new file mode 100644 index 000000000000..9d9cb4ace35a --- /dev/null +++ b/lib/libc/tests/secure/fortify_poll_test.c @@ -0,0 +1,531 @@ +/* @generated by `generate-fortify-tests.lua "poll"` */ + +#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 + +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_symlink(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; + } + + ATF_REQUIRE_EQ(0, lseek(fd, 0, SEEK_SET)); + return (fd); +} + +static void +disable_coredumps(void) +{ + struct rlimit rl = { 0 }; + + if (setrlimit(RLIMIT_CORE, &rl) == -1) + _exit(EX_OSERR); +} + +/* + * 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(poll_before_end); +ATF_TC_BODY(poll_before_end, tc) +{ +#define BUF &__stack.__buf + struct { + uint8_t padding_l; + struct pollfd __buf[4]; + uint8_t padding_r; + } __stack; + const size_t __bufsz __unused = sizeof(__stack.__buf); + const size_t __len = 4 - 1; + const size_t __idx __unused = __len - 1; + + for (size_t i = 0; i < howmany(__bufsz, sizeof(struct pollfd)); i++) { + __stack.__buf[i].fd = -1; + } + + poll(__stack.__buf, __len, 0); +#undef BUF + +} + +ATF_TC_WITHOUT_HEAD(poll_end); +ATF_TC_BODY(poll_end, tc) +{ +#define BUF &__stack.__buf + struct { + uint8_t padding_l; + struct pollfd __buf[4]; + uint8_t padding_r; + } __stack; + const size_t __bufsz __unused = sizeof(__stack.__buf); + const size_t __len = 4; + const size_t __idx __unused = __len - 1; + + for (size_t i = 0; i < howmany(__bufsz, sizeof(struct pollfd)); i++) { + __stack.__buf[i].fd = -1; + } + + poll(__stack.__buf, __len, 0); +#undef BUF + +} + +ATF_TC_WITHOUT_HEAD(poll_after_end); +ATF_TC_BODY(poll_after_end, tc) +{ +#define BUF &__stack.__buf + struct { + uint8_t padding_l; + struct pollfd __buf[4]; + uint8_t padding_r; + } __stack; + const size_t __bufsz __unused = sizeof(__stack.__buf); + const size_t __len = 4 + 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(); + for (size_t i = 0; i < howmany(__bufsz, sizeof(struct pollfd)); i++) { + __stack.__buf[i].fd = -1; + } + + poll(__stack.__buf, __len, 0); + _exit(EX_SOFTWARE); /* Should have aborted. */ + +monitor: + while (waitpid(__child, &__status, 0) != __child) { + ATF_REQUIRE_EQ(EINTR, errno); + } + + if (!WIFSIGNALED(__status)) { + switch (WEXITSTATUS(__status)) { + case EX_SOFTWARE: + atf_tc_fail("FORTIFY_SOURCE failed to abort"); + break; + case EX_OSERR: + atf_tc_fail("setrlimit(2) failed"); + break; + default: + atf_tc_fail("child exited with status %d", + WEXITSTATUS(__status)); + } + } else { + ATF_REQUIRE_EQ(SIGABRT, WTERMSIG(__status)); + } +#undef BUF + +} + +ATF_TC_WITHOUT_HEAD(poll_heap_before_end); +ATF_TC_BODY(poll_heap_before_end, tc) +{ +#define BUF __stack.__buf + struct { + uint8_t padding_l; + struct pollfd * __buf; + uint8_t padding_r; + } __stack; + const size_t __bufsz __unused = sizeof(*__stack.__buf) * (4); + const size_t __len = 4 - 1; + const size_t __idx __unused = __len - 1; + + __stack.__buf = malloc(__bufsz); + for (size_t i = 0; i < howmany(__bufsz, sizeof(struct pollfd)); i++) { + __stack.__buf[i].fd = -1; + } + + poll(__stack.__buf, __len, 0); +#undef BUF + +} + +ATF_TC_WITHOUT_HEAD(poll_heap_end); +ATF_TC_BODY(poll_heap_end, tc) +{ +#define BUF __stack.__buf + struct { + uint8_t padding_l; + struct pollfd * __buf; + uint8_t padding_r; + } __stack; + const size_t __bufsz __unused = sizeof(*__stack.__buf) * (4); + const size_t __len = 4; + const size_t __idx __unused = __len - 1; + + __stack.__buf = malloc(__bufsz); + for (size_t i = 0; i < howmany(__bufsz, sizeof(struct pollfd)); i++) { + __stack.__buf[i].fd = -1; + } + + poll(__stack.__buf, __len, 0); +#undef BUF + +} + +ATF_TC_WITHOUT_HEAD(poll_heap_after_end); +ATF_TC_BODY(poll_heap_after_end, tc) +{ +#define BUF __stack.__buf + struct { + uint8_t padding_l; + struct pollfd * __buf; + uint8_t padding_r; + } __stack; + const size_t __bufsz __unused = sizeof(*__stack.__buf) * (4); + const size_t __len = 4 + 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); + for (size_t i = 0; i < howmany(__bufsz, sizeof(struct pollfd)); i++) { + __stack.__buf[i].fd = -1; + } + + poll(__stack.__buf, __len, 0); + _exit(EX_SOFTWARE); /* Should have aborted. */ + +monitor: + while (waitpid(__child, &__status, 0) != __child) { + ATF_REQUIRE_EQ(EINTR, errno); + } + + if (!WIFSIGNALED(__status)) { + switch (WEXITSTATUS(__status)) { + case EX_SOFTWARE: + atf_tc_fail("FORTIFY_SOURCE failed to abort"); + break; + case EX_OSERR: + atf_tc_fail("setrlimit(2) failed"); + break; + default: + atf_tc_fail("child exited with status %d", + WEXITSTATUS(__status)); + } + } else { + ATF_REQUIRE_EQ(SIGABRT, WTERMSIG(__status)); + } +#undef BUF + +} + +ATF_TC_WITHOUT_HEAD(ppoll_before_end); +ATF_TC_BODY(ppoll_before_end, tc) +{ +#define BUF &__stack.__buf + struct { + uint8_t padding_l; + struct pollfd __buf[4]; + uint8_t padding_r; + } __stack; + const size_t __bufsz __unused = sizeof(__stack.__buf); + const size_t __len = 4 - 1; + const size_t __idx __unused = __len - 1; + struct timespec tv = { 0 }; + + for (size_t i = 0; i < howmany(__bufsz, sizeof(struct pollfd)); i++) { + __stack.__buf[i].fd = -1; + } + + ppoll(__stack.__buf, __len, &tv, NULL); +#undef BUF + +} + +ATF_TC_WITHOUT_HEAD(ppoll_end); +ATF_TC_BODY(ppoll_end, tc) +{ +#define BUF &__stack.__buf + struct { + uint8_t padding_l; + struct pollfd __buf[4]; + uint8_t padding_r; + } __stack; + const size_t __bufsz __unused = sizeof(__stack.__buf); + const size_t __len = 4; + const size_t __idx __unused = __len - 1; + struct timespec tv = { 0 }; + + for (size_t i = 0; i < howmany(__bufsz, sizeof(struct pollfd)); i++) { + __stack.__buf[i].fd = -1; + } + + ppoll(__stack.__buf, __len, &tv, NULL); +#undef BUF + +} + +ATF_TC_WITHOUT_HEAD(ppoll_after_end); +ATF_TC_BODY(ppoll_after_end, tc) +{ +#define BUF &__stack.__buf + struct { + uint8_t padding_l; + struct pollfd __buf[4]; + uint8_t padding_r; + } __stack; + const size_t __bufsz __unused = sizeof(__stack.__buf); + const size_t __len = 4 + 1; + const size_t __idx __unused = __len - 1; + pid_t __child; + int __status; + struct timespec tv = { 0 }; + + __child = fork(); + ATF_REQUIRE(__child >= 0); + if (__child > 0) + goto monitor; + + /* Child */ + disable_coredumps(); + for (size_t i = 0; i < howmany(__bufsz, sizeof(struct pollfd)); i++) { + __stack.__buf[i].fd = -1; + } + + ppoll(__stack.__buf, __len, &tv, NULL); + _exit(EX_SOFTWARE); /* Should have aborted. */ + +monitor: + while (waitpid(__child, &__status, 0) != __child) { + ATF_REQUIRE_EQ(EINTR, errno); + } + + if (!WIFSIGNALED(__status)) { + switch (WEXITSTATUS(__status)) { + case EX_SOFTWARE: + atf_tc_fail("FORTIFY_SOURCE failed to abort"); + break; + case EX_OSERR: + atf_tc_fail("setrlimit(2) failed"); + break; + default: + atf_tc_fail("child exited with status %d", + WEXITSTATUS(__status)); + } + } else { + ATF_REQUIRE_EQ(SIGABRT, WTERMSIG(__status)); + } +#undef BUF + +} + +ATF_TC_WITHOUT_HEAD(ppoll_heap_before_end); +ATF_TC_BODY(ppoll_heap_before_end, tc) +{ +#define BUF __stack.__buf + struct { + uint8_t padding_l; + struct pollfd * __buf; + uint8_t padding_r; + } __stack; + const size_t __bufsz __unused = sizeof(*__stack.__buf) * (4); + const size_t __len = 4 - 1; + const size_t __idx __unused = __len - 1; + struct timespec tv = { 0 }; + + __stack.__buf = malloc(__bufsz); + for (size_t i = 0; i < howmany(__bufsz, sizeof(struct pollfd)); i++) { + __stack.__buf[i].fd = -1; + } + + ppoll(__stack.__buf, __len, &tv, NULL); +#undef BUF + +} + +ATF_TC_WITHOUT_HEAD(ppoll_heap_end); +ATF_TC_BODY(ppoll_heap_end, tc) +{ +#define BUF __stack.__buf + struct { + uint8_t padding_l; + struct pollfd * __buf; + uint8_t padding_r; + } __stack; + const size_t __bufsz __unused = sizeof(*__stack.__buf) * (4); + const size_t __len = 4; + const size_t __idx __unused = __len - 1; + struct timespec tv = { 0 }; + + __stack.__buf = malloc(__bufsz); + for (size_t i = 0; i < howmany(__bufsz, sizeof(struct pollfd)); i++) { + __stack.__buf[i].fd = -1; + } + + ppoll(__stack.__buf, __len, &tv, NULL); +#undef BUF + +} + +ATF_TC_WITHOUT_HEAD(ppoll_heap_after_end); +ATF_TC_BODY(ppoll_heap_after_end, tc) +{ +#define BUF __stack.__buf + struct { + uint8_t padding_l; + struct pollfd * __buf; + uint8_t padding_r; + } __stack; + const size_t __bufsz __unused = sizeof(*__stack.__buf) * (4); + const size_t __len = 4 + 1; + const size_t __idx __unused = __len - 1; + pid_t __child; + int __status; + struct timespec tv = { 0 }; + + __child = fork(); + ATF_REQUIRE(__child >= 0); + if (__child > 0) + goto monitor; + + /* Child */ + disable_coredumps(); + __stack.__buf = malloc(__bufsz); + for (size_t i = 0; i < howmany(__bufsz, sizeof(struct pollfd)); i++) { + __stack.__buf[i].fd = -1; + } + + ppoll(__stack.__buf, __len, &tv, NULL); + _exit(EX_SOFTWARE); /* Should have aborted. */ + +monitor: + while (waitpid(__child, &__status, 0) != __child) { + ATF_REQUIRE_EQ(EINTR, errno); + } + + if (!WIFSIGNALED(__status)) { + switch (WEXITSTATUS(__status)) { + case EX_SOFTWARE: + atf_tc_fail("FORTIFY_SOURCE failed to abort"); + break; + case EX_OSERR: + atf_tc_fail("setrlimit(2) failed"); + break; + default: + atf_tc_fail("child exited with status %d", + WEXITSTATUS(__status)); + } + } else { + ATF_REQUIRE_EQ(SIGABRT, WTERMSIG(__status)); + } +#undef BUF + +} + +ATF_TP_ADD_TCS(tp) +{ + ATF_TP_ADD_TC(tp, poll_before_end); + ATF_TP_ADD_TC(tp, poll_end); + ATF_TP_ADD_TC(tp, poll_after_end); + ATF_TP_ADD_TC(tp, poll_heap_before_end); + ATF_TP_ADD_TC(tp, poll_heap_end); + ATF_TP_ADD_TC(tp, poll_heap_after_end); + ATF_TP_ADD_TC(tp, ppoll_before_end); + ATF_TP_ADD_TC(tp, ppoll_end); + ATF_TP_ADD_TC(tp, ppoll_after_end); + ATF_TP_ADD_TC(tp, ppoll_heap_before_end); + ATF_TP_ADD_TC(tp, ppoll_heap_end); + ATF_TP_ADD_TC(tp, ppoll_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 index fe0f14acd988..035d84b316e6 100644 --- a/lib/libc/tests/secure/fortify_stdio_test.c +++ b/lib/libc/tests/secure/fortify_stdio_test.c @@ -11,6 +11,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 index 8306abb5f9e2..5651d3107e91 100644 --- a/lib/libc/tests/secure/fortify_string_test.c +++ b/lib/libc/tests/secure/fortify_string_test.c @@ -11,6 +11,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 index f9cb1e4917f7..28f5e9ec4940 100644 --- a/lib/libc/tests/secure/fortify_strings_test.c +++ b/lib/libc/tests/secure/fortify_strings_test.c @@ -11,6 +11,7 @@ #include #include #include +#include #include #include #include diff --git a/lib/libc/tests/secure/fortify_unistd_test.c b/lib/libc/tests/secure/fortify_unistd_test.c index 76702321676a..dddc23596368 100644 --- a/lib/libc/tests/secure/fortify_unistd_test.c +++ b/lib/libc/tests/secure/fortify_unistd_test.c @@ -11,6 +11,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 index bff1162c7d83..bb654bbf2f71 100755 --- a/lib/libc/tests/secure/generate-fortify-tests.lua +++ b/lib/libc/tests/secure/generate-fortify-tests.lua @@ -69,6 +69,7 @@ local includes = { "errno.h", "fcntl.h", "limits.h", + "poll.h", "signal.h", "stdio.h", "stdlib.h", @@ -90,6 +91,12 @@ local function excludes_stack_overflow(disposition, is_heap) return (not is_heap) and disposition > 0 end +local poll_init = [[ + for (size_t i = 0; i < howmany(__bufsz, sizeof(struct pollfd)); i++) { + __stack.__buf[i].fd = -1; + } +]] + local printf_stackvars = "\tchar srcvar[__len + 10];\n" local printf_init = [[ memset(srcvar, 'A', sizeof(srcvar) - 1); @@ -132,6 +139,33 @@ local string_init = [[ -- circumstances it's useful to use a different type (e.g., for alignment -- requirements). local all_tests = { + poll = { + -- + { + func = "poll", + bufsize = "4", + buftype = "struct pollfd[]", + arguments = { + "__buf", + "__len", + "0", + }, + init = poll_init, + }, + { + func = "ppoll", + bufsize = "4", + buftype = "struct pollfd[]", + arguments = { + "__buf", + "__len", + "&tv", + "NULL", + }, + stackvars = "\tstruct timespec tv = { 0 };\n", + init = poll_init, + }, + }, stdio = { -- { diff --git a/sys/sys/poll.h b/sys/sys/poll.h index 0ae4e0f4dd15..d5f2c0373942 100644 --- a/sys/sys/poll.h +++ b/sys/sys/poll.h @@ -109,6 +109,10 @@ typedef __sigset_t sigset_t; #endif +#if defined(_FORTIFY_SOURCE) && _FORTIFY_SOURCE > 0 +#include +#endif + __BEGIN_DECLS int poll(struct pollfd _pfd[], nfds_t _nfds, int _timeout); #if __BSD_VISIBLE