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