diff --git a/Kconfig b/Kconfig index fc271e583e..c524fcb5e5 100644 --- a/Kconfig +++ b/Kconfig @@ -597,6 +597,18 @@ endmenu # Customize Header Files menu "Debug Options" +config FORTIFY_SOURCE + int "Fortify Source" + default 0 + range 0 3 + ---help--- + Detect overflows of buffers in common string and memory functions + where the compiler can determine and validate the buffer sizes. + 0 does not have any checks. + 1 will only check for out-of-bounds at compile time. + 2 will only perform out-of-bounds checks on stack variables. + 3 On the basis of 2, add an out-of-bounds check for dynamically allocated variables. + config NDEBUG bool "Define NDEBUG globally" default !DEBUG_ASSERTIONS diff --git a/include/nuttx/compiler.h b/include/nuttx/compiler.h index e4a89b888e..feef1a0956 100644 --- a/include/nuttx/compiler.h +++ b/include/nuttx/compiler.h @@ -87,6 +87,38 @@ # define CONFIG_HAVE_BUILTIN_FFSLL 1 # endif +# if CONFIG_FORTIFY_SOURCE > 0 +# if !defined(__OPTIMIZE__) || (__OPTIMIZE__) <= 0 +# warning requires compiling with optimization (-O2 or higher) +# endif +# if CONFIG_FORTIFY_SOURCE == 3 +# if __GNUC__ < 12 || (defined(__clang__) && __clang_major__ < 12) +# error compiler version less than 12 does not support dynamic object size +# endif + +# define fortify_size(__o, type) __builtin_dynamic_object_size(__o, type) +# else +# define fortify_size(__o, type) __builtin_object_size(__o, type) +# endif + +# define fortify_assert(condition) do \ + { \ + if (!(condition)) \ + { \ + __builtin_trap(); \ + } \ + } \ + while (0) + +# define fortify_va_arg_pack __builtin_va_arg_pack +# define fortify_str(s) #s +# define fortify_real(p,fn) __typeof__(fn) __real_##fn __asm__(fortify_str(p) #fn) +# define fortify_function(fn) fortify_real(__USER_LABEL_PREFIX__, fn); \ + extern __inline__ \ + __attribute__((__always_inline__, \ + __gnu_inline__, __artificial__)) +# endif + /* Pre-processor */ # define CONFIG_CPP_HAVE_VARARGS 1 /* Supports variable argument macros */ diff --git a/include/stdio.h b/include/stdio.h index 655c5c560c..9f30124de8 100644 --- a/include/stdio.h +++ b/include/stdio.h @@ -244,6 +244,67 @@ int pclose(FILE *stream); FILE *popen(FAR const char *command, FAR const char *mode) popen_like; #endif +#if CONFIG_FORTIFY_SOURCE > 0 +fortify_function(fgets) FAR char *fgets(FAR char *s, int n, FAR FILE *stream) +{ + fortify_assert((size_t)n <= fortify_size(s, 0)); + return __real_fgets(s, n, stream); +} + +fortify_function(fread) size_t fread(FAR void *ptr, size_t size, + size_t n_items, FAR FILE *stream) +{ + fortify_assert(n_items == 0 || (n_items * size) / n_items == size); + fortify_assert(n_items * size <= fortify_size(ptr, 0)); + return __real_fread(ptr, size, n_items, stream); +} + +fortify_function(fwrite) size_t fwrite(FAR const void *ptr, size_t size, + size_t n_items, FAR FILE *stream) +{ + fortify_assert(n_items == 0 || (n_items * size) / n_items == size); + fortify_assert(n_items * size <= fortify_size(ptr, 0)); + return __real_fwrite(ptr, size, n_items, stream); +} + +fortify_function(vsnprintf) int vsnprintf(FAR char *buf, size_t size, + FAR const IPTR char *format, + va_list ap) +{ + fortify_assert(size <= fortify_size(buf, 0)); + return __real_vsnprintf(buf, size, format, ap); +} + +fortify_function(vsprintf) int vsprintf(FAR char *dest, + FAR const IPTR char *src, + va_list ap) +{ + int ret; + + ret = __real_vsprintf(dest, src, ap); + fortify_assert(ret == -1 || (size_t)ret <= fortify_size(dest, 0)); + return ret; +} + +fortify_function(snprintf) int snprintf(FAR char *buf, size_t size, + FAR const IPTR char *format, ...) +{ + fortify_assert(size <= fortify_size(buf, 0)); + return __real_snprintf(buf, size, format, fortify_va_arg_pack()); +} + +fortify_function(sprintf) int sprintf(FAR char *buf, + FAR const IPTR char *fmt, + ...) +{ + int ret; + + ret = __real_sprintf(buf, fmt, fortify_va_arg_pack()); + fortify_assert(ret == -1 || (size_t)ret <= fortify_size(buf, 0)); + return ret; +} +#endif + #undef EXTERN #if defined(__cplusplus) } diff --git a/include/stdlib.h b/include/stdlib.h index cd8f04e998..f186dc5245 100644 --- a/include/stdlib.h +++ b/include/stdlib.h @@ -291,6 +291,29 @@ FAR const char *getprogname(void); int __cxa_atexit(CODE void (*func)(FAR void *), FAR void *arg, FAR void *dso_handle); +#if CONFIG_FORTIFY_SOURCE > 0 +fortify_function(realpath) FAR char *realpath(FAR const char *path, + FAR char *resolved) +{ + FAR char *ret = __real_realpath(path, resolved); + if (ret != NULL && resolved != NULL) + { + size_t len = 1; + FAR char *p; + + p = ret; + while (*p++ != '\0') + { + len++; + } + + fortify_assert(len <= fortify_size(resolved, 0)); + } + + return ret; +} +#endif + #undef EXTERN #if defined(__cplusplus) } diff --git a/include/string.h b/include/string.h index 0e537b014c..41ad7dc40b 100644 --- a/include/string.h +++ b/include/string.h @@ -108,6 +108,107 @@ FAR void *memmem(FAR const void *haystack, size_t haystacklen, void explicit_bzero(FAR void *s, size_t n); int timingsafe_bcmp(FAR const void *b1, FAR const void *b2, size_t n); +#if CONFIG_FORTIFY_SOURCE > 0 +fortify_function(strcat) FAR char *strcat(FAR char *dest, + FAR const char *src) +{ + fortify_assert(strlen(dest) + strlen(src) + 1 <= fortify_size(dest, 0)); + return __real_strcat(dest, src); +} + +fortify_function(strlcat) size_t strlcat(FAR char *dst, + FAR const char *src, + size_t dsize) +{ + fortify_assert(dsize <= fortify_size(dst, 0)); + return __real_strlcat(dst, src, dsize); +} + +fortify_function(strncat) FAR char *strncat(FAR char *dest, + FAR const char *src, + size_t n) +{ + fortify_assert(n <= fortify_size(dest, 0)); + return __real_strncat(dest, src, n); +} + +fortify_function(strcpy) FAR char *strcpy(FAR char *dest, + FAR const char *src) +{ + fortify_assert(strlen(src) + 1 <= fortify_size(dest, 0)); + return __real_strcpy(dest, src); +} + +fortify_function(stpcpy) FAR char *stpcpy(FAR char *dest, + FAR const char *src) +{ + fortify_assert(strlen(src) + 1 <= fortify_size(dest, 0)); + return __real_stpcpy(dest, src); +} + +fortify_function(stpncpy) FAR char *stpncpy(FAR char *dest, + FAR const char *src, + size_t n) +{ + fortify_assert(n <= fortify_size(dest, 0)); + return __real_stpncpy(dest, src, n); +} + +fortify_function(strlcpy) size_t strlcpy(FAR char *dst, + FAR const char *src, + size_t siz) +{ + fortify_assert(siz <= fortify_size(dst, 0)); + return __real_strlcpy(dst, src, siz); +} + +fortify_function(strncpy) FAR char *strncpy(FAR char *dest, + FAR const char *src, + size_t n) +{ + fortify_assert(n <= fortify_size(dest, 0)); + return __real_strncpy(dest, src, n); +} + +fortify_function(memmove) FAR void *memmove(FAR void *dest, + FAR const void *src, + size_t count) +{ + fortify_assert(count <= fortify_size(dest, 0) && + count <= fortify_size(src, 0)); + return __real_memmove(dest, src, count); +} + +fortify_function(memcpy) FAR void *memcpy(FAR void *dest, + FAR const void *src, + size_t n) +{ + fortify_assert(n <= fortify_size(dest, 0) && n <= fortify_size(src, 0)); + return __real_memcpy(dest, src, n); +} + +fortify_function(memset) FAR void *memset(FAR void *s, int c, size_t n) +{ + fortify_assert(n <= fortify_size(s, 0)); + return __real_memset(s, c, n); +} + +fortify_function(mempcpy) FAR void *mempcpy(FAR void *dest, + FAR const void *src, + size_t n) +{ + fortify_assert(n <= fortify_size(dest, 0) && n <= fortify_size(src, 0)); + return __real_mempcpy(dest, src, n); +} + +fortify_function(explicit_bzero) void explicit_bzero(FAR void *s, + size_t n) +{ + fortify_assert(n <= fortify_size(s, 0)); + __real_explicit_bzero(s, n); +} +#endif + #undef EXTERN #if defined(__cplusplus) } diff --git a/include/strings.h b/include/strings.h index 96d198cb72..ce4ae612a5 100644 --- a/include/strings.h +++ b/include/strings.h @@ -93,6 +93,14 @@ int strncasecmp(FAR const char *, FAR const char *, size_t); void bzero(FAR void *s, size_t n); +#if CONFIG_FORTIFY_SOURCE > 0 +fortify_function(bzero) void bzero(FAR void *s, size_t n) +{ + fortify_assert(n <= fortify_size(s, 0)); + return bzero(s, n); +} +#endif + #undef EXTERN #if defined(__cplusplus) } diff --git a/include/sys/poll.h b/include/sys/poll.h index 08b936aff6..94904ffd50 100644 --- a/include/sys/poll.h +++ b/include/sys/poll.h @@ -149,6 +149,23 @@ int poll_fdsetup(int fd, FAR struct pollfd *fds, bool setup); void poll_default_cb(FAR struct pollfd *fds); void poll_notify(FAR struct pollfd **afds, int nfds, pollevent_t eventset); +#if CONFIG_FORTIFY_SOURCE > 0 +fortify_function(poll) int poll(FAR struct pollfd *fds, + nfds_t nfds, int timeout) +{ + fortify_assert(nfds <= fortify_size(fds, 0) / sizeof(struct pollfd)); + return __real_poll(fds, nfds, timeout); +} + +fortify_function(ppoll) int ppoll(FAR struct pollfd *fds, nfds_t nfds, + FAR const struct timespec *timeout_ts, + FAR const sigset_t *sigmask) +{ + fortify_assert(nfds <= fortify_size(fds, 0) / sizeof(struct pollfd)); + return __real_ppoll(fds, nfds, timeout_ts, sigmask); +} +#endif + #undef EXTERN #if defined(__cplusplus) } diff --git a/include/sys/socket.h b/include/sys/socket.h index d411cbff46..05b13bdaaf 100644 --- a/include/sys/socket.h +++ b/include/sys/socket.h @@ -435,6 +435,40 @@ int getpeername(int sockfd, FAR struct sockaddr *addr, ssize_t recvmsg(int sockfd, FAR struct msghdr *msg, int flags); ssize_t sendmsg(int sockfd, FAR struct msghdr *msg, int flags); +#if CONFIG_FORTIFY_SOURCE > 0 +fortify_function(send) ssize_t send(int sockfd, FAR const void *buf, + size_t len, int flags) +{ + fortify_assert(len <= fortify_size(buf, 0)); + return __real_send(sockfd, buf, len, flags); +} + +fortify_function(sendto) ssize_t sendto(int sockfd, FAR const void *buf, + size_t len, int flags, + FAR const struct sockaddr *to, + socklen_t tolen) +{ + fortify_assert(len <= fortify_size(buf, 0)); + return __real_sendto(sockfd, buf, len, flags, to, tolen); +} + +fortify_function(recv) ssize_t recv(int sockfd, FAR void *buf, + size_t len, int flags) +{ + fortify_assert(len <= fortify_size(buf, 0)); + return __real_recv(sockfd, buf, len, flags); +} + +fortify_function(recvfrom) ssize_t recvfrom(int sockfd, FAR void *buf, + size_t len, int flags, + FAR struct sockaddr *from, + FAR socklen_t *fromlen) +{ + fortify_assert(len <= fortify_size(buf, 0)); + return __real_recvfrom(sockfd, buf, len, flags, from, fromlen); +} +#endif + #undef EXTERN #if defined(__cplusplus) } diff --git a/include/unistd.h b/include/unistd.h index c7b98e043d..dca646bb5c 100644 --- a/include/unistd.h +++ b/include/unistd.h @@ -434,6 +434,74 @@ int getentropy(FAR void *buffer, size_t length); void sync(void); +#if CONFIG_FORTIFY_SOURCE > 0 +fortify_function(getcwd) FAR char *getcwd(FAR char *buf, + size_t size) +{ + fortify_assert(size <= fortify_size(buf, 0)); + return __real_getcwd(buf, size); +} + +fortify_function(gethostname) int gethostname(FAR char *name, + size_t namelen) +{ + fortify_assert(namelen <= fortify_size(name, 0)); + return __real_gethostname(name, namelen); +} + +fortify_function(pread) ssize_t pread(int fd, FAR void *buf, + size_t nbytes, off_t offset) +{ + fortify_assert(nbytes <= fortify_size(buf, 0)); + return __real_pread(fd, buf, nbytes, offset); +} + +fortify_function(read) ssize_t read(int fd, FAR void *buf, + size_t nbytes) +{ + fortify_assert(nbytes <= fortify_size(buf, 0)); + return __real_read(fd, buf, nbytes); +} + +fortify_function(readlink) ssize_t readlink(FAR const char *path, + FAR char *buf, + size_t bufsize) +{ + fortify_assert(bufsize <= fortify_size(buf, 0)); + return __real_readlink(path, buf, bufsize); +} + +fortify_function(readlinkat) ssize_t readlinkat(int dirfd, + FAR const char *path, + FAR char *buf, + size_t bufsize) +{ + fortify_assert(bufsize <= fortify_size(buf, 0)); + return __real_readlinkat(dirfd, path, buf, bufsize); +} + +fortify_function(ttyname_r) int ttyname_r(int fd, FAR char *buf, + size_t buflen) +{ + fortify_assert(buflen <= fortify_size(buf, 0)); + return __real_ttyname_r(fd, buf, buflen); +} + +fortify_function(pwrite) ssize_t pwrite(int fd, FAR const void *buf, + size_t nbytes, off_t offset) +{ + fortify_assert(nbytes <= fortify_size(buf, 0)); + return __real_pwrite(fd, buf, nbytes, offset); +} + +fortify_function(write) ssize_t write(int fd, FAR const void *buf, + size_t nbytes) +{ + fortify_assert(nbytes <= fortify_size(buf, 0)); + return __real_write(fd, buf, nbytes); +} +#endif + #undef EXTERN #if defined(__cplusplus) } diff --git a/include/wchar.h b/include/wchar.h index 51b5415468..ab2ee10bf8 100644 --- a/include/wchar.h +++ b/include/wchar.h @@ -218,6 +218,133 @@ FAR wchar_t *wmemset(FAR wchar_t *, wchar_t, size_t); int wprintf(FAR const wchar_t *, ...); int wscanf(FAR const wchar_t *, ...); +#if CONFIG_FORTIFY_SOURCE > 0 +fortify_function(fgetws) FAR wchar_t *fgetws(FAR wchar_t *s, int n, + FAR FILE *stream) +{ + fortify_assert((size_t)n <= fortify_size(s, 0) / sizeof(wchar_t)); + return __real_fgetws(s, n, stream); +} + +fortify_function(mbsnrtowcs) size_t mbsnrtowcs(FAR wchar_t *dst, + FAR const char **src, + size_t nms, + size_t len, + FAR mbstate_t *ps) +{ + fortify_assert(len <= fortify_size(dst, 0) / sizeof(wchar_t)); + return __real_mbsnrtowcs(dst, src, nms, len, ps); +} + +fortify_function(mbsrtowcs) size_t mbsrtowcs(FAR wchar_t *dst, + FAR const char **src, + size_t len, + FAR mbstate_t *ps) +{ + fortify_assert(len <= fortify_size(dst, 0) / sizeof(wchar_t)); + return __real_mbsrtowcs(dst, src, len, ps); +} + +fortify_function(wcrtomb) size_t wcrtomb(FAR char *s, wchar_t wc, + FAR mbstate_t *ps) +{ + size_t ret = __real_wcrtomb(s, wc, ps); + + fortify_assert(s == NULL || ret <= fortify_size(s, 0)); + return ret; +} + +fortify_function(wcscpy) FAR wchar_t *wcscpy(FAR wchar_t *dst, + FAR const wchar_t *src) +{ + fortify_assert(wcslen(src) + 1 <= fortify_size(dst, 0) / sizeof(wchar_t)); + return __real_wcscpy(dst, src); +} + +fortify_function(wcslcpy) size_t wcslcpy(FAR wchar_t *dst, + FAR const wchar_t *src, + size_t siz) +{ + fortify_assert(siz <= fortify_size(dst, 0) / sizeof(wchar_t)); + return __real_wcslcpy(dst, src, siz); +} + +fortify_function(wcscat) FAR wchar_t *wcscat(FAR wchar_t *dst, + FAR const wchar_t *src) +{ + fortify_assert(wcslen(dst) + wcslen(src) + 1 <= + fortify_size(dst, 0) / sizeof(wchar_t)); + return __real_wcscat(dst, src); +} + +fortify_function(wcsncat) FAR wchar_t *wcsncat(FAR wchar_t *dst, + FAR const wchar_t *src, + size_t siz) +{ + fortify_assert(siz <= fortify_size(dst, 0) / sizeof(wchar_t)); + return __real_wcsncat(dst, src, siz); +} + +fortify_function(wcslcat) size_t wcslcat(FAR wchar_t *dst, + FAR const wchar_t *src, + size_t siz) +{ + fortify_assert(siz <= fortify_size(dst, 0) / sizeof(wchar_t)); + return __real_wcslcat(dst, src, siz); +} + +fortify_function(wcsncpy) FAR wchar_t *wcsncpy(FAR wchar_t *dst, + FAR const wchar_t *src, + size_t siz) +{ + fortify_assert(siz <= fortify_size(dst, 0) / sizeof(wchar_t)); + return __real_wcsncpy(dst, src, siz); +} + +fortify_function(wcsnrtombs) size_t wcsnrtombs(FAR char *dst, + FAR const wchar_t **src, + size_t nwc, size_t len, + FAR mbstate_t *ps) +{ + fortify_assert(len <= fortify_size(dst, 0)); + return __real_wcsnrtombs(dst, src, nwc, len, ps); +} + +fortify_function(wcsrtombs) size_t wcsrtombs(FAR char *dst, + FAR const wchar_t **src, + size_t len, + FAR mbstate_t *ps) +{ + fortify_assert(len <= fortify_size(dst, 0)); + return __real_wcsrtombs(dst, src, len, ps); +} + +fortify_function(wmemcpy) FAR wchar_t *wmemcpy(FAR wchar_t *d, + FAR const wchar_t *s, + size_t n) +{ + fortify_assert(n <= fortify_size(d, 0) / sizeof(wchar_t)); + return __real_wmemcpy(d, s, n); +} + +fortify_function(wmemmove) FAR wchar_t *wmemmove(FAR wchar_t *d, + FAR const wchar_t *s, + size_t n) +{ + fortify_assert(n <= fortify_size(d, 0) / sizeof(wchar_t)); + return __real_wmemmove(d, s, n); +} + +fortify_function(wmemset) FAR wchar_t *wmemset(FAR wchar_t *s, + wchar_t c, + size_t n) +{ + fortify_assert(n <= fortify_size(s, 0) / sizeof(wchar_t)); + return __real_wmemset(s, c, n); +} + +#endif + #undef EXTERN #ifdef __cplusplus }