summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorStephen Dolan <mu@netsoc.tcd.ie>2012-12-24 17:11:18 +0000
committerStephen Dolan <mu@netsoc.tcd.ie>2012-12-24 17:11:18 +0000
commitcedda2084d2f6b331ba0a73e05f0b77ee7995c86 (patch)
tree8a91fd88b2ce655780f3399954c7c37d2c368b95
parent0fed03f35a63c01e89c0ba91cfe92dfc226d1e86 (diff)
Sneaky valgrind trick to detect stack memory issues.
After something is popped from a stack, we overwrite the memory with uninitialised data (if JQ_DEBUG is on). This means that valgrind reports use-after-pop as an uninitialised memory error.
-rw-r--r--forkable_stack.h13
-rw-r--r--jv_alloc.c9
-rw-r--r--jv_alloc.h11
3 files changed, 31 insertions, 2 deletions
diff --git a/forkable_stack.h b/forkable_stack.h
index c96c132e..74fcf433 100644
--- a/forkable_stack.h
+++ b/forkable_stack.h
@@ -100,13 +100,22 @@ static void* forkable_stack_peek_next(struct forkable_stack* s, void* top) {
// Returns 1 if the next forkable_stack_pop will permanently remove an
// object from the stack (i.e. the top object was not saved with a fork)
static int forkable_stack_pop_will_free(struct forkable_stack* s) {
- return s->pos < s->savedlimit ? 1 : 0;
+ return s->pos < s->savedlimit;
}
static void forkable_stack_pop(struct forkable_stack* s) {
forkable_stack_check(s);
struct forkable_stack_header* elem = forkable_stack_peek(s);
- s->pos += elem->next_delta;
+ int reclaim_upto = s->pos + elem->next_delta < s->savedlimit ?
+ s->pos + elem->next_delta : s->savedlimit;
+ assert(reclaim_upto <= s->length);
+ int reclaimed = reclaim_upto > s->pos ? reclaim_upto - s->pos : 0;
+ assert(0 <= reclaimed && s->pos + reclaimed <= s->length);
+ // s->pos <= i < reclaim_upto means that position i is to be freed
+ assert((reclaimed > 0) == forkable_stack_pop_will_free(s));
+ int new_pos = s->pos + elem->next_delta;
+ jv_mem_invalidate(elem, reclaimed);
+ s->pos = new_pos;
}
diff --git a/jv_alloc.c b/jv_alloc.c
index 320f16e1..e127029d 100644
--- a/jv_alloc.c
+++ b/jv_alloc.c
@@ -26,3 +26,12 @@ void* jv_mem_realloc(void* p, size_t sz) {
}
return p;
}
+
+#if JQ_DEBUG
+volatile char jv_mem_uninitialised;
+__attribute__((constructor)) void jv_mem_uninit_setup(){
+ char* p = malloc(1);
+ jv_mem_uninitialised = *p;
+ free(p);
+}
+#endif
diff --git a/jv_alloc.h b/jv_alloc.h
index 0613d587..b4e72c0d 100644
--- a/jv_alloc.h
+++ b/jv_alloc.h
@@ -3,6 +3,17 @@
#include <stddef.h>
+#if JQ_DEBUG
+extern volatile char jv_mem_uninitialised;
+#endif
+
+static void jv_mem_invalidate(void* mem, size_t n) {
+#if JQ_DEBUG
+ char* m = mem;
+ while (n--) *m++ ^= jv_mem_uninitialised ^ jv_mem_uninitialised;
+#endif
+}
+
void* jv_mem_alloc(size_t);
void jv_mem_free(void*);
__attribute__((warn_unused_result)) void* jv_mem_realloc(void*, size_t);