mirror of
https://libwebsockets.org/repo/libwebsockets
synced 2024-11-24 01:39:33 +00:00
36e7e8af78
This adds an api allowing fault injection path implementations to get hold of pseudo-random numbers between an externally-provided range. You can set it using, eg, --fault-injection "f1(10%),f1_delay(123..456)" while f1 shows how to decide whether to inject the fault and f1_delay provides a pseudo-random number between the two values for the fault implementation code to use.
448 lines
9.6 KiB
C
448 lines
9.6 KiB
C
/*
|
|
* lws System Fault Injection
|
|
*
|
|
* Copyright (C) 2019 - 2021 Andy Green <andy@warmcat.com>
|
|
*
|
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
* of this software and associated documentation files (the "Software"), to
|
|
* deal in the Software without restriction, including without limitation the
|
|
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
|
* sell copies of the Software, and to permit persons to whom the Software is
|
|
* furnished to do so, subject to the following conditions:
|
|
*
|
|
* The above copyright notice and this permission notice shall be included in
|
|
* all copies or substantial portions of the Software.
|
|
*
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
|
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
|
* IN THE SOFTWARE.
|
|
*/
|
|
|
|
#include "private-lib-core.h"
|
|
|
|
#include <assert.h>
|
|
|
|
static lws_fi_priv_t *
|
|
lws_fi_lookup(const lws_fi_ctx_t *fic, const char *name)
|
|
{
|
|
lws_start_foreach_dll(struct lws_dll2 *, p, fic->fi_owner.head) {
|
|
lws_fi_priv_t *pv = lws_container_of(p, lws_fi_priv_t, list);
|
|
|
|
if (!strcmp(pv->fi.name, name))
|
|
return pv;
|
|
|
|
} lws_end_foreach_dll(p);
|
|
|
|
return NULL;
|
|
}
|
|
|
|
int
|
|
lws_fi(const lws_fi_ctx_t *fic, const char *name)
|
|
{
|
|
lws_fi_priv_t *pv;
|
|
int n;
|
|
|
|
pv = lws_fi_lookup(fic, name);
|
|
|
|
if (!pv)
|
|
return 0;
|
|
|
|
switch (pv->fi.type) {
|
|
case LWSFI_ALWAYS:
|
|
goto inject;
|
|
|
|
case LWSFI_DETERMINISTIC:
|
|
pv->fi.times++;
|
|
if (pv->fi.times >= pv->fi.pre)
|
|
if (pv->fi.times < pv->fi.pre + pv->fi.count)
|
|
goto inject;
|
|
return 0;
|
|
|
|
case LWSFI_PROBABILISTIC:
|
|
if (lws_xos_percent((lws_xos_t *)&fic->xos, (int)pv->fi.pre))
|
|
goto inject;
|
|
return 0;
|
|
|
|
case LWSFI_PATTERN:
|
|
case LWSFI_PATTERN_ALLOC:
|
|
n = (int)((pv->fi.times++) % pv->fi.count);
|
|
if (pv->fi.pattern[n >> 3] & (1 << (n & 7)))
|
|
goto inject;
|
|
|
|
return 0;
|
|
|
|
default:
|
|
return 0;
|
|
}
|
|
|
|
return 0;
|
|
|
|
inject:
|
|
lwsl_warn("%s: Injecting fault %s->%s\n", __func__,
|
|
fic->name ? fic->name : "unk", pv->fi.name);
|
|
|
|
return 1;
|
|
}
|
|
|
|
int
|
|
lws_fi_range(const lws_fi_ctx_t *fic, const char *name, uint64_t *result)
|
|
{
|
|
lws_fi_priv_t *pv;
|
|
uint64_t d;
|
|
|
|
pv = lws_fi_lookup(fic, name);
|
|
|
|
if (!pv)
|
|
return 1;
|
|
|
|
if (pv->fi.type != LWSFI_RANGE) {
|
|
lwsl_err("%s: fault %s is not a 123..456 range\n",
|
|
__func__, name);
|
|
return 1;
|
|
}
|
|
|
|
d = pv->fi.count - pv->fi.pre;
|
|
|
|
*result = pv->fi.pre + (lws_xos((lws_xos_t *)&fic->xos) % d);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
_lws_fi_user_wsi_fi(struct lws *wsi, const char *name)
|
|
{
|
|
return lws_fi(&wsi->fic, name);
|
|
}
|
|
|
|
int
|
|
_lws_fi_user_context_fi(struct lws_context *ctx, const char *name)
|
|
{
|
|
return lws_fi(&ctx->fic, name);
|
|
}
|
|
|
|
#if defined(LWS_WITH_SECURE_STREAMS)
|
|
int
|
|
_lws_fi_user_ss_fi(struct lws_ss_handle *h, const char *name)
|
|
{
|
|
return lws_fi(&h->fic, name);
|
|
}
|
|
|
|
#if defined(LWS_WITH_SECURE_STREAMS_PROXY_API)
|
|
int
|
|
_lws_fi_user_sspc_fi(struct lws_sspc_handle *h, const char *name)
|
|
{
|
|
return lws_fi(&h->fic, name);
|
|
}
|
|
#endif
|
|
#endif
|
|
|
|
int
|
|
lws_fi_add(lws_fi_ctx_t *fic, const lws_fi_t *fi)
|
|
{
|
|
lws_fi_priv_t *pv;
|
|
size_t n = strlen(fi->name);
|
|
|
|
pv = lws_malloc(sizeof(*pv) + n + 1, __func__);
|
|
if (!pv)
|
|
return 1;
|
|
|
|
lws_dll2_clear(&pv->list);
|
|
|
|
memcpy(&pv->fi, fi, sizeof(*fi));
|
|
pv->fi.name = (const char *)&pv[1];
|
|
memcpy(&pv[1], fi->name, n + 1);
|
|
|
|
lws_dll2_add_tail(&pv->list, &fic->fi_owner);
|
|
|
|
return 0;
|
|
}
|
|
|
|
void
|
|
lws_fi_remove(lws_fi_ctx_t *fic, const char *name)
|
|
{
|
|
lws_fi_priv_t *pv = lws_fi_lookup(fic, name);
|
|
|
|
if (!pv)
|
|
return;
|
|
|
|
lws_dll2_remove(&pv->list);
|
|
lws_free(pv);
|
|
}
|
|
|
|
void
|
|
lws_fi_import(lws_fi_ctx_t *fic_dest, const lws_fi_ctx_t *fic_src)
|
|
{
|
|
|
|
/* inherit the PRNG seed for our context from source guy too */
|
|
lws_xos_init(&fic_dest->xos, lws_xos((lws_xos_t *)&fic_src->xos));
|
|
|
|
lws_start_foreach_dll_safe(struct lws_dll2 *, p, p1,
|
|
fic_src->fi_owner.head) {
|
|
lws_fi_priv_t *pv = lws_container_of(p, lws_fi_priv_t, list);
|
|
|
|
lws_dll2_remove(&pv->list);
|
|
lws_dll2_add_tail(&pv->list, &fic_dest->fi_owner);
|
|
|
|
} lws_end_foreach_dll_safe(p, p1);
|
|
}
|
|
|
|
static void
|
|
do_inherit(lws_fi_ctx_t *fic_dest, lws_fi_t *pfi, size_t trim)
|
|
{
|
|
lws_fi_t fi = *pfi;
|
|
|
|
fi.name += trim;
|
|
|
|
lwsl_info("%s: %s: %s inherited as %s\n", __func__, fic_dest->name,
|
|
pfi->name, fi.name);
|
|
|
|
if (fi.type == LWSFI_PATTERN_ALLOC) {
|
|
fi.pattern = lws_malloc((size_t)((fi.count >> 3) + 1), __func__);
|
|
if (!fi.pattern)
|
|
return;
|
|
memcpy((uint8_t *)fi.pattern, pfi->pattern,
|
|
(size_t)((fi.count >> 3) + 1));
|
|
}
|
|
|
|
lws_fi_add(fic_dest, &fi);
|
|
}
|
|
|
|
void
|
|
lws_fi_inherit_copy(lws_fi_ctx_t *fic_dest, const lws_fi_ctx_t *fic_src,
|
|
const char *scope, const char *value)
|
|
{
|
|
size_t sl = 0, vl = 0;
|
|
|
|
if (scope)
|
|
sl = strlen(scope);
|
|
|
|
if (value)
|
|
vl = strlen(value);
|
|
|
|
lws_start_foreach_dll_safe(struct lws_dll2 *, p, p1,
|
|
fic_src->fi_owner.head) {
|
|
lws_fi_priv_t *pv = lws_container_of(p, lws_fi_priv_t, list);
|
|
size_t nl = strlen(pv->fi.name);
|
|
|
|
if (!scope)
|
|
do_inherit(fic_dest, &pv->fi, 0);
|
|
else
|
|
if (nl > sl + 2 &&
|
|
!strncmp(pv->fi.name, scope, sl) &&
|
|
pv->fi.name[sl] == '/')
|
|
do_inherit(fic_dest, &pv->fi, sl + 1);
|
|
else {
|
|
if (value && nl > sl + vl + 2 &&
|
|
pv->fi.name[sl] == '=' &&
|
|
!strncmp(pv->fi.name + sl + 1, value, vl) &&
|
|
pv->fi.name[sl + 1 + vl] == '/')
|
|
do_inherit(fic_dest, &pv->fi, sl + vl + 2);
|
|
}
|
|
|
|
} lws_end_foreach_dll_safe(p, p1);
|
|
}
|
|
|
|
void
|
|
lws_fi_destroy(const lws_fi_ctx_t *fic)
|
|
{
|
|
lws_start_foreach_dll_safe(struct lws_dll2 *, p, p1,
|
|
fic->fi_owner.head) {
|
|
lws_fi_priv_t *pv = lws_container_of(p, lws_fi_priv_t, list);
|
|
|
|
if (pv->fi.type == LWSFI_PATTERN_ALLOC && pv->fi.pattern) {
|
|
lws_free((void *)pv->fi.pattern);
|
|
pv->fi.pattern = NULL;
|
|
}
|
|
|
|
lws_dll2_remove(&pv->list);
|
|
lws_free(pv);
|
|
|
|
} lws_end_foreach_dll_safe(p, p1);
|
|
}
|
|
|
|
/*
|
|
* We want to support these kinds of qualifier
|
|
*
|
|
* myfault true always
|
|
* myfault(10%) true 10% of the time
|
|
* myfault(....X X) true when X
|
|
* myfault2(20..3000) pick a number between 20 and 3000
|
|
*/
|
|
|
|
enum {
|
|
PARSE_NAME,
|
|
PARSE_WHEN,
|
|
PARSE_PC,
|
|
PARSE_ENDBR,
|
|
PARSE_COMMA
|
|
};
|
|
|
|
void
|
|
lws_fi_deserialize(lws_fi_ctx_t *fic, const char *sers)
|
|
{
|
|
int state = PARSE_NAME, m;
|
|
struct lws_tokenize ts;
|
|
lws_fi_t fi;
|
|
char nm[64];
|
|
|
|
/*
|
|
* Go through the comma-separated list of faults
|
|
* creating them and adding to the lws_context info
|
|
*/
|
|
|
|
lws_tokenize_init(&ts, sers, LWS_TOKENIZE_F_DOT_NONTERM |
|
|
LWS_TOKENIZE_F_NO_INTEGERS |
|
|
LWS_TOKENIZE_F_NO_FLOATS |
|
|
LWS_TOKENIZE_F_EQUALS_NONTERM |
|
|
LWS_TOKENIZE_F_SLASH_NONTERM |
|
|
LWS_TOKENIZE_F_MINUS_NONTERM);
|
|
ts.len = (unsigned int)strlen(sers);
|
|
if (ts.len < 1 || ts.len > 10240)
|
|
return;
|
|
|
|
do {
|
|
ts.e = (int8_t)lws_tokenize(&ts);
|
|
switch (ts.e) {
|
|
case LWS_TOKZE_TOKEN:
|
|
|
|
if (state == PARSE_NAME) {
|
|
/*
|
|
* One fault to inject looks like, eg,
|
|
*
|
|
* vh=xxx/listenskt
|
|
*/
|
|
|
|
memset(&fi, 0, sizeof(fi));
|
|
|
|
lws_strnncpy(nm, ts.token, ts.token_len,
|
|
sizeof(nm));
|
|
fi.name = nm;
|
|
fi.type = LWSFI_ALWAYS;
|
|
|
|
lwsl_notice("%s: name %.*s\n", __func__,
|
|
(int)ts.token_len, ts.token);
|
|
|
|
/* added later, potentially after (when) */
|
|
break;
|
|
}
|
|
if (state == PARSE_WHEN) {
|
|
/* it's either numeric (then % or ..num2), or
|
|
* .X pattern */
|
|
|
|
lwsl_notice("%s: when\n", __func__);
|
|
|
|
if (*ts.token == '.' || *ts.token == 'X') {
|
|
uint8_t *pat;
|
|
size_t n;
|
|
|
|
/*
|
|
* pattern... we need to allocate it
|
|
*/
|
|
fi.type = LWSFI_PATTERN_ALLOC;
|
|
pat = lws_zalloc((ts.token_len >> 3) + 1,
|
|
__func__);
|
|
if (!pat)
|
|
return;
|
|
fi.pattern = pat;
|
|
fi.count = (uint64_t)ts.token_len;
|
|
|
|
for (n = 0; n < ts.token_len; n++)
|
|
if (ts.token[n] == 'X')
|
|
pat[n >> 3] = (uint8_t)(
|
|
pat[n >> 3] |
|
|
(1 << (n & 7)));
|
|
|
|
lwsl_hexdump_notice(pat,
|
|
(ts.token_len >> 3) + 1);
|
|
|
|
state = PARSE_ENDBR;
|
|
break;
|
|
}
|
|
|
|
fi.pre = (uint64_t)atoll(ts.token);
|
|
|
|
for (m = 0; m < (int)ts.token_len - 1; m++)
|
|
if (ts.token[m] < '0' ||
|
|
ts.token[m] > '9')
|
|
break;
|
|
|
|
/*
|
|
* We can understand num% or num..num
|
|
*/
|
|
|
|
if (m != (int)ts.token_len &&
|
|
ts.token[m] == '.' &&
|
|
ts.token[m + 1] == '.') {
|
|
fi.count = (uint64_t)atoll(
|
|
&ts.token[m + 2]);
|
|
fi.type = LWSFI_RANGE;
|
|
state = PARSE_ENDBR;
|
|
|
|
if (fi.pre >= fi.count) {
|
|
lwsl_err("%s: range must have "
|
|
"smaller first!\n",
|
|
__func__);
|
|
}
|
|
|
|
lwsl_notice("%s: range %llx .."
|
|
"%llx\n", __func__,
|
|
(unsigned long long)fi.pre,
|
|
(unsigned long long)fi.count);
|
|
break;
|
|
}
|
|
|
|
lwsl_notice("%s: prob %d%%\n", __func__,
|
|
(int)fi.pre);
|
|
fi.type = LWSFI_PROBABILISTIC;
|
|
state = PARSE_PC;
|
|
break;
|
|
}
|
|
break;
|
|
|
|
case LWS_TOKZE_DELIMITER:
|
|
if (*ts.token == ',') {
|
|
lws_fi_add(fic, &fi);
|
|
state = PARSE_NAME;
|
|
break;
|
|
}
|
|
if (*ts.token == '(') {
|
|
lwsl_notice("%s: (\n", __func__);
|
|
if (state != PARSE_NAME) {
|
|
lwsl_err("%s: misplaced (\n", __func__);
|
|
return;
|
|
}
|
|
state = PARSE_WHEN;
|
|
break;
|
|
}
|
|
if (*ts.token == ')') {
|
|
if (state != PARSE_ENDBR) {
|
|
lwsl_err("%s: misplaced )\n", __func__);
|
|
return;
|
|
}
|
|
state = PARSE_NAME;
|
|
break;
|
|
}
|
|
if (*ts.token == '%') {
|
|
if (state != PARSE_PC) {
|
|
lwsl_err("%s: misplaced %%\n", __func__);
|
|
return;
|
|
}
|
|
state = PARSE_ENDBR;
|
|
break;
|
|
}
|
|
break;
|
|
|
|
case LWS_TOKZE_ENDED:
|
|
lws_fi_add(fic, &fi);
|
|
return;
|
|
|
|
default:
|
|
return;
|
|
}
|
|
} while (ts.e > 0);
|
|
}
|