Page MenuHomeFreeBSD

D45678.diff
No OneTemporary

D45678.diff

diff --git a/etc/mtree/BSD.tests.dist b/etc/mtree/BSD.tests.dist
--- a/etc/mtree/BSD.tests.dist
+++ b/etc/mtree/BSD.tests.dist
@@ -372,6 +372,8 @@
..
rpc
..
+ secure
+ ..
setjmp
..
ssp
diff --git a/lib/libc/tests/Makefile b/lib/libc/tests/Makefile
--- a/lib/libc/tests/Makefile
+++ b/lib/libc/tests/Makefile
@@ -13,6 +13,7 @@
TESTS_SUBDIRS+= regex
TESTS_SUBDIRS+= resolv
TESTS_SUBDIRS+= rpc
+TESTS_SUBDIRS+= secure
TESTS_SUBDIRS+= setjmp
TESTS_SUBDIRS+= stdio
TESTS_SUBDIRS+= stdlib
diff --git a/lib/libc/tests/secure/Makefile b/lib/libc/tests/secure/Makefile
new file mode 100644
--- /dev/null
+++ b/lib/libc/tests/secure/Makefile
@@ -0,0 +1,20 @@
+.include <bsd.own.mk>
+
+TESTSDIR:= ${TESTSBASE}/${RELDIR:C/libc\/tests/libc/}
+
+FORTIFY_TCATS+= stdio
+FORTIFY_TCATS+= string
+FORTIFY_TCATS+= strings
+FORTIFY_TCATS+= unistd
+
+# Manually run after updating the test generator.
+generate-tests: .PHONY
+.for tcat in ${FORTIFY_TCATS}
+ATF_TESTS_C+= fortify_${tcat}_test
+
+generate-tests: generate-tests-${tcat}
+generate-tests-${tcat}: .PHONY
+ ${.CURDIR}/generate-fortify-tests.lua ${tcat} > ${.CURDIR}/fortify_${tcat}_test.c
+.endfor
+
+.include <bsd.test.mk>
diff --git a/lib/libc/tests/secure/fortify_stdio_test.c b/lib/libc/tests/secure/fortify_stdio_test.c
new file mode 100644
--- /dev/null
+++ b/lib/libc/tests/secure/fortify_stdio_test.c
@@ -0,0 +1,383 @@
+/* @generated by `generate-fortify-tests.lua "stdio"` */
+
+#define _FORTIFY_SOURCE 2
+#define TMPFILE_SIZE (1024 * 32)
+
+#include <sys/param.h>
+#include <sys/resource.h>
+#include <sys/time.h>
+#include <sys/wait.h>
+#include <dirent.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <strings.h>
+#include <sysexits.h>
+#include <unistd.h>
+#include <atf-c.h>
+
+/*
+ * 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);
+}
+
+ATF_TC_WITHOUT_HEAD(sprintf_before_end);
+ATF_TC_BODY(sprintf_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;
+ char srcvar[__len + 10];
+
+ memset(srcvar, 'A', sizeof(srcvar) - 1);
+ srcvar[sizeof(srcvar) - 1] = '\0';
+
+ sprintf(__stack.__buf, "%.*s", (int)__len - 1, srcvar);
+#undef BUF
+
+}
+
+ATF_TC_WITHOUT_HEAD(sprintf_end);
+ATF_TC_BODY(sprintf_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;
+ char srcvar[__len + 10];
+
+ memset(srcvar, 'A', sizeof(srcvar) - 1);
+ srcvar[sizeof(srcvar) - 1] = '\0';
+
+ sprintf(__stack.__buf, "%.*s", (int)__len - 1, srcvar);
+#undef BUF
+
+}
+
+ATF_TC_WITHOUT_HEAD(sprintf_heap_before_end);
+ATF_TC_BODY(sprintf_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;
+ char srcvar[__len + 10];
+
+ __stack.__buf = malloc(__bufsz);
+ memset(srcvar, 'A', sizeof(srcvar) - 1);
+ srcvar[sizeof(srcvar) - 1] = '\0';
+
+ sprintf(__stack.__buf, "%.*s", (int)__len - 1, srcvar);
+#undef BUF
+
+}
+
+ATF_TC_WITHOUT_HEAD(sprintf_heap_end);
+ATF_TC_BODY(sprintf_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;
+ char srcvar[__len + 10];
+
+ __stack.__buf = malloc(__bufsz);
+ memset(srcvar, 'A', sizeof(srcvar) - 1);
+ srcvar[sizeof(srcvar) - 1] = '\0';
+
+ sprintf(__stack.__buf, "%.*s", (int)__len - 1, srcvar);
+#undef BUF
+
+}
+
+ATF_TC_WITHOUT_HEAD(sprintf_heap_after_end);
+ATF_TC_BODY(sprintf_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;
+ char srcvar[__len + 10];
+
+ __child = fork();
+ ATF_REQUIRE(__child >= 0);
+ if (__child > 0)
+ goto monitor;
+
+ /* Child */
+ disable_coredumps();
+ __stack.__buf = malloc(__bufsz);
+ memset(srcvar, 'A', sizeof(srcvar) - 1);
+ srcvar[sizeof(srcvar) - 1] = '\0';
+
+ sprintf(__stack.__buf, "%.*s", (int)__len - 1, srcvar);
+ _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(snprintf_before_end);
+ATF_TC_BODY(snprintf_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;
+ char srcvar[__len + 10];
+
+ memset(srcvar, 'A', sizeof(srcvar) - 1);
+ srcvar[sizeof(srcvar) - 1] = '\0';
+
+ snprintf(__stack.__buf, __len, "%.*s", (int)__len - 1, srcvar);
+#undef BUF
+
+}
+
+ATF_TC_WITHOUT_HEAD(snprintf_end);
+ATF_TC_BODY(snprintf_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;
+ char srcvar[__len + 10];
+
+ memset(srcvar, 'A', sizeof(srcvar) - 1);
+ srcvar[sizeof(srcvar) - 1] = '\0';
+
+ snprintf(__stack.__buf, __len, "%.*s", (int)__len - 1, srcvar);
+#undef BUF
+
+}
+
+ATF_TC_WITHOUT_HEAD(snprintf_heap_before_end);
+ATF_TC_BODY(snprintf_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;
+ char srcvar[__len + 10];
+
+ __stack.__buf = malloc(__bufsz);
+ memset(srcvar, 'A', sizeof(srcvar) - 1);
+ srcvar[sizeof(srcvar) - 1] = '\0';
+
+ snprintf(__stack.__buf, __len, "%.*s", (int)__len - 1, srcvar);
+#undef BUF
+
+}
+
+ATF_TC_WITHOUT_HEAD(snprintf_heap_end);
+ATF_TC_BODY(snprintf_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;
+ char srcvar[__len + 10];
+
+ __stack.__buf = malloc(__bufsz);
+ memset(srcvar, 'A', sizeof(srcvar) - 1);
+ srcvar[sizeof(srcvar) - 1] = '\0';
+
+ snprintf(__stack.__buf, __len, "%.*s", (int)__len - 1, srcvar);
+#undef BUF
+
+}
+
+ATF_TC_WITHOUT_HEAD(snprintf_heap_after_end);
+ATF_TC_BODY(snprintf_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;
+ char srcvar[__len + 10];
+
+ __child = fork();
+ ATF_REQUIRE(__child >= 0);
+ if (__child > 0)
+ goto monitor;
+
+ /* Child */
+ disable_coredumps();
+ __stack.__buf = malloc(__bufsz);
+ memset(srcvar, 'A', sizeof(srcvar) - 1);
+ srcvar[sizeof(srcvar) - 1] = '\0';
+
+ snprintf(__stack.__buf, __len, "%.*s", (int)__len - 1, srcvar);
+ _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, sprintf_before_end);
+ ATF_TP_ADD_TC(tp, sprintf_end);
+ ATF_TP_ADD_TC(tp, sprintf_heap_before_end);
+ ATF_TP_ADD_TC(tp, sprintf_heap_end);
+ ATF_TP_ADD_TC(tp, sprintf_heap_after_end);
+ ATF_TP_ADD_TC(tp, snprintf_before_end);
+ ATF_TP_ADD_TC(tp, snprintf_end);
+ ATF_TP_ADD_TC(tp, snprintf_heap_before_end);
+ ATF_TP_ADD_TC(tp, snprintf_heap_end);
+ ATF_TP_ADD_TC(tp, snprintf_heap_after_end);
+ return (atf_no_error());
+}
diff --git a/lib/libc/tests/secure/fortify_string_test.c b/lib/libc/tests/secure/fortify_string_test.c
new file mode 100644
--- /dev/null
+++ b/lib/libc/tests/secure/fortify_string_test.c
@@ -0,0 +1,1415 @@
+/* @generated by `generate-fortify-tests.lua "string"` */
+
+#define _FORTIFY_SOURCE 2
+#define TMPFILE_SIZE (1024 * 32)
+
+#include <sys/param.h>
+#include <sys/resource.h>
+#include <sys/time.h>
+#include <sys/wait.h>
+#include <dirent.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <strings.h>
+#include <sysexits.h>
+#include <unistd.h>
+#include <atf-c.h>
+
+/*
+ * 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);
+}
+
+ATF_TC_WITHOUT_HEAD(memcpy_before_end);
+ATF_TC_BODY(memcpy_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;
+ char src[__len + 10];
+
+ memcpy(__stack.__buf, src, __len);
+#undef BUF
+
+}
+
+ATF_TC_WITHOUT_HEAD(memcpy_end);
+ATF_TC_BODY(memcpy_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;
+ char src[__len + 10];
+
+ memcpy(__stack.__buf, src, __len);
+#undef BUF
+
+}
+
+ATF_TC_WITHOUT_HEAD(memcpy_heap_before_end);
+ATF_TC_BODY(memcpy_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;
+ char src[__len + 10];
+
+ __stack.__buf = malloc(__bufsz);
+
+ memcpy(__stack.__buf, src, __len);
+#undef BUF
+
+}
+
+ATF_TC_WITHOUT_HEAD(memcpy_heap_end);
+ATF_TC_BODY(memcpy_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;
+ char src[__len + 10];
+
+ __stack.__buf = malloc(__bufsz);
+
+ memcpy(__stack.__buf, src, __len);
+#undef BUF
+
+}
+
+ATF_TC_WITHOUT_HEAD(memcpy_heap_after_end);
+ATF_TC_BODY(memcpy_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;
+ char src[__len + 10];
+
+ __child = fork();
+ ATF_REQUIRE(__child >= 0);
+ if (__child > 0)
+ goto monitor;
+
+ /* Child */
+ disable_coredumps();
+ __stack.__buf = malloc(__bufsz);
+
+ memcpy(__stack.__buf, src, __len);
+ _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(memmove_before_end);
+ATF_TC_BODY(memmove_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;
+ char src[__len + 10];
+
+ memmove(__stack.__buf, src, __len);
+#undef BUF
+
+}
+
+ATF_TC_WITHOUT_HEAD(memmove_end);
+ATF_TC_BODY(memmove_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;
+ char src[__len + 10];
+
+ memmove(__stack.__buf, src, __len);
+#undef BUF
+
+}
+
+ATF_TC_WITHOUT_HEAD(memmove_heap_before_end);
+ATF_TC_BODY(memmove_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;
+ char src[__len + 10];
+
+ __stack.__buf = malloc(__bufsz);
+
+ memmove(__stack.__buf, src, __len);
+#undef BUF
+
+}
+
+ATF_TC_WITHOUT_HEAD(memmove_heap_end);
+ATF_TC_BODY(memmove_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;
+ char src[__len + 10];
+
+ __stack.__buf = malloc(__bufsz);
+
+ memmove(__stack.__buf, src, __len);
+#undef BUF
+
+}
+
+ATF_TC_WITHOUT_HEAD(memmove_heap_after_end);
+ATF_TC_BODY(memmove_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;
+ char src[__len + 10];
+
+ __child = fork();
+ ATF_REQUIRE(__child >= 0);
+ if (__child > 0)
+ goto monitor;
+
+ /* Child */
+ disable_coredumps();
+ __stack.__buf = malloc(__bufsz);
+
+ memmove(__stack.__buf, src, __len);
+ _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(memset_before_end);
+ATF_TC_BODY(memset_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;
+
+ memset(__stack.__buf, 0, __len);
+#undef BUF
+
+}
+
+ATF_TC_WITHOUT_HEAD(memset_end);
+ATF_TC_BODY(memset_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;
+
+ memset(__stack.__buf, 0, __len);
+#undef BUF
+
+}
+
+ATF_TC_WITHOUT_HEAD(memset_heap_before_end);
+ATF_TC_BODY(memset_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;
+
+ __stack.__buf = malloc(__bufsz);
+
+ memset(__stack.__buf, 0, __len);
+#undef BUF
+
+}
+
+ATF_TC_WITHOUT_HEAD(memset_heap_end);
+ATF_TC_BODY(memset_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;
+
+ __stack.__buf = malloc(__bufsz);
+
+ memset(__stack.__buf, 0, __len);
+#undef BUF
+
+}
+
+ATF_TC_WITHOUT_HEAD(memset_heap_after_end);
+ATF_TC_BODY(memset_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;
+
+ __child = fork();
+ ATF_REQUIRE(__child >= 0);
+ if (__child > 0)
+ goto monitor;
+
+ /* Child */
+ disable_coredumps();
+ __stack.__buf = malloc(__bufsz);
+
+ memset(__stack.__buf, 0, __len);
+ _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(stpcpy_before_end);
+ATF_TC_BODY(stpcpy_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;
+ char src[__len];
+
+ memset(__stack.__buf, 0, __len);
+ memset(src, 'A', __len - 1);
+ src[__len - 1] = '\0';
+
+ stpcpy(__stack.__buf, src);
+#undef BUF
+
+}
+
+ATF_TC_WITHOUT_HEAD(stpcpy_end);
+ATF_TC_BODY(stpcpy_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;
+ char src[__len];
+
+ memset(__stack.__buf, 0, __len);
+ memset(src, 'A', __len - 1);
+ src[__len - 1] = '\0';
+
+ stpcpy(__stack.__buf, src);
+#undef BUF
+
+}
+
+ATF_TC_WITHOUT_HEAD(stpcpy_heap_before_end);
+ATF_TC_BODY(stpcpy_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;
+ char src[__len];
+
+ __stack.__buf = malloc(__bufsz);
+ memset(__stack.__buf, 0, __len);
+ memset(src, 'A', __len - 1);
+ src[__len - 1] = '\0';
+
+ stpcpy(__stack.__buf, src);
+#undef BUF
+
+}
+
+ATF_TC_WITHOUT_HEAD(stpcpy_heap_end);
+ATF_TC_BODY(stpcpy_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;
+ char src[__len];
+
+ __stack.__buf = malloc(__bufsz);
+ memset(__stack.__buf, 0, __len);
+ memset(src, 'A', __len - 1);
+ src[__len - 1] = '\0';
+
+ stpcpy(__stack.__buf, src);
+#undef BUF
+
+}
+
+ATF_TC_WITHOUT_HEAD(stpcpy_heap_after_end);
+ATF_TC_BODY(stpcpy_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;
+ char src[__len];
+
+ __child = fork();
+ ATF_REQUIRE(__child >= 0);
+ if (__child > 0)
+ goto monitor;
+
+ /* Child */
+ disable_coredumps();
+ __stack.__buf = malloc(__bufsz);
+ memset(__stack.__buf, 0, __len);
+ memset(src, 'A', __len - 1);
+ src[__len - 1] = '\0';
+
+ stpcpy(__stack.__buf, src);
+ _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(stpncpy_before_end);
+ATF_TC_BODY(stpncpy_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;
+ char src[__len];
+
+ memset(__stack.__buf, 0, __len);
+ memset(src, 'A', __len - 1);
+ src[__len - 1] = '\0';
+
+ stpncpy(__stack.__buf, src, __len);
+#undef BUF
+
+}
+
+ATF_TC_WITHOUT_HEAD(stpncpy_end);
+ATF_TC_BODY(stpncpy_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;
+ char src[__len];
+
+ memset(__stack.__buf, 0, __len);
+ memset(src, 'A', __len - 1);
+ src[__len - 1] = '\0';
+
+ stpncpy(__stack.__buf, src, __len);
+#undef BUF
+
+}
+
+ATF_TC_WITHOUT_HEAD(stpncpy_heap_before_end);
+ATF_TC_BODY(stpncpy_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;
+ char src[__len];
+
+ __stack.__buf = malloc(__bufsz);
+ memset(__stack.__buf, 0, __len);
+ memset(src, 'A', __len - 1);
+ src[__len - 1] = '\0';
+
+ stpncpy(__stack.__buf, src, __len);
+#undef BUF
+
+}
+
+ATF_TC_WITHOUT_HEAD(stpncpy_heap_end);
+ATF_TC_BODY(stpncpy_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;
+ char src[__len];
+
+ __stack.__buf = malloc(__bufsz);
+ memset(__stack.__buf, 0, __len);
+ memset(src, 'A', __len - 1);
+ src[__len - 1] = '\0';
+
+ stpncpy(__stack.__buf, src, __len);
+#undef BUF
+
+}
+
+ATF_TC_WITHOUT_HEAD(stpncpy_heap_after_end);
+ATF_TC_BODY(stpncpy_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;
+ char src[__len];
+
+ __child = fork();
+ ATF_REQUIRE(__child >= 0);
+ if (__child > 0)
+ goto monitor;
+
+ /* Child */
+ disable_coredumps();
+ __stack.__buf = malloc(__bufsz);
+ memset(__stack.__buf, 0, __len);
+ memset(src, 'A', __len - 1);
+ src[__len - 1] = '\0';
+
+ stpncpy(__stack.__buf, src, __len);
+ _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(strcat_before_end);
+ATF_TC_BODY(strcat_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;
+ char src[__len];
+
+ memset(__stack.__buf, 0, __len);
+ memset(src, 'A', __len - 1);
+ src[__len - 1] = '\0';
+
+ strcat(__stack.__buf, src);
+#undef BUF
+
+}
+
+ATF_TC_WITHOUT_HEAD(strcat_end);
+ATF_TC_BODY(strcat_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;
+ char src[__len];
+
+ memset(__stack.__buf, 0, __len);
+ memset(src, 'A', __len - 1);
+ src[__len - 1] = '\0';
+
+ strcat(__stack.__buf, src);
+#undef BUF
+
+}
+
+ATF_TC_WITHOUT_HEAD(strcat_heap_before_end);
+ATF_TC_BODY(strcat_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;
+ char src[__len];
+
+ __stack.__buf = malloc(__bufsz);
+ memset(__stack.__buf, 0, __len);
+ memset(src, 'A', __len - 1);
+ src[__len - 1] = '\0';
+
+ strcat(__stack.__buf, src);
+#undef BUF
+
+}
+
+ATF_TC_WITHOUT_HEAD(strcat_heap_end);
+ATF_TC_BODY(strcat_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;
+ char src[__len];
+
+ __stack.__buf = malloc(__bufsz);
+ memset(__stack.__buf, 0, __len);
+ memset(src, 'A', __len - 1);
+ src[__len - 1] = '\0';
+
+ strcat(__stack.__buf, src);
+#undef BUF
+
+}
+
+ATF_TC_WITHOUT_HEAD(strcat_heap_after_end);
+ATF_TC_BODY(strcat_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;
+ char src[__len];
+
+ __child = fork();
+ ATF_REQUIRE(__child >= 0);
+ if (__child > 0)
+ goto monitor;
+
+ /* Child */
+ disable_coredumps();
+ __stack.__buf = malloc(__bufsz);
+ memset(__stack.__buf, 0, __len);
+ memset(src, 'A', __len - 1);
+ src[__len - 1] = '\0';
+
+ strcat(__stack.__buf, src);
+ _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(strncat_before_end);
+ATF_TC_BODY(strncat_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;
+ char src[__len];
+
+ memset(__stack.__buf, 0, __len);
+ memset(src, 'A', __len - 1);
+ src[__len - 1] = '\0';
+
+ strncat(__stack.__buf, src, __len);
+#undef BUF
+
+}
+
+ATF_TC_WITHOUT_HEAD(strncat_end);
+ATF_TC_BODY(strncat_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;
+ char src[__len];
+
+ memset(__stack.__buf, 0, __len);
+ memset(src, 'A', __len - 1);
+ src[__len - 1] = '\0';
+
+ strncat(__stack.__buf, src, __len);
+#undef BUF
+
+}
+
+ATF_TC_WITHOUT_HEAD(strncat_heap_before_end);
+ATF_TC_BODY(strncat_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;
+ char src[__len];
+
+ __stack.__buf = malloc(__bufsz);
+ memset(__stack.__buf, 0, __len);
+ memset(src, 'A', __len - 1);
+ src[__len - 1] = '\0';
+
+ strncat(__stack.__buf, src, __len);
+#undef BUF
+
+}
+
+ATF_TC_WITHOUT_HEAD(strncat_heap_end);
+ATF_TC_BODY(strncat_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;
+ char src[__len];
+
+ __stack.__buf = malloc(__bufsz);
+ memset(__stack.__buf, 0, __len);
+ memset(src, 'A', __len - 1);
+ src[__len - 1] = '\0';
+
+ strncat(__stack.__buf, src, __len);
+#undef BUF
+
+}
+
+ATF_TC_WITHOUT_HEAD(strncat_heap_after_end);
+ATF_TC_BODY(strncat_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;
+ char src[__len];
+
+ __child = fork();
+ ATF_REQUIRE(__child >= 0);
+ if (__child > 0)
+ goto monitor;
+
+ /* Child */
+ disable_coredumps();
+ __stack.__buf = malloc(__bufsz);
+ memset(__stack.__buf, 0, __len);
+ memset(src, 'A', __len - 1);
+ src[__len - 1] = '\0';
+
+ strncat(__stack.__buf, src, __len);
+ _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(strcpy_before_end);
+ATF_TC_BODY(strcpy_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;
+ char src[__len];
+
+ memset(__stack.__buf, 0, __len);
+ memset(src, 'A', __len - 1);
+ src[__len - 1] = '\0';
+
+ strcpy(__stack.__buf, src);
+#undef BUF
+
+}
+
+ATF_TC_WITHOUT_HEAD(strcpy_end);
+ATF_TC_BODY(strcpy_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;
+ char src[__len];
+
+ memset(__stack.__buf, 0, __len);
+ memset(src, 'A', __len - 1);
+ src[__len - 1] = '\0';
+
+ strcpy(__stack.__buf, src);
+#undef BUF
+
+}
+
+ATF_TC_WITHOUT_HEAD(strcpy_heap_before_end);
+ATF_TC_BODY(strcpy_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;
+ char src[__len];
+
+ __stack.__buf = malloc(__bufsz);
+ memset(__stack.__buf, 0, __len);
+ memset(src, 'A', __len - 1);
+ src[__len - 1] = '\0';
+
+ strcpy(__stack.__buf, src);
+#undef BUF
+
+}
+
+ATF_TC_WITHOUT_HEAD(strcpy_heap_end);
+ATF_TC_BODY(strcpy_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;
+ char src[__len];
+
+ __stack.__buf = malloc(__bufsz);
+ memset(__stack.__buf, 0, __len);
+ memset(src, 'A', __len - 1);
+ src[__len - 1] = '\0';
+
+ strcpy(__stack.__buf, src);
+#undef BUF
+
+}
+
+ATF_TC_WITHOUT_HEAD(strcpy_heap_after_end);
+ATF_TC_BODY(strcpy_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;
+ char src[__len];
+
+ __child = fork();
+ ATF_REQUIRE(__child >= 0);
+ if (__child > 0)
+ goto monitor;
+
+ /* Child */
+ disable_coredumps();
+ __stack.__buf = malloc(__bufsz);
+ memset(__stack.__buf, 0, __len);
+ memset(src, 'A', __len - 1);
+ src[__len - 1] = '\0';
+
+ strcpy(__stack.__buf, src);
+ _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(strncpy_before_end);
+ATF_TC_BODY(strncpy_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;
+ char src[__len];
+
+ memset(__stack.__buf, 0, __len);
+ memset(src, 'A', __len - 1);
+ src[__len - 1] = '\0';
+
+ strncpy(__stack.__buf, src, __len);
+#undef BUF
+
+}
+
+ATF_TC_WITHOUT_HEAD(strncpy_end);
+ATF_TC_BODY(strncpy_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;
+ char src[__len];
+
+ memset(__stack.__buf, 0, __len);
+ memset(src, 'A', __len - 1);
+ src[__len - 1] = '\0';
+
+ strncpy(__stack.__buf, src, __len);
+#undef BUF
+
+}
+
+ATF_TC_WITHOUT_HEAD(strncpy_heap_before_end);
+ATF_TC_BODY(strncpy_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;
+ char src[__len];
+
+ __stack.__buf = malloc(__bufsz);
+ memset(__stack.__buf, 0, __len);
+ memset(src, 'A', __len - 1);
+ src[__len - 1] = '\0';
+
+ strncpy(__stack.__buf, src, __len);
+#undef BUF
+
+}
+
+ATF_TC_WITHOUT_HEAD(strncpy_heap_end);
+ATF_TC_BODY(strncpy_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;
+ char src[__len];
+
+ __stack.__buf = malloc(__bufsz);
+ memset(__stack.__buf, 0, __len);
+ memset(src, 'A', __len - 1);
+ src[__len - 1] = '\0';
+
+ strncpy(__stack.__buf, src, __len);
+#undef BUF
+
+}
+
+ATF_TC_WITHOUT_HEAD(strncpy_heap_after_end);
+ATF_TC_BODY(strncpy_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;
+ char src[__len];
+
+ __child = fork();
+ ATF_REQUIRE(__child >= 0);
+ if (__child > 0)
+ goto monitor;
+
+ /* Child */
+ disable_coredumps();
+ __stack.__buf = malloc(__bufsz);
+ memset(__stack.__buf, 0, __len);
+ memset(src, 'A', __len - 1);
+ src[__len - 1] = '\0';
+
+ strncpy(__stack.__buf, src, __len);
+ _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, memcpy_before_end);
+ ATF_TP_ADD_TC(tp, memcpy_end);
+ ATF_TP_ADD_TC(tp, memcpy_heap_before_end);
+ ATF_TP_ADD_TC(tp, memcpy_heap_end);
+ ATF_TP_ADD_TC(tp, memcpy_heap_after_end);
+ ATF_TP_ADD_TC(tp, memmove_before_end);
+ ATF_TP_ADD_TC(tp, memmove_end);
+ ATF_TP_ADD_TC(tp, memmove_heap_before_end);
+ ATF_TP_ADD_TC(tp, memmove_heap_end);
+ ATF_TP_ADD_TC(tp, memmove_heap_after_end);
+ ATF_TP_ADD_TC(tp, memset_before_end);
+ ATF_TP_ADD_TC(tp, memset_end);
+ ATF_TP_ADD_TC(tp, memset_heap_before_end);
+ ATF_TP_ADD_TC(tp, memset_heap_end);
+ ATF_TP_ADD_TC(tp, memset_heap_after_end);
+ ATF_TP_ADD_TC(tp, stpcpy_before_end);
+ ATF_TP_ADD_TC(tp, stpcpy_end);
+ ATF_TP_ADD_TC(tp, stpcpy_heap_before_end);
+ ATF_TP_ADD_TC(tp, stpcpy_heap_end);
+ ATF_TP_ADD_TC(tp, stpcpy_heap_after_end);
+ ATF_TP_ADD_TC(tp, stpncpy_before_end);
+ ATF_TP_ADD_TC(tp, stpncpy_end);
+ ATF_TP_ADD_TC(tp, stpncpy_heap_before_end);
+ ATF_TP_ADD_TC(tp, stpncpy_heap_end);
+ ATF_TP_ADD_TC(tp, stpncpy_heap_after_end);
+ ATF_TP_ADD_TC(tp, strcat_before_end);
+ ATF_TP_ADD_TC(tp, strcat_end);
+ ATF_TP_ADD_TC(tp, strcat_heap_before_end);
+ ATF_TP_ADD_TC(tp, strcat_heap_end);
+ ATF_TP_ADD_TC(tp, strcat_heap_after_end);
+ ATF_TP_ADD_TC(tp, strncat_before_end);
+ ATF_TP_ADD_TC(tp, strncat_end);
+ ATF_TP_ADD_TC(tp, strncat_heap_before_end);
+ ATF_TP_ADD_TC(tp, strncat_heap_end);
+ ATF_TP_ADD_TC(tp, strncat_heap_after_end);
+ ATF_TP_ADD_TC(tp, strcpy_before_end);
+ ATF_TP_ADD_TC(tp, strcpy_end);
+ ATF_TP_ADD_TC(tp, strcpy_heap_before_end);
+ ATF_TP_ADD_TC(tp, strcpy_heap_end);
+ ATF_TP_ADD_TC(tp, strcpy_heap_after_end);
+ ATF_TP_ADD_TC(tp, strncpy_before_end);
+ ATF_TP_ADD_TC(tp, strncpy_end);
+ ATF_TP_ADD_TC(tp, strncpy_heap_before_end);
+ ATF_TP_ADD_TC(tp, strncpy_heap_end);
+ ATF_TP_ADD_TC(tp, strncpy_heap_after_end);
+ return (atf_no_error());
+}
diff --git a/lib/libc/tests/secure/fortify_strings_test.c b/lib/libc/tests/secure/fortify_strings_test.c
new file mode 100644
--- /dev/null
+++ b/lib/libc/tests/secure/fortify_strings_test.c
@@ -0,0 +1,354 @@
+/* @generated by `generate-fortify-tests.lua "strings"` */
+
+#define _FORTIFY_SOURCE 2
+#define TMPFILE_SIZE (1024 * 32)
+
+#include <sys/param.h>
+#include <sys/resource.h>
+#include <sys/time.h>
+#include <sys/wait.h>
+#include <dirent.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <strings.h>
+#include <sysexits.h>
+#include <unistd.h>
+#include <atf-c.h>
+
+/*
+ * 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);
+}
+
+ATF_TC_WITHOUT_HEAD(bcopy_before_end);
+ATF_TC_BODY(bcopy_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;
+ char src[__len + 10];
+
+ bcopy(src, __stack.__buf, __len);
+#undef BUF
+
+}
+
+ATF_TC_WITHOUT_HEAD(bcopy_end);
+ATF_TC_BODY(bcopy_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;
+ char src[__len + 10];
+
+ bcopy(src, __stack.__buf, __len);
+#undef BUF
+
+}
+
+ATF_TC_WITHOUT_HEAD(bcopy_heap_before_end);
+ATF_TC_BODY(bcopy_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;
+ char src[__len + 10];
+
+ __stack.__buf = malloc(__bufsz);
+
+ bcopy(src, __stack.__buf, __len);
+#undef BUF
+
+}
+
+ATF_TC_WITHOUT_HEAD(bcopy_heap_end);
+ATF_TC_BODY(bcopy_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;
+ char src[__len + 10];
+
+ __stack.__buf = malloc(__bufsz);
+
+ bcopy(src, __stack.__buf, __len);
+#undef BUF
+
+}
+
+ATF_TC_WITHOUT_HEAD(bcopy_heap_after_end);
+ATF_TC_BODY(bcopy_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;
+ char src[__len + 10];
+
+ __child = fork();
+ ATF_REQUIRE(__child >= 0);
+ if (__child > 0)
+ goto monitor;
+
+ /* Child */
+ disable_coredumps();
+ __stack.__buf = malloc(__bufsz);
+
+ bcopy(src, __stack.__buf, __len);
+ _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(bzero_before_end);
+ATF_TC_BODY(bzero_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;
+
+ bzero(__stack.__buf, __len);
+#undef BUF
+
+}
+
+ATF_TC_WITHOUT_HEAD(bzero_end);
+ATF_TC_BODY(bzero_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;
+
+ bzero(__stack.__buf, __len);
+#undef BUF
+
+}
+
+ATF_TC_WITHOUT_HEAD(bzero_heap_before_end);
+ATF_TC_BODY(bzero_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;
+
+ __stack.__buf = malloc(__bufsz);
+
+ bzero(__stack.__buf, __len);
+#undef BUF
+
+}
+
+ATF_TC_WITHOUT_HEAD(bzero_heap_end);
+ATF_TC_BODY(bzero_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;
+
+ __stack.__buf = malloc(__bufsz);
+
+ bzero(__stack.__buf, __len);
+#undef BUF
+
+}
+
+ATF_TC_WITHOUT_HEAD(bzero_heap_after_end);
+ATF_TC_BODY(bzero_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;
+
+ __child = fork();
+ ATF_REQUIRE(__child >= 0);
+ if (__child > 0)
+ goto monitor;
+
+ /* Child */
+ disable_coredumps();
+ __stack.__buf = malloc(__bufsz);
+
+ bzero(__stack.__buf, __len);
+ _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, bcopy_before_end);
+ ATF_TP_ADD_TC(tp, bcopy_end);
+ ATF_TP_ADD_TC(tp, bcopy_heap_before_end);
+ ATF_TP_ADD_TC(tp, bcopy_heap_end);
+ ATF_TP_ADD_TC(tp, bcopy_heap_after_end);
+ ATF_TP_ADD_TC(tp, bzero_before_end);
+ ATF_TP_ADD_TC(tp, bzero_end);
+ ATF_TP_ADD_TC(tp, bzero_heap_before_end);
+ ATF_TP_ADD_TC(tp, bzero_heap_end);
+ ATF_TP_ADD_TC(tp, bzero_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
new file mode 100644
--- /dev/null
+++ b/lib/libc/tests/secure/fortify_unistd_test.c
@@ -0,0 +1,505 @@
+/* @generated by `generate-fortify-tests.lua "unistd"` */
+
+#define _FORTIFY_SOURCE 2
+#define TMPFILE_SIZE (1024 * 32)
+
+#include <sys/param.h>
+#include <sys/resource.h>
+#include <sys/time.h>
+#include <sys/wait.h>
+#include <dirent.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <strings.h>
+#include <sysexits.h>
+#include <unistd.h>
+#include <atf-c.h>
+
+/*
+ * 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);
+}
+
+ATF_TC_WITHOUT_HEAD(getcwd_before_end);
+ATF_TC_BODY(getcwd_before_end, tc)
+{
+#define BUF &__stack.__buf
+ struct {
+ uint8_t padding_l;
+ unsigned char __buf[8];
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(__stack.__buf);
+ const size_t __len = 8 - 1;
+ const size_t __idx __unused = __len - 1;
+
+ getcwd(__stack.__buf, __len);
+#undef BUF
+
+}
+
+ATF_TC_WITHOUT_HEAD(getcwd_end);
+ATF_TC_BODY(getcwd_end, tc)
+{
+#define BUF &__stack.__buf
+ struct {
+ uint8_t padding_l;
+ unsigned char __buf[8];
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(__stack.__buf);
+ const size_t __len = 8;
+ const size_t __idx __unused = __len - 1;
+
+ getcwd(__stack.__buf, __len);
+#undef BUF
+
+}
+
+ATF_TC_WITHOUT_HEAD(getcwd_heap_before_end);
+ATF_TC_BODY(getcwd_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) * (8);
+ const size_t __len = 8 - 1;
+ const size_t __idx __unused = __len - 1;
+
+ __stack.__buf = malloc(__bufsz);
+
+ getcwd(__stack.__buf, __len);
+#undef BUF
+
+}
+
+ATF_TC_WITHOUT_HEAD(getcwd_heap_end);
+ATF_TC_BODY(getcwd_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) * (8);
+ const size_t __len = 8;
+ const size_t __idx __unused = __len - 1;
+
+ __stack.__buf = malloc(__bufsz);
+
+ getcwd(__stack.__buf, __len);
+#undef BUF
+
+}
+
+ATF_TC_WITHOUT_HEAD(getcwd_heap_after_end);
+ATF_TC_BODY(getcwd_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) * (8);
+ const size_t __len = 8 + 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);
+
+ getcwd(__stack.__buf, __len);
+ _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(read_before_end);
+ATF_TC_BODY(read_before_end, tc)
+{
+#define BUF &__stack.__buf
+ struct {
+ uint8_t padding_l;
+ unsigned char __buf[41];
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(__stack.__buf);
+ const size_t __len = 41 - 1;
+ const size_t __idx __unused = __len - 1;
+ int fd;
+
+ fd = new_tmpfile(); /* Cannot fail */
+
+ read(fd, __stack.__buf, __len);
+#undef BUF
+
+}
+
+ATF_TC_WITHOUT_HEAD(read_end);
+ATF_TC_BODY(read_end, tc)
+{
+#define BUF &__stack.__buf
+ struct {
+ uint8_t padding_l;
+ unsigned char __buf[41];
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(__stack.__buf);
+ const size_t __len = 41;
+ const size_t __idx __unused = __len - 1;
+ int fd;
+
+ fd = new_tmpfile(); /* Cannot fail */
+
+ read(fd, __stack.__buf, __len);
+#undef BUF
+
+}
+
+ATF_TC_WITHOUT_HEAD(read_heap_before_end);
+ATF_TC_BODY(read_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) * (41);
+ const size_t __len = 41 - 1;
+ const size_t __idx __unused = __len - 1;
+ int fd;
+
+ __stack.__buf = malloc(__bufsz);
+ fd = new_tmpfile(); /* Cannot fail */
+
+ read(fd, __stack.__buf, __len);
+#undef BUF
+
+}
+
+ATF_TC_WITHOUT_HEAD(read_heap_end);
+ATF_TC_BODY(read_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) * (41);
+ const size_t __len = 41;
+ const size_t __idx __unused = __len - 1;
+ int fd;
+
+ __stack.__buf = malloc(__bufsz);
+ fd = new_tmpfile(); /* Cannot fail */
+
+ read(fd, __stack.__buf, __len);
+#undef BUF
+
+}
+
+ATF_TC_WITHOUT_HEAD(read_heap_after_end);
+ATF_TC_BODY(read_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) * (41);
+ const size_t __len = 41 + 1;
+ const size_t __idx __unused = __len - 1;
+ pid_t __child;
+ int __status;
+ int fd;
+
+ __child = fork();
+ ATF_REQUIRE(__child >= 0);
+ if (__child > 0)
+ goto monitor;
+
+ /* Child */
+ disable_coredumps();
+ __stack.__buf = malloc(__bufsz);
+ fd = new_tmpfile(); /* Cannot fail */
+
+ read(fd, __stack.__buf, __len);
+ _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(readlink_before_end);
+ATF_TC_BODY(readlink_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;
+ const char *path;
+
+ path = new_symlink(__len); /* Cannot fail */
+
+ readlink(path, __stack.__buf, __len);
+#undef BUF
+
+}
+
+ATF_TC_WITHOUT_HEAD(readlink_end);
+ATF_TC_BODY(readlink_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;
+ const char *path;
+
+ path = new_symlink(__len); /* Cannot fail */
+
+ readlink(path, __stack.__buf, __len);
+#undef BUF
+
+}
+
+ATF_TC_WITHOUT_HEAD(readlink_heap_before_end);
+ATF_TC_BODY(readlink_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;
+ const char *path;
+
+ __stack.__buf = malloc(__bufsz);
+ path = new_symlink(__len); /* Cannot fail */
+
+ readlink(path, __stack.__buf, __len);
+#undef BUF
+
+}
+
+ATF_TC_WITHOUT_HEAD(readlink_heap_end);
+ATF_TC_BODY(readlink_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;
+ const char *path;
+
+ __stack.__buf = malloc(__bufsz);
+ path = new_symlink(__len); /* Cannot fail */
+
+ readlink(path, __stack.__buf, __len);
+#undef BUF
+
+}
+
+ATF_TC_WITHOUT_HEAD(readlink_heap_after_end);
+ATF_TC_BODY(readlink_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;
+ const char *path;
+
+ __child = fork();
+ ATF_REQUIRE(__child >= 0);
+ if (__child > 0)
+ goto monitor;
+
+ /* Child */
+ disable_coredumps();
+ __stack.__buf = malloc(__bufsz);
+ path = new_symlink(__len); /* Cannot fail */
+
+ readlink(path, __stack.__buf, __len);
+ _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, getcwd_before_end);
+ ATF_TP_ADD_TC(tp, getcwd_end);
+ ATF_TP_ADD_TC(tp, getcwd_heap_before_end);
+ ATF_TP_ADD_TC(tp, getcwd_heap_end);
+ ATF_TP_ADD_TC(tp, getcwd_heap_after_end);
+ ATF_TP_ADD_TC(tp, read_before_end);
+ ATF_TP_ADD_TC(tp, read_end);
+ ATF_TP_ADD_TC(tp, read_heap_before_end);
+ ATF_TP_ADD_TC(tp, read_heap_end);
+ ATF_TP_ADD_TC(tp, read_heap_after_end);
+ ATF_TP_ADD_TC(tp, readlink_before_end);
+ ATF_TP_ADD_TC(tp, readlink_end);
+ ATF_TP_ADD_TC(tp, readlink_heap_before_end);
+ ATF_TP_ADD_TC(tp, readlink_heap_end);
+ ATF_TP_ADD_TC(tp, readlink_heap_after_end);
+ return (atf_no_error());
+}
diff --git a/lib/libc/tests/secure/generate-fortify-tests.lua b/lib/libc/tests/secure/generate-fortify-tests.lua
new file mode 100755
--- /dev/null
+++ b/lib/libc/tests/secure/generate-fortify-tests.lua
@@ -0,0 +1,706 @@
+#!/usr/libexec/flua
+--
+-- 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 AUTHOR 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 AUTHOR 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.
+--
+
+-- THEORY OF OPERATION
+--
+-- generate-fortify-tests.lua is intended to test fortified functions as found
+-- mostly in the various headers in /usr/include/ssp. Each fortified function
+-- gets three basic tests:
+--
+-- 1. Write just before the end of the buffer,
+-- 2. Write right at the end of the buffer,
+-- 3. Write just after the end of the buffer.
+--
+-- Each test is actually generated twice: once with a buffer on the stack, and
+-- again with a buffer on the heap, to confirm that __builtin_object_size(3) can
+-- deduce the buffer size in both scenarios. The tests work by setting up the
+-- stack with our buffer (and some padding on either side to avoid tripping any
+-- other stack or memory protection), doing any initialization as described by
+-- the test definition, then calling the fortified function with the buffer as
+-- outlined by the test definition.
+--
+-- For the 'before' and 'at' the end tests, we're ensuring that valid writes
+-- that are on the verge of being invalid aren't accidentally being detected as
+-- invalid.
+--
+-- The 'after' test is the one that actually tests the functional benefit of
+-- _FORTIFY_SOURCE by violating a boundary that should trigger an abort. As
+-- such, this test differs more from the other two in that it has to fork() off
+-- the fortified function call so that we can monitor for a SIGABRT and
+-- pass/fail the test at function end appropriately.
+
+-- Some tests, like the FD_*() macros, may define these differently. For
+-- instance, for fd sets we're varying the index we pass and not using arbitrary
+-- buffers. Other tests that don't use the length in any way may physically
+-- vary the buffer size for each test case when we'd typically vary the length
+-- we're requesting a write for.
+
+local includes = {
+ "sys/param.h",
+ "sys/resource.h",
+ "sys/time.h",
+ "sys/wait.h",
+ "dirent.h",
+ "errno.h",
+ "fcntl.h",
+ "limits.h",
+ "signal.h",
+ "stdio.h",
+ "stdlib.h",
+ "string.h",
+ "strings.h",
+ "sysexits.h",
+ "unistd.h",
+ "atf-c.h",
+}
+
+local tests_added = {}
+
+-- Some of these will need to be excluded because clang sees the wrong size when
+-- an array is embedded inside a struct, we'll get something that looks more
+-- like __builtin_object_size(ptr, 0) than it does the correct
+-- __builtin_object_size(ptr, 1) (i.e., includes the padding after). This is
+-- almost certainly a bug in llvm.
+local function excludes_stack_overflow(disposition, is_heap)
+ return (not is_heap) and disposition > 0
+end
+
+local printf_stackvars = "\tchar srcvar[__len + 10];\n"
+local printf_init = [[
+ memset(srcvar, 'A', sizeof(srcvar) - 1);
+ srcvar[sizeof(srcvar) - 1] = '\0';
+]]
+
+local string_stackvars = "\tchar src[__len];\n"
+local string_init = [[
+ memset(__stack.__buf, 0, __len);
+ memset(src, 'A', __len - 1);
+ src[__len - 1] = '\0';
+]]
+
+-- Each test entry describes how to test a given function. We need to know how
+-- to construct the buffer, we need to know the argument set we're dealing with,
+-- and we need to know what we're passing to each argument. We could be passing
+-- fixed values, or we could be passing the __buf under test.
+--
+-- definition:
+-- func: name of the function under test to call
+-- bufsize: size of buffer to generate, defaults to 42
+-- buftype: type of buffer to generate, defaults to unsigned char[]
+-- arguments: __buf, __len, or the name of a variable placed on the stack
+-- exclude: a function(disposition, is_heap) that returns true if this combo
+-- should be excluded.
+-- stackvars: extra variables to be placed on the stack, should be a string
+-- optionally formatted with tabs and newlines
+-- init: extra code to inject just before the function call for initialization
+-- of the buffer or any of the above-added stackvars; also a string
+-- uses_len: bool-ish, necessary if arguments doesn't include either __idx or
+-- or __len so that the test generator doesn't try to vary the size of the
+-- buffer instead of just manipulating __idx/__len to try and induce an
+-- overflow.
+--
+-- Most tests will just use the default bufsize/buftype, but under some
+-- circumstances it's useful to use a different type (e.g., for alignment
+-- requirements).
+local all_tests = {
+ stdio = {
+ -- <stdio.h>
+ {
+ func = "sprintf",
+ arguments = {
+ "__buf",
+ "\"%.*s\"",
+ "(int)__len - 1", -- - 1 for NUL terminator
+ "srcvar",
+ },
+ exclude = excludes_stack_overflow,
+ stackvars = printf_stackvars,
+ init = printf_init,
+ },
+ {
+ func = "snprintf",
+ arguments = {
+ "__buf",
+ "__len",
+ "\"%.*s\"",
+ "(int)__len - 1", -- - 1 for NUL terminator
+ "srcvar",
+ },
+ exclude = excludes_stack_overflow,
+ stackvars = printf_stackvars,
+ init = printf_init,
+ },
+ },
+ string = {
+ -- <string.h>
+ {
+ func = "memcpy",
+ arguments = {
+ "__buf",
+ "src",
+ "__len",
+ },
+ exclude = excludes_stack_overflow,
+ stackvars = "\tchar src[__len + 10];\n",
+ },
+ {
+ func = "memmove",
+ arguments = {
+ "__buf",
+ "src",
+ "__len",
+ },
+ exclude = excludes_stack_overflow,
+ stackvars = "\tchar src[__len + 10];\n",
+ },
+ {
+ func = "memset",
+ arguments = {
+ "__buf",
+ "0",
+ "__len",
+ },
+ exclude = excludes_stack_overflow,
+ },
+ {
+ func = "stpcpy",
+ arguments = {
+ "__buf",
+ "src",
+ },
+ exclude = excludes_stack_overflow,
+ stackvars = string_stackvars,
+ init = string_init,
+ uses_len = true,
+ },
+ {
+ func = "stpncpy",
+ arguments = {
+ "__buf",
+ "src",
+ "__len",
+ },
+ exclude = excludes_stack_overflow,
+ stackvars = string_stackvars,
+ init = string_init,
+ },
+ {
+ func = "strcat",
+ arguments = {
+ "__buf",
+ "src",
+ },
+ exclude = excludes_stack_overflow,
+ stackvars = string_stackvars,
+ init = string_init,
+ uses_len = true,
+ },
+ {
+ func = "strncat",
+ arguments = {
+ "__buf",
+ "src",
+ "__len",
+ },
+ exclude = excludes_stack_overflow,
+ stackvars = string_stackvars,
+ init = string_init,
+ },
+ {
+ func = "strcpy",
+ arguments = {
+ "__buf",
+ "src",
+ },
+ exclude = excludes_stack_overflow,
+ stackvars = string_stackvars,
+ init = string_init,
+ uses_len = true,
+ },
+ {
+ func = "strncpy",
+ arguments = {
+ "__buf",
+ "src",
+ "__len",
+ },
+ exclude = excludes_stack_overflow,
+ stackvars = string_stackvars,
+ init = string_init,
+ },
+ },
+ strings = {
+ -- <strings.h>
+ {
+ func = "bcopy",
+ arguments = {
+ "src",
+ "__buf",
+ "__len",
+ },
+ exclude = excludes_stack_overflow,
+ stackvars = "\tchar src[__len + 10];\n",
+ },
+ {
+ func = "bzero",
+ arguments = {
+ "__buf",
+ "__len",
+ },
+ exclude = excludes_stack_overflow,
+ },
+ },
+ unistd = {
+ -- <unistd.h>
+ {
+ func = "getcwd",
+ bufsize = "8",
+ arguments = {
+ "__buf",
+ "__len",
+ },
+ exclude = excludes_stack_overflow,
+ },
+ {
+ func = "read",
+ bufsize = "41",
+ arguments = {
+ "fd",
+ "__buf",
+ "__len",
+ },
+ exclude = excludes_stack_overflow,
+ stackvars = "\tint fd;\n",
+ init = [[
+ fd = new_tmpfile(); /* Cannot fail */
+]],
+ },
+ {
+ func = "readlink",
+ arguments = {
+ "path",
+ "__buf",
+ "__len",
+ },
+ exclude = excludes_stack_overflow,
+ stackvars = "\tconst char *path;\n",
+ init = [[
+ path = new_symlink(__len); /* Cannot fail */
+]],
+ },
+ },
+}
+
+local function write_test_boilerplate(fh, name, body)
+ fh:write("ATF_TC_WITHOUT_HEAD(" .. name .. ");\n")
+ fh:write("ATF_TC_BODY(" .. name .. ", tc)\n")
+ fh:write("{\n" .. body .. "\n}\n\n")
+ return name
+end
+
+local function generate_test_name(func, variant, disposition, heap)
+ local basename = func
+ if variant then
+ basename = basename .. "_" .. variant
+ end
+ if heap then
+ basename = basename .. "_heap"
+ end
+ if disposition < 0 then
+ return basename .. "_before_end"
+ elseif disposition == 0 then
+ return basename .. "_end"
+ else
+ return basename .. "_after_end"
+ end
+end
+
+local function array_type(buftype)
+ if not buftype:match("%[%]") then
+ return nil
+ end
+
+ return buftype:gsub("%[%]", "")
+end
+
+local function configurable(def, idx)
+ local cfgitem = def[idx]
+
+ if not cfgitem then
+ return nil
+ end
+
+ if type(cfgitem) == "function" then
+ return cfgitem()
+ end
+
+ return cfgitem
+end
+
+local function generate_stackframe(buftype, bufsize, disposition, heap, def)
+ local function len_offset(inverted, disposition)
+ -- Tests that don't use __len in their arguments may use an
+ -- inverted sense because we can't just specify a length that
+ -- would induce an access just after the end. Instead, we have
+ -- to manipulate the buffer size to be too short so that the
+ -- function under test would write one too many.
+ if disposition < 0 then
+ return ((inverted and " + ") or " - ") .. "1"
+ elseif disposition == 0 then
+ return ""
+ else
+ return ((inverted and " - ") or " + ") .. "1"
+ end
+ end
+
+ local function test_uses_len(def)
+ if def.uses_len then
+ return true
+ end
+
+ for _, arg in ipairs(def.arguments) do
+ if arg:match("__len") or arg:match("__idx") then
+ return true
+ end
+ end
+
+ return false
+ end
+
+
+ -- This is perhaps a little convoluted, but we toss the buffer into a
+ -- struct on the stack to guarantee that we have at least one valid
+ -- byte on either side of the buffer -- a measure to make sure that
+ -- we're tripping _FORTIFY_SOURCE specifically in the buffer + 1 case,
+ -- rather than some other stack or memory protection.
+ local vars = "\tstruct {\n"
+ vars = vars .. "\t\tuint8_t padding_l;\n"
+
+ local uses_len = test_uses_len(def)
+ local bufsize_offset = len_offset(not uses_len, disposition)
+ local buftype_elem = array_type(buftype)
+ local size_expr = bufsize
+
+ if not uses_len then
+ -- If the length isn't in use, we have to vary the buffer size
+ -- since the fortified function likely has some internal size
+ -- constraint that it's supposed to be checking.
+ size_expr = size_expr .. bufsize_offset
+ end
+
+ if not heap and buftype_elem then
+ -- Array type: size goes after identifier
+ vars = vars .. "\t\t" .. buftype_elem ..
+ " __buf[" .. size_expr .. "];\n"
+ else
+ local basic_type = buftype_elem or buftype
+
+ -- Heap tests obviously just put a pointer on the stack that
+ -- points to our new allocation, but we leave it in the padded
+ -- struct just to simplify our generator.
+ if heap then
+ basic_type = basic_type .. " *"
+ end
+ vars = vars .. "\t\t" .. basic_type .. " __buf;\n"
+ end
+
+ -- padding_r is our just-past-the-end padding that we use to make sure
+ -- that there's a valid portion after the buffer that isn't being
+ -- included in our function calls. If we didn't have it, then we'd have
+ -- a hard time feeling confident that an abort on the just-after tests
+ -- isn't maybe from some other memory or stack protection.
+ vars = vars .. "\t\tuint8_t padding_r;\n"
+ vars = vars .. "\t} __stack;\n"
+
+ -- Not all tests will use __bufsz, but some do for, e.g., clearing
+ -- memory..
+ vars = vars .. "\tconst size_t __bufsz __unused = "
+ if heap then
+ local scalar = 1
+ if buftype_elem then
+ scalar = size_expr
+ end
+
+ vars = vars .. "sizeof(*__stack.__buf) * (" .. scalar .. ");\n"
+ else
+ vars = vars .. "sizeof(__stack.__buf);\n"
+ end
+
+ vars = vars .. "\tconst size_t __len = " .. bufsize ..
+ bufsize_offset .. ";\n"
+ vars = vars .. "\tconst size_t __idx __unused = __len - 1;\n"
+
+ -- For overflow testing, we need to fork() because we're expecting the
+ -- test to ultimately abort()/_exit(). Then we can collect the exit
+ -- status and report appropriately.
+ if disposition > 0 then
+ vars = vars .. "\tpid_t __child;\n"
+ vars = vars .. "\tint __status;\n"
+ end
+
+ -- Any other stackvars defined by the test get placed after everything
+ -- else.
+ vars = vars .. (configurable(def, "stackvars") or "")
+
+ return vars
+end
+
+local function write_test(fh, func, disposition, heap, def)
+ local testname = generate_test_name(func, def.variant, disposition, heap)
+ local buftype = def.buftype or "unsigned char[]"
+ local bufsize = def.bufsize or 42
+ local body = ""
+
+ if def.exclude and def.exclude(disposition, heap) then
+ return
+ end
+
+ local function need_addr(buftype)
+ return not (buftype:match("%[%]") or buftype:match("%*"))
+ end
+
+ if heap then
+ body = body .. "#define BUF __stack.__buf\n"
+ else
+ body = body .. "#define BUF &__stack.__buf\n"
+ end
+
+ -- Setup the buffer
+ body = body .. generate_stackframe(buftype, bufsize, disposition, heap, def) ..
+ "\n"
+
+ -- Any early initialization goes before we would fork for the just-after
+ -- tests, because they may want to skip the test based on some criteria
+ -- and we can't propagate that up very easily once we're forked.
+ local early_init = configurable(def, "early_init")
+ body = body .. (early_init or "")
+ if early_init then
+ body = body .. "\n"
+ end
+
+ -- Fork off, iff we're testing some access past the end of the buffer.
+ if disposition > 0 then
+ body = body .. [[
+ __child = fork();
+ ATF_REQUIRE(__child >= 0);
+ if (__child > 0)
+ goto monitor;
+
+ /* Child */
+ disable_coredumps();
+]]
+ end
+
+ local bufvar = "__stack.__buf"
+ if heap then
+ -- Buffer needs to be initialized because it's a heap allocation.
+ body = body .. "\t" .. bufvar .. " = malloc(__bufsz);\n"
+ end
+
+ -- Non-early init happens just after the fork in the child, not the
+ -- monitor. This is used to setup any other buffers we may need, for
+ -- instance.
+ local extra_init = configurable(def, "init")
+ body = body .. (extra_init or "")
+
+ if heap or extra_init then
+ body = body .. "\n"
+ end
+
+ -- Setup the function call with arguments as described in the test
+ -- definition.
+ body = body .. "\t" .. func .. "("
+
+ for idx, arg in ipairs(def.arguments) do
+ if idx > 1 then
+ body = body .. ", "
+ end
+
+ if arg == "__buf" then
+ if not heap and need_addr(buftype) then
+ body = body .. "&"
+ end
+
+ body = body .. bufvar
+ else
+ local argname = arg
+
+ if def.value_of then
+ argname = argname or def.value_of(arg)
+ end
+
+ body = body .. argname
+ end
+ end
+
+ body = body .. ");\n"
+
+ -- Monitor stuff follows, for OOB access.
+ if disposition <= 0 then
+ goto skip
+ end
+
+ body = body .. [[
+ _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));
+ }
+]]
+
+::skip::
+ body = body .. "#undef BUF\n"
+ return write_test_boilerplate(fh, testname, body)
+end
+
+-- main()
+local tests
+local tcat = assert(arg[1], "usage: generate-fortify-tests.lua <category>")
+for k, defs in pairs(all_tests) do
+ if k == tcat then
+ tests = defs
+ break
+ end
+end
+
+assert(tests, "category " .. tcat .. " not found")
+
+local fh = io.stdout
+fh:write("/* @" .. "generated" .. " by `generate-fortify-tests.lua \"" ..
+ tcat .. "\"` */\n\n")
+fh:write("#define _FORTIFY_SOURCE 2\n")
+fh:write("#define TMPFILE_SIZE (1024 * 32)\n")
+
+fh:write("\n")
+for _, inc in ipairs(includes) do
+ fh:write("#include <" .. inc .. ">\n")
+end
+
+fh:write([[
+
+/*
+ * 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);
+}
+
+]])
+
+for _, def in pairs(tests) do
+ local func = def.func
+ local function write_tests(heap)
+ -- Dispositions here are relative to the buffer size prescribed
+ -- by the test definition.
+ local dispositions = def.dispositions or { -1, 0, 1 }
+
+ for _, disposition in ipairs(dispositions) do
+ tests_added[#tests_added + 1] = write_test(fh, func, disposition, heap, def)
+ end
+ end
+
+ write_tests(false)
+ write_tests(true)
+end
+
+fh:write("ATF_TP_ADD_TCS(tp)\n")
+fh:write("{\n")
+for idx = 1, #tests_added do
+ fh:write("\tATF_TP_ADD_TC(tp, " .. tests_added[idx] .. ");\n")
+end
+fh:write("\treturn (atf_no_error());\n")
+fh:write("}\n")

File Metadata

Mime Type
text/plain
Expires
Mon, Apr 28, 4:06 AM (14 h, 45 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
17825470
Default Alt Text
D45678.diff (79 KB)

Event Timeline