From fb4baa688bdb15c7776b424cce38cfc70f5aa524 Mon Sep 17 00:00:00 2001 From: Jouni Malinen Date: Sat, 19 Dec 2009 21:47:56 +0200 Subject: [PATCH] Add memory allocation analyzer to verify OS wrapper use WPA_TRACE=y builds will now verify that memory allocation in done consistently using os_{zalloc,malloc,realloc,strdup,free} (i.e., no mixing of os_* functions and unwrapper functions). In addition, some common memory allocation issues (double-free, memory leaks, etc.) are detected automatically. --- hostapd/Makefile | 4 + src/utils/os.h | 25 +++--- src/utils/os_unix.c | 163 ++++++++++++++++++++++++++++++++++++---- src/utils/trace.c | 2 +- wpa_supplicant/Makefile | 3 + 5 files changed, 174 insertions(+), 23 deletions(-) diff --git a/hostapd/Makefile b/hostapd/Makefile index 21b4a6606..0d52c81a2 100644 --- a/hostapd/Makefile +++ b/hostapd/Makefile @@ -720,6 +720,10 @@ hostapd: $(BCHECK) $(OBJS) $(CC) $(LDFLAGS) -o hostapd $(OBJS) $(LIBS) OBJS_c = hostapd_cli.o ../src/common/wpa_ctrl.o ../src/utils/os_$(CONFIG_OS).o +ifdef CONFIG_WPA_TRACE +OBJS_c += ../src/utils/trace.o +OBJS_c += ../src/utils/wpa_debug.o +endif hostapd_cli: $(OBJS_c) $(CC) $(LDFLAGS) -o hostapd_cli $(OBJS_c) diff --git a/src/utils/os.h b/src/utils/os.h index d6dfea682..f4723d875 100644 --- a/src/utils/os.h +++ b/src/utils/os.h @@ -1,6 +1,6 @@ /* - * wpa_supplicant/hostapd / OS specific functions - * Copyright (c) 2005-2006, Jouni Malinen + * OS specific functions + * Copyright (c) 2005-2009, Jouni Malinen * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as @@ -379,6 +379,12 @@ int os_snprintf(char *str, size_t size, const char *format, ...); #else /* OS_NO_C_LIB_DEFINES */ +#ifdef WPA_TRACE +void * os_malloc(size_t size); +void * os_realloc(void *ptr, size_t size); +void os_free(void *ptr); +char * os_strdup(const char *s); +#else /* WPA_TRACE */ #ifndef os_malloc #define os_malloc(s) malloc((s)) #endif @@ -388,6 +394,14 @@ int os_snprintf(char *str, size_t size, const char *format, ...); #ifndef os_free #define os_free(p) free((p)) #endif +#ifndef os_strdup +#ifdef _MSC_VER +#define os_strdup(s) _strdup(s) +#else +#define os_strdup(s) strdup(s) +#endif +#endif +#endif /* WPA_TRACE */ #ifndef os_memcpy #define os_memcpy(d, s, n) memcpy((d), (s), (n)) @@ -402,13 +416,6 @@ int os_snprintf(char *str, size_t size, const char *format, ...); #define os_memcmp(s1, s2, n) memcmp((s1), (s2), (n)) #endif -#ifndef os_strdup -#ifdef _MSC_VER -#define os_strdup(s) _strdup(s) -#else -#define os_strdup(s) strdup(s) -#endif -#endif #ifndef os_strlen #define os_strlen(s) strlen(s) #endif diff --git a/src/utils/os_unix.c b/src/utils/os_unix.c index bc2fc40dd..6748e752f 100644 --- a/src/utils/os_unix.c +++ b/src/utils/os_unix.c @@ -1,6 +1,6 @@ /* - * wpa_supplicant/hostapd / OS specific functions for UNIX/POSIX systems - * Copyright (c) 2005-2006, Jouni Malinen + * OS specific functions for UNIX/POSIX systems + * Copyright (c) 2005-2009, Jouni Malinen * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as @@ -16,6 +16,28 @@ #include "os.h" +#ifdef WPA_TRACE + +#include "common.h" +#include "list.h" +#include "wpa_debug.h" +#include "trace.h" + +static struct dl_list alloc_list; + +#define ALLOC_MAGIC 0xa84ef1b2 +#define FREED_MAGIC 0x67fd487a + +struct os_alloc_trace { + unsigned int magic; + struct dl_list list; + size_t len; + WPA_TRACE_INFO +}; + +#endif /* WPA_TRACE */ + + void os_sleep(os_time_t sec, os_time_t usec) { if (sec) @@ -172,16 +194,16 @@ char * os_rel2abs_path(const char *rel_path) int last_errno; if (rel_path[0] == '/') - return strdup(rel_path); + return os_strdup(rel_path); for (;;) { - buf = malloc(len); + buf = os_malloc(len); if (buf == NULL) return NULL; cwd = getcwd(buf, len); if (cwd == NULL) { last_errno = errno; - free(buf); + os_free(buf); if (last_errno != ERANGE) return NULL; len *= 2; @@ -193,29 +215,51 @@ char * os_rel2abs_path(const char *rel_path) } } - cwd_len = strlen(cwd); - rel_len = strlen(rel_path); + cwd_len = os_strlen(cwd); + rel_len = os_strlen(rel_path); ret_len = cwd_len + 1 + rel_len + 1; - ret = malloc(ret_len); + ret = os_malloc(ret_len); if (ret) { - memcpy(ret, cwd, cwd_len); + os_memcpy(ret, cwd, cwd_len); ret[cwd_len] = '/'; - memcpy(ret + cwd_len + 1, rel_path, rel_len); + os_memcpy(ret + cwd_len + 1, rel_path, rel_len); ret[ret_len - 1] = '\0'; } - free(buf); + os_free(buf); return ret; } int os_program_init(void) { +#ifdef WPA_TRACE + dl_list_init(&alloc_list); +#endif /* WPA_TRACE */ return 0; } void os_program_deinit(void) { +#ifdef WPA_TRACE + struct os_alloc_trace *a; + unsigned long total = 0; + dl_list_for_each(a, &alloc_list, struct os_alloc_trace, list) { + total += a->len; + if (a->magic != ALLOC_MAGIC) { + wpa_printf(MSG_INFO, "MEMLEAK[%p]: invalid magic 0x%x " + "len %lu", + a, a->magic, (unsigned long) a->len); + continue; + } + wpa_printf(MSG_INFO, "MEMLEAK[%p]: len %lu", + a, (unsigned long) a->len); + wpa_trace_dump("memleak", a); + } + if (total) + wpa_printf(MSG_INFO, "MEMLEAK: total %lu bytes", + (unsigned long) total); +#endif /* WPA_TRACE */ } @@ -250,7 +294,7 @@ char * os_readfile(const char *name, size_t *len) *len = ftell(f); fseek(f, 0, SEEK_SET); - buf = malloc(*len); + buf = os_malloc(*len); if (buf == NULL) { fclose(f); return NULL; @@ -258,7 +302,7 @@ char * os_readfile(const char *name, size_t *len) if (fread(buf, 1, *len, f) != *len) { fclose(f); - free(buf); + os_free(buf); return NULL; } @@ -268,10 +312,12 @@ char * os_readfile(const char *name, size_t *len) } +#ifndef WPA_TRACE void * os_zalloc(size_t size) { return calloc(1, size); } +#endif /* WPA_TRACE */ size_t os_strlcpy(char *dest, const char *src, size_t siz) @@ -297,3 +343,94 @@ size_t os_strlcpy(char *dest, const char *src, size_t siz) return s - src - 1; } + + +#ifdef WPA_TRACE + +void * os_malloc(size_t size) +{ + struct os_alloc_trace *a; + a = malloc(sizeof(*a) + size); + if (a == NULL) + return NULL; + a->magic = ALLOC_MAGIC; + dl_list_add(&alloc_list, &a->list); + a->len = size; + wpa_trace_record(a); + return a + 1; +} + + +void * os_realloc(void *ptr, size_t size) +{ + struct os_alloc_trace *a; + size_t copy_len; + void *n; + + if (ptr == NULL) + return os_malloc(size); + + a = ptr - sizeof(*a); + if (a->magic != ALLOC_MAGIC) { + wpa_printf(MSG_INFO, "REALLOC[%p]: invalid magic 0x%x%s", + a, a->magic, + a->magic == FREED_MAGIC ? " (already freed)" : ""); + wpa_trace_show("Invalid os_realloc() call"); + abort(); + } + n = os_malloc(size); + if (n == NULL) + return NULL; + copy_len = a->len; + if (copy_len > size) + copy_len = size; + os_memcpy(n, a + 1, copy_len); + os_free(ptr); + return n; +} + + +void os_free(void *ptr) +{ + struct os_alloc_trace *a; + + if (ptr == NULL) + return; + a = ptr - sizeof(*a); + if (a->magic != ALLOC_MAGIC) { + wpa_printf(MSG_INFO, "FREE[%p]: invalid magic 0x%x%s", + a, a->magic, + a->magic == FREED_MAGIC ? " (already freed)" : ""); + wpa_trace_show("Invalid os_free() call"); + abort(); + } + dl_list_del(&a->list); + a->magic = FREED_MAGIC; + + free(a); +} + + +void * os_zalloc(size_t size) +{ + void *ptr = os_malloc(size); + if (ptr) + os_memset(ptr, 0, size); + return ptr; +} + + +char * os_strdup(const char *s) +{ + size_t len; + char *d; + len = os_strlen(s); + d = os_malloc(len + 1); + if (d == NULL) + return NULL; + os_memcpy(d, s, len); + d[len] = '\0'; + return d; +} + +#endif /* WPA_TRACE */ diff --git a/src/utils/trace.c b/src/utils/trace.c index 1c4481d42..18502e388 100644 --- a/src/utils/trace.c +++ b/src/utils/trace.c @@ -29,7 +29,7 @@ void wpa_trace_dump_func(const char *title, void **btrace, int btrace_num) for (i = 0; i < btrace_num; i++) wpa_printf(MSG_INFO, "[%d]: %p: %s", i, btrace[i], sym ? sym[i] : ""); - os_free(sym); + free(sym); wpa_printf(MSG_INFO, "WPA_TRACE: %s - END", title); } diff --git a/wpa_supplicant/Makefile b/wpa_supplicant/Makefile index 6e3a8d54e..9980ac777 100644 --- a/wpa_supplicant/Makefile +++ b/wpa_supplicant/Makefile @@ -71,6 +71,9 @@ OBJS_c += ../src/utils/os_$(CONFIG_OS).o ifdef CONFIG_WPA_TRACE CFLAGS += -DWPA_TRACE OBJS += ../src/utils/trace.o +OBJS_p += ../src/utils/trace.o +OBJS_c += ../src/utils/trace.o +OBJS_c += ../src/utils/wpa_debug.o endif ifndef CONFIG_ELOOP