[asterisk-commits] tilghman: branch tilghman/malloc_hold r208743 - in /team/tilghman/malloc_hold...

SVN commits to the Asterisk project asterisk-commits at lists.digium.com
Fri Jul 24 17:35:46 CDT 2009


Author: tilghman
Date: Fri Jul 24 17:35:43 2009
New Revision: 208743

URL: http://svn.asterisk.org/svn-view/asterisk?view=rev&rev=208743
Log:
Save what I have, for a memory error detection that detects both bad writes AND bad reads.

Modified:
    team/tilghman/malloc_hold/configure
    team/tilghman/malloc_hold/include/asterisk/autoconfig.h.in
    team/tilghman/malloc_hold/main/astmm.c

Modified: team/tilghman/malloc_hold/include/asterisk/autoconfig.h.in
URL: http://svn.asterisk.org/svn-view/asterisk/team/tilghman/malloc_hold/include/asterisk/autoconfig.h.in?view=diff&rev=208743&r1=208742&r2=208743
==============================================================================
--- team/tilghman/malloc_hold/include/asterisk/autoconfig.h.in (original)
+++ team/tilghman/malloc_hold/include/asterisk/autoconfig.h.in Fri Jul 24 17:35:43 2009
@@ -594,6 +594,9 @@
 /* Define to the version of this package. */
 #undef PACKAGE_VERSION
 
+/* Define to 1 if the C compiler supports function prototypes. */
+#undef PROTOTYPES
+
 /* Define to necessary symbol if this constant uses a non-standard name on
    your system. */
 #undef PTHREAD_CREATE_JOINABLE
@@ -609,6 +612,11 @@
 
 /* Define to the type of arg 5 for `select'. */
 #undef SELECT_TYPE_ARG5
+
+/* Define to 1 if the `setvbuf' function takes the buffering type as its
+   second argument and the buffer pointer as the third, as on System V before
+   release 3. */
+#undef SETVBUF_REVERSED
 
 /* The size of `int', as computed by sizeof. */
 #undef SIZEOF_INT
@@ -630,46 +638,50 @@
 /* Define to 1 if your <sys/time.h> declares `struct tm'. */
 #undef TM_IN_SYS_TIME
 
-/* Enable extensions on AIX 3, Interix.  */
+/* Define to 1 if on AIX 3.
+   System headers sometimes define this.
+   We just want to avoid a redefinition error message.  */
 #ifndef _ALL_SOURCE
 # undef _ALL_SOURCE
 #endif
+
+/* Number of bits in a file offset, on hosts where this is settable. */
+#undef _FILE_OFFSET_BITS
+
 /* Enable GNU extensions on systems that have them.  */
 #ifndef _GNU_SOURCE
 # undef _GNU_SOURCE
 #endif
-/* Enable threading extensions on Solaris.  */
+
+/* Define to 1 to make fseeko visible on some hosts (e.g. glibc 2.2). */
+#undef _LARGEFILE_SOURCE
+
+/* Define for large files, on AIX-style hosts. */
+#undef _LARGE_FILES
+
+/* Define to 1 if on MINIX. */
+#undef _MINIX
+
+/* Define to 2 if the system does not provide POSIX.1 features except with
+   this defined. */
+#undef _POSIX_1_SOURCE
+
+/* Define to 1 if you need to in order for `stat' and other things to work. */
+#undef _POSIX_SOURCE
+
+/* Enable extensions on Solaris.  */
+#ifndef __EXTENSIONS__
+# undef __EXTENSIONS__
+#endif
 #ifndef _POSIX_PTHREAD_SEMANTICS
 # undef _POSIX_PTHREAD_SEMANTICS
 #endif
-/* Enable extensions on HP NonStop.  */
 #ifndef _TANDEM_SOURCE
 # undef _TANDEM_SOURCE
 #endif
