Add backtrace-based error path testing mechanism
authorJouni Malinen <j@w1.fi>
Sun, 28 Jun 2015 18:35:43 +0000 (21:35 +0300)
committerJouni Malinen <j@w1.fi>
Mon, 29 Jun 2015 20:23:56 +0000 (23:23 +0300)
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>
hostapd/ctrl_iface.c
src/utils/os.h
src/utils/os_unix.c
wpa_supplicant/ctrl_iface.c

index 8ab2941..4eee851 100644 (file)
@@ -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))
index 77250d6..2c24631 100644 (file)
@@ -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 */
index e0c1125..e425904 100644 (file)
@@ -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)
index d0d70e9..21e5849 100644 (file)
@@ -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)