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);
ck_memclear_s_hook(void * buf __attribute_unused__,rsize_t len __attribute_unused__)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 *
ck_memset_compat(void * s,int c,size_t n)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
ck_memclear_s(void * const s,const rsize_t smax,rsize_t n)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
ck_strerror_s(char * const s,const rsize_t maxsize,const errno_t errnum)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
ck_memeq_const_time(const void * a,size_t alen,const void * b,size_t blen)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
ck_memeq_const_time_fixed_len(const void * a,const void * b,const size_t len)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
ck_backtrace(FILE * fp)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__
__attribute_nonnull__()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
ck_bt(const char * filename,unsigned int line,const char * msg)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
ck_bt_abort(const char * filename,unsigned int line,const char * msg)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__
ck_assert_failed(const char * filename,unsigned int line,const char * msg)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