1
0
Fork 0
mirror of https://github.com/warmcat/libwebsockets.git synced 2025-03-30 00:00:16 +01:00

fault injection: pseudorandom 64-bit range support

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.
This commit is contained in:
Andy Green 2021-06-29 11:54:26 +01:00
parent fabe78d222
commit 36e7e8af78
3 changed files with 120 additions and 15 deletions

View file

@ -232,15 +232,26 @@ matches an object, the fault will be injected every time. It's also possible
to make the fault inject itself at a random probability, or in a cyclic pattern, to make the fault inject itself at a random probability, or in a cyclic pattern,
by giving additional information in brackets, eg by giving additional information in brackets, eg
|Syntax|Meaning| |Syntax|Used with|Meaning|
|---|---| |---|---|---|
|`wsi/thefault`|Inject the fault every time| |`wsi/thefault`|lws_fi()|Inject the fault every time|
|`wsi/thefault(10%)`|Randomly inject the fault at 10% probability| |`wsi/thefault(10%)`|lws_fi()|Randomly inject the fault at 10% probability|
|`wsi/thefault(.............X.X)`|Inject the fault on the 14th and 16th try, every 16 tries| |`wsi/thefault(.............X.X)`|lws_fi()|Inject the fault on the 14th and 16th try, every 16 tries|
|`wsi/thefault2(123..456)`|lws_fi_range()|Pick a number between 123 and 456|
You must quote the strings containing these symbols, since they may otherwise be You must quote the strings containing these symbols, since they may otherwise be
interpreted by your shell. interpreted by your shell.
The last example above does not decide whether to inject the fault via `lws_fi()`
like the others. Instead you can use it via `lws_fi_range()` as part of the
fault processing, on a secondary fault injection name. For example you may have
a fault `myfault` you use with `lws_fi()` to decide when to inject the fault,
and then a second, related fault name `myfault_delay` to allow you to add code
to delay the fault action by some random amount of ms within an externally-
given range. You can get a pseudo-random number within the externally-given
range by calling `lws_fi_range()` on `myfault_delay`, and control the whole
thing by giving, eg, `"myfault(10%),myfault_delay(123..456)"`
## Well-known fault names in lws ## Well-known fault names in lws
|Scope|Namespc|Name|Fault effect| |Scope|Namespc|Name|Fault effect|

View file

