xref: /lighttpd1.4/src/ck.c (revision df362494)
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