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