git: cf8e5289a110 - main - include: ssp: round out fortification of current set of headers

From: Kyle Evans <kevans_at_FreeBSD.org>
Date: Sat, 13 Jul 2024 05:23:00 UTC
The branch main has been updated by kevans:

URL: https://cgit.FreeBSD.org/src/commit/?id=cf8e5289a110954600f135024d1515a77d0ae34d

commit cf8e5289a110954600f135024d1515a77d0ae34d
Author:     Kyle Evans <kevans@FreeBSD.org>
AuthorDate: 2024-07-13 05:16:10 +0000
Commit:     Kyle Evans <kevans@FreeBSD.org>
CommitDate: 2024-07-13 05:16:24 +0000

    include: ssp: round out fortification of current set of headers
    
    ssp/ssp.h needed some improvements:
     - `len` isn't always a size_t, it may need casted
     - In some cases we may want to use a len that isn't specified as a
        parameter (e.g., L_ctermid), so __ssp_redirect() should be more
        flexible.
     - In other cases we may want additional checking, so pull all of the
        declaration bits out of __ssp_redirect_raw() so that some functions
        can implement the body themselves.
    
    strlcat/strlcpy should be the last of the fortified functions that get
    their own __*_chk symbols, and these cases are only done to be
    consistent with the rest of the str*() set.
    
    Reviewed by:    markj
    Sponsored by:   Klara, Inc.
    Sponsored by:   Stormshield
    Differential Revision:  https://reviews.freebsd.org/D45679
---
 include/ssp/ssp.h                                |   16 +-
 include/ssp/stdio.h                              |   18 +
 include/ssp/string.h                             |   22 +
 include/ssp/strings.h                            |    5 +
 include/ssp/unistd.h                             |   36 +-
 include/stdio.h                                  |   37 +-
 include/string.h                                 |   29 +-
 include/strings.h                                |   11 +-
 lib/libc/amd64/string/strlcat.c                  |    2 +
 lib/libc/gen/ctermid.c                           |    5 +-
 lib/libc/gen/getdomainname.c                     |    3 +-
 lib/libc/gen/getentropy.c                        |    3 +-
 lib/libc/gen/getgrouplist.c                      |    3 +-
 lib/libc/gen/gethostname.c                       |    3 +-
 lib/libc/gen/getlogin.c                          |    3 +-
 lib/libc/gen/ttyname.c                           |    3 +-
 lib/libc/secure/Makefile.inc                     |    4 +-
 lib/libc/secure/Symbol.map                       |    2 +
 lib/libc/secure/strlcat_chk.c                    |   70 +
 lib/libc/secure/strlcpy_chk.c                    |   43 +
 lib/libc/stdio/fread.c                           |    4 +-
 lib/libc/stdio/gets_s.c                          |    3 +-
 lib/libc/stdio/tmpnam.c                          |    3 +-
 lib/libc/string/mempcpy.c                        |    4 +-
 lib/libc/string/strerror.c                       |    4 +-
 lib/libc/string/strlcat.c                        |    2 +
 lib/libc/string/strlcpy.c                        |    2 +
 lib/libc/string/strncpy.c                        |    2 +
 lib/libc/tests/secure/fortify_stdio_test.c       | 1024 +++++++++++++-
 lib/libc/tests/secure/fortify_string_test.c      |  478 +++++++
 lib/libc/tests/secure/fortify_strings_test.c     |  165 +++
 lib/libc/tests/secure/fortify_unistd_test.c      | 1592 +++++++++++++++++++++-
 lib/libc/tests/secure/generate-fortify-tests.lua |  271 ++++
 lib/libutil/tests/trimdomain-nodomain_test.c     |    4 +-
 lib/libutil/tests/trimdomain_test.c              |    4 +-
 sys/libkern/explicit_bzero.c                     |    3 +-
 sys/sys/libkern.h                                |    6 +
 37 files changed, 3745 insertions(+), 144 deletions(-)

diff --git a/include/ssp/ssp.h b/include/ssp/ssp.h
index de109da4959e..6ebc23288391 100644
--- a/include/ssp/ssp.h
+++ b/include/ssp/ssp.h
@@ -67,21 +67,25 @@
 #define __ssp_bos0(ptr) __builtin_object_size(ptr, 0)
 
 #define __ssp_check(buf, len, bos) \
-	if (bos(buf) != (size_t)-1 && len > bos(buf)) \
+	if (bos(buf) != (size_t)-1 && (size_t)len > bos(buf)) \
 		__chk_fail()
