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.
This commit is contained in:
1 parent
0fed03f35a
commit
cedda2084d
3 files changed
+31
-2
No files matched your search
+11
-2
@@ -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
|
// 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)
|
// 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) {
|
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) {
|
static void forkable_stack_pop(struct forkable_stack* s) {
|
||||||
forkable_stack_check(s);
|
forkable_stack_check(s);
|
||||||
struct forkable_stack_header* elem = forkable_stack_peek(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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -26,3 +26,12 @@ void* jv_mem_realloc(void* p, size_t sz) {
|
|||||||
}
|
}
|
||||||
return p;
|
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
|
||||||
+11
@@ -3,6 +3,17 @@
|
|||||||
|
|
||||||
#include <stddef.h>
|
#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_alloc(size_t);
|
||||||
void jv_mem_free(void*);
|
void jv_mem_free(void*);
|
||||||
__attribute__((warn_unused_result)) void* jv_mem_realloc(void*, size_t);
|
__attribute__((warn_unused_result)) void* jv_mem_realloc(void*, size_t);
|
||||||
|
|||||||
Reference in new issue
Block a user