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