diff --git a/include/ssp/string.h b/include/ssp/string.h --- a/include/ssp/string.h +++ b/include/ssp/string.h @@ -109,6 +109,8 @@ __ssp_bos_icheck3_restrict(mempcpy, void *, const void *) __ssp_bos_icheck3(memmove, void *, const void *) __ssp_bos_icheck3(memset, void *, int) +__ssp_redirect(void *, memset_explicit, (void *__buf, int __ch, size_t __len), + (__buf, __ch, __len)); __ssp_bos_icheck2_restrict(stpcpy, char *, const char *) __ssp_bos_icheck3_restrict(stpncpy, char *, const char *) __ssp_bos_icheck2_restrict(strcpy, char *, const char *) diff --git a/include/string.h b/include/string.h --- a/include/string.h +++ b/include/string.h @@ -71,6 +71,9 @@ void *(mempcpy)(void * __restrict, const void * __restrict, size_t); #endif void *(memset)(void *, int, size_t); +#if __BSD_VISIBLE +void *memset_explicit(void *, int, size_t); +#endif #if __POSIX_VISIBLE >= 200809 char *(stpcpy)(char * __restrict, const char * __restrict); char *(stpncpy)(char * __restrict, const char * __restrict, size_t); diff --git a/lib/libc/string/Makefile.inc b/lib/libc/string/Makefile.inc --- a/lib/libc/string/Makefile.inc +++ b/lib/libc/string/Makefile.inc @@ -11,6 +11,7 @@ ffs.c ffsl.c ffsll.c fls.c flsl.c flsll.c \ memccpy.c memchr.c memrchr.c memcmp.c \ memcpy.c memmem.c memmove.c mempcpy.c memset.c memset_s.c \ + memset_explicit.c \ stpcpy.c stpncpy.c strcasecmp.c \ strcat.c strcasestr.c strchr.c strchrnul.c strcmp.c strcoll.c strcpy.c\ strcspn.c strdup.c strerror.c strlcat.c strlcpy.c strlen.c strmode.c \ @@ -62,7 +63,8 @@ MLINKS+=index.3 rindex.3 MLINKS+=memchr.3 memrchr.3 MLINKS+=memcpy.3 mempcpy.3 -MLINKS+=memset.3 memset_s.3 +MLINKS+=memset.3 memset_s.3 \ + memset.3 memset_explicit.3 MLINKS+=strcasecmp.3 strncasecmp.3 \ strcasecmp.3 strcasecmp_l.3 \ strcasecmp.3 strncasecmp_l.3 diff --git a/lib/libc/string/Symbol.map b/lib/libc/string/Symbol.map --- a/lib/libc/string/Symbol.map +++ b/lib/libc/string/Symbol.map @@ -116,6 +116,10 @@ wmempcpy; }; +FBSD_1.8 { + memset_explicit; +}; + FBSDprivate_1.0 { __strtok_r; }; diff --git a/lib/libc/string/memset.3 b/lib/libc/string/memset.3 --- a/lib/libc/string/memset.3 +++ b/lib/libc/string/memset.3 @@ -29,7 +29,7 @@ .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" SUCH DAMAGE. .\" -.Dd August 19, 2018 +.Dd October 24, 2024 .Dt MEMSET 3 .Os .Sh NAME @@ -41,6 +41,8 @@ .In string.h .Ft void * .Fn memset "void *dest" "int c" "size_t len" +.Ft void * +.Fn memset_explicit "void *dest" "int c" "size_t len" .Fd #define __STDC_WANT_LIB_EXT1__ 1 .Ft errno_t .Fn memset_s "void *dest" "rsize_t destsz" "int c" "rsize_t len" @@ -68,6 +70,13 @@ is an invalid pointer. .Pp The +.Fn memset_explicit +function behaves the same as +.Fn memset, but will not be removed by a compiler's dead store +optimization pass, making it useful for clearing sensitive memory +such as a password. +.Pp +The .Fn memset_s function behaves the same as .Fn memset @@ -108,7 +117,9 @@ .Sh RETURN VALUES The .Fn memset -function returns its first argument. +and +.Fn memset_explicit +functions return their first argument. The .Fn memset_s function returns zero on success, non-zero on error. @@ -128,3 +139,6 @@ conforms to .St -isoC-2011 K.3.7.4.1. +.Fn memset_explicit +conforms to +.St -isoC-2024 . diff --git a/lib/libc/string/memset_explicit.c b/lib/libc/string/memset_explicit.c new file mode 100644 --- /dev/null +++ b/lib/libc/string/memset_explicit.c @@ -0,0 +1,27 @@ +/*- + * SPDF-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2024 Robert Clausecker + */ + +#include +#include + +__attribute__((weak)) void __memset_explicit_hook(void *, int, size_t); + +__attribute__((weak)) void +__memset_explicit_hook(void *buf, int ch, size_t len) +{ + (void)buf; + (void)ch; + (void)len; +} + +void * +__ssp_real(memset_explicit)(void *buf, int ch, size_t len) +{ + memset(buf, ch, len); + __memset_explicit_hook(buf, ch, len); + + return (buf); +} diff --git a/lib/libc/tests/secure/fortify_string_test.c b/lib/libc/tests/secure/fortify_string_test.c --- a/lib/libc/tests/secure/fortify_string_test.c +++ b/lib/libc/tests/secure/fortify_string_test.c @@ -685,6 +685,133 @@ } +ATF_TC_WITHOUT_HEAD(memset_explicit_before_end); +ATF_TC_BODY(memset_explicit_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_explicit(__stack.__buf, 0, __len); +#undef BUF + +} + +ATF_TC_WITHOUT_HEAD(memset_explicit_end); +ATF_TC_BODY(memset_explicit_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_explicit(__stack.__buf, 0, __len); +#undef BUF + +} + +ATF_TC_WITHOUT_HEAD(memset_explicit_heap_before_end); +ATF_TC_BODY(memset_explicit_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_explicit(__stack.__buf, 0, __len); +#undef BUF + +} + +ATF_TC_WITHOUT_HEAD(memset_explicit_heap_end); +ATF_TC_BODY(memset_explicit_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_explicit(__stack.__buf, 0, __len); +#undef BUF + +} + +ATF_TC_WITHOUT_HEAD(memset_explicit_heap_after_end); +ATF_TC_BODY(memset_explicit_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_explicit(__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) { @@ -1899,6 +2026,11 @@ 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, memset_explicit_before_end); + ATF_TP_ADD_TC(tp, memset_explicit_end); + ATF_TP_ADD_TC(tp, memset_explicit_heap_before_end); + ATF_TP_ADD_TC(tp, memset_explicit_heap_end); + ATF_TP_ADD_TC(tp, memset_explicit_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); diff --git a/lib/libc/tests/secure/generate-fortify-tests.lua b/lib/libc/tests/secure/generate-fortify-tests.lua --- a/lib/libc/tests/secure/generate-fortify-tests.lua +++ b/lib/libc/tests/secure/generate-fortify-tests.lua @@ -630,6 +630,15 @@ }, exclude = excludes_stack_overflow, }, + { + func = "memset_explicit", + arguments = { + "__buf", + "0", + "__len", + }, + exclude = excludes_stack_overflow, + }, { func = "stpcpy", arguments = {