-#define __ssp_redirect_raw(rtype, fun, symbol, args, call, cond, bos) \
+
+#define __ssp_redirect_raw_impl(rtype, fun, symbol, args) \
 rtype __ssp_real_(fun) args __RENAME(symbol); \
 __ssp_inline rtype fun args __RENAME(__ssp_protected_ ## fun); \
-__ssp_inline rtype fun args { \
+__ssp_inline rtype fun args
+
+#define __ssp_redirect_raw(rtype, fun, symbol, args, call, cond, bos, len) \
+__ssp_redirect_raw_impl(rtype, fun, symbol, args) { \
 	if (cond) \
-		__ssp_check(__buf, __len, bos); \
+		__ssp_check(__buf, len, bos); \
 	return __ssp_real_(fun) call; \
 }
 
 #define __ssp_redirect(rtype, fun, args, call) \
-    __ssp_redirect_raw(rtype, fun, fun, args, call, 1, __ssp_bos)
+    __ssp_redirect_raw(rtype, fun, fun, args, call, 1, __ssp_bos, __len)
 #define __ssp_redirect0(rtype, fun, args, call) \
-    __ssp_redirect_raw(rtype, fun, fun, args, call, 1, __ssp_bos0)
+    __ssp_redirect_raw(rtype, fun, fun, args, call, 1, __ssp_bos0, __len)
 
 #include <machine/_stdint.h>
 
diff --git a/include/ssp/stdio.h b/include/ssp/stdio.h
index 4bca1de7d4f9..f7a390f315a4 100644
--- a/include/ssp/stdio.h
+++ b/include/ssp/stdio.h
@@ -37,6 +37,24 @@
 #include <ssp/ssp.h>
 
 __BEGIN_DECLS
+#if __SSP_FORTIFY_LEVEL > 0
+#if __POSIX_VISIBLE
+__ssp_redirect_raw(char *, ctermid, ctermid, (char *__buf), (__buf),
+    __buf != NULL, __ssp_bos, L_ctermid);
+#if __BSD_VISIBLE
+__ssp_redirect_raw(char *, ctermid_r, ctermid_r, (char *__buf), (__buf),
+    __buf != NULL, __ssp_bos, L_ctermid);
+#endif /* __BSD_VISIBLE */
+#endif /* __POSIX_VISIBLE */
+__ssp_redirect(size_t, fread, (void *__restrict __buf, size_t __len,
+    size_t __nmemb, FILE *__restrict __fp), (__buf, __len, __nmemb, __fp));
+__ssp_redirect(size_t, fread_unlocked, (void *__restrict __buf, size_t __len,
+    size_t __nmemb, FILE *__restrict __fp), (__buf, __len, __nmemb, __fp));
+__ssp_redirect(char *, gets_s, (char *__buf, rsize_t __len), (__buf, __len));
+__ssp_redirect_raw(char *, tmpnam, tmpnam, (char *__buf), (__buf), 1,
+    __ssp_bos, L_tmpnam);
+#endif
+
 int __sprintf_chk(char *__restrict, int, size_t, const char *__restrict, ...)
     __printflike(4, 5);
 int __vsprintf_chk(char *__restrict, int, size_t, const char *__restrict,
diff --git a/include/ssp/string.h b/include/ssp/string.h
index ceb4ba2a2174..b9f2dceb1df5 100644
--- a/include/ssp/string.h
+++ b/include/ssp/string.h
@@ -45,7 +45,9 @@ char *__stpncpy_chk(char *, const char *, size_t, size_t);
 char *__strcat_chk(char *, const char *, size_t);
 char *__strcpy_chk(char *, const char *, size_t);
 char *__strncat_chk(char *, const char *, size_t, size_t);
+size_t __strlcat_chk(char *, const char *, size_t, size_t);
 char *__strncpy_chk(char *, const char *, size_t, size_t);
+size_t __strlcpy_chk(char *, const char *, size_t, size_t);
 __END_DECLS
 
 #if __SSP_FORTIFY_LEVEL > 0
@@ -110,8 +112,24 @@ __ssp_bos_icheck2_restrict(stpcpy, char *, const char *)
 __ssp_bos_icheck3_restrict(stpncpy, char *, const char *)
 __ssp_bos_icheck2_restrict(strcpy, char *, const char *)
 __ssp_bos_icheck2_restrict(strcat, char *, const char *)
+__ssp_redirect0(int, strerror_r, (int __errnum, char *__buf, size_t __len),
+    (__errnum, __buf, __len));
 __ssp_bos_icheck3_restrict(strncpy, char *, const char *)
 __ssp_bos_icheck3_restrict(strncat, char *, const char *)
+
+__ssp_redirect_raw_impl(void *, mempcpy, mempcpy,
+    (void *__restrict buf, const void *__restrict src, size_t len))
+{
+	const size_t slen = __ssp_bos(buf);
+
+	if (len > slen)
+		__chk_fail();
+
+	if (__ssp_overlap(src, buf, len))
+		__chk_fail();
+
+	return (__ssp_real(mempcpy)(buf, src, len));
+}
 __END_DECLS
 
 #define memcpy(dst, src, len) __ssp_bos_check3(memcpy, dst, src, len)
@@ -122,7 +140,11 @@ __END_DECLS
 #define stpncpy(dst, src, len) __ssp_bos_check3(stpncpy, dst, src, len)
 #define strcpy(dst, src) __ssp_bos_check2(strcpy, dst, src)
 #define strcat(dst, src) __ssp_bos_check2(strcat, dst, src)
+#define strlcpy(dst, src, dstlen) \
+    __strlcpy_chk(dst, src, dstlen, __ssp_bos(dst))
 #define strncpy(dst, src, len) __ssp_bos_check3(strncpy, dst, src, len)
+#define strlcat(dst, src, dstlen) \
+    __strlcat_chk(dst, src, dstlen, __ssp_bos(dst))
 #define strncat(dst, src, len) __ssp_bos_check3(strncat, dst, src, len)
 
 #endif /* __SSP_FORTIFY_LEVEL > 0 */
diff --git a/include/ssp/strings.h b/include/ssp/strings.h
index 51b11a14ee87..79b70eba1c5c 100644
--- a/include/ssp/strings.h
+++ b/include/ssp/strings.h
@@ -63,5 +63,10 @@
 
 #define	bzero(dst, len)	_ssp_bzero(__ssp_var(dstv), dst, __ssp_var(lenv), len)
 
+__BEGIN_DECLS
+__ssp_redirect(void, explicit_bzero, (void *__buf, size_t __len),
+    (__buf, __len));
+__END_DECLS
+
 #endif /* __SSP_FORTIFY_LEVEL > 0 */
 #endif /* _SSP_STRINGS_H_ */
diff --git a/include/ssp/unistd.h b/include/ssp/unistd.h
index bcd3664116cc..7e9d72343dde 100644
--- a/include/ssp/unistd.h
+++ b/include/ssp/unistd.h
@@ -43,14 +43,46 @@ __BEGIN_DECLS
 #define	_FORTIFY_SOURCE_read	read
 #endif
 
-__ssp_redirect0(ssize_t, _FORTIFY_SOURCE_read, (int __fd, void *__buf,
+__ssp_inline size_t
+__ssp_gid_bos(const void *ptr)
+{
+	size_t ptrsize = __ssp_bos(ptr);
+
+	if (ptrsize == (size_t)-1)
+		return (ptrsize);
+
+	return (ptrsize / sizeof(gid_t));
+}
+
+__ssp_redirect_raw(int, getgrouplist, getgrouplist,
+    (const char *__name, gid_t __base, gid_t *__buf, int *__lenp),
+    (__name, __base, __buf, __lenp), 1, __ssp_gid_bos, *__lenp);
+
+__ssp_redirect_raw(int, getgroups, getgroups, (int __len, gid_t *__buf),
+    (__len, __buf), 1, __ssp_gid_bos, __len);
+
+__ssp_redirect(int, getloginclass, (char *__buf, size_t __len),
+    (__buf, __len));
+
+__ssp_redirect(ssize_t, _FORTIFY_SOURCE_read, (int __fd, void *__buf,
     size_t __len), (__fd, __buf, __len));
+__ssp_redirect(ssize_t, pread, (int __fd, void *__buf, size_t __len,
+    off_t __offset), (__fd, __buf, __len, __offset));
 
 __ssp_redirect(ssize_t, readlink, (const char *__restrict __path, \
     char *__restrict __buf, size_t __len), (__path, __buf, __len));
+__ssp_redirect(ssize_t, readlinkat, (int __fd, const char *__restrict __path,
+	char *__restrict __buf, size_t __len), (__fd, __path, __buf, __len));
 
 __ssp_redirect_raw(char *, getcwd, getcwd, (char *__buf, size_t __len),
-    (__buf, __len), __buf != 0, __ssp_bos);
+    (__buf, __len), __buf != 0, __ssp_bos, __len);
+
+__ssp_redirect(int, getdomainname, (char *__buf, int __len), (__buf, __len));
+__ssp_redirect(int, getentropy, (void *__buf, size_t __len), (__buf, __len));
+__ssp_redirect(int, gethostname, (char *__buf, size_t __len), (__buf, __len));
+__ssp_redirect(int, getlogin_r, (char *__buf, size_t __len), (__buf, __len));
+__ssp_redirect(int, ttyname_r, (int __fd, char *__buf, size_t __len),
+    (__fd, __buf, __len));
 
 __END_DECLS
 
diff --git a/include/stdio.h b/include/stdio.h
index ea53816cf1d4..b0190d25eb4f 100644
--- a/include/stdio.h
+++ b/include/stdio.h
@@ -238,6 +238,21 @@ __END_DECLS
 #define	stdout	__stdoutp
 #define	stderr	__stderrp
 
+/*
+ * Functions defined in all versions of POSIX 1003.1.
+ */
+#if __BSD_VISIBLE || (__POSIX_VISIBLE && __POSIX_VISIBLE <= 199506)
+#define	L_cuserid	17	/* size for cuserid(3); MAXLOGNAME, legacy */
+#endif
+
+#if __POSIX_VISIBLE
+#define	L_ctermid	1024	/* size for ctermid(3); PATH_MAX */
+#endif /* __POSIX_VISIBLE */
+
+#if !defined(_STANDALONE) && defined(_FORTIFY_SOURCE) && _FORTIFY_SOURCE > 0
+#include <ssp/stdio.h>
+#endif
+
 __BEGIN_DECLS
 #ifdef _XLOCALE_H_
 #include <xlocale/_stdio.h>
@@ -252,7 +267,7 @@ int	 ferror(FILE *);
 int	 fflush(FILE *);
 int	 fgetc(FILE *);
 int	 fgetpos(FILE * __restrict, fpos_t * __restrict);
-char	*fgets(char * __restrict, int, FILE * __restrict);
+char	*(fgets)(char * __restrict, int, FILE * __restrict);
 FILE	*fopen(const char * __restrict, const char * __restrict);
 int	 fprintf(FILE * __restrict, const char * __restrict, ...);
 int	 fputc(int, FILE *);
@@ -280,7 +295,7 @@ void	 rewind(FILE *);
 int	 scanf(const char * __restrict, ...);
 void	 setbuf(FILE * __restrict, char * __restrict);
 int	 setvbuf(FILE * __restrict, char * __restrict, int, size_t);
-int	 sprintf(char * __restrict, const char * __restrict, ...);
+int	 (sprintf)(char * __restrict, const char * __restrict, ...);
 int	 sscanf(const char * __restrict, const char * __restrict, ...);
 FILE	*tmpfile(void);
 char	*tmpnam(char *);
@@ -288,13 +303,13 @@ int	 ungetc(int, FILE *);
 int	 vfprintf(FILE * __restrict, const char * __restrict,
 	    __va_list);
 int	 vprintf(const char * __restrict, __va_list);
-int	 vsprintf(char * __restrict, const char * __restrict,
+int	 (vsprintf)(char * __restrict, const char * __restrict,
 	    __va_list);
 
 #if __ISO_C_VISIBLE >= 1999 || __POSIX_VISIBLE >= 199506
-int	 snprintf(char * __restrict, size_t, const char * __restrict,
+int	 (snprintf)(char * __restrict, size_t, const char * __restrict,
 	    ...) __printflike(3, 4);
-int	 vsnprintf(char * __restrict, size_t, const char * __restrict,
+int	 (vsnprintf)(char * __restrict, size_t, const char * __restrict,
 	    __va_list) __printflike(3, 0);
 #endif
 #if __ISO_C_VISIBLE >= 1999
@@ -305,16 +320,7 @@ int	 vsscanf(const char * __restrict, const char * __restrict, __va_list)
 	    __scanflike(2, 0);
 #endif
 
-/*
- * Functions defined in all versions of POSIX 1003.1.
- */
-#if __BSD_VISIBLE || (__POSIX_VISIBLE && __POSIX_VISIBLE <= 199506)
-#define	L_cuserid	17	/* size for cuserid(3); MAXLOGNAME, legacy */
-#endif
-
 #if __POSIX_VISIBLE
-#define	L_ctermid	1024	/* size for ctermid(3); PATH_MAX */
-
 char	*ctermid(char *);
 FILE	*fdopen(int, const char *);
 int	 fileno(FILE *);
@@ -530,7 +536,4 @@ extern int __isthreaded;
 __END_DECLS
 __NULLABILITY_PRAGMA_POP
 
-#if !defined(_STANDALONE) && defined(_FORTIFY_SOURCE) && _FORTIFY_SOURCE > 0
-#include <ssp/stdio.h>
-#endif
 #endif /* !_STDIO_H_ */
diff --git a/include/string.h b/include/string.h
index ce605117daa6..c9d3e1add1a1 100644
--- a/include/string.h
+++ b/include/string.h
@@ -49,6 +49,10 @@ typedef	__size_t	size_t;
 #define	_SIZE_T_DECLARED
 #endif
 
+#if !defined(_STANDALONE) && defined(_FORTIFY_SOURCE) && _FORTIFY_SOURCE > 0
+#include <ssp/string.h>
+#endif
+
 __BEGIN_DECLS
 #if __XSI_VISIBLE >= 600
 void	*memccpy(void * __restrict, const void * __restrict, int, size_t);
@@ -58,23 +62,23 @@ void	*memchr(const void *, int, size_t) __pure;
 void	*memrchr(const void *, int, size_t) __pure;
 #endif
 int	 memcmp(const void *, const void *, size_t) __pure;
-void	*memcpy(void * __restrict, const void * __restrict, size_t);
+void	*(memcpy)(void * __restrict, const void * __restrict, size_t);
 #if __BSD_VISIBLE
 void	*memmem(const void *, size_t, const void *, size_t) __pure;
 #endif
-void	*memmove(void *, const void *, size_t);
+void	*(memmove)(void *, const void *, size_t);
 #if __BSD_VISIBLE
 void	*mempcpy(void * __restrict, const void * __restrict, size_t);
 #endif
-void	*memset(void *, int, size_t);
+void	*(memset)(void *, int, size_t);
 #if __POSIX_VISIBLE >= 200809
-char	*stpcpy(char * __restrict, const char * __restrict);
-char	*stpncpy(char * __restrict, const char * __restrict, size_t);
+char	*(stpcpy)(char * __restrict, const char * __restrict);
+char	*(stpncpy)(char * __restrict, const char * __restrict, size_t);
 #endif
 #if __BSD_VISIBLE
 char	*strcasestr(const char *, const char *) __pure;
 #endif
-char	*strcat(char * __restrict, const char * __restrict);
+char	*(strcat)(char * __restrict, const char * __restrict);
 char	*strchr(const char *, int) __pure;
 #if __BSD_VISIBLE
 char	*strchrnul(const char*, int) __pure;
@@ -82,7 +86,7 @@ int	 strverscmp(const char *, const char *) __pure;
 #endif
 int	 strcmp(const char *, const char *) __pure;
 int	 strcoll(const char *, const char *);
-char	*strcpy(char * __restrict, const char * __restrict);
+char	*(strcpy)(char * __restrict, const char * __restrict);
 size_t	 strcspn(const char *, const char *) __pure;
 #if __POSIX_VISIBLE >= 200112 || __XSI_VISIBLE
 char	*strdup(const char *) __malloc_like;
@@ -92,8 +96,8 @@ char	*strerror(int);
 int	 strerror_r(int, char *, size_t);
 #endif
 #if __BSD_VISIBLE
-size_t	 strlcat(char * __restrict, const char * __restrict, size_t);
-size_t	 strlcpy(char * __restrict, const char * __restrict, size_t);
+size_t	 (strlcat)(char * __restrict, const char * __restrict, size_t);
+size_t	 (strlcpy)(char * __restrict, const char * __restrict, size_t);
 #endif
 size_t	 strlen(const char *) __pure;
 #if __BSD_VISIBLE
@@ -105,9 +109,9 @@ typedef	__mode_t	mode_t;
 
 void	 strmode(mode_t, char *);
 #endif
-char	*strncat(char * __restrict, const char * __restrict, size_t);
+char	*(strncat)(char * __restrict, const char * __restrict, size_t);
 int	 strncmp(const char *, const char *, size_t) __pure;
-char	*strncpy(char * __restrict, const char * __restrict, size_t);
+char	*(strncpy)(char * __restrict, const char * __restrict, size_t);
 #if __POSIX_VISIBLE >= 200809
 char	*strndup(const char *, size_t) __malloc_like;
 size_t	 strnlen(const char *, size_t) __pure;
@@ -168,7 +172,4 @@ errno_t memset_s(void *, rsize_t, int, rsize_t);
 #endif /* __EXT1_VISIBLE */
 __END_DECLS
 
-#if !defined(_STANDALONE) && defined(_FORTIFY_SOURCE) && _FORTIFY_SOURCE > 0
-#include <ssp/string.h>
-#endif
 #endif /* _STRING_H_ */
diff --git a/include/strings.h b/include/strings.h
index 511f7c03cb3c..889f43bd2311 100644
--- a/include/strings.h
+++ b/include/strings.h
@@ -37,11 +37,15 @@ typedef	__size_t	size_t;
 #define	_SIZE_T_DECLARED
 #endif
 
+#if !defined(_STANDALONE) && defined(_FORTIFY_SOURCE) && _FORTIFY_SOURCE > 0
+#include <ssp/strings.h>
+#endif
+
 __BEGIN_DECLS
 #if __BSD_VISIBLE || __POSIX_VISIBLE <= 200112
 int	 bcmp(const void *, const void *, size_t) __pure;	/* LEGACY */
-void	 bcopy(const void *, void *, size_t);			/* LEGACY */
-void	 bzero(void *, size_t);					/* LEGACY */
+void	 (bcopy)(const void *, void *, size_t);			/* LEGACY */
+void	 (bzero)(void *, size_t);				/* LEGACY */
 #endif
 #if __BSD_VISIBLE
 void	 explicit_bzero(void *, size_t);
@@ -68,7 +72,4 @@ int	 strncasecmp(const char *, const char *, size_t) __pure;
 #endif
 __END_DECLS
 
-#if !defined(_STANDALONE) && defined(_FORTIFY_SOURCE) && _FORTIFY_SOURCE > 0
-#include <ssp/strings.h>
-#endif
 #endif /* _STRINGS_H_ */
diff --git a/lib/libc/amd64/string/strlcat.c b/lib/libc/amd64/string/strlcat.c
index 0c1e1c5d05f7..94fdc0963dc3 100644
--- a/lib/libc/amd64/string/strlcat.c
+++ b/lib/libc/amd64/string/strlcat.c
@@ -8,6 +8,8 @@
 
 #include <string.h>
 
+#undef strlcat	/* FORTIFY_SOURCE */
+
 void *__memchr(const void *, int, size_t);
 size_t __strlcpy(char *restrict, const char *restrict, size_t);
 
diff --git a/lib/libc/gen/ctermid.c b/lib/libc/gen/ctermid.c
index 9265d402930c..fb117b3c8ded 100644
--- a/lib/libc/gen/ctermid.c
+++ b/lib/libc/gen/ctermid.c
@@ -34,11 +34,12 @@
 #include <paths.h>
 #include <stdio.h>
 #include <string.h>
+#include <ssp/ssp.h>
 
 #define	LEN_PATH_DEV	(sizeof(_PATH_DEV) - 1)
 
 char *
-ctermid(char *s)
+__ssp_real(ctermid)(char *s)
 {
 	static char def[sizeof(_PATH_DEV) + SPECNAMELEN];
 	struct stat sb;
@@ -62,7 +63,7 @@ ctermid(char *s)
 }
 
 char *
-ctermid_r(char *s)
+__ssp_real(ctermid_r)(char *s)
 {
 
 	return (s != NULL ? ctermid(s) : NULL);
diff --git a/lib/libc/gen/getdomainname.c b/lib/libc/gen/getdomainname.c
index a9527b36a247..c0be7465f967 100644
--- a/lib/libc/gen/getdomainname.c
+++ b/lib/libc/gen/getdomainname.c
@@ -33,9 +33,10 @@
 #include <sys/sysctl.h>
 
 #include <unistd.h>
+#include <ssp/ssp.h>
 
 int
-getdomainname(char *name, int namelen)
+__ssp_real(getdomainname)(char *name, int namelen)
 {
 	int mib[2];
 	size_t size;
diff --git a/lib/libc/gen/getentropy.c b/lib/libc/gen/getentropy.c
index 38cd515e74d7..40b84af65f83 100644
--- a/lib/libc/gen/getentropy.c
+++ b/lib/libc/gen/getentropy.c
@@ -35,6 +35,7 @@
 #include <stdbool.h>
 #include <stdlib.h>
 #include <unistd.h>
+#include <ssp/ssp.h>
 
 #include "libc_private.h"
 
@@ -105,7 +106,7 @@ getentropy_fallback(void *buf, size_t buflen)
 }
 
 int
-getentropy(void *buf, size_t buflen)
+__ssp_real(getentropy)(void *buf, size_t buflen)
 {
 	ssize_t rd;
 	bool have_getrandom;
diff --git a/lib/libc/gen/getgrouplist.c b/lib/libc/gen/getgrouplist.c
index 1c29b249f8c4..5bd06bc5121f 100644
--- a/lib/libc/gen/getgrouplist.c
+++ b/lib/libc/gen/getgrouplist.c
@@ -37,11 +37,12 @@
 #include <grp.h>
 #include <string.h>
 #include <unistd.h>
+#include <ssp/ssp.h>
 
 extern int __getgroupmembership(const char *, gid_t, gid_t *, int, int *);
 
 int
-getgrouplist(const char *uname, gid_t agroup, gid_t *groups, int *grpcnt)
+__ssp_real(getgrouplist)(const char *uname, gid_t agroup, gid_t *groups, int *grpcnt)
 {
 	return __getgroupmembership(uname, agroup, groups, *grpcnt, grpcnt);
 }
diff --git a/lib/libc/gen/gethostname.c b/lib/libc/gen/gethostname.c
index 36e988c91ecc..66d401fad846 100644
--- a/lib/libc/gen/gethostname.c
+++ b/lib/libc/gen/gethostname.c
@@ -34,9 +34,10 @@
 
 #include <errno.h>
 #include <unistd.h>
+#include <ssp/ssp.h>
 
 int
-gethostname(char *name, size_t namelen)
+__ssp_real(gethostname)(char *name, size_t namelen)
 {
 	int mib[2];
 
diff --git a/lib/libc/gen/getlogin.c b/lib/libc/gen/getlogin.c
index 55ed83795f7d..f8a3fb079067 100644
--- a/lib/libc/gen/getlogin.c
+++ b/lib/libc/gen/getlogin.c
@@ -37,6 +37,7 @@
 #include <unistd.h>
 #include "namespace.h"
 #include <pthread.h>
+#include <ssp/ssp.h>
 #include "un-namespace.h"
 
 #include "libc_private.h"
@@ -54,7 +55,7 @@ getlogin(void)
 }
 
 int
-getlogin_r(char *logname, size_t namelen)
+__ssp_real(getlogin_r)(char *logname, size_t namelen)
 {
 	char tmpname[MAXLOGNAME];
 	int	len;
diff --git a/lib/libc/gen/ttyname.c b/lib/libc/gen/ttyname.c
index 268b2e0f7b65..f1e2f401fe5d 100644
--- a/lib/libc/gen/ttyname.c
+++ b/lib/libc/gen/ttyname.c
@@ -42,6 +42,7 @@
 #include <paths.h>
 #include <errno.h>
 #include "reentrant.h"
+#include <ssp/ssp.h>
 #include "un-namespace.h"
 
 #include "libc_private.h"
@@ -53,7 +54,7 @@ static thread_key_t	ttyname_key;
 static int		ttyname_keycreated = 0;
 
 int
-ttyname_r(int fd, char *buf, size_t len)
+__ssp_real(ttyname_r)(int fd, char *buf, size_t len)
 {
 	size_t used;
 
diff --git a/lib/libc/secure/Makefile.inc b/lib/libc/secure/Makefile.inc
index 28289127c7a6..5d10612e67a8 100644
--- a/lib/libc/secure/Makefile.inc
+++ b/lib/libc/secure/Makefile.inc
@@ -6,8 +6,8 @@
 # _FORTIFY_SOURCE
 SRCS+=	fgets_chk.c memcpy_chk.c memmove_chk.c memset_chk.c \
 	snprintf_chk.c sprintf_chk.c stpcpy_chk.c stpncpy_chk.c \
-	strcat_chk.c strcpy_chk.c strncat_chk.c strncpy_chk.c \
-	vsnprintf_chk.c vsprintf_chk.c
+	strcat_chk.c strcpy_chk.c strlcat_chk.c strncat_chk.c strlcpy_chk.c \
+	strncpy_chk.c vsnprintf_chk.c vsprintf_chk.c
 
 CFLAGS.snprintf_chk.c+=	-Wno-unused-parameter
 CFLAGS.sprintf_chk.c+=	-Wno-unused-parameter
diff --git a/lib/libc/secure/Symbol.map b/lib/libc/secure/Symbol.map
index 0d854039955f..1f12fe059367 100644
--- a/lib/libc/secure/Symbol.map
+++ b/lib/libc/secure/Symbol.map
@@ -15,7 +15,9 @@ FBSD_1.8 {
 	__stpncpy_chk;
 	__strcat_chk;
 	__strcpy_chk;
+	__strlcat_chk;
 	__strncat_chk;
+	__strlcpy_chk;
 	__strncpy_chk;
 	__vsnprintf_chk;
 	__vsprintf_chk;
diff --git a/lib/libc/secure/strlcat_chk.c b/lib/libc/secure/strlcat_chk.c
new file mode 100644
index 000000000000..26448bd37af0
--- /dev/null
+++ b/lib/libc/secure/strlcat_chk.c
@@ -0,0 +1,70 @@
+/*	$OpenBSD: strlcat.c,v 1.15 2015/03/02 21:41:08 millert Exp $	*/
+
+/*
+ * SPDX-License-Identifier: ISC
+ *
+ * Copyright (c) 1998, 2015 Todd C. Miller <Todd.Miller@courtesan.com>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <stdio.h>
+#include <string.h>
+
+#include <ssp/string.h>
+
+/*
+ * Appends src to string dst of size dsize (unlike strncat, dsize is the
+ * full size of dst, not space left).  At most dsize-1 characters
+ * will be copied.  Always NUL terminates (unless dsize <= strlen(dst)).
+ * Returns strlen(src) + MIN(dsize, strlen(initial dst)).
+ * If retval >= dsize, truncation occurred.
+ */
+size_t
+__strlcat_chk(char * __restrict dst, const char * __restrict src, size_t dsize,
+    size_t dbufsize)
+{
+	const char *odst = dst;
+	const char *osrc = src;
+	size_t n = dsize;
+	size_t dlen;
+
+	if (dsize > dbufsize)
+		__chk_fail();
+
+	/* Find the end of dst and adjust bytes left but don't go past end. */
+	while (n-- != 0 && *dst != '\0') {
+		dst++;
+	}
+
+	dlen = dst - odst;
+	n = dsize - dlen;
+
+	if (n-- == 0)
+		return (dlen + strlen(src));
+	while (*src != '\0') {
+		if (n != 0) {
+			if (dbufsize-- == 0)
+				__chk_fail();
+			*dst++ = *src;
+			n--;
+		}
+
+		src++;
+	}
+
+	if (dbufsize-- == 0)
+		__chk_fail();
+	*dst = '\0';
+	return (dlen + (src - osrc));	/* count does not include NUL */
+}
diff --git a/lib/libc/secure/strlcpy_chk.c b/lib/libc/secure/strlcpy_chk.c
new file mode 100644
index 000000000000..8c11ee3e07eb
--- /dev/null
+++ b/lib/libc/secure/strlcpy_chk.c
@@ -0,0 +1,43 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2024, Klara, Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/types.h>
+#include <string.h>
+
+#include <ssp/string.h>
+#undef strlcpy
+
+size_t
+__strlcpy_chk(char * __restrict dst, const char * __restrict src, size_t dsize,
+    size_t dbufsize)
+{
+
+	if (dsize > dbufsize)
+		__chk_fail();
+
+	return (strlcpy(dst, src, dsize));
+}
diff --git a/lib/libc/stdio/fread.c b/lib/libc/stdio/fread.c
index bf943fdd1d0d..65d9ecf94366 100644
--- a/lib/libc/stdio/fread.c
+++ b/lib/libc/stdio/fread.c
@@ -37,6 +37,7 @@
 #include <stdint.h>
 #include <stdio.h>
 #include <string.h>
+#include <ssp/ssp.h>
 #include "un-namespace.h"
 #include "local.h"
 #include "libc_private.h"
@@ -46,7 +47,8 @@
  */
 
 size_t
-fread(void * __restrict buf, size_t size, size_t count, FILE * __restrict fp)
+__ssp_real(fread)(void * __restrict buf, size_t size, size_t count,
+    FILE * __restrict fp)
 {
 	size_t ret;
 
diff --git a/lib/libc/stdio/gets_s.c b/lib/libc/stdio/gets_s.c
index 9a8cf34916fb..41e379507483 100644
--- a/lib/libc/stdio/gets_s.c
+++ b/lib/libc/stdio/gets_s.c
@@ -39,6 +39,7 @@
 #include <unistd.h>
 #include <stdint.h>
 #include <stdio.h>
+#include <ssp/ssp.h>
 #include "un-namespace.h"
 #include "libc_private.h"
 #include "local.h"
@@ -77,7 +78,7 @@ _gets_s(char *buf, rsize_t n)
 
 /* ISO/IEC 9899:2011 K.3.7.4.1 */
 char *
-gets_s(char *buf, rsize_t n)
+__ssp_real(gets_s)(char *buf, rsize_t n)
 {
 	char *ret;
 	if (buf == NULL) {
diff --git a/lib/libc/stdio/tmpnam.c b/lib/libc/stdio/tmpnam.c
index d7c436928cd7..fab4253e2834 100644
--- a/lib/libc/stdio/tmpnam.c
+++ b/lib/libc/stdio/tmpnam.c
@@ -36,6 +36,7 @@
 
 #include <stdio.h>
 #include <unistd.h>
+#include <ssp/ssp.h>
 
 __warn_references(tmpnam,
     "warning: tmpnam() possibly used unsafely; consider using mkstemp()");
@@ -43,7 +44,7 @@ __warn_references(tmpnam,
 extern char *_mktemp(char *);
 
 char *
-tmpnam(char *s)
+__ssp_real(tmpnam)(char *s)
 {
 	static u_long tmpcount;
 	static char buf[L_tmpnam];
diff --git a/lib/libc/string/mempcpy.c b/lib/libc/string/mempcpy.c
index 619371632922..86e44cdebb85 100644
--- a/lib/libc/string/mempcpy.c
+++ b/lib/libc/string/mempcpy.c
@@ -29,9 +29,11 @@
  */
 
 #include <string.h>
+#include <ssp/ssp.h>
 
 void *
-mempcpy(void *__restrict dst, const void *__restrict src, size_t len)
+__ssp_real(mempcpy)(void *__restrict dst, const void *__restrict src,
+    size_t len)
 {
 	return ((char *)memcpy(dst, src, len) + len);
 }
diff --git a/lib/libc/string/strerror.c b/lib/libc/string/strerror.c
index ecad55caa673..922bb0284497 100644
--- a/lib/libc/string/strerror.c
+++ b/lib/libc/string/strerror.c
@@ -38,6 +38,8 @@
 #include <string.h>
 #include <stdio.h>
 
+#include <ssp/ssp.h>
+
 #include "errlst.h"
 #include "../locale/xlocale_private.h"
 #include "libc_private.h"
@@ -114,7 +116,7 @@ __strerror_rl(int errnum, char *strerrbuf, size_t buflen, locale_t locale)
 }
 
 int
-strerror_r(int errnum, char *strerrbuf, size_t buflen)
+__ssp_real(strerror_r)(int errnum, char *strerrbuf, size_t buflen)
 {
 	return (__strerror_rl(errnum, strerrbuf, buflen, __get_locale()));
 }
diff --git a/lib/libc/string/strlcat.c b/lib/libc/string/strlcat.c
index bdb302def7b0..fc18fad179db 100644
--- a/lib/libc/string/strlcat.c
+++ b/lib/libc/string/strlcat.c
@@ -19,6 +19,8 @@
 #include <sys/types.h>
 #include <string.h>
 
+#undef strlcat	/* FORTIFY_SOURCE */
+
 /*
  * Appends src to string dst of size dsize (unlike strncat, dsize is the
  * full size of dst, not space left).  At most dsize-1 characters
diff --git a/lib/libc/string/strlcpy.c b/lib/libc/string/strlcpy.c
index 58a42e321f6a..79f7ab19cdfd 100644
--- a/lib/libc/string/strlcpy.c
+++ b/lib/libc/string/strlcpy.c
@@ -19,6 +19,8 @@
 #include <sys/types.h>
 #include <string.h>
 
+#undef strlcpy	/* FORTIFY_SOURCE */
+
 /*
  * Copy string src to buffer dst of size dsize.  At most dsize-1
  * chars will be copied.  Always NUL terminates (unless dsize == 0).
diff --git a/lib/libc/string/strncpy.c b/lib/libc/string/strncpy.c
index b1df82a2dbf8..67240a855196 100644
--- a/lib/libc/string/strncpy.c
+++ b/lib/libc/string/strncpy.c
@@ -34,6 +34,8 @@
 
 #include <string.h>
 
+#undef strncpy	/* FORTIFY_SOURCE */
+
 /*
  * Copy src to dst, truncating or null-padding to always copy n bytes.
  * Return dst.
diff --git a/lib/libc/tests/secure/fortify_stdio_test.c b/lib/libc/tests/secure/fortify_stdio_test.c
index 20ecdab89a8b..fe0f14acd988 100644
--- a/lib/libc/tests/secure/fortify_stdio_test.c
+++ b/lib/libc/tests/secure/fortify_stdio_test.c
@@ -20,6 +20,23 @@
 #include <unistd.h>
 #include <atf-c.h>
 
+static FILE * __unused
+new_fp(size_t __len)
+{
+	static char fpbuf[LINE_MAX];
+	FILE *fp;
+
+	ATF_REQUIRE(__len <= sizeof(fpbuf));
+
+	memset(fpbuf, 'A', sizeof(fpbuf) - 1);
+	fpbuf[sizeof(fpbuf) - 1] = '\0';
+
+	fp = fmemopen(fpbuf, sizeof(fpbuf), "rb");
+	ATF_REQUIRE(fp != NULL);
+
+	return (fp);
+}
+
 /*
  * Create a new symlink to use for readlink(2) style tests, we'll just use a
  * random target name to have something interesting to look at.
@@ -55,28 +72,700 @@ new_tmpfile(void)
 	size_t written;
 	int fd;
 
-	fd = open("tmpfile", O_RDWR | O_CREAT | O_TRUNC, 0644);
-	ATF_REQUIRE(fd >= 0);
+	fd = open("tmpfile", O_RDWR | O_CREAT | O_TRUNC, 0644);
+	ATF_REQUIRE(fd >= 0);
+
+	written = 0;
+	while (written < TMPFILE_SIZE) {
+		rv = write(fd, buf, sizeof(buf));
+		ATF_REQUIRE(rv > 0);
+
+		written += rv;
+	}
+
+	ATF_REQUIRE_EQ(0, lseek(fd, 0, SEEK_SET));
+	return (fd);
+}
+
+static void
+disable_coredumps(void)
+{
+	struct rlimit rl = { 0 };
+
+	if (setrlimit(RLIMIT_CORE, &rl) == -1)
+		_exit(EX_OSERR);
+}
+
+/*
+ * Replaces stdin with a file that we can actually read from, for tests where
+ * we want a FILE * or fd that we can get data from.
+ */
+static void __unused
+replace_stdin(void)
+{
+	int fd;
+
+	fd = new_tmpfile();
+
+	(void)dup2(fd, STDIN_FILENO);
+	if (fd != STDIN_FILENO)
+		close(fd);
+}
+
+ATF_TC_WITHOUT_HEAD(ctermid_before_end);
+ATF_TC_BODY(ctermid_before_end, tc)
+{
+#define BUF &__stack.__buf
+	struct {
+		uint8_t padding_l;
+		unsigned char __buf[L_ctermid + 1];
+		uint8_t padding_r;
+	} __stack;
*** 3922 LINES SKIPPED ***