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