Add backtrace-based error path testing mechanism
The new TEST_FAIL and GET_FAIL control interface commands can be used similarly to the earlier TEST_ALLOC_FAIL/GET_ALLOC_FAIL design. The new version is more generic framework allowing any function to be annotated for failure testing with the TEST_FAIL() macro. This mechanism is only available in builds with CONFIG_WPA_TRACE_BFD=y and CONFIG_TESTING_OPTIONS=y. For other builds, the TEST_FAIL() macro is defined to return 0 to allow the compiler to remove the test code from normal production builds. As the first test site, allow os_get_random() to be marked for failing based on call backtrace. Signed-off-by: Jouni Malinen <j@w1.fi>
This commit is contained in:
parent
0937ea746b
commit
2da525651d
4 changed files with 170 additions and 0 deletions
|
@ -1746,6 +1746,45 @@ static int hostapd_ctrl_get_alloc_fail(struct hostapd_data *hapd,
|
||||||
#endif /* WPA_TRACE_BFD */
|
#endif /* WPA_TRACE_BFD */
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static int hostapd_ctrl_test_fail(struct hostapd_data *hapd, char *cmd)
|
||||||
|
{
|
||||||
|
#ifdef WPA_TRACE_BFD
|
||||||
|
extern char wpa_trace_test_fail_func[256];
|
||||||
|
extern unsigned int wpa_trace_test_fail_after;
|
||||||
|
char *pos;
|
||||||
|
|
||||||
|
wpa_trace_test_fail_after = atoi(cmd);
|
||||||
|
pos = os_strchr(cmd, ':');
|
||||||
|
if (pos) {
|
||||||
|
pos++;
|
||||||
|
os_strlcpy(wpa_trace_test_fail_func, pos,
|
||||||
|
sizeof(wpa_trace_test_fail_func));
|
||||||
|
} else {
|
||||||
|
wpa_trace_test_fail_after = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
#else /* WPA_TRACE_BFD */
|
||||||
|
return -1;
|
||||||
|
#endif /* WPA_TRACE_BFD */
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static int hostapd_ctrl_get_fail(struct hostapd_data *hapd,
|
||||||
|
char *buf, size_t buflen)
|
||||||
|
{
|
||||||
|
#ifdef WPA_TRACE_BFD
|
||||||
|
extern char wpa_trace_test_fail_func[256];
|
||||||
|
extern unsigned int wpa_trace_test_fail_after;
|
||||||
|
|
||||||
|
return os_snprintf(buf, buflen, "%u:%s", wpa_trace_test_fail_after,
|
||||||
|
wpa_trace_test_fail_func);
|
||||||
|
#else /* WPA_TRACE_BFD */
|
||||||
|
return -1;
|
||||||
|
#endif /* WPA_TRACE_BFD */
|
||||||
|
}
|
||||||
|
|
||||||
#endif /* CONFIG_TESTING_OPTIONS */
|
#endif /* CONFIG_TESTING_OPTIONS */
|
||||||
|
|
||||||
|
|
||||||
|
@ -2079,6 +2118,11 @@ static void hostapd_ctrl_iface_receive(int sock, void *eloop_ctx,
|
||||||
} else if (os_strcmp(buf, "GET_ALLOC_FAIL") == 0) {
|
} else if (os_strcmp(buf, "GET_ALLOC_FAIL") == 0) {
|
||||||
reply_len = hostapd_ctrl_get_alloc_fail(hapd, reply,
|
reply_len = hostapd_ctrl_get_alloc_fail(hapd, reply,
|
||||||
reply_size);
|
reply_size);
|
||||||
|
} else if (os_strncmp(buf, "TEST_FAIL ", 10) == 0) {
|
||||||
|
if (hostapd_ctrl_test_fail(hapd, buf + 10) < 0)
|
||||||
|
reply_len = -1;
|
||||||
|
} else if (os_strcmp(buf, "GET_FAIL") == 0) {
|
||||||
|
reply_len = hostapd_ctrl_get_fail(hapd, reply, reply_size);
|
||||||
#endif /* CONFIG_TESTING_OPTIONS */
|
#endif /* CONFIG_TESTING_OPTIONS */
|
||||||
} else if (os_strncmp(buf, "CHAN_SWITCH ", 12) == 0) {
|
} else if (os_strncmp(buf, "CHAN_SWITCH ", 12) == 0) {
|
||||||
if (hostapd_ctrl_iface_chan_switch(hapd->iface, buf + 12))
|
if (hostapd_ctrl_iface_chan_switch(hapd->iface, buf + 12))
|
||||||
|
|
|
@ -646,4 +646,12 @@ int os_exec(const char *program, const char *arg, int wait_completion);
|
||||||
#define strcpy OS_DO_NOT_USE_strcpy
|
#define strcpy OS_DO_NOT_USE_strcpy
|
||||||
#endif /* OS_REJECT_C_LIB_FUNCTIONS */
|
#endif /* OS_REJECT_C_LIB_FUNCTIONS */
|
||||||
|
|
||||||
|
|
||||||
|
#if defined(WPA_TRACE_BFD) && defined(CONFIG_TESTING_OPTIONS)
|
||||||
|
#define TEST_FAIL() testing_test_fail()
|
||||||
|
int testing_test_fail(void);
|
||||||
|
#else
|
||||||
|
#define TEST_FAIL() 0
|
||||||
|
#endif
|
||||||
|
|
||||||
#endif /* OS_H */
|
#endif /* OS_H */
|
||||||
|
|
|
@ -226,6 +226,9 @@ int os_get_random(unsigned char *buf, size_t len)
|
||||||
FILE *f;
|
FILE *f;
|
||||||
size_t rc;
|
size_t rc;
|
||||||
|
|
||||||
|
if (TEST_FAIL())
|
||||||
|
return -1;
|
||||||
|
|
||||||
f = fopen("/dev/urandom", "rb");
|
f = fopen("/dev/urandom", "rb");
|
||||||
if (f == NULL) {
|
if (f == NULL) {
|
||||||
printf("Could not open /dev/urandom.\n");
|
printf("Could not open /dev/urandom.\n");
|
||||||
|
@ -548,6 +551,78 @@ static int testing_fail_alloc(void)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
char wpa_trace_test_fail_func[256] = { 0 };
|
||||||
|
unsigned int wpa_trace_test_fail_after;
|
||||||
|
|
||||||
|
int testing_test_fail(void)
|
||||||
|
{
|
||||||
|
const char *func[WPA_TRACE_LEN];
|
||||||
|
size_t i, res, len;
|
||||||
|
char *pos, *next;
|
||||||
|
int match;
|
||||||
|
|
||||||
|
if (!wpa_trace_test_fail_after)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
res = wpa_trace_calling_func(func, WPA_TRACE_LEN);
|
||||||
|
i = 0;
|
||||||
|
if (i < res && os_strcmp(func[i], __func__) == 0)
|
||||||
|
i++;
|
||||||
|
|
||||||
|
pos = wpa_trace_test_fail_func;
|
||||||
|
|
||||||
|
match = 0;
|
||||||
|
while (i < res) {
|
||||||
|
int allow_skip = 1;
|
||||||
|
int maybe = 0;
|
||||||
|
|
||||||
|
if (*pos == '=') {
|
||||||
|
allow_skip = 0;
|
||||||
|
pos++;
|
||||||
|
} else if (*pos == '?') {
|
||||||
|
maybe = 1;
|
||||||
|
pos++;
|
||||||
|
}
|
||||||
|
next = os_strchr(pos, ';');
|
||||||
|
if (next)
|
||||||
|
len = next - pos;
|
||||||
|
else
|
||||||
|
len = os_strlen(pos);
|
||||||
|
if (os_memcmp(pos, func[i], len) != 0) {
|
||||||
|
if (maybe && next) {
|
||||||
|
pos = next + 1;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (allow_skip) {
|
||||||
|
i++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if (!next) {
|
||||||
|
match = 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
pos = next + 1;
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
if (!match)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
wpa_trace_test_fail_after--;
|
||||||
|
if (wpa_trace_test_fail_after == 0) {
|
||||||
|
wpa_printf(MSG_INFO, "TESTING: fail at %s",
|
||||||
|
wpa_trace_test_fail_func);
|
||||||
|
for (i = 0; i < res; i++)
|
||||||
|
wpa_printf(MSG_INFO, "backtrace[%d] = %s",
|
||||||
|
(int) i, func[i]);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
#else
|
#else
|
||||||
|
|
||||||
static inline int testing_fail_alloc(void)
|
static inline int testing_fail_alloc(void)
|
||||||
|
|
|
@ -7562,6 +7562,44 @@ static int wpas_ctrl_get_alloc_fail(struct wpa_supplicant *wpa_s,
|
||||||
#endif /* WPA_TRACE_BFD */
|
#endif /* WPA_TRACE_BFD */
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static int wpas_ctrl_test_fail(struct wpa_supplicant *wpa_s, char *cmd)
|
||||||
|
{
|
||||||
|
#ifdef WPA_TRACE_BFD
|
||||||
|
extern char wpa_trace_test_fail_func[256];
|
||||||
|
extern unsigned int wpa_trace_test_fail_after;
|
||||||
|
char *pos;
|
||||||
|
|
||||||
|
wpa_trace_test_fail_after = atoi(cmd);
|
||||||
|
pos = os_strchr(cmd, ':');
|
||||||
|
if (pos) {
|
||||||
|
pos++;
|
||||||
|
os_strlcpy(wpa_trace_test_fail_func, pos,
|
||||||
|
sizeof(wpa_trace_test_fail_func));
|
||||||
|
} else {
|
||||||
|
wpa_trace_test_fail_after = 0;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
#else /* WPA_TRACE_BFD */
|
||||||
|
return -1;
|
||||||
|
#endif /* WPA_TRACE_BFD */
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static int wpas_ctrl_get_fail(struct wpa_supplicant *wpa_s,
|
||||||
|
char *buf, size_t buflen)
|
||||||
|
{
|
||||||
|
#ifdef WPA_TRACE_BFD
|
||||||
|
extern char wpa_trace_test_fail_func[256];
|
||||||
|
extern unsigned int wpa_trace_test_fail_after;
|
||||||
|
|
||||||
|
return os_snprintf(buf, buflen, "%u:%s", wpa_trace_test_fail_after,
|
||||||
|
wpa_trace_test_fail_func);
|
||||||
|
#else /* WPA_TRACE_BFD */
|
||||||
|
return -1;
|
||||||
|
#endif /* WPA_TRACE_BFD */
|
||||||
|
}
|
||||||
|
|
||||||
#endif /* CONFIG_TESTING_OPTIONS */
|
#endif /* CONFIG_TESTING_OPTIONS */
|
||||||
|
|
||||||
|
|
||||||
|
@ -8579,6 +8617,11 @@ char * wpa_supplicant_ctrl_iface_process(struct wpa_supplicant *wpa_s,
|
||||||
reply_len = -1;
|
reply_len = -1;
|
||||||
} else if (os_strcmp(buf, "GET_ALLOC_FAIL") == 0) {
|
} else if (os_strcmp(buf, "GET_ALLOC_FAIL") == 0) {
|
||||||
reply_len = wpas_ctrl_get_alloc_fail(wpa_s, reply, reply_size);
|
reply_len = wpas_ctrl_get_alloc_fail(wpa_s, reply, reply_size);
|
||||||
|
} else if (os_strncmp(buf, "TEST_FAIL ", 10) == 0) {
|
||||||
|
if (wpas_ctrl_test_fail(wpa_s, buf + 10) < 0)
|
||||||
|
reply_len = -1;
|
||||||
|
} else if (os_strcmp(buf, "GET_FAIL") == 0) {
|
||||||
|
reply_len = wpas_ctrl_get_fail(wpa_s, reply, reply_size);
|
||||||
#endif /* CONFIG_TESTING_OPTIONS */
|
#endif /* CONFIG_TESTING_OPTIONS */
|
||||||
} else if (os_strncmp(buf, "VENDOR_ELEM_ADD ", 16) == 0) {
|
} else if (os_strncmp(buf, "VENDOR_ELEM_ADD ", 16) == 0) {
|
||||||
if (wpas_ctrl_vendor_elem_add(wpa_s, buf + 16) < 0)
|
if (wpas_ctrl_vendor_elem_add(wpa_s, buf + 16) < 0)
|
||||||
|
|
Loading…
Reference in a new issue