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
29static lws_fi_priv_t *
30lws_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
43int
44lws_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
84inject:
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
91int
92lws_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
115int
116_lws_fi_user_wsi_fi(struct lws *wsi, const char *name)
117{
118	return lws_fi(&wsi->fic, name);
119}
120
121int
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)
128int
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)
135int
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
143int
144lws_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
164void
165lws_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
176void
177lws_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
193static void
194do_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
214void
215lws_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
249void
250lws_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
276enum {
277	PARSE_NAME,
278	PARSE_WHEN,
279	PARSE_PC,
280	PARSE_ENDBR,
281	PARSE_COMMA
282};
283
284void
285lws_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