1 /* 2 * ck - C11 Annex K wrappers (selected functions; not complete) 3 * 4 * ck is also an abbreviation for "check". 5 * These are validating, checking functions. 6 * 7 * Copyright(c) 2016 Glenn Strauss gstrauss()gluelogic.com All rights reserved 8 * License: BSD 3-clause (same as lighttpd) 9 */ 10 #ifndef __STDC_WANT_LIB_EXT1__ 11 #define __STDC_WANT_LIB_EXT1__ 1 12 #endif 13 #ifndef _XOPEN_SOURCE 14 #define _XOPEN_SOURCE 700 15 #endif 16 #ifndef _NETBSD_SOURCE 17 #define _NETBSD_SOURCE 18 #endif 19 #ifdef __OpenBSD__ 20 #ifndef _BSD_SOURCE 21 #define _BSD_SOURCE 22 #endif 23 #endif 24 #include "first.h" 25 26 #include "ck.h" 27 28 #include <stdlib.h> /* abort() getenv() getenv_s() 29 * calloc() malloc() realloc() */ 30 #include <string.h> /* memcpy() memset() memset_s() explicit_bzero() 31 * strerror() strerror_r() strerror_s() strlen() */ 32 33 #ifdef __STDC_LIB_EXT1__ 34 #ifndef HAVE_MEMSET_S 35 #define HAVE_MEMSET_S 36 #endif 37 #else 38 #include <errno.h> 39 #include <stdio.h> /* snprintf() */ 40 #endif 41 42 #ifndef HAVE_MEMSET_S 43 44 #ifdef _WIN32 45 #define VC_EXTRALEAN 46 #define WIN32_LEAN_AND_MEAN 47 #include <windows.h> /* SecureZeroMemory() */ 48 /*(Windows XP and later provide SecureZeroMemory())*/ 49 #define HAVE_SECUREZEROMEMORY 50 #else /* !_WIN32 */ 51 #ifdef HAVE_SIGNAL 52 #include <signal.h> /* sig_atomic_t */ 53 #else 54 typedef int sig_atomic_t; 55 #endif 56 /*#include <plasma/plasma_membar.h>*/ /* plasma_membar_ccfence() */ 57 #endif 58 59 #endif /* !HAVE_MEMSET_S */ 60 61 62 #if !defined(HAVE_MEMSET_S) \ 63 && !defined(HAVE_EXPLICIT_BZERO) \ 64 && !defined(HAVE_EXPLICIT_MEMSET) \ 65 && !defined(HAVE_SECUREZEROMEMORY) 66 67 typedef void *(*ck_memclear_func_t)(void *, int, size_t); 68 extern volatile ck_memclear_func_t ck_memclear_func; 69 volatile ck_memclear_func_t ck_memclear_func = memset; 70 71 #ifdef HAVE_WEAK_SYMBOLS 72 /* it seems weak functions are never inlined, even for static builds */ 73 __attribute__((__weak__)) 74 void ck_memclear_s_hook (void *buf, rsize_t len); 75 void ck_memclear_s_hook (void *buf __attribute_unused__, 76 rsize_t len __attribute_unused__) 77 { 78 /*(application might define func to call OPENSSL_cleanse(), if available)*/ 79 (void)(buf); /* UNUSED */ 80 (void)(len); /* UNUSED */ 81 } 82 #endif /* HAVE_WEAK_SYMBOLS */ 83 84 static void * 85 ck_memset_compat(void *s, int c, size_t n) 86 { 87 /* attempt to inhibit compiler/linker heuristics which might elide memset() 88 * - insert compiler optimization fences around memset() 89 * - access s through volatile pointer at volatile index after memset() 90 * - pass s to weak (overridable) func to create additional data dependency 91 */ 92 93 if (0 == n) /*(must check n > 0 since s[0] will be accessed)*/ 94 return s; 95 96 static volatile sig_atomic_t vzero; 97 volatile unsigned char *vs = (volatile unsigned char *)s; 98 do { 99 /*plasma_membar_ccfence();*/ 100 ck_memclear_func(s, c, n); 101 /*plasma_membar_ccfence();*/ 102 } while (vs[vzero] != c); 103 104 #ifdef HAVE_WEAK_SYMBOLS 105 ck_memclear_s_hook(s, n); 106 #endif 107 108 return s; 109 } 110 111 #endif 112 113 114 errno_t 115 ck_memclear_s (void * const s, const rsize_t smax, rsize_t n) 116 { 117 #ifdef HAVE_MEMSET_S 118 119 return memset_s(s, smax, 0, n); 120 121 #else 122 123 if (NULL == s) 124 /* runtime constraint violation */ 125 return EINVAL; 126 if (RSIZE_MAX < smax) 127 /* runtime constraint violation */ 128 return E2BIG; 129 130 errno_t rc = 0; 131 if (RSIZE_MAX < n) { 132 /* runtime constraint violation */ 133 rc = EINVAL; 134 n = smax; 135 } 136 if (smax < n) { 137 /* runtime constraint violation */ 138 rc = EOVERFLOW; 139 n = smax; 140 } 141 142 #if defined(HAVE_EXPLICIT_BZERO) 143 explicit_bzero(s, n); 144 #elif defined(HAVE_EXPLICIT_MEMSET) 145 explicit_memset(s, 0, n); 146 #elif defined(HAVE_SECUREZEROMEMORY) 147 SecureZeroMemory(s, n); 148 #else 149 ck_memset_compat(s, 0, n); 150 #endif 151 152 return rc; 153 154 #endif 155 } 156 157 158 #if 0 /*(not currently used in lighttpd; lighttpd process env is stable)*/ 159 errno_t 160 ck_getenv_s (size_t * const restrict len, 161 char * const restrict value, const rsize_t maxsize, 162 const char * const restrict name) 163 { 164 #ifdef __STDC_LIB_EXT1__ 165 166 return getenv_s(len, value, maxsize, name); 167 168 #else 169 170 if (NULL == name || RSIZE_MAX < maxsize || (0 != maxsize && NULL == value)){ 171 /* runtime constraint violation */ 172 if (NULL != len) 173 *len = 0; 174 if (NULL != value && maxsize) 175 *value = '\0'; 176 return EINVAL; 177 } 178 179 const char * const v = getenv(name); 180 if (NULL != v) { 181 const size_t vlen = strlen(v); 182 if (NULL != len) 183 *len = vlen; 184 if (vlen < maxsize) { 185 memcpy(value, v, vlen+1); 186 return 0; 187 } 188 else { 189 if (maxsize) 190 *value = '\0'; 191 return ERANGE; 192 } 193 } 194 else { 195 if (NULL != len) 196 *len = 0; 197 if (maxsize) 198 *value = '\0'; 199 #ifdef ENODATA 200 return ENODATA; 201 #else 202 return ENOENT; 203 #endif 204 } 205 206 #endif 207 } 208 #endif 209 210 211 errno_t 212 ck_strerror_s (char * const s, const rsize_t maxsize, const errno_t errnum) 213 { 214 #ifdef __STDC_LIB_EXT1__ 215 216 return strerror_s(s, maxsize, errnum); 217 218 #else 219 220 if (NULL == s || 0 == maxsize || RSIZE_MAX < maxsize) { 221 /* runtime constraint violation */ 222 return EINVAL; 223 } 224 225 /*(HAVE_STRERROR_R defined after tests by configure.ac or SConstruct)*/ 226 #if !defined(HAVE_STRERROR_R) && !defined(HAVE_CONFIG_H) 227 #define HAVE_STRERROR_R 1 228 #endif /*(assume strerror_r() available if no config.h)*/ 229 230 #ifdef HAVE_STRERROR_R 231 char buf[1024]; 232 #if defined(_GNU_SOURCE) && defined(__GLIBC__) 233 const char *errstr = strerror_r(errnum,buf,sizeof(buf)); 234 #else /* XSI-compliant strerror_r() */ 235 const char *errstr = (0 == strerror_r(errnum,buf,sizeof(buf))) ? buf : NULL; 236 #endif 237 #else /* !HAVE_STRERROR_R */ 238 const char *errstr = strerror(errnum); 239 #endif 240 if (NULL != errstr) { 241 const size_t errlen = strlen(errstr); 242 if (errlen < maxsize) { 243 memcpy(s, errstr, errlen+1); 244 return 0; 245 } 246 else { 247 memcpy(s, errstr, maxsize-1); 248 s[maxsize-1] = '\0'; 249 /*(fall through; not enough space to store entire error string)*/ 250 } 251 } 252 else { 253 if ((rsize_t)snprintf(s, maxsize, "Unknown error %d", errnum) < maxsize) 254 return 0; 255 /*(else fall through; not enough space to store entire error string)*/ 256 } 257 258 /*(not enough space to store entire error string)*/ 259 if (maxsize > 3) 260 memcpy(s+maxsize-4, "...", 3); 261 return ERANGE; 262 263 #endif 264 } 265 266 267 int 268 ck_memeq_const_time (const void *a, size_t alen, const void *b, size_t blen) 269 { 270 /* constant time memory compare for equality */ 271 /* rounds to next multiple of 64 to avoid potentially leaking exact 272 * string lengths when subject to high precision timing attacks 273 */ 274 /* Note: some libs provide similar funcs but might not obscure length, e.g. 275 * OpenSSL: 276 * int CRYPTO_memcmp(const void * in_a, const void * in_b, size_t len) 277 * Note: some OS provide similar funcs but might not obscure length, e.g. 278 * OpenBSD: int timingsafe_bcmp(const void *b1, const void *b2, size_t len) 279 * NetBSD: int consttime_memequal(void *b1, void *b2, size_t len) 280 */ 281 const volatile unsigned char * const av = 282 (const unsigned char *)(alen ? a : ""); 283 const volatile unsigned char * const bv = 284 (const unsigned char *)(blen ? b : ""); 285 size_t lim = ((alen >= blen ? alen : blen) + 0x3F) & ~0x3F; 286 int diff = (alen != blen); /*(never match if string length mismatch)*/ 287 alen -= (alen != 0); 288 blen -= (blen != 0); 289 for (size_t i = 0, j = 0; lim; --lim) { 290 diff |= (av[i] ^ bv[j]); 291 i += (i < alen); 292 j += (j < blen); 293 } 294 return (0 == diff); 295 } 296 297 298 int 299 ck_memeq_const_time_fixed_len (const void *a, const void *b, const size_t len) 300 { 301 /* constant time memory compare for equality for fixed len (e.g. digests) 302 * (padding not necessary for digests, which have fixed, defined lengths) */ 303 /* caller should prefer ck_memeq_const_time() if not operating on digests */ 304 const volatile unsigned char * const av = (const unsigned char *)a; 305 const volatile unsigned char * const bv = (const unsigned char *)b; 306 int diff = 0; 307 for (size_t i = 0; i < len; ++i) { 308 diff |= (av[i] ^ bv[i]); 309 } 310 return (0 == diff); 311 } 312 313 314 void * 315 ck_malloc (size_t nbytes) 316 { 317 void *ptr = malloc(nbytes); 318 ck_assert(NULL != ptr); 319 return ptr; 320 } 321 322 323 void * 324 ck_calloc (size_t nmemb, size_t elt_sz) 325 { 326 void *ptr = calloc(nmemb, elt_sz); 327 ck_assert(NULL != ptr); 328 return ptr; 329 } 330 331 332 void * 333 ck_realloc_u32 (void **list, size_t n, size_t x, size_t elt_sz) 334 { 335 #ifdef HAVE_REALLOCARRAY /*(not currently detected by build)*/ 336 ck_assert(x <= UINT32_MAX && n <= UINT32_MAX - x); 337 void *ptr = reallocarray(*list, n + x, elt_sz); 338 #else 339 ck_assert(x <= UINT32_MAX && n <= UINT32_MAX - x && n+x <= SIZE_MAX/elt_sz); 340 void *ptr = realloc(*list, (n + x) * elt_sz); 341 #endif 342 ck_assert(NULL != ptr); 343 return (*list = ptr); 344 } 345 346 347 348 349 #include <stdio.h> /* fflush() fprintf() snprintf() */ 350 351 #ifdef HAVE_LIBUNWIND 352 #define UNW_LOCAL_ONLY 353 #include <libunwind.h> 354 __attribute_cold__ 355 __attribute_noinline__ 356 static void 357 ck_backtrace (FILE *fp) 358 { 359 int rc; 360 unsigned int frame = 0; 361 unw_word_t ip; 362 unw_word_t offset; 363 unw_cursor_t cursor; 364 unw_context_t context; 365 unw_proc_info_t procinfo; 366 char name[256]; 367 368 rc = unw_getcontext(&context); 369 if (0 != rc) goto error; 370 rc = unw_init_local(&cursor, &context); 371 if (0 != rc) goto error; 372 373 fprintf(fp, "Backtrace:\n"); 374 while (0 < (rc = unw_step(&cursor))) { 375 ++frame; 376 ip = 0; 377 rc = unw_get_reg(&cursor, UNW_REG_IP, &ip); 378 if (0 != rc) break; 379 if (0 == ip) { 380 /* without an IP the other functions are useless; 381 * unw_get_proc_name would return UNW_EUNSPEC */ 382 fprintf(fp, "%u: (nil)\n", frame); 383 continue; 384 } 385 386 rc = unw_get_proc_info(&cursor, &procinfo); 387 if (0 != rc) break; 388 389 offset = 0; 390 rc = unw_get_proc_name(&cursor, name, sizeof(name), &offset); 391 if (0 != rc) { 392 switch (-rc) { 393 case UNW_ENOMEM: 394 memcpy(name + sizeof(name) - 4, "...", 4); 395 break; 396 case UNW_ENOINFO: 397 name[0] = '?'; 398 name[1] = '\0'; 399 break; 400 default: 401 snprintf(name, sizeof(name), 402 "?? (unw_get_proc_name error %d)", -rc); 403 break; 404 } 405 } 406 407 fprintf(fp, "%.2u: [%.012lx] (+%04x) %s\n", 408 frame,(long unsigned)(uintptr_t)ip,(unsigned int)offset,name); 409 } 410 if (0 == rc) 411 return; 412 413 error: 414 fprintf(fp, "Error while generating backtrace: unwind error %i\n",(int)-rc); 415 } 416 #endif 417 418 419 __attribute_noinline__ 420 __attribute_nonnull__() 421 static void 422 ck_bt_stderr (const char *filename, unsigned int line, const char *msg, const char *fmt) 423 { 424 fprintf(stderr, fmt, filename, line, msg); 425 #ifdef HAVE_LIBUNWIND 426 ck_backtrace(stderr); 427 #endif 428 fflush(stderr); 429 } 430 431 432 void 433 ck_bt (const char *filename, unsigned int line, const char *msg) 434 { 435 ck_bt_stderr(filename, line, msg, "%s.%u: %s\n"); 436 } 437 438 439 __attribute_noreturn__ 440 void 441 ck_bt_abort (const char *filename, unsigned int line, const char *msg) 442 { 443 ck_bt(filename, line, msg); 444 abort(); 445 } 446 447 448 __attribute_noreturn__ 449 void ck_assert_failed(const char *filename, unsigned int line, const char *msg) 450 { 451 /* same as ck_bt_abort() but add "assertion failed: " prefix here 452 * to avoid bloating string tables in callers */ 453 ck_bt_stderr(filename, line, msg, "%s.%u: assertion failed: %s\n"); 454 abort(); 455 } 456