Page Menu
Home
FreeBSD
Search
Configure Global Search
Log In
Files
F115687999
D45678.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Flag For Later
Award Token
Size
79 KB
Referenced Files
None
Subscribers
None
D45678.diff
View Options
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
Details
Attached
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)
Attached To
Mode
D45678: libc: tests: add testing infrastructure for _FORTIFY_SOURCE
Attached
Detach File
Event Timeline
Log In to Comment