Add support for testing memory allocation failures
authorJouni Malinen <j@w1.fi>
Mon, 5 Jan 2015 18:30:28 +0000 (20:30 +0200)
committerJouni Malinen <j@w1.fi>
Wed, 7 Jan 2015 11:19:43 +0000 (13:19 +0200)
The new control interface command TEST_ALLOC_FAIL and GET_ALLOC_FAIL can
now be used to trigger memory allocation failures for testing purposes.
TEST_ALLOC_FAIL sets a failure conditions with
<count>:func[;func][;func]... string and GET_ALLOC_FAIL returns the
current state using the same format. Whenever an allocation is made with
a matching backtrace of calling functions, the count is decremented by
one and once zero is reached, the allocation is forced to fail.

Function names can be prefixed with either '=' or '?' to get different
matching behavior. '=' requires this specific function to be the next
one in the backtrace (i.e., do not skip any other functions in the list
which is the default behavior). '?' allows the function to be optionally
present in the backtrace.

Signed-off-by: Jouni Malinen <j@w1.fi>
hostapd/ctrl_iface.c
src/utils/os_unix.c
src/utils/trace.c
src/utils/trace.h
wpa_supplicant/ctrl_iface.c

index 0e35aa6..6c81211 100644 (file)
@@ -1692,6 +1692,45 @@ done:
        return res < 0 ? -1 : 0;
 }
 
+
+static int hostapd_ctrl_test_alloc_fail(struct hostapd_data *hapd, char *cmd)
+{
+#ifdef WPA_TRACE_BFD
+       extern char wpa_trace_fail_func[256];
+       extern unsigned int wpa_trace_fail_after;
+       char *pos;
+
+       wpa_trace_fail_after = atoi(cmd);
+       pos = os_strchr(cmd, ':');
+       if (pos) {
+               pos++;
+               os_strlcpy(wpa_trace_fail_func, pos,
+                          sizeof(wpa_trace_fail_func));
+       } else {
+               wpa_trace_fail_after = 0;
+       }
+
+       return 0;
+#else /* WPA_TRACE_BFD */
+       return -1;
+#endif /* WPA_TRACE_BFD */
+}
+
+
+static int hostapd_ctrl_get_alloc_fail(struct hostapd_data *hapd,
+                                      char *buf, size_t buflen)
+{
+#ifdef WPA_TRACE_BFD
+       extern char wpa_trace_fail_func[256];
+       extern unsigned int wpa_trace_fail_after;
+
+       return os_snprintf(buf, buflen, "%u:%s", wpa_trace_fail_after,
+                          wpa_trace_fail_func);
+#else /* WPA_TRACE_BFD */
+       return -1;
+#endif /* WPA_TRACE_BFD */
+}
+
 #endif /* CONFIG_TESTING_OPTIONS */
 
 
