From 86c39754f215ec00186a649c09cf0b2eea21c872 Mon Sep 17 00:00:00 2001 From: Glenn Strauss Date: Fri, 21 May 2021 22:22:45 -0400 Subject: [PATCH] [core] ck.[ch] - C11 Annex K wrappers (selected functions; not complete) (import from one of my development branches from 2016) define safe_memclear() -> ck_memzero() for transition --- SConstruct | 1 + configure.ac | 1 + src/CMakeLists.txt | 3 +- src/Makefile.am | 2 +- src/SConscript | 2 +- src/ck.c | 256 ++++++++++++++++++++++++++++++++++++++++++++ src/ck.h | 49 +++++++++ src/config.h.cmake | 1 + src/meson.build | 3 +- src/safe_memclear.c | 65 ----------- src/safe_memclear.h | 3 +- 11 files changed, 316 insertions(+), 70 deletions(-) create mode 100644 src/ck.c create mode 100644 src/ck.h delete mode 100644 src/safe_memclear.c diff --git a/SConstruct b/SConstruct index fd5a325d..f3252825 100644 --- a/SConstruct +++ b/SConstruct @@ -450,6 +450,7 @@ if 1: 'strchr', 'strdup', 'strerror', + 'strerror_r', 'strftime', 'strstr', 'strtol', diff --git a/configure.ac b/configure.ac index 9bacd1b3..f7bf8626 100644 --- a/configure.ac +++ b/configure.ac @@ -1516,6 +1516,7 @@ AC_CHECK_FUNCS([\ sigaction \ signal \ srandom \ + strerror_r \ timegm \ writev \ ]) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index b240b26c..f0356646 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -192,6 +192,7 @@ check_function_exists(sigaction HAVE_SIGACTION) check_function_exists(signal HAVE_SIGNAL) check_function_exists(sigtimedwait HAVE_SIGTIMEDWAIT) check_function_exists(srandom HAVE_SRANDOM) +check_function_exists(strerror_r HAVE_STRERROR_R) check_function_exists(strptime HAVE_STRPTIME) check_function_exists(syslog HAVE_SYSLOG) check_function_exists(writev HAVE_WRITEV) @@ -767,7 +768,7 @@ set(COMMON_SRC request.c sock_addr.c rand.c - safe_memclear.c + ck.c ) if(WIN32) diff --git a/src/Makefile.am b/src/Makefile.am index 3ae76bcd..4fb228fe 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -88,7 +88,7 @@ common_src=base64.c buffer.c burl.c log.c \ rand.c \ request.c \ sock_addr.c \ - safe_memclear.c + ck.c src = server.c response.c connections.c h2.c reqpool.c \ sock_addr_cache.c \ diff --git a/src/SConscript b/src/SConscript index 3efe41d4..ce0f13b4 100644 --- a/src/SConscript +++ b/src/SConscript @@ -74,7 +74,7 @@ common_src = Split("base64.c buffer.c burl.c log.c \ request.c \ sock_addr.c \ rand.c \ - safe_memclear.c \ + ck.c \ ") src = Split("server.c response.c connections.c h2.c reqpool.c \ diff --git a/src/ck.c b/src/ck.c new file mode 100644 index 00000000..5bdc4b01 --- /dev/null +++ b/src/ck.c @@ -0,0 +1,256 @@ +/* + * ck - C11 Annex K wrappers (selected functions; not complete) + * + * ck is also an abbreviation for "check". + * These are validating, checking functions. + * + * Copyright(c) 2016 Glenn Strauss gstrauss()gluelogic.com All rights reserved + * License: BSD 3-clause (same as lighttpd) + */ +#ifndef __STDC_WANT_LIB_EXT1__ +#define __STDC_WANT_LIB_EXT1__ 1 +#endif +#ifndef _XOPEN_SOURCE +#define _XOPEN_SOURCE 700 +#endif +#ifndef _NETBSD_SOURCE +#define _NETBSD_SOURCE +#endif +#include "first.h" + +#include "ck.h" + +#include /* getenv() getenv_s() */ +#include /* memcpy() memset() memset_s() explicit_bzero() + * strerror() strerror_r() strerror_s() strlen() */ + +#ifdef __STDC_LIB_EXT1__ +#ifndef HAVE_MEMSET_S +#define HAVE_MEMSET_S +#endif +#else +#include +#include /* snprintf() */ +#endif + +#ifndef HAVE_MEMSET_S + +#ifdef _WIN32 +#define VC_EXTRALEAN +#define WIN32_LEAN_AND_MEAN +#include /* SecureZeroMemory() */ +/*(Windows XP and later provide SecureZeroMemory())*/ +#define HAVE_SECUREZEROMEMORY +#else /* !_WIN32 */ +#ifdef HAVE_SIGNAL +#include /* sig_atomic_t */ +#else +typedef int sig_atomic_t; +#endif +/*#include */ /* plasma_membar_ccfence() */ +#endif + +#endif /* !HAVE_MEMSET_S */ + + +#if !defined(HAVE_MEMSET_S) \ + && !defined(HAVE_EXPLICIT_BZERO) \ + && !defined(HAVE_EXPLICIT_MEMSET) \ + && !defined(HAVE_SECUREZEROMEMORY) + +typedef void *(*ck_memclear_func_t)(void *, int, size_t); +extern volatile ck_memclear_func_t ck_memclear_func; +volatile ck_memclear_func_t ck_memclear_func = memset; + +#ifdef HAVE_WEAK_SYMBOLS +/* it seems weak functions are never inlined, even for static builds */ +__attribute__((__weak__)) +void ck_memclear_s_hook (void *buf, rsize_t len); +void ck_memclear_s_hook (void *buf __attribute_unused__, + rsize_t len __attribute_unused__) +{ + /*(application might define func to call OPENSSL_cleanse(), if available)*/ + (void)(buf); /* UNUSED */ + (void)(len); /* UNUSED */ +} +#endif /* HAVE_WEAK_SYMBOLS */ + +static void * +ck_memset_compat(void *s, int c, size_t n) +{ + /* attempt to inhibit compiler/linker heuristics which might elide memset() + * - insert compiler optimization fences around memset() + * - access s through volatile pointer at volatile index after memset() + * - pass s to weak (overridable) func to create additional data dependency + */ + + if (0 == n) /*(must check n > 0 since s[0] will be accessed)*/ + return s; + + static volatile sig_atomic_t vzero; + volatile unsigned char *vs = (volatile unsigned char *)s; + do { + /*plasma_membar_ccfence();*/ + ck_memclear_func(s, c, n); + /*plasma_membar_ccfence();*/ + } while (vs[vzero] != c); + + #ifdef HAVE_WEAK_SYMBOLS + ck_memclear_s_hook(s, n); + #endif + + return s; +} + +#endif + + +errno_t +ck_memclear_s (void * const s, const rsize_t smax, rsize_t n) +{ + #ifdef HAVE_MEMSET_S + + return memset_s(s, smax, 0, n); + + #else + + if (NULL == s) + /* runtime constraint violation */ + return EINVAL; + if (RSIZE_MAX < smax) + /* runtime constraint violation */ + return E2BIG; + + errno_t rc = 0; + if (RSIZE_MAX < n) { + /* runtime constraint violation */ + rc = EINVAL; + n = smax; + } + if (smax < n) { + /* runtime constraint violation */ + rc = EOVERFLOW; + n = smax; + } + + #if defined(HAVE_EXPLICIT_BZERO) + explicit_bzero(s, n); + #elif defined(HAVE_EXPLICIT_MEMSET) + explicit_memset(s, 0, n); + #elif defined(HAVE_SECUREZEROMEMORY) + SecureZeroMemory(s, n); + #else + ck_memset_compat(s, 0, n); + #endif + + return rc; + + #endif +} + + +errno_t +ck_getenv_s (size_t * const restrict len, + char * const restrict value, const rsize_t maxsize, + const char * const restrict name) +{ + #ifdef __STDC_LIB_EXT1__ + + return getenv_s(len, value, maxsize, name); + + #else + + if (NULL == name || RSIZE_MAX < maxsize || (0 != maxsize && NULL == value)){ + /* runtime constraint violation */ + if (NULL != len) + *len = 0; + if (NULL != value && maxsize) + *value = '\0'; + return EINVAL; + } + + const char * const v = getenv(name); + if (NULL != v) { + const size_t vlen = strlen(v); + if (NULL != len) + *len = vlen; + if (vlen < maxsize) { + memcpy(value, v, vlen+1); + return 0; + } + else { + if (maxsize) + *value = '\0'; + return ERANGE; + } + } + else { + if (NULL != len) + *len = 0; + if (maxsize) + *value = '\0'; + #ifdef ENODATA + return ENODATA; + #else + return ENOENT; + #endif + } + + #endif +} + + +errno_t +ck_strerror_s (char * const s, const rsize_t maxsize, const errno_t errnum) +{ + #ifdef __STDC_LIB_EXT1__ + + return strerror_s(s, maxsize, errnum); + + #else + + if (NULL == s || 0 == maxsize || RSIZE_MAX < maxsize) { + /* runtime constraint violation */ + return EINVAL; + } + + /*(HAVE_STRERROR_R defined after tests by configure.ac or SConstruct)*/ + #if !defined(HAVE_STRERROR_R) && !defined(HAVE_CONFIG_H) + #define HAVE_STRERROR_R 1 + #endif /*(assume strerror_r() available if no config.h)*/ + + #ifdef HAVE_STRERROR_R + char buf[1024]; + #if defined(_GNU_SOURCE) && defined(__GLIBC__) + const char *errstr = strerror_r(errnum,buf,sizeof(buf)); + #else /* XSI-compliant strerror_r() */ + const char *errstr = (0 == strerror_r(errnum,buf,sizeof(buf))) ? buf : NULL; + #endif + #else /* !HAVE_STRERROR_R */ + const char *errstr = strerror(errnum); + #endif + if (NULL != errstr) { + const size_t errlen = strlen(errstr); + if (errlen < maxsize) { + memcpy(s, errstr, errlen+1); + return 0; + } + else { + memcpy(s, errstr, maxsize-1); + s[maxsize-1] = '\0'; + /*(fall through; not enough space to store entire error string)*/ + } + } + else { + if ((rsize_t)snprintf(s, maxsize, "Unknown error %d", errnum) < maxsize) + return 0; + /*(else fall through; not enough space to store entire error string)*/ + } + + /*(not enough space to store entire error string)*/ + if (maxsize > 3) + memcpy(s+maxsize-4, "...", 3); + return ERANGE; + + #endif +} diff --git a/src/ck.h b/src/ck.h new file mode 100644 index 00000000..7f41db8a --- /dev/null +++ b/src/ck.h @@ -0,0 +1,49 @@ +/* + * ck - C11 Annex K wrappers (selected functions; not complete) + * + * ck is also an abbreviation for "check". + * These are validating, checking functions. + * + * Copyright(c) 2016 Glenn Strauss gstrauss()gluelogic.com All rights reserved + * License: BSD 3-clause (same as lighttpd) + */ +#ifndef INCLUDED_CK_H +#define INCLUDED_CK_H +#ifndef __STDC_WANT_LIB_EXT1__ /*(enable C11 Annex K ext1 *_s functions)*/ +#define __STDC_WANT_LIB_EXT1__ 1 +#endif +#include "first.h" +#ifdef __FreeBSD__ +#include +#endif + +__BEGIN_DECLS + + +#ifndef RSIZE_MAX +#define RSIZE_MAX (SIZE_MAX >> 1) +typedef size_t rsize_t; +typedef int errno_t; +#endif + + +errno_t ck_getenv_s (size_t * restrict len, char * restrict value, rsize_t maxsize, const char * restrict name); + +/*(ck_memclear_s() is not from C11 Annex K + * ck_memclear_s() is similar to memset_s() using constant byte 0 for fill)*/ +errno_t ck_memclear_s (void *s, rsize_t smax, rsize_t n); + +/*(ck_memzero() is not from C11 Annex K + * ck_memzero() is a convenience wrapper around ck_memclear_s())*/ +static inline errno_t ck_memzero(void *s, rsize_t n); +static inline errno_t ck_memzero(void *s, rsize_t n) { + return ck_memclear_s(s, n, n); +} + +errno_t ck_strerror_s (char *s, rsize_t maxsize, errno_t errnum); + + +__END_DECLS + + +#endif diff --git a/src/config.h.cmake b/src/config.h.cmake index 258839d0..eac665e1 100644 --- a/src/config.h.cmake +++ b/src/config.h.cmake @@ -144,6 +144,7 @@ #cmakedefine HAVE_SIGACTION #cmakedefine HAVE_SIGNAL #cmakedefine HAVE_SIGTIMEDWAIT +#cmakedefine HAVE_STRERROR_R #cmakedefine HAVE_STRPTIME #cmakedefine HAVE_SYSLOG #cmakedefine HAVE_TIMEGM diff --git a/src/meson.build b/src/meson.build index 0d180fd2..b3ab283e 100644 --- a/src/meson.build +++ b/src/meson.build @@ -142,6 +142,7 @@ conf_data.set('HAVE_SIGACTION', compiler.has_function('sigaction', args: defs)) conf_data.set('HAVE_SIGNAL', compiler.has_function('signal', args: defs)) conf_data.set('HAVE_SIGTIMEDWAIT', compiler.has_function('sigtimedwait', args: defs)) conf_data.set('HAVE_SRANDOM', compiler.has_function('srandom', args: defs)) +conf_data.set('HAVE_STRERROR_R', compiler.has_function('strerror_r', args: defs)) conf_data.set('HAVE_STRPTIME', compiler.has_function('strptime', args: defs)) conf_data.set('HAVE_SYSLOG', compiler.has_function('syslog', args: defs)) conf_data.set('HAVE_TIMEGM', compiler.has_function('timegm', args: defs)) @@ -739,7 +740,7 @@ common_src = [ 'plugin.c', 'rand.c', 'request.c', - 'safe_memclear.c', + 'ck.c', 'sock_addr.c', 'stat_cache.c', 'vector.c', diff --git a/src/safe_memclear.c b/src/safe_memclear.c deleted file mode 100644 index c6d106a8..00000000 --- a/src/safe_memclear.c +++ /dev/null @@ -1,65 +0,0 @@ -#include "first.h" - -#include "safe_memclear.h" - -#include - -#if defined(_WIN32) && !defined(__CYGWIN__) -#include -/*(Windows XP and later provide SecureZeroMemory())*/ -#define HAVE_SECUREZEROMEMORY -#endif - -#if !defined(HAVE_MEMSET_S) \ - && !defined(HAVE_EXPLICIT_BZERO) \ - && !defined(HAVE_EXPLICIT_MEMSET) \ - && !defined(HAVE_SECUREZEROMEMORY) - -typedef void *(*safe_memclear_func_t)(void *, int, size_t); -extern volatile safe_memclear_func_t safe_memclear_func; -volatile safe_memclear_func_t safe_memclear_func = memset; - -# if defined(HAVE_WEAK_SYMBOLS) -/* it seems weak functions are never inlined, even for static builds */ -__attribute__((weak)) void __li_safe_memset_hook(void *buf, size_t len); - -void __li_safe_memset_hook(void *buf, size_t len) -{ - UNUSED(buf); - UNUSED(len); -} -# endif /* HAVE_WEAK_SYMBOLS */ - -static void* safe_memset(void *s, int c, size_t n) -{ - if (n > 0) { - volatile unsigned volatile_zero = 0; - volatile unsigned char *vs = (volatile unsigned char*)s; - - do { - safe_memclear_func(s, c, n); - } while (vs[volatile_zero] != (unsigned char)c); -# if defined(HAVE_WEAK_SYMBOLS) - __li_safe_memset_hook(s, n); -# endif /* HAVE_WEAK_SYMBOLS */ - } - - return s; -} - -#endif - - -void safe_memclear(void *s, size_t n) { -#if defined(HAVE_MEMSET_S) - memset_s(s, n, 0, n); -#elif defined(HAVE_EXPLICIT_BZERO) - explicit_bzero(s, n); -#elif defined(HAVE_EXPLICIT_MEMSET) - explicit_memset(s, 0, n); -#elif defined(HAVE_SECUREZEROMEMORY) - SecureZeroMemory(s, n); -#else - safe_memset(s, 0, n); -#endif -} diff --git a/src/safe_memclear.h b/src/safe_memclear.h index 9fd25636..94bf9e77 100644 --- a/src/safe_memclear.h +++ b/src/safe_memclear.h @@ -2,6 +2,7 @@ #define _SAFE_MEMCLEAR_H_ #include "first.h" -void safe_memclear(void *s, size_t n); +#include "ck.h" +#define safe_memclear ck_memzero #endif