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