@@ -2013,6 +2052,12 @@ static void hostapd_ctrl_iface_receive(int sock, void *eloop_ctx,
        } else if (os_strncmp(buf, "DATA_TEST_FRAME ", 16) == 0) {
                if (hostapd_ctrl_iface_data_test_frame(hapd, buf + 16) < 0)
                        reply_len = -1;
+       } else if (os_strncmp(buf, "TEST_ALLOC_FAIL ", 16) == 0) {
+               if (hostapd_ctrl_test_alloc_fail(hapd, buf + 16) < 0)
+                       reply_len = -1;
+       } else if (os_strcmp(buf, "GET_ALLOC_FAIL") == 0) {
+               reply_len = hostapd_ctrl_get_alloc_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 523a4d0..86fbd0a 100644 (file)
@@ -467,9 +467,105 @@ int os_memcmp_const(const void *a, const void *b, size_t len)
 
 #ifdef WPA_TRACE
 
+#if defined(WPA_TRACE_BFD) && defined(CONFIG_TESTING_OPTIONS)
+char wpa_trace_fail_func[256] = { 0 };
+unsigned int wpa_trace_fail_after;
+
+static int testing_fail_alloc(void)
+{
+       const char *func[WPA_TRACE_LEN];
+       size_t i, res, len;
+       char *pos, *next;
+       int match;
+
+       if (!wpa_trace_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++;
+       if (i < res && os_strcmp(func[i], "os_malloc") == 0)
+               i++;
+       if (i < res && os_strcmp(func[i], "os_zalloc") == 0)
+               i++;
+       if (i < res && os_strcmp(func[i], "os_calloc") == 0)
+               i++;
+       if (i < res && os_strcmp(func[i], "os_realloc") == 0)
+               i++;
+       if (i < res && os_strcmp(func[i], "os_realloc_array") == 0)
+               i++;
+       if (i < res && os_strcmp(func[i], "os_strdup") == 0)
+               i++;
+
+       pos = wpa_trace_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_fail_after--;
+       if (wpa_trace_fail_after == 0) {
+               wpa_printf(MSG_INFO, "TESTING: fail allocation at %s",
+                          wpa_trace_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)
+{
+       return 0;
+}
+#endif
+
 void * os_malloc(size_t size)
 {
        struct os_alloc_trace *a;
+
+       if (testing_fail_alloc())
+               return NULL;
+
        a = malloc(sizeof(*a) + size);
        if (a == NULL)
                return NULL;
index 14fa40e..2673753 100644 (file)
@@ -243,6 +243,52 @@ void wpa_trace_dump_funcname(const char *title, void *pc)
        wpa_trace_bfd_addr(pc);
 }
 
+
+size_t wpa_trace_calling_func(const char *buf[], size_t len)
+{
+       bfd *abfd;
+       void *btrace_res[WPA_TRACE_LEN];
+       int i, btrace_num;
+       size_t pos = 0;
+
+       if (len == 0)
+               return 0;
+       if (len > WPA_TRACE_LEN)
+               len = WPA_TRACE_LEN;
+
+       wpa_trace_bfd_init();
+       abfd = cached_abfd;
+       if (!abfd)
+               return 0;
+
+       btrace_num = backtrace(btrace_res, len);
+       if (btrace_num < 1)
+               return 0;
+
+       for (i = 0; i < btrace_num; i++) {
+               struct bfd_data data;
+
+               data.pc = (bfd_hostptr_t) btrace_res[i];
+               data.found = FALSE;
+               bfd_map_over_sections(abfd, find_addr_sect, &data);
+
+               while (data.found || !data.function) {
+                       if (pos > 0 ||
+                           os_strcmp(data.function, __func__) != 0) {
+                               buf[pos++] = data.function;
+                               if (pos == len)
+                                       return pos;
+                       }
+
+                       data.found = bfd_find_inliner_info(abfd, &data.filename,
+                                                          &data.function,
+                                                          &data.line);
+               }
+       }
+
+       return pos;
+}
+
 #else /* WPA_TRACE_BFD */
 
 #define wpa_trace_bfd_init() do { } while (0)
index 38f43fb..43ed86c 100644 (file)
@@ -40,6 +40,7 @@ void wpa_trace_add_ref_func(struct wpa_trace_ref *ref, const void *addr);
                        dl_list_del(&(ptr)->wpa_trace_ref_##name.list); \
        } while (0)
 void wpa_trace_check_ref(const void *addr);
+size_t wpa_trace_calling_func(const char *buf[], size_t len);
 
 #else /* WPA_TRACE */
 
index acdc90d..4400081 100644 (file)
@@ -6859,6 +6859,44 @@ done:
        return res < 0 ? -1 : 0;
 }
 
+
+static int wpas_ctrl_test_alloc_fail(struct wpa_supplicant *wpa_s, char *cmd)
+{
+#ifdef WPA_TRACE_BFD
+       extern char wpa_trace_fail_func[256];
+       extern unsigned int wpa_trace_fail_after;
+       char *pos;
+
+       wpa_trace_fail_after = atoi(cmd);
+       pos = os_strchr(cmd, ':');
+       if (pos) {
+               pos++;
+               os_strlcpy(wpa_trace_fail_func, pos,
+                          sizeof(wpa_trace_fail_func));
+       } else {
+               wpa_trace_fail_after = 0;
+       }
+       return 0;
+#else /* WPA_TRACE_BFD */
+       return -1;
+#endif /* WPA_TRACE_BFD */
+}
+
+
+static int wpas_ctrl_get_alloc_fail(struct wpa_supplicant *wpa_s,
+                                   char *buf, size_t buflen)
+{
+#ifdef WPA_TRACE_BFD
+       extern char wpa_trace_fail_func[256];
+       extern unsigned int wpa_trace_fail_after;
+
+       return os_snprintf(buf, buflen, "%u:%s", wpa_trace_fail_after,
+                          wpa_trace_fail_func);
+#else /* WPA_TRACE_BFD */
+       return -1;
+#endif /* WPA_TRACE_BFD */
+}
+
 #endif /* CONFIG_TESTING_OPTIONS */
 
 
@@ -7841,6 +7879,11 @@ char * wpa_supplicant_ctrl_iface_process(struct wpa_supplicant *wpa_s,
        } else if (os_strncmp(buf, "DATA_TEST_FRAME ", 16) == 0) {
                if (wpas_ctrl_iface_data_test_frame(wpa_s, buf + 16) < 0)
                        reply_len = -1;
+       } else if (os_strncmp(buf, "TEST_ALLOC_FAIL ", 16) == 0) {
+               if (wpas_ctrl_test_alloc_fail(wpa_s, buf + 16) < 0)
+                       reply_len = -1;
+       } else if (os_strcmp(buf, "GET_ALLOC_FAIL") == 0) {
+               reply_len = wpas_ctrl_get_alloc_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)