@ -72,6 +72,7 @@ enum {
LWSFI_PROBABILISTIC, /* .pre % chance of injection */ LWSFI_PROBABILISTIC, /* .pre % chance of injection */
LWSFI_PATTERN, /* use .count bits in .pattern after .pre */ LWSFI_PATTERN, /* use .count bits in .pattern after .pre */
LWSFI_PATTERN_ALLOC, /* as _PATTERN, but .pattern is malloc'd */ LWSFI_PATTERN_ALLOC, /* as _PATTERN, but .pattern is malloc'd */
LWSFI_RANGE /* pick a number between pre and count */
}; };
typedef struct lws_fi { typedef struct lws_fi {
@ -108,6 +109,27 @@ typedef struct lws_fi_ctx {
LWS_VISIBLE LWS_EXTERN int LWS_VISIBLE LWS_EXTERN int
lws_fi(const lws_fi_ctx_t *fic, const char *fi_name); lws_fi(const lws_fi_ctx_t *fic, const char *fi_name);
/**
* lws_fi_range() - get a random number from a range
*
* \param fic: fault injection tracking context
* \param fi_name: name of fault injection
* \param result: points to uint64_t to be set to the result
*
* This lets you get a random number from an externally-set range, set using a
* fault injection syntax like "myfault(123..456)". That will cause us to
* return a number between those two inclusive, from the seeded PRNG.
*
* This is useful when you used lws_fi() with its own fault name to decide
* whether to inject the fault, and then the code to cause the fault needs
* additional constrained pseudo-random fuzzing for, eg, delays before issuing
* the fault.
*
* Returns 0 if \p *result is set, else nonzero for failure.
*/
LWS_VISIBLE LWS_EXTERN int
lws_fi_range(const lws_fi_ctx_t *fic, const char *name, uint64_t *result);
/** /**
* lws_fi_add() - add an allocated copy of fault injection to a context * lws_fi_add() - add an allocated copy of fault injection to a context
* *

View file

@ -88,6 +88,30 @@ inject:
return 1; 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 int
_lws_fi_user_wsi_fi(struct lws *wsi, const char *name) _lws_fi_user_wsi_fi(struct lws *wsi, const char *name)
{ {
@ -240,20 +264,30 @@ lws_fi_destroy(const lws_fi_ctx_t *fic)
} lws_end_foreach_dll_safe(p, p1); } 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 { enum {
PARSE_NAME, PARSE_NAME,
PARSE_WHEN, PARSE_WHEN,
PARSE_PC, PARSE_PC,
PARSE_ENDBR PARSE_ENDBR,
PARSE_COMMA
}; };
void void
lws_fi_deserialize(lws_fi_ctx_t *fic, const char *sers) lws_fi_deserialize(lws_fi_ctx_t *fic, const char *sers)
{ {
int state = PARSE_NAME, m;
struct lws_tokenize ts; struct lws_tokenize ts;
lws_fi_t fi; lws_fi_t fi;
char nm[64]; char nm[64];
int state = PARSE_NAME;
/* /*
* Go through the comma-separated list of faults * Go through the comma-separated list of faults
@ -284,17 +318,20 @@ lws_fi_deserialize(lws_fi_ctx_t *fic, const char *sers)
memset(&fi, 0, sizeof(fi)); memset(&fi, 0, sizeof(fi));
lws_strnncpy(nm, ts.token, ts.token_len, sizeof(nm)); lws_strnncpy(nm, ts.token, ts.token_len,
sizeof(nm));
fi.name = nm; fi.name = nm;
fi.type = LWSFI_ALWAYS; fi.type = LWSFI_ALWAYS;
lwsl_notice("%s: name %.*s\n", __func__, (int)ts.token_len, ts.token); lwsl_notice("%s: name %.*s\n", __func__,
(int)ts.token_len, ts.token);
/* added later, potentially after (when) */ /* added later, potentially after (when) */
break; break;
} }
if (state == PARSE_WHEN) { if (state == PARSE_WHEN) {
/* it's either numeric or a pattern */ /* it's either numeric (then % or ..num2), or
* .X pattern */
lwsl_notice("%s: when\n", __func__); lwsl_notice("%s: when\n", __func__);
@ -306,7 +343,8 @@ lws_fi_deserialize(lws_fi_ctx_t *fic, const char *sers)
* pattern... we need to allocate it * pattern... we need to allocate it
*/ */
fi.type = LWSFI_PATTERN_ALLOC; fi.type = LWSFI_PATTERN_ALLOC;
pat = lws_zalloc((ts.token_len >> 3) + 1, __func__); pat = lws_zalloc((ts.token_len >> 3) + 1,
__func__);
if (!pat) if (!pat)
return; return;
fi.pattern = pat; fi.pattern = pat;
@ -315,16 +353,50 @@ lws_fi_deserialize(lws_fi_ctx_t *fic, const char *sers)
for (n = 0; n < ts.token_len; n++) for (n = 0; n < ts.token_len; n++)
if (ts.token[n] == 'X') if (ts.token[n] == 'X')
pat[n >> 3] = (uint8_t)( pat[n >> 3] = (uint8_t)(
pat[n >> 3] | (1 << (n & 7))); pat[n >> 3] |
(1 << (n & 7)));
lwsl_hexdump_notice(pat, (ts.token_len >> 3) + 1); lwsl_hexdump_notice(pat,
(ts.token_len >> 3) + 1);
state = PARSE_ENDBR; state = PARSE_ENDBR;
break; break;
} }
fi.pre = (uint64_t)atoi(ts.token); fi.pre = (uint64_t)atoll(ts.token);
lwsl_notice("%s: prob %d%%\n", __func__, (int)fi.pre);
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; fi.type = LWSFI_PROBABILISTIC;
state = PARSE_PC; state = PARSE_PC;
break; break;