[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
This commit is contained in:
Glenn Strauss 2021-05-21 22:22:45 -04:00
parent d4c1855578
commit 86c39754f2
11 changed files with 316 additions and 70 deletions

View File

@ -450,6 +450,7 @@ if 1:
'strchr',
'strdup',
'strerror',
'strerror_r',
'strftime',
'strstr',
'strtol',

View File

@ -1516,6 +1516,7 @@ AC_CHECK_FUNCS([\
sigaction \
signal \
srandom \
strerror_r \
timegm \
writev \
])

View File

@ -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)

View File

@ -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 \

View File

@ -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 \

256
src/ck.c Normal file
View File

@ -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 <stdlib.h> /* getenv() getenv_s() */
#include <string.h> /* 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 <errno.h>
#include <stdio.h> /* snprintf() */
#endif
#ifndef HAVE_MEMSET_S
#ifdef _WIN32
#define VC_EXTRALEAN
#define WIN32_LEAN_AND_MEAN
#include <windows.h> /* SecureZeroMemory() */
/*(Windows XP and later provide SecureZeroMemory())*/
#define HAVE_SECUREZEROMEMORY
#else /* !_WIN32 */
#ifdef HAVE_SIGNAL
#include <signal.h> /* sig_atomic_t */
#else
typedef int sig_atomic_t;
#endif
/*#include <plasma/plasma_membar.h>*/ /* 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
}

49
src/ck.h Normal file
View File

@ -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 <errno.h>
#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

View File

@ -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

View File

@ -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',

View File

@ -1,65 +0,0 @@
#include "first.h"
#include "safe_memclear.h"
#include <string.h>
#if defined(_WIN32) && !defined(__CYGWIN__)
#include <WinBase.h>
/*(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
}

View File

@ -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