Add support for testing memory allocation failures
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>
This commit is contained in:
parent
52b3943c94
commit
a156ffda61
5 changed files with 231 additions and 0 deletions
|
@ -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))
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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 */
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
Loading…
Reference in a new issue