1 /*
2 * Copyright (c) 2012 The Native Client Authors. All rights reserved.
3 * Use of this source code is governed by a BSD-style license that can be
4 * found in the LICENSE file.
5 */
6
7 #include <stdlib.h>
8 #include <string.h>
9
10 #include "native_client/src/trusted/fault_injection/fault_injection.h"
11
12 #include "native_client/src/include/build_config.h"
13 #include "native_client/src/include/portability.h"
14 #include "native_client/src/include/portability_string.h"
15 #include "native_client/src/shared/platform/nacl_check.h"
16 #include "native_client/src/shared/platform/nacl_log.h"
17 #include "native_client/src/shared/platform/nacl_sync.h"
18 #include "native_client/src/shared/platform/nacl_sync_checked.h"
19
20 #if 0
21 /* to crash on error, rather than let a confused process keep running */
22 # define NACL_FI_CHECK(bool_expr) CHECK(bool_expr)
23 #else
24 # define NACL_FI_CHECK(bool_expr) \
25 do { \
26 if (!(bool_expr)) { \
27 NaClLog(LOG_ERROR, \
28 "FaultInjection: file %s, line %d: CHECK failed: %s\n", \
29 __FILE__, __LINE__, #bool_expr); \
30 } \
31 } while (0)
32 #endif
33
34 #if NACL_LINUX
35 # define NACL_HAS_STRNDUP 1
36
37 /* We could use TSD, but TLS variables are faster */
38 # define NACL_USE_TLS 1
39 # define NACL_USE_TSD 0
40 # define NACL_USE_TLSALLOC 0
41
42 #elif NACL_OSX
43 # define NACL_HAS_STRNDUP 0
44 /* We can only use TSD because Mac OS X does not have TLS variables */
45 # define NACL_USE_TLS 0
46 # define NACL_USE_TSD 1
47 # define NACL_USE_TLSALLOC 0
48
49 #elif NACL_WINDOWS
50 /*
51 * for Windows 2003 server, Windows XP, and earlier, code in DLLs that
52 * use __declspec(thread) has problems. See
53 *
54 http://msdn.microsoft.com/en-us/library/windows/desktop/ms684175(v=vs.85).aspx
55 *
56 * Windows Server 2003 and Windows XP: The Visual C++ compiler
57 * supports a syntax that enables you to declare thread-local
58 * variables: _declspec(thread). If you use this syntax in a DLL, you
59 * will not be able to load the DLL explicitly using LoadLibrary on
60 * versions of Windows prior to Windows Vista. If your DLL will be
61 * loaded explicitly, you must use the thread local storage functions
62 * instead of _declspec(thread). For an example, see Using Thread
63 * Local Storage in a Dynamic Link Library.
64 *
65 http://msdn.microsoft.com/en-us/library/windows/desktop/ms686997(v=vs.85).aspx
66 */
67 # define NACL_HAS_STRNDUP 0
68 /* could use TLS if not built into a DLL, otherwise must use TLSALLOC */
69 # define NACL_USE_TLSALLOC 1
70 # include <windows.h>
71 #endif
72
73 #define NACL_FAULT_INJECT_ASSUME_HINT_CORRECT 1
74
75 #if NACL_USE_TSD
76 # include <pthread.h>
77 #endif
78
79 #if !NACL_HAS_STRNDUP
my_strndup(char const * s,size_t n)80 static char *my_strndup(char const *s, size_t n) {
81 char *d = (char *) malloc(n + 1);
82 if (NULL != d) {
83 strncpy(d, s, n);
84 d[n] = '\0';
85 }
86 return d;
87 }
88 #define strndup(s,n) my_strndup((s), (n))
89 #endif
90
91 struct NaClFaultExpr {
92 int pass; /* bool */
93 uintptr_t fault_value; /* only if !pass */
94 uintptr_t count; /* size_t, but uintptr_t to re-use digit parser */
95 };
96
97 struct NaClFaultInjectInfo {
98 char const *call_site_name;
99 int thread_specific_p;
100 /*
101 * bool: true if we should use thread specific counter; global
102 * counter otherwise.
103 */
104 struct NaClFaultExpr *expr;
105 size_t num_expr;
106 size_t size_expr;
107 };
108
109 /* array of NaClFaultInjectInfo */
110 static struct NaClFaultInjectInfo *gNaClFaultInjectInfo = NULL;
111 /* number in use */
112 static size_t gNaClNumFaultInjectInfo = 0;
113 /* number allocated */
114 static size_t gNaClSizeFaultInjectInfo = 0;
115
116 /*
117 * Increment count_in_expr until we reach count in NaClFaultExpr, then
118 * "carry" by resetting count_in_expr to 0 and incrementing expr_ix to
119 * move to the next NaClFaultExpr. When expr_ix reaches num_expr, we
120 * have exhausted the fault_control_expression and should just pass
121 * all calls through to the real function. This makes call-site
122 * processing constant time, once the call-site's entry is found.
123 */
124 struct NaClFaultInjectCallSiteCount {
125 size_t expr_ix;
126 size_t count_in_expr;
127 };
128
129 /*
130 * Array of call site counters indexed by position of call site name in
131 * the NaClFaultInjectInfo list, for global fault_control_expressions.
132 *
133 * This array contains call sites that are explicitly mentioned by the
134 * NACL_FAULT_INJECTION environment variable, not all call-site names
135 * used in the code base.
136 */
137 struct NaClFaultInjectCallSiteCount *gNaClFaultInjectCallSites = 0;
138 struct NaClMutex *gNaClFaultInjectMu = 0;
139 /* global counters mu */
140
141
142 #if NACL_USE_TLS
143 static THREAD
144 struct NaClFaultInjectCallSiteCount *gTls_FaultInjectionCount = NULL;
145 static THREAD
146 uintptr_t gTls_FaultInjectValue = 0;
147 #elif NACL_USE_TSD
148 typedef pthread_key_t nacl_thread_specific_key_t;
149 #elif NACL_USE_TLSALLOC
150 typedef DWORD nacl_thread_specific_key_t;
151 #else
152 # error "Cannot implement thread-specific counters for fault injection"
153 #endif
154 #if NACL_USE_TSD || NACL_USE_TLSALLOC
155 nacl_thread_specific_key_t gFaultInjectCountKey;
156 nacl_thread_specific_key_t gFaultInjectValueKey;
157 int gFaultInjectionHasValidKeys = 0;
158 #endif
159
160 #if NACL_USE_TSD
161 /*
162 * Abstraction around TSD.
163 */
NaClFaultInjectCallSiteCounterDtor(void * value)164 static void NaClFaultInjectCallSiteCounterDtor(void *value) {
165 free((void *) value);
166 }
NaClFaultInjectionThreadKeysCreate(void)167 static void NaClFaultInjectionThreadKeysCreate(void) {
168 int status;
169
170 status = pthread_key_create(&gFaultInjectCountKey,
171 NaClFaultInjectCallSiteCounterDtor);
172 NACL_FI_CHECK(0 == status);
173 if (0 != status) {
174 return;
175 }
176 status = pthread_key_create(&gFaultInjectValueKey, NULL);
177 NACL_FI_CHECK(0 == status);
178 if (0 != status) {
179 pthread_key_delete(gFaultInjectValueKey);
180 return;
181 }
182 gFaultInjectionHasValidKeys = 1;
183 }
NaClFaultInjectionThreadGetSpecific(nacl_thread_specific_key_t key)184 static void *NaClFaultInjectionThreadGetSpecific(
185 nacl_thread_specific_key_t key) {
186 if (!gFaultInjectionHasValidKeys) {
187 return NULL;
188 }
189 return pthread_getspecific(key);
190 }
NaClFaultInjectionThreadSetSpecific(nacl_thread_specific_key_t key,void * value)191 static void NaClFaultInjectionThreadSetSpecific(
192 nacl_thread_specific_key_t key,
193 void *value) {
194 int status;
195
196 if (!gFaultInjectionHasValidKeys) {
197 return;
198 }
199 status = pthread_setspecific(key, value);
200 /*
201 * Internal consistency error, probably memory corruption. Leave as
202 * CHECK since otherwise using process would die soon anyway.
203 */
204 CHECK(0 == status);
205 (void) status; /* in case CHECK ever get changed to DCHECK or is removed */
206 }
207 #endif
208 #if NACL_USE_TLSALLOC
209 /*
210 * Abstraction around TlsAlloc.
211 */
NaClFaultInjectionThreadKeysCreate(void)212 static void NaClFaultInjectionThreadKeysCreate(void) {
213 gFaultInjectCountKey = TlsAlloc();
214 NACL_FI_CHECK(TLS_OUT_OF_INDEXES != gFaultInjectCountKey);
215 if (TLS_OUT_OF_INDEXES == gFaultInjectCountKey) {
216 return;
217 }
218 gFaultInjectValueKey = TlsAlloc();
219 NACL_FI_CHECK(TLS_OUT_OF_INDEXES != gFaultInjectValueKey);
220 if (TLS_OUT_OF_INDEXES == gFaultInjectValueKey) {
221 TlsFree(gFaultInjectCountKey);
222 return;
223 }
224 gFaultInjectionHasValidKeys = 1;
225 }
NaClFaultInjectionThreadGetSpecific(nacl_thread_specific_key_t key)226 static void *NaClFaultInjectionThreadGetSpecific(
227 nacl_thread_specific_key_t key) {
228 if (!gFaultInjectionHasValidKeys) {
229 return NULL;
230 }
231 return TlsGetValue(key);
232 }
NaClFaultInjectionThreadSetSpecific(nacl_thread_specific_key_t key,void * value)233 static void NaClFaultInjectionThreadSetSpecific(
234 nacl_thread_specific_key_t key,
235 void *value) {
236 BOOL status;
237
238 if (!gFaultInjectionHasValidKeys) {
239 return;
240 }
241 status = TlsSetValue(key, value);
242 /*
243 * Internal consistency error, probably memory corruption. Leave as
244 * CHECK since otherwise using process would die soon anyway.
245 */
246 CHECK(status);
247 (void) status;
248 }
249 #endif
250
251
NaClFaultInjectGrowIfNeeded(void)252 static void NaClFaultInjectGrowIfNeeded(void) {
253 size_t new_size;
254 struct NaClFaultInjectInfo *info;
255 if (gNaClNumFaultInjectInfo < gNaClSizeFaultInjectInfo) {
256 return;
257 }
258 new_size = 2 * gNaClSizeFaultInjectInfo;
259 if (0 == new_size) {
260 new_size = 4;
261 }
262 if (new_size > (~(size_t) 0) / sizeof *info) {
263 NaClLog(LOG_FATAL, "Too many fault injection records\n");
264 }
265 info = (struct NaClFaultInjectInfo *) realloc(gNaClFaultInjectInfo,
266 new_size * sizeof *info);
267 /* Leave as CHECK since otherwise using process would die soon anyway */
268 CHECK(NULL != info);
269 gNaClFaultInjectInfo = info;
270 }
271
272 /*
273 * Takes ownership of the contents of |entry|.
274 */
NaClFaultInjectAddEntry(struct NaClFaultInjectInfo const * entry)275 static void NaClFaultInjectAddEntry(struct NaClFaultInjectInfo const *entry) {
276 NaClFaultInjectGrowIfNeeded();
277 gNaClFaultInjectInfo[gNaClNumFaultInjectInfo++] = *entry;
278 }
279
NaClFaultInjectAllocGlobalCounters(void)280 static void NaClFaultInjectAllocGlobalCounters(void) {
281 size_t ix;
282
283 gNaClFaultInjectCallSites = (struct NaClFaultInjectCallSiteCount *)
284 malloc(gNaClNumFaultInjectInfo * sizeof *gNaClFaultInjectCallSites);
285 /* Leave as CHECK since otherwise using process would die soon anyway */
286 CHECK(NULL != gNaClFaultInjectCallSites);
287 gNaClFaultInjectMu = (struct NaClMutex *) malloc(
288 gNaClNumFaultInjectInfo * sizeof *gNaClFaultInjectMu);
289 /* Leave as CHECK since otherwise using process would die soon anyway */
290 CHECK(NULL != gNaClFaultInjectMu);
291 for (ix = 0; ix < gNaClNumFaultInjectInfo; ++ix) {
292 gNaClFaultInjectCallSites[ix].expr_ix = 0;
293 gNaClFaultInjectCallSites[ix].count_in_expr = 0;
294
295 NaClXMutexCtor(&gNaClFaultInjectMu[ix]);
296 }
297 }
298
NaClFaultInjectFreeGlobalCounters(void)299 static void NaClFaultInjectFreeGlobalCounters(void) {
300 size_t ix;
301
302 free(gNaClFaultInjectCallSites);
303 gNaClFaultInjectCallSites = NULL;
304 for (ix = 0; ix < gNaClNumFaultInjectInfo; ++ix) {
305 NaClMutexDtor(&gNaClFaultInjectMu[ix]);
306 }
307 free(gNaClFaultInjectMu);
308 }
309
310 #if NACL_USE_TLS
NaClFaultInjectFindThreadCounter(size_t counter_ix)311 static struct NaClFaultInjectCallSiteCount *NaClFaultInjectFindThreadCounter(
312 size_t counter_ix) {
313 /*
314 * Internal consistency error, probably memory corruption. Leave as
315 * CHECK since otherwise using process would die soon anyway.
316 */
317 CHECK(counter_ix < gNaClNumFaultInjectInfo);
318
319 if (NULL == gTls_FaultInjectionCount) {
320 struct NaClFaultInjectCallSiteCount *counters;
321 size_t ix;
322
323 counters = (struct NaClFaultInjectCallSiteCount *)
324 malloc(gNaClNumFaultInjectInfo * sizeof *counters);
325 /* Leave as CHECK since otherwise using process would die soon anyway */
326 CHECK(NULL != counters);
327
328 for (ix = 0; ix < gNaClNumFaultInjectInfo; ++ix) {
329 counters[ix].expr_ix = 0;
330 counters[ix].count_in_expr = 0;
331 }
332 gTls_FaultInjectionCount = counters;
333 }
334
335 return &gTls_FaultInjectionCount[counter_ix];
336 }
337
NaClFaultInjectionSetValue(uintptr_t location)338 static void NaClFaultInjectionSetValue(uintptr_t location) {
339 gTls_FaultInjectValue = location;
340 }
341
NaClFaultInjectionGetValue(void)342 static size_t NaClFaultInjectionGetValue(void) {
343 return gTls_FaultInjectValue;
344 }
345
NaClFaultInjectionPreThreadExitCleanup(void)346 void NaClFaultInjectionPreThreadExitCleanup(void) {
347 free(gTls_FaultInjectionCount);
348 gTls_FaultInjectionCount = NULL;
349 }
350
351 #elif NACL_USE_TSD || NACL_USE_TLSALLOC
NaClFaultInjectFindThreadCounter(size_t counter_ix)352 static struct NaClFaultInjectCallSiteCount *NaClFaultInjectFindThreadCounter(
353 size_t counter_ix) {
354 struct NaClFaultInjectCallSiteCount *counters;
355
356 /*
357 * Internal consistency error, probably memory corruption. Leave as
358 * CHECK since otherwise using process would die soon anyway.
359 */
360 CHECK(counter_ix < gNaClNumFaultInjectInfo);
361
362 if (!gFaultInjectionHasValidKeys) {
363 return NULL;
364 }
365
366 counters = (struct NaClFaultInjectCallSiteCount *)
367 NaClFaultInjectionThreadGetSpecific(
368 gFaultInjectCountKey);
369 if (NULL == counters) {
370 size_t ix;
371
372 counters = (struct NaClFaultInjectCallSiteCount *)
373 malloc(gNaClNumFaultInjectInfo * sizeof *counters);
374 /* Leave as CHECK since otherwise using process would die soon anyway */
375 CHECK(NULL != counters);
376
377 for (ix = 0; ix < gNaClNumFaultInjectInfo; ++ix) {
378 counters[ix].expr_ix = 0;
379 counters[ix].count_in_expr = 0;
380 }
381 NaClFaultInjectionThreadSetSpecific(gFaultInjectCountKey,
382 (void *) counters);
383 }
384 return &counters[counter_ix];
385 }
386
NaClFaultInjectionSetValue(uintptr_t value)387 static void NaClFaultInjectionSetValue(uintptr_t value) {
388 NaClFaultInjectionThreadSetSpecific(gFaultInjectValueKey,
389 (void *) value);
390 }
391
NaClFaultInjectionGetValue(void)392 static uintptr_t NaClFaultInjectionGetValue(void) {
393 return (uintptr_t)
394 NaClFaultInjectionThreadGetSpecific(gFaultInjectValueKey);
395 }
396
NaClFaultInjectionPreThreadExitCleanup(void)397 void NaClFaultInjectionPreThreadExitCleanup(void) {
398 /*
399 * pthread_key_create registered NaClFaultInjectCallSiteCounterDtor.
400 */
401 return;
402 }
403
404 #else
405 # error "Cannot implement thread-specific counters for fault injection"
406 #endif
407
408 /*
409 * Fault Control Expression Parser functions. Returns parsing success
410 * or fail.
411 */
412
413 /*
414 * NaClFaultInjectionParseHelperAddFaultExpr adds a new NaClFaultExpr
415 * |expr| to the NaClFaultInjectInfo object in |out_info|, growing the
416 * array of NaClFaultExpr as needed.
417 */
NaClFaultInjectionParseHelperAddFaultExpr(struct NaClFaultInjectInfo * out_info,struct NaClFaultExpr * expr)418 static void NaClFaultInjectionParseHelperAddFaultExpr(
419 struct NaClFaultInjectInfo *out_info,
420 struct NaClFaultExpr *expr) {
421 size_t new_count;
422 struct NaClFaultExpr *new_exprs;
423
424 if (out_info->num_expr == out_info->size_expr) {
425 new_count = 2 * out_info->size_expr;
426 if (0 == new_count) {
427 new_count = 4;
428 }
429 new_exprs = (struct NaClFaultExpr *) realloc(out_info->expr,
430 new_count * sizeof *new_exprs);
431 /* Leave as CHECK since otherwise using process would die soon anyway */
432 CHECK(NULL != new_exprs);
433 out_info->expr = new_exprs;
434 out_info->size_expr = new_count;
435 }
436 NaClLog(6,
437 "NaClFaultInject: adding %c(%"NACL_PRIdPTR
438 ",%"NACL_PRIuPTR") at %"NACL_PRIuS"\n",
439 expr->pass ? 'P' : 'F', expr->fault_value, expr->count,
440 out_info->num_expr);
441 out_info->expr[out_info->num_expr++] = *expr;
442 }
443
444 /*
445 * NaClFaultInjectionParseNumber consumes numbers (<count> or <value>)
446 * from |*digits|, advancing the in/out pointer to the character that
447 * terminates the parse. The resultant number is put into |*out|.
448 *
449 * This is not a strict parser, since it permits '@' to be used for
450 * the <value> non-terminal.
451 */
NaClFaultInjectionParseNumber(uintptr_t * out,char const ** digits)452 static int NaClFaultInjectionParseNumber(uintptr_t *out,
453 char const **digits) {
454 char const *p = *digits;
455 char *pp;
456
457 if ('@' == *p) {
458 *out = ~(uintptr_t) 0;
459 *digits = p+1;
460 return 1;
461 }
462 *out = strtoul(p, &pp, 0);
463 if (pp != p) {
464 *digits = pp;
465 return 1;
466 }
467 return 0;
468 }
469
470 /*
471 * NaClFaultInjectionParsePassOrFailSeq consumes <pass_or_fail_seq> of
472 * the grammar from |fault_ctrl| until the terminating NUL character,
473 * filling out |out_info| as it goes.
474 */
NaClFaultInjectionParsePassOrFailSeq(struct NaClFaultInjectInfo * out_info,char const * fault_ctrl)475 static int NaClFaultInjectionParsePassOrFailSeq(
476 struct NaClFaultInjectInfo *out_info,
477 char const *fault_ctrl) {
478 struct NaClFaultExpr expr;
479
480 for (;;) {
481 switch (*fault_ctrl) {
482 case '\0':
483 return 1;
484 case 'P':
485 expr.pass = 1;
486 ++fault_ctrl;
487 if (!NaClFaultInjectionParseNumber(&expr.count, &fault_ctrl)) {
488 expr.count = 1;
489 }
490 expr.fault_value = 0; /* not used during injection execution */
491 NaClFaultInjectionParseHelperAddFaultExpr(out_info, &expr);
492 break;
493 case 'F':
494 expr.pass = 0;
495 ++fault_ctrl;
496 if (!NaClFaultInjectionParseNumber(&expr.fault_value, &fault_ctrl)) {
497 expr.fault_value = 0;
498 }
499 if ('/' == *fault_ctrl) {
500 ++fault_ctrl;
501 if (!NaClFaultInjectionParseNumber(&expr.count, &fault_ctrl)) {
502 NaClLog(LOG_ERROR,
503 "NaClLogInject: bad fault count\n");
504 return 0;
505 }
506 } else {
507 expr.count = 1;
508 }
509 NaClFaultInjectionParseHelperAddFaultExpr(out_info, &expr);
510 break;
511 default:
512 NaClLog(LOG_ERROR,
513 "NaClFaultInjection: expected 'P' or 'F', got '%c'\n",
514 *fault_ctrl);
515 return 0;
516 }
517 if ('+' == *fault_ctrl) {
518 ++fault_ctrl;
519 }
520 }
521 }
522
523 /*
524 * NaClFaultInjectionParseFaultControlExpr consumes
525 * <fault_control_expression> from |fault_ctrl| until the terminating
526 * ASCII NUL character, filling in |out_info|.
527 */
NaClFaultInjectionParseFaultControlExpr(struct NaClFaultInjectInfo * out_info,char const * fault_ctrl)528 static int NaClFaultInjectionParseFaultControlExpr(
529 struct NaClFaultInjectInfo *out_info,
530 char const *fault_ctrl) {
531 NaClLog(6, "NaClFaultInject: control sequence %s\n", fault_ctrl);
532 if ('T' == *fault_ctrl) {
533 out_info->thread_specific_p = 1;
534 ++fault_ctrl;
535 } else if ('G' == *fault_ctrl) {
536 out_info->thread_specific_p = 0;
537 ++fault_ctrl;
538 } else {
539 NaClLog(LOG_ERROR,
540 "NaClFaultInjection: fault control expression should indicate"
541 " if the counter is thread-local or global\n");
542 /*
543 * Should we default to global?
544 */
545 return 0;
546 }
547 return NaClFaultInjectionParsePassOrFailSeq(out_info, fault_ctrl);
548 }
549
NaClFaultInjectionParseConfigEntry(struct NaClFaultInjectInfo * out_info,char * entry_start)550 static int NaClFaultInjectionParseConfigEntry(
551 struct NaClFaultInjectInfo *out_info,
552 char *entry_start) {
553 char *equal = strchr(entry_start, '=');
554 if (NULL == equal) {
555 NaClLog(LOG_ERROR,
556 "NaClFaultInject: control entry %s malformed, no equal sign\n",
557 entry_start);
558 return 0;
559 }
560 out_info->call_site_name = strndup(entry_start, equal - entry_start);
561 /* Leave as CHECK since otherwise using process would die soon anyway */
562 CHECK(NULL != out_info->call_site_name);
563 out_info->thread_specific_p = 0;
564 out_info->expr = NULL;
565 out_info->num_expr = 0;
566 out_info->size_expr = 0;
567
568 return NaClFaultInjectionParseFaultControlExpr(out_info, equal+1);
569 }
570
NaClFaultInjectionModuleInternalInit(void)571 void NaClFaultInjectionModuleInternalInit(void) {
572 char *config;
573 char *cur_entry;
574 char *sep;
575 char *next_entry;
576 struct NaClFaultInjectInfo fi_entry;
577
578 #if (NACL_USE_TSD || NACL_USE_TLSALLOC)
579 NaClFaultInjectionThreadKeysCreate();
580 #endif
581
582 config = getenv("NACL_FAULT_INJECTION");
583 if (NULL == config) {
584 return;
585 }
586 /* get a definitely-mutable version that we will free later */
587 config = STRDUP(config);
588 /* Leave as CHECK since otherwise using process would die soon anyway */
589 CHECK(NULL != config);
590 for (cur_entry = config; '\0' != *cur_entry; cur_entry = next_entry) {
591 sep = strpbrk(cur_entry, ",:");
592 if (NULL == sep) {
593 sep = cur_entry + strlen(cur_entry);
594 next_entry = sep;
595 } else {
596 *sep = '\0';
597 next_entry = sep + 1;
598 }
599 /* parse cur_entry */
600 if (!NaClFaultInjectionParseConfigEntry(&fi_entry, cur_entry)) {
601 NaClLog(LOG_FATAL,
602 "NaClFaultInjection: syntax error in configuration; environment"
603 " variable NACL_FAULT_INJECTION contains %s, which is not"
604 " syntactically correct.\n",
605 cur_entry);
606 }
607 NaClFaultInjectAddEntry(&fi_entry);
608 }
609 free((void *) config);
610 NaClFaultInjectAllocGlobalCounters();
611 }
612
NaClFaultInjectionModuleInternalFini(void)613 void NaClFaultInjectionModuleInternalFini(void) {
614 size_t ix;
615
616 NaClFaultInjectFreeGlobalCounters();
617 for (ix = 0; ix < gNaClNumFaultInjectInfo; ++ix) {
618 free((void *) gNaClFaultInjectInfo[ix].call_site_name); /* strndup'd */
619 free((void *) gNaClFaultInjectInfo[ix].expr);
620 }
621 free((void *) gNaClFaultInjectInfo);
622 gNaClFaultInjectInfo = NULL;
623 gNaClNumFaultInjectInfo = 0;
624 gNaClSizeFaultInjectInfo = 0;
625 }
626
NaClFaultInjectionModuleInit(void)627 void NaClFaultInjectionModuleInit(void) {
628 static int initialized = 0;
629
630 if (initialized) {
631 return;
632 }
633 NaClFaultInjectionModuleInternalInit();
634 initialized = 1;
635 }
636
NaClFaultInjectionFaultP(char const * site_name)637 int NaClFaultInjectionFaultP(char const *site_name) {
638 int rv;
639 struct NaClFaultInjectInfo const *entry = NULL;
640 size_t ix;
641 struct NaClFaultInjectCallSiteCount *counter;
642 struct NaClFaultExpr *expr;
643
644 for (ix = 0; ix < gNaClNumFaultInjectInfo; ++ix) {
645 if (!strcmp(site_name, gNaClFaultInjectInfo[ix].call_site_name)) {
646 NaClLog(6, "NaClFaultInject: found %s\n", site_name);
647 break;
648 }
649 }
650 if (ix == gNaClNumFaultInjectInfo) {
651 return 0;
652 }
653 entry = &gNaClFaultInjectInfo[ix];
654 if (entry->thread_specific_p) {
655 NaClLog(6, "NaClFaultInject: thread-specific counter\n");
656 counter = NaClFaultInjectFindThreadCounter(ix);
657 } else {
658 NaClLog(6, "NaClFaultInject: global counter\n");
659 NaClXMutexLock(&gNaClFaultInjectMu[ix]);
660 counter = &gNaClFaultInjectCallSites[ix];
661 }
662 if (NULL == counter) {
663 return 0;
664 }
665 /*
666 * check counter against entry, and if a fault should be injected,
667 * set Value for NaClFaultInjectionValue and set return value to
668 * true; otherwise set return value false. bump counter.
669 */
670 NaClLog(6, "NaClFaultInject: counter(%"NACL_PRIxS",%"NACL_PRIxS")\n",
671 counter->expr_ix, counter->count_in_expr);
672 if (counter->expr_ix >= entry->num_expr) {
673 rv = 0;
674 } else {
675 expr = &entry->expr[counter->expr_ix];
676 if (expr->pass) {
677 rv = 0;
678 } else {
679 NaClLog(6, "NaClFaultInject: should fail, value %"NACL_PRIxPTR"\n",
680 expr->fault_value);
681 rv = 1;
682 NaClFaultInjectionSetValue(expr->fault_value);
683 }
684 /* bump counter, possibly carry */
685 if (++counter->count_in_expr >= expr->count) {
686 counter->count_in_expr = 0;
687 ++counter->expr_ix;
688 }
689 }
690 if (!entry->thread_specific_p) {
691 NaClXMutexUnlock(&gNaClFaultInjectMu[ix]);
692 }
693 return rv;
694 }
695
NaClFaultInjectionValue(void)696 uintptr_t NaClFaultInjectionValue(void) {
697 return NaClFaultInjectionGetValue();
698 }
699