1 /*
2 * lws System Fault Injection
3 *
4 * Copyright (C) 2019 - 2021 Andy Green <andy@warmcat.com>
5 *
6 * Permission is hereby granted, free of charge, to any person obtaining a copy
7 * of this software and associated documentation files (the "Software"), to
8 * deal in the Software without restriction, including without limitation the
9 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
10 * sell copies of the Software, and to permit persons to whom the Software is
11 * furnished to do so, subject to the following conditions:
12 *
13 * The above copyright notice and this permission notice shall be included in
14 * all copies or substantial portions of the Software.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
22 * IN THE SOFTWARE.
23 */
24
25 #include "private-lib-core.h"
26
27 #include <assert.h>
28
29 static lws_fi_priv_t *
lws_fi_lookup(const lws_fi_ctx_t * fic,const char * name)30 lws_fi_lookup(const lws_fi_ctx_t *fic, const char *name)
31 {
32 lws_start_foreach_dll(struct lws_dll2 *, p, fic->fi_owner.head) {
33 lws_fi_priv_t *pv = lws_container_of(p, lws_fi_priv_t, list);
34
35 if (!strcmp(pv->fi.name, name))
36 return pv;
37
38 } lws_end_foreach_dll(p);
39
40 return NULL;
41 }
42
43 int
lws_fi(const lws_fi_ctx_t * fic,const char * name)44 lws_fi(const lws_fi_ctx_t *fic, const char *name)
45 {
46 lws_fi_priv_t *pv;
47 int n;
48
49 pv = lws_fi_lookup(fic, name);
50
51 if (!pv)
52 return 0;
53
54 switch (pv->fi.type) {
55 case LWSFI_ALWAYS:
56 goto inject;
57
58 case LWSFI_DETERMINISTIC:
59 pv->fi.times++;
60 if (pv->fi.times >= pv->fi.pre)
61 if (pv->fi.times < pv->fi.pre + pv->fi.count)
62 goto inject;
63 return 0;
64
65 case LWSFI_PROBABILISTIC:
66 if (lws_xos_percent((lws_xos_t *)&fic->xos, (int)pv->fi.pre))
67 goto inject;
68 return 0;
69
70 case LWSFI_PATTERN:
71 case LWSFI_PATTERN_ALLOC:
72 n = (int)((pv->fi.times++) % pv->fi.count);
73 if (pv->fi.pattern[n >> 3] & (1 << (n & 7)))
74 goto inject;
75
76 return 0;
77
78 default:
79 return 0;
80 }
81
82 return 0;
83
84 inject:
85 lwsl_warn("%s: Injecting fault %s->%s\n", __func__,
86 fic->name ? fic->name : "unk", pv->fi.name);
87
88 return 1;
89 }
90
91 int
lws_fi_range(const lws_fi_ctx_t * fic,const char * name,uint64_t * result)92 lws_fi_range(const lws_fi_ctx_t *fic, const char *name, uint64_t *result)
93 {
94 lws_fi_priv_t *pv;
95 uint64_t d;
96
97 pv = lws_fi_lookup(fic, name);
98
99 if (!pv)
100 return 1;
101
102 if (pv->fi.type != LWSFI_RANGE) {
103 lwsl_err("%s: fault %s is not a 123..456 range\n",
104 __func__, name);
105 return 1;
106 }
107
108 d = pv->fi.count - pv->fi.pre;
109
110 *result = pv->fi.pre + (lws_xos((lws_xos_t *)&fic->xos) % d);
111
112 return 0;
113 }
114
115 int
_lws_fi_user_wsi_fi(struct lws * wsi,const char * name)116 _lws_fi_user_wsi_fi(struct lws *wsi, const char *name)
117 {
118 return lws_fi(&wsi->fic, name);
119 }
120
121 int
_lws_fi_user_context_fi(struct lws_context * ctx,const char * name)122 _lws_fi_user_context_fi(struct lws_context *ctx, const char *name)
123 {
124 return lws_fi(&ctx->fic, name);
125 }
126
127 #if defined(LWS_WITH_SECURE_STREAMS)
128 int
_lws_fi_user_ss_fi(struct lws_ss_handle * h,const char * name)129 _lws_fi_user_ss_fi(struct lws_ss_handle *h, const char *name)
130 {
131 return lws_fi(&h->fic, name);
132 }
133
134 #if defined(LWS_WITH_SECURE_STREAMS_PROXY_API)
135 int
_lws_fi_user_sspc_fi(struct lws_sspc_handle * h,const char * name)136 _lws_fi_user_sspc_fi(struct lws_sspc_handle *h, const char *name)
137 {
138 return lws_fi(&h->fic, name);
139 }
140 #endif
141 #endif
142
143 int
lws_fi_add(lws_fi_ctx_t * fic,const lws_fi_t * fi)144 lws_fi_add(lws_fi_ctx_t *fic, const lws_fi_t *fi)
145 {
146 lws_fi_priv_t *pv;
147 size_t n = strlen(fi->name);
148
149 pv = lws_malloc(sizeof(*pv) + n + 1, __func__);
150 if (!pv)
151 return 1;
152
153 lws_dll2_clear(&pv->list);
154
155 memcpy(&pv->fi, fi, sizeof(*fi));
156 pv->fi.name = (const char *)&pv[1];
157 memcpy(&pv[1], fi->name, n + 1);
158
159 lws_dll2_add_tail(&pv->list, &fic->fi_owner);
160
161 return 0;
162 }
163
164 void
lws_fi_remove(lws_fi_ctx_t * fic,const char * name)165 lws_fi_remove(lws_fi_ctx_t *fic, const char *name)
166 {
167 lws_fi_priv_t *pv = lws_fi_lookup(fic, name);
168
169 if (!pv)
170 return;
171
172 lws_dll2_remove(&pv->list);
173 lws_free(pv);
174 }
175
176 void
lws_fi_import(lws_fi_ctx_t * fic_dest,const lws_fi_ctx_t * fic_src)177 lws_fi_import(lws_fi_ctx_t *fic_dest, const lws_fi_ctx_t *fic_src)
178 {
179
180 /* inherit the PRNG seed for our context from source guy too */
181 lws_xos_init(&fic_dest->xos, lws_xos((lws_xos_t *)&fic_src->xos));
182
183 lws_start_foreach_dll_safe(struct lws_dll2 *, p, p1,
184 fic_src->fi_owner.head) {
185 lws_fi_priv_t *pv = lws_container_of(p, lws_fi_priv_t, list);
186
187 lws_dll2_remove(&pv->list);
188 lws_dll2_add_tail(&pv->list, &fic_dest->fi_owner);
189
190 } lws_end_foreach_dll_safe(p, p1);
191 }
192
193 static void
do_inherit(lws_fi_ctx_t * fic_dest,lws_fi_t * pfi,size_t trim)194 do_inherit(lws_fi_ctx_t *fic_dest, lws_fi_t *pfi, size_t trim)
195 {
196 lws_fi_t fi = *pfi;
197
198 fi.name += trim;
199
200 lwsl_info("%s: %s: %s inherited as %s\n", __func__, fic_dest->name,
201 pfi->name, fi.name);
202
203 if (fi.type == LWSFI_PATTERN_ALLOC) {
204 fi.pattern = lws_malloc((size_t)((fi.count >> 3) + 1), __func__);
205 if (!fi.pattern)
206 return;
207 memcpy((uint8_t *)fi.pattern, pfi->pattern,
208 (size_t)((fi.count >> 3) + 1));
209 }
210
211 lws_fi_add(fic_dest, &fi);
212 }
213
214 void
lws_fi_inherit_copy(lws_fi_ctx_t * fic_dest,const lws_fi_ctx_t * fic_src,const char * scope,const char * value)215 lws_fi_inherit_copy(lws_fi_ctx_t *fic_dest, const lws_fi_ctx_t *fic_src,
216 const char *scope, const char *value)
217 {
218 size_t sl = 0, vl = 0;
219
220 if (scope)
221 sl = strlen(scope);
222
223 if (value)
224 vl = strlen(value);
225
226 lws_start_foreach_dll_safe(struct lws_dll2 *, p, p1,
227 fic_src->fi_owner.head) {
228 lws_fi_priv_t *pv = lws_container_of(p, lws_fi_priv_t, list);
229 size_t nl = strlen(pv->fi.name);
230
231 if (!scope)
232 do_inherit(fic_dest, &pv->fi, 0);
233 else
234 if (nl > sl + 2 &&
235 !strncmp(pv->fi.name, scope, sl) &&
236 pv->fi.name[sl] == '/')
237 do_inherit(fic_dest, &pv->fi, sl + 1);
238 else {
239 if (value && nl > sl + vl + 2 &&
240 pv->fi.name[sl] == '=' &&
241 !strncmp(pv->fi.name + sl + 1, value, vl) &&
242 pv->fi.name[sl + 1 + vl] == '/')
243 do_inherit(fic_dest, &pv->fi, sl + vl + 2);
244 }
245
246 } lws_end_foreach_dll_safe(p, p1);
247 }
248
249 void
lws_fi_destroy(const lws_fi_ctx_t * fic)250 lws_fi_destroy(const lws_fi_ctx_t *fic)
251 {
252 lws_start_foreach_dll_safe(struct lws_dll2 *, p, p1,
253 fic->fi_owner.head) {
254 lws_fi_priv_t *pv = lws_container_of(p, lws_fi_priv_t, list);
255
256 if (pv->fi.type == LWSFI_PATTERN_ALLOC && pv->fi.pattern) {
257 lws_free((void *)pv->fi.pattern);
258 pv->fi.pattern = NULL;
259 }
260
261 lws_dll2_remove(&pv->list);
262 lws_free(pv);
263
264 } lws_end_foreach_dll_safe(p, p1);
265 }
266
267 /*
268 * We want to support these kinds of qualifier
269 *
270 * myfault true always
271 * myfault(10%) true 10% of the time
272 * myfault(....X X) true when X
273 * myfault2(20..3000) pick a number between 20 and 3000
274 */
275
276 enum {
277 PARSE_NAME,
278 PARSE_WHEN,
279 PARSE_PC,
280 PARSE_ENDBR,
281 PARSE_COMMA
282 };
283
284 void
lws_fi_deserialize(lws_fi_ctx_t * fic,const char * sers)285 lws_fi_deserialize(lws_fi_ctx_t *fic, const char *sers)
286 {
287 int state = PARSE_NAME, m;
288 struct lws_tokenize ts;
289 lws_fi_t fi;
290 char nm[64];
291
292 /*
293 * Go through the comma-separated list of faults
294 * creating them and adding to the lws_context info
295 */
296
297 lws_tokenize_init(&ts, sers, LWS_TOKENIZE_F_DOT_NONTERM |
298 LWS_TOKENIZE_F_NO_INTEGERS |
299 LWS_TOKENIZE_F_NO_FLOATS |
300 LWS_TOKENIZE_F_EQUALS_NONTERM |
301 LWS_TOKENIZE_F_SLASH_NONTERM |
302 LWS_TOKENIZE_F_MINUS_NONTERM);
303 ts.len = (unsigned int)strlen(sers);
304 if (ts.len < 1 || ts.len > 10240)
305 return;
306
307 do {
308 ts.e = (int8_t)lws_tokenize(&ts);
309 switch (ts.e) {
310 case LWS_TOKZE_TOKEN:
311
312 if (state == PARSE_NAME) {
313 /*
314 * One fault to inject looks like, eg,
315 *
316 * vh=xxx/listenskt
317 */
318
319 memset(&fi, 0, sizeof(fi));
320
321 lws_strnncpy(nm, ts.token, ts.token_len,
322 sizeof(nm));
323 fi.name = nm;
324 fi.type = LWSFI_ALWAYS;
325
326 lwsl_notice("%s: name %.*s\n", __func__,
327 (int)ts.token_len, ts.token);
328
329 /* added later, potentially after (when) */
330 break;
331 }
332 if (state == PARSE_WHEN) {
333 /* it's either numeric (then % or ..num2), or
334 * .X pattern */
335
336 lwsl_notice("%s: when\n", __func__);
337
338 if (*ts.token == '.' || *ts.token == 'X') {
339 uint8_t *pat;
340 size_t n;
341
342 /*
343 * pattern... we need to allocate it
344 */
345 fi.type = LWSFI_PATTERN_ALLOC;
346 pat = lws_zalloc((ts.token_len >> 3) + 1,
347 __func__);
348 if (!pat)
349 return;
350 fi.pattern = pat;
351 fi.count = (uint64_t)ts.token_len;
352
353 for (n = 0; n < ts.token_len; n++)
354 if (ts.token[n] == 'X')
355 pat[n >> 3] = (uint8_t)(
356 pat[n >> 3] |
357 (1 << (n & 7)));
358
359 lwsl_hexdump_notice(pat,
360 (ts.token_len >> 3) + 1);
361
362 state = PARSE_ENDBR;
363 break;
364 }
365
366 fi.pre = (uint64_t)atoll(ts.token);
367
368 for (m = 0; m < (int)ts.token_len - 1; m++)
369 if (ts.token[m] < '0' ||
370 ts.token[m] > '9')
371 break;
372
373 /*
374 * We can understand num% or num..num
375 */
376
377 if (m != (int)ts.token_len &&
378 ts.token[m] == '.' &&
379 ts.token[m + 1] == '.') {
380 fi.count = (uint64_t)atoll(
381 &ts.token[m + 2]);
382 fi.type = LWSFI_RANGE;
383 state = PARSE_ENDBR;
384
385 if (fi.pre >= fi.count) {
386 lwsl_err("%s: range must have "
387 "smaller first!\n",
388 __func__);
389 }
390
391 lwsl_notice("%s: range %llx .."
392 "%llx\n", __func__,
393 (unsigned long long)fi.pre,
394 (unsigned long long)fi.count);
395 break;
396 }
397
398 lwsl_notice("%s: prob %d%%\n", __func__,
399 (int)fi.pre);
400 fi.type = LWSFI_PROBABILISTIC;
401 state = PARSE_PC;
402 break;
403 }
404 break;
405
406 case LWS_TOKZE_DELIMITER:
407 if (*ts.token == ',') {
408 lws_fi_add(fic, &fi);
409 state = PARSE_NAME;
410 break;
411 }
412 if (*ts.token == '(') {
413 lwsl_notice("%s: (\n", __func__);
414 if (state != PARSE_NAME) {
415 lwsl_err("%s: misplaced (\n", __func__);
416 return;
417 }
418 state = PARSE_WHEN;
419 break;
420 }
421 if (*ts.token == ')') {
422 if (state != PARSE_ENDBR) {
423 lwsl_err("%s: misplaced )\n", __func__);
424 return;
425 }
426 state = PARSE_NAME;
427 break;
428 }
429 if (*ts.token == '%') {
430 if (state != PARSE_PC) {
431 lwsl_err("%s: misplaced %%\n", __func__);
432 return;
433 }
434 state = PARSE_ENDBR;
435 break;
436 }
437 break;
438
439 case LWS_TOKZE_ENDED:
440 lws_fi_add(fic, &fi);
441 return;
442
443 default:
444 return;
445 }
446 } while (ts.e > 0);
447 }
448