From 2da525651d9aa49854bff51f7e4faf9273f68868 Mon Sep 17 00:00:00 2001 From: Jouni Malinen Date: Sun, 28 Jun 2015 21:35:43 +0300 Subject: [PATCH] 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 --- hostapd/ctrl_iface.c | 44 ++++++++++++++++++++++++++ src/utils/os.h | 8 +++++ src/utils/os_unix.c | 75 +++++++++++++++++++++++++++++++++++++++++++++ wpa_supplicant/ctrl_iface.c | 43 ++++++++++++++++++++++++++ 4 files changed, 170 insertions(+) diff --git a/hostapd/ctrl_iface.c b/hostapd/ctrl_iface.c index 8ab2941..4eee851 100644 --- a/hostapd/ctrl_iface.c +++ b/hostapd/ctrl_iface.c @@ -1746,6 +1746,45 @@ static int hostapd_ctrl_get_alloc_fail(struct hostapd_data *hapd, #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 */ @@ -2079,6 +2118,11 @@ static void hostapd_ctrl_iface_receive(int sock, void *eloop_ctx, } else if (os_strcmp(buf, "GET_ALLOC_FAIL") == 0) { reply_len = hostapd_ctrl_get_alloc_fail(hapd, reply, 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 */ } else if (os_strncmp(buf, "CHAN_SWITCH ", 12) == 0) { if (hostapd_ctrl_iface_chan_switch(hapd->iface, buf + 12)) diff --git a/src/utils/os.h b/src/utils/os.h index 77250d6..2c24631 100644 --- a/src/utils/os.h +++ b/src/utils/os.h @@ -646,4 +646,12 @@ int os_exec(const char *program, const char *arg, int wait_completion); #define strcpy OS_DO_NOT_USE_strcpy #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 */ diff --git a/src/utils/os_unix.c b/src/utils/os_unix.c index e0c1125..e425904 100644 --- a/src/utils/os_unix.c +++ b/src/utils/os_unix.c @@ -226,6 +226,9 @@ int os_get_random(unsigned char *buf, size_t len) FILE *f; size_t rc; + if (TEST_FAIL()) + return -1; + f = fopen("/dev/urandom", "rb"); if (f == NULL) { printf("Could not open /dev/urandom.\n"); @@ -548,6 +551,78 @@ static int testing_fail_alloc(void) 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 static inline int testing_fail_alloc(void) diff --git a/wpa_supplicant/ctrl_iface.c b/wpa_supplicant/ctrl_iface.c index d0d70e9..21e5849 100644 --- a/wpa_supplicant/ctrl_iface.c +++ b/wpa_supplicant/ctrl_iface.c @@ -7562,6 +7562,44 @@ static int wpas_ctrl_get_alloc_fail(struct wpa_supplicant *wpa_s, #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 */ @@ -8579,6 +8617,11 @@ char * wpa_supplicant_ctrl_iface_process(struct wpa_supplicant *wpa_s, reply_len = -1; } else if (os_strcmp(buf, "GET_ALLOC_FAIL") == 0) { 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 */ } else if (os_strncmp(buf, "VENDOR_ELEM_ADD ", 16) == 0) { if (wpas_ctrl_vendor_elem_add(wpa_s, buf + 16) < 0) -- 2.1.4