-/* Enable general extensions on Solaris.  */
-#ifndef __EXTENSIONS__
-# undef __EXTENSIONS__
-#endif
-
-
-/* Number of bits in a file offset, on hosts where this is settable. */
-#undef _FILE_OFFSET_BITS
-
-/* Define to 1 to make fseeko visible on some hosts (e.g. glibc 2.2). */
-#undef _LARGEFILE_SOURCE
-
-/* Define for large files, on AIX-style hosts. */
-#undef _LARGE_FILES
-
-/* Define to 1 if on MINIX. */
-#undef _MINIX
-
-/* Define to 2 if the system does not provide POSIX.1 features except with
-   this defined. */
-#undef _POSIX_1_SOURCE
-
-/* Define to 1 if you need to in order for `stat' and other things to work. */
-#undef _POSIX_SOURCE
+
+/* Define like PROTOTYPES; this can be used by system headers. */
+#undef __PROTOTYPES
 
 /* Define to empty if `const' does not conform to ANSI C. */
 #undef const

Modified: team/tilghman/malloc_hold/main/astmm.c
URL: http://svn.asterisk.org/svn-view/asterisk/team/tilghman/malloc_hold/main/astmm.c?view=diff&rev=208743&r1=208742&r2=208743
==============================================================================
--- team/tilghman/malloc_hold/main/astmm.c (original)
+++ team/tilghman/malloc_hold/main/astmm.c Fri Jul 24 17:35:43 2009
@@ -33,6 +33,11 @@
 #include <string.h>
 #include <stddef.h>
 #include <time.h>
+#include <sys/mman.h>
+#include <signal.h>
+#ifdef MALLOC_HOLD
+#include <malloc.h>
+#endif /* MALLOC_HOLD */
 
 #include "asterisk/cli.h"
 #include "asterisk/logger.h"
@@ -45,6 +50,26 @@
 #endif
 
 #define SOME_PRIME 563
+
+/* Cannot include utils.h, as it defines things that this file implements,
+ * but could eventually put these defines in a separate header file. */
+int ast_pthread_create_stack(pthread_t *thread, pthread_attr_t *attr, void *(*start_routine)(void *),
+			     void *data, size_t stacksize, const char *file, const char *caller,
+			     int line, const char *start_fn);
+#define AST_STACKSIZE (((sizeof(void *) * 8 * 8) - 16) * 1024)
+
+#if defined(LOW_MEMORY)
+#define AST_BACKGROUND_STACKSIZE (((sizeof(void *) * 8 * 2) - 16) * 1024)
+#else
+#define AST_BACKGROUND_STACKSIZE AST_STACKSIZE
+#endif
+
+#define ast_pthread_create_background(a, b, c, d) ast_pthread_create_stack(a, b, c, d,			\
+									   AST_BACKGROUND_STACKSIZE,	\
+									   __FILE__, __FUNCTION__,	\
+									   __LINE__, #c)
+#endif
+#define ARRAY_LEN(a) (sizeof(a) / sizeof(a[0]))
 
 enum func_type {
 	FUNC_CALLOC = 1,
@@ -70,6 +95,19 @@
 #define FENCE_MAGIC 0xdeadbeef
 
 static FILE *mmlog;
+
+#ifdef MALLOC_HOLD
+static ssize_t pagesize = -1;
+static struct sigaction default_sigsegv;
+#endif
+
+static struct violations {
+	void *base_addr;
+	void *violation;
+	int error;
+	time_t when;
+} violations[100] = { { NULL, } };
+static int last_violation = -1;
 
 /* NOTE: Be EXTREMELY careful with modifying this structure; the total size of this structure
    must result in 'automatic' alignment so that the 'fence' field lands exactly at the end of
@@ -85,12 +123,20 @@
 
 static struct ast_region {
 	struct ast_region *next;
+	size_t len;
+	size_t fullsize;
+#ifdef MALLOC_HOLD
+	char free_file[40];
+	char free_func[40];
+	unsigned int free_lineno;
 	unsigned int freetime;
-	unsigned int unused;
-	size_t len;
-	char file[40];
-	char func[40];
-	unsigned int lineno;
+	int mperm;
+	int violation;
+	void *vlocation;
+#endif
+	char alloc_file[40];
+	char alloc_func[40];
+	unsigned int alloc_lineno;
 	enum func_type which;
 	unsigned int cache;		/* region was allocated as part of a cache pool */
 	unsigned int fence;
@@ -121,16 +167,37 @@
 	void *ptr = NULL;
 	unsigned int *fence;
 	int hash;
-
+#ifdef MALLOC_HOLD
+	size_t fullsize = size + sizeof(*reg) + sizeof(*fence);
+
+	if (pagesize == -1 && (pagesize = sysconf(_SC_PAGESIZE)) == -1) {
+		ast_log(LOG_ERROR, "Cannot retrieve system memory page size?");
+		abort();
+	}
+
+	/*!
+	 * What, expand the size to cover the full page?  Doesn't that eat memory?
+	 * Yes, but consider that we aren't the only consumers of memory in this
+	 * process.  Supposing we only allocated half a page.  Malloc would be free
+	 * to allocate the remaining space to something else, like perhaps a libc
+	 * internal structure.  If we then protect that memory, and libc tries to
+	 * access it, we flag a memory error where none existed.  Thus, we must
+	 * only allocate full memory pages.
+	 */
+	fullsize = (fullsize / pagesize) * pagesize == fullsize ? fullsize : ((fullsize / pagesize) + 1) * pagesize;
+	if (!(reg = memalign(pagesize, fullsize))) {
+#else /* !MALLOC_HOLD */
 	if (!(reg = malloc(size + sizeof(*reg) + sizeof(*fence)))) {
+#endif
 		astmm_log("Memory Allocation Failure - '%d' bytes in function %s "
 			  "at line %d of %s\n", (int) size, func, lineno, file);
 	}
 
-	ast_copy_string(reg->file, file, sizeof(reg->file));
-	ast_copy_string(reg->func, func, sizeof(reg->func));
-	reg->lineno = lineno;
+	ast_copy_string(reg->alloc_file, file, sizeof(reg->alloc_file));
+	ast_copy_string(reg->alloc_func, func, sizeof(reg->alloc_func));
+	reg->alloc_lineno = lineno;
 	reg->len = size;
+	reg->fullsize = fullsize;
 	reg->which = which;
 	reg->cache = cache;
 	ptr = reg->data;
@@ -165,7 +232,8 @@
 	return len;
 }
 
-static void __ast_free_region(void *ptr, const char *file, int lineno, const char *func)
+void __ast_free_region(void *ptr, const char *file, int lineno, const char *func);
+void __ast_free_region(void *ptr, const char *file, int lineno, const char *func)
 {
 	int hash;
 	struct ast_region *reg, *prev = NULL;
@@ -190,23 +258,34 @@
 			fence = (unsigned int *)(reg->data + reg->len);
 			if (reg->fence != FENCE_MAGIC) {
 				astmm_log("WARNING: Low fence violation at %p, in %s of %s, "
-					"line %d\n", reg->data, reg->func, reg->file, reg->lineno);
+					"line %d, freed in %s of %s, line %d\n", reg->data,
+					reg->alloc_func, reg->alloc_file, reg->alloc_lineno,
+					reg->free_func, reg->free_file, reg->free_lineno);
+				/* Restore FENCE_MAGIC, as it's needed for extended checking */
+				reg->fence = FENCE_MAGIC;
 			}
 			if (get_unaligned_uint32(fence) != FENCE_MAGIC) {
 				astmm_log("WARNING: High fence violation at %p, in %s of %s, "
-					"line %d\n", reg->data, reg->func, reg->file, reg->lineno);
-			}
-
-			memset(ptr, 0, reg->len);
-			reg->freetime = now;
-
-			/* Move to "free" list */
+					"line %d, freed in %s of %s, line %d\n", reg->data,
+					reg->alloc_func, reg->alloc_file, reg->alloc_lineno,
+					reg->free_func, reg->free_file, reg->free_lineno);
+			}
+
+			/* Remove from "allocated" list */
 			if (prev) {
 				prev->next = reg->next;
 			} else {
 				regions[hash] = reg->next;
 			}
 			reg->next = NULL;
+
+			reg->mperm = PROT_NONE;
+			reg->violation = 0;
+			reg->vlocation = NULL;
+			reg->freetime = now;
+			ast_copy_string(reg->free_file, file, sizeof(reg->free_file));
+			ast_copy_string(reg->free_func, func, sizeof(reg->free_func));
+			reg->free_lineno = lineno;
 
 			/* By inserting at the end of the list, we ensure that we won't need
 			 * to search the list for entries to delete, but merely prune the
@@ -216,9 +295,12 @@
 				lastfree = reg;
 				regions[SOME_PRIME] = reg;
 			} else {
+				mprotect(lastfree, sizeof(*lastfree), PROT_READ | PROT_WRITE);
 				lastfree->next = reg;
+				mprotect(lastfree, sizeof(*lastfree), PROT_NONE);
 				lastfree = reg;
 			}
+			mprotect(reg, reg->fullsize, PROT_NONE);
 			break;
 		}
 		prev = reg;
@@ -247,11 +329,11 @@
 		fence = (unsigned int *)(reg->data + reg->len);
 		if (reg->fence != FENCE_MAGIC) {
 			astmm_log("WARNING: Low fence violation at %p, in %s of %s, "
-				"line %d\n", reg->data, reg->func, reg->file, reg->lineno);
+				"line %d\n", reg->data, reg->alloc_func, reg->alloc_file, reg->alloc_lineno);
 		}
 		if (get_unaligned_uint32(fence) != FENCE_MAGIC) {
 			astmm_log("WARNING: High fence violation at %p, in %s of %s, "
-				"line %d\n", reg->data, reg->func, reg->file, reg->lineno);
+				"line %d\n", reg->data, reg->alloc_func, reg->alloc_file, reg->alloc_lineno);
 		}
 		free(reg);
 	} else {
@@ -403,22 +485,22 @@
 	ast_mutex_lock(&reglock);
 	for (x = 0; x < SOME_PRIME; x++) {
 		for (reg = regions[x]; reg; reg = reg->next) {
-			if (!fn || !strcasecmp(fn, reg->file) || !strcasecmp(fn, "anomolies")) {
+			if (!fn || !strcasecmp(fn, reg->alloc_file) || !strcasecmp(fn, "anomolies")) {
 				fence = (unsigned int *)(reg->data + reg->len);
 				if (reg->fence != FENCE_MAGIC) {
 					astmm_log("WARNING: Low fence violation at %p, "
 						"in %s of %s, line %d\n", reg->data, 
-						reg->func, reg->file, reg->lineno);
+						reg->alloc_func, reg->alloc_file, reg->alloc_lineno);
 				}
 				if (get_unaligned_uint32(fence) != FENCE_MAGIC) {
 					astmm_log("WARNING: High fence violation at %p, in %s of %s, "
-						"line %d\n", reg->data, reg->func, reg->file, reg->lineno);
+						"line %d\n", reg->data, reg->alloc_func, reg->alloc_file, reg->alloc_lineno);
 				}
 			}
-			if (!fn || !strcasecmp(fn, reg->file)) {
+			if (!fn || !strcasecmp(fn, reg->alloc_file)) {
 				ast_cli(fd, "%10d bytes allocated%s in %20s at line %5d of %s\n", 
 					(int) reg->len, reg->cache ? " (cache)" : "", 
-					reg->func, reg->lineno, reg->file);
+					reg->alloc_func, reg->alloc_lineno, reg->alloc_file);
 				len += reg->len;
 				if (reg->cache)
 					cache_len += reg->len;
@@ -458,17 +540,17 @@
 	ast_mutex_lock(&reglock);
 	for (x = 0; x < SOME_PRIME; x++) {
 		for (reg = regions[x]; reg; reg = reg->next) {
-			if (fn && strcasecmp(fn, reg->file))
+			if (fn && strcasecmp(fn, reg->alloc_file))
 				continue;
 
 			for (cur = list; cur; cur = cur->next) {
-				if ((!fn && !strcmp(cur->fn, reg->file)) || (fn && !strcmp(cur->fn, reg->func)))
+				if ((!fn && !strcmp(cur->fn, reg->alloc_file)) || (fn && !strcmp(cur->fn, reg->alloc_func)))
 					break;
 			}
 			if (!cur) {
 				cur = alloca(sizeof(*cur));
 				memset(cur, 0, sizeof(*cur));
-				ast_copy_string(cur->fn, fn ? reg->func : reg->file, sizeof(cur->fn));
+				ast_copy_string(cur->fn, fn ? reg->alloc_func : reg->alloc_file, sizeof(cur->fn));
 				cur->next = list;
 				list = cur;
 			}
@@ -547,35 +629,74 @@
 static void *memory_destructor(void *notused)
 {
 	struct ast_region *cur;
+	int last_seen_violation = -1;
 	for (;;) {
+		/* Any errors to report? */
+		if (last_seen_violation != last_violation) {
+			for (last_seen_violation++; last_seen_violation != last_violation;
+					last_seen_violation = (last_seen_violation == ARRAY_LEN(violations) - 1) ? 0 : last_seen_violation + 1) {
+				if ((cur = violations[last_seen_violation].base_addr) == NULL) {
+					memset(&violations[last_seen_violation], 0, sizeof(violations[0]));
+					continue;
+				}
+				astmm_log("Intercepted invalid %s to %p (%p+%d), originally allocated in %s at %s:%d and freed in %s at %s:%d\n",
+					violations[last_seen_violation].error == PROT_READ ? "read" : "write",
+					violations[last_seen_violation].violation, cur->data,
+					violations[last_seen_violation].violation - (void *) cur->data,
+					cur->alloc_func, cur->alloc_file, cur->alloc_lineno,
+					cur->free_func, cur->free_file, cur->free_lineno);
+				memset(&violations[last_seen_violation], 0, sizeof(violations[0]));
+				cur->violation = 0;
+				cur->mperm = PROT_NONE;
+				mprotect(cur, cur->fullsize, PROT_NONE);
+			}
+			last_seen_violation--;
+		}
 		ast_mutex_lock(&reglock);
 		/* Delete any segment which is free for at least 120 seconds */
-		while ((cur = regions[SOME_PRIME]) && cur->freetime < time(NULL) - 120) {
-			unsigned char *ptr;
-
-			for (ptr = cur->data; ptr < cur->data + cur->len; ptr++) {
-				if (*ptr != 0) {
-					astmm_log("WARNING: memory (%p) written to after being freed "
-						"at %p (+%d/%d), (originally in %s of %s, line %d)\n",
-						cur->data, ptr, ptr - cur->data, cur->len, cur->func, cur->file, cur->lineno);
-					break;
-				}
-			}
-
+		while ((cur = regions[SOME_PRIME]) && !mprotect(cur, sizeof(*cur), PROT_READ) && cur->freetime < time(NULL) - 120) {
+			int i;
+
+			if (mprotect(cur, cur->fullsize, PROT_READ)) {
+				astmm_log("ERROR: cannot access freed memory at %p, (originally in %s of %s, line %d, freed in %s of %s, line %d)\n",
+					cur->data, cur->alloc_func, cur->alloc_file, cur->alloc_lineno, cur->free_func, cur->free_file, cur->free_lineno);
+				/* XXX crash, perhaps? XXX (should not ever happen) */
+			}
+
+			cur->mperm = PROT_READ;
 			regions[SOME_PRIME] = cur->next;
 			if (cur == lastfree) {
-				astmm_log("Freeing last bit of memory in free list.\n");
 				lastfree = NULL;
 			}
 #if 0 /* Verification that this thread is running only */
-			astmm_log("Freeing memory at %p (size %d, originally allocated in %s of %s, line %d)\n", cur->data, cur->len, cur->func, cur->file, cur->lineno);
-#endif
+			astmm_log("Freeing memory at %p (size %d, originally allocated in %s of %s, line %d)\n", cur->data, cur->len, cur->alloc_func, cur->alloc_file, cur->alloc_lineno);
+#endif
+			if (cur->violation) {
+				for (i = 0; i < ARRAY_LEN(violations); i++) {
+					if (cur == violations[i].base_addr) {
+						astmm_log("Intercepted invalid %s to %p (%p+%d), originally allocated in %s at %s:%d and freed in %s at %s:%d\n",
+							violations[last_seen_violation].error == PROT_READ ? "read" : "write",
+							violations[last_seen_violation].violation, cur->data,
+							violations[last_seen_violation].violation - (void *) cur->data,
+							cur->alloc_func, cur->alloc_file, cur->alloc_lineno,
+							cur->free_func, cur->free_file, cur->free_lineno);
+						memset(&violations[i], 0, sizeof(violations[i]));
+						break;
+					}
+				}
+			}
 			free(cur);
 		}
-		ast_mutex_unlock(&reglock);
-		if (regions[SOME_PRIME]) {
-			sleep(120 - (time(NULL) - regions[SOME_PRIME]->freetime));
+		if ((cur = regions[SOME_PRIME])) {
+			int sleep_time = 120 - (time(NULL) - cur->freetime);
+
+			/* Re-protect region opened to reads while we examined cur->freetime */
+			mprotect(cur, sizeof(*cur), PROT_NONE);
+
+			ast_mutex_unlock(&reglock);
+			sleep(sleep_time);
 		} else {
+			ast_mutex_unlock(&reglock);
 			astmm_log("Nothing in free list.  Sleeping for 120 seconds.\n");
 			sleep(120);
 		}
@@ -584,24 +705,113 @@
 	return NULL;
 }
 
-/* Cannot include utils.h, as it defines things that this file implements,
- * but could eventually put these defines in a separate header file. */
-int ast_pthread_create_stack(pthread_t *thread, pthread_attr_t *attr, void *(*start_routine)(void *),
-			     void *data, size_t stacksize, const char *file, const char *caller,
-			     int line, const char *start_fn);
-#define AST_STACKSIZE (((sizeof(void *) * 8 * 8) - 16) * 1024)
-
-#if defined(LOW_MEMORY)
-#define AST_BACKGROUND_STACKSIZE (((sizeof(void *) * 8 * 2) - 16) * 1024)
-#else
-#define AST_BACKGROUND_STACKSIZE AST_STACKSIZE
-#endif
-
-#define ast_pthread_create_background(a, b, c, d) ast_pthread_create_stack(a, b, c, d,			\
-									   AST_BACKGROUND_STACKSIZE,	\
-									   __FILE__, __FUNCTION__,	\
-									   __LINE__, #c)
-#endif
+static void handle_sigsegv(int signal, siginfo_t *info, void *unused)
+{
+	struct ast_region *cur;
+
+	if (signal != SIGSEGV) {
+		return;
+	}
+
+	/* Access violation errors are the only things I handle;
+	 * anything else goes to the default handler. */
+	if (info->si_code != SEGV_ACCERR || info->si_addr == NULL) {
+		if (default_sigsegv.sa_flags & SA_SIGINFO && default_sigsegv.sa_sigaction != NULL) {
+			(default_sigsegv.sa_sigaction)(signal, info, unused);
+		} else if (default_sigsegv.sa_handler != NULL) {
+			(default_sigsegv.sa_handler)(signal);
+		} else {
+			abort();
+		}
+		return;
+	}
+
+	/* Find base address */
+	cur = (void *) ((size_t) info->si_addr / pagesize * pagesize);
+	mprotect(cur, sizeof(*cur), PROT_READ);
+	while (cur->fence != FENCE_MAGIC && cur->len > cur->fullsize) {
+		/*!
+		 * This loop is kind of weird.  By calling this routine, we expect that
+		 * the memory segment is one allocated by us, so this should terminate
+		 * when we find FENCE_MAGIC.  OTOH, it's possible that this wasn't us,
+		 * and we'll eventually step on something that wasn't an access problem,
+		 * the signal handler will fire again, call the default handler, and we'll
+		 * crash.  That is what we'd expect to happen, anyway, if this wasn't our
+		 * segment, so all is good.
+		 */
+		cur -= pagesize;
+		mprotect(cur, sizeof(*cur), PROT_READ);
+	}
+
+	/*!
+	 * The general rule of thumb is don't try to allocate memory or output to a
+	 * file descriptor in a signal handler.  Therefore, we must otherwise raise
+	 * flags for later reporting.  This might not be completely true, but
+	 * consider what signal handler this is.  This isn't the place to split
+	 * hairs.
+	 */
+	if (cur->mperm == PROT_READ) {
+		int newperm = PROT_READ | PROT_WRITE | PROT_EXEC;
+		mprotect(cur, cur->fullsize, newperm);
+		cur->mperm = newperm;
+		if (cur->vlocation == info->si_addr) {
+			cur->violation = PROT_WRITE;
+			if (violations[last_violation].base_addr == cur) {
+				violations[last_violation].error = PROT_WRITE;
+			}
+		}
+	} else {
+		/* We're about to modify, so let us write */
+		mprotect(cur, cur->fullsize, PROT_READ | PROT_WRITE);
+		cur->mperm = PROT_READ;
+		cur->vlocation = info->si_addr;
+		cur->violation = PROT_READ;
+		mprotect(cur, cur->fullsize, cur->mperm);
+
+		if (++last_violation > ARRAY_LEN(violations)) {
+			last_violation = 0;
+		}
+		violations[last_violation].base_addr = cur;
+		violations[last_violation].violation = info->si_addr;
+		violations[last_violation].when = time(NULL);
+		violations[last_violation].error = cur->violation;
+	}
+
+	/*!
+	 * Okay, if we've done our job right, we now know that the user
+	 * stepped on memory that was freed, we've intercepted that access,
+	 * allowed the memory to be read/written, and return.  This will allow
+	 * the (previously illegal) operation to continue, as if the segfault
+	 * never actually occurred.  Yes, that seems weird, but that's the way
+	 * this signal works.
+	 *
+	 * Now here's the interesting part.  This signal handler will get
+	 * re-entered immediately if we didn't clear the right protection, which
+	 * is what we designed, when we differentiate between an attempted read
+	 * (which won't come back) and a write (which will).  We can therefore
+	 * determine which type of access occurred to free'd memory.
+	 *
+	 * The rub is that once we unprotect this memory, we cannot easily
+	 * re-protect the memory, since returning releases the control from the
+	 * signal handler.  Thus, we only will detect the FIRST illegal access
+	 * of (this segment of) free'd memory, not any subsequent.  This hopefully
+	 * is sufficient for our purposes.
+	 */
+}
+
+static void mm_cleanup(void)
+{
+	struct ast_region *reg;
+	ast_mutex_lock(&reglock);
+	while ((reg = regions[SOME_PRIME])) {
+		mprotect(reg, sizeof(*reg), PROT_READ | PROT_WRITE);
+		mprotect(reg, reg->fullsize, PROT_READ | PROT_WRITE);
+		reg->violation = 0;
+		regions[SOME_PRIME] = reg->next;
+		free(reg);
+	}
+	ast_mutex_unlock(&reglock);
+}
 
 void __ast_mm_init(void)
 {
@@ -609,6 +819,21 @@
 	size_t pad = sizeof(struct ast_region) - offsetof(struct ast_region, data);
 #ifdef MALLOC_HOLD
 	pthread_t notused;
+	struct sigaction sa = { .sa_flags = SA_SIGINFO | SA_NODEFER, .sa_sigaction = handle_sigsegv, };
+
+	sigemptyset(&sa.sa_mask);
+	if (sigaction(SIGSEGV, &sa, &default_sigsegv) == -1) {
+		ast_log(LOG_ERROR, "Cannot handle segmentation faults?");
+		abort();
+	}
+
+	if ((pagesize = sysconf(_SC_PAGESIZE)) == -1) {
+		ast_log(LOG_ERROR, "Cannot retrieve system memory page size?");
+		abort();
+	}
+
+	/* Remove all memory protections before we exit */
+	ast_register_atexit(mm_cleanup);
 
 	ast_pthread_create_background(&notused, NULL, memory_destructor, NULL);
 #endif




More information about the asterisk-commits mailing list