Add memory allocation analyzer to verify OS wrapper use
authorJouni Malinen <j@w1.fi>
Sat, 19 Dec 2009 19:47:56 +0000 (21:47 +0200)
committerJouni Malinen <j@w1.fi>
Sat, 19 Dec 2009 19:47:56 +0000 (21:47 +0200)
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
src/utils/os.h
src/utils/os_unix.c
src/utils/trace.c
wpa_supplicant/Makefile

index 21b4a66..0d52c81 100644 (file)
@@ -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)
 
index d6dfea6..f4723d8 100644 (file)
@@ -1,6 +1,6 @@
 /*
- * wpa_supplicant/hostapd / OS specific functions
- * Copyright (c) 2005-2006, Jouni Malinen <j@w1.fi>
+ * OS specific functions
+ * Copyright (c) 2005-2009, Jouni Malinen <j@w1.fi>
  *
  * 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
index bc2fc40..6748e75 100644 (file)
@@ -1,6 +1,6 @@
 /*
- * wpa_supplicant/hostapd / OS specific functions for UNIX/POSIX systems
- * Copyright (c) 2005-2006, Jouni Malinen <j@w1.fi>
+ * OS specific functions for UNIX/POSIX systems
+ * Copyright (c) 2005-2009, Jouni Malinen <j@w1.fi>
  *
  * 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
 
 #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 */
index 1c4481d..18502e3 100644 (file)
@@ -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);
 }
 
index 6e3a8d5..9980ac7 100644 (file)
@@ -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