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 + +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 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * 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 = { + -- + { + 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 = { + -- + { + 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 = { + -- + { + 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 = { + -- + { + 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 ") +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")