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:
parent
fabe78d222
commit
36e7e8af78
3 changed files with 120 additions and 15 deletions
|
@ -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|
|
||||||
|
|
|
@ -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
|
||||||
*
|
*
|
||||||
|
|
|
@ -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;
|
||||||
|
|
Loading…
Add table
Reference in a new issue