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