Expand the coverage of canaries to object allocations, expand logging to object

allocations and all deallocations.  It's now possible to implement
malloc_history entirely parsing the dump file (or doing /dev/fd style tricks
send it straight to a monitoring process).
main
theraven 15 years ago
parent c9e54c382d
commit 851328268c

@ -26,6 +26,11 @@ static BOOL finalizeThreaded;
* Should we do some (not 100% reliable) buffer overflow checking. * Should we do some (not 100% reliable) buffer overflow checking.
*/ */
static size_t canarySize; static size_t canarySize;
/**
* The canary value. Used to check for overruns. When an allocation is
* finalized, we check whether it ends with this value.
*/
static uint32_t canary;
/** /**
* Destination to write allocation log to. This can be used to implement the * Destination to write allocation log to. This can be used to implement the
* equivalent of malloc_history * equivalent of malloc_history
@ -58,15 +63,15 @@ struct objc_slot* objc_get_slot(Class cls, SEL selector);
#endif #endif
#ifdef NO_EXECINFO #ifdef NO_EXECINFO
static inline void dump_stack(void *addr) {} static inline void dump_stack(char *msg, void *addr) {}
#else #else
#include <execinfo.h> #include <execinfo.h>
static inline void dump_stack(void *addr) static inline void dump_stack(char *msg, void *addr)
{ {
if (NULL == allocationLog) { return; } if (NULL == allocationLog) { return; }
void *array[30]; void *array[30];
int frames = backtrace(array, 30); int frames = backtrace(array, 30);
fprintf(allocationLog, "Allocating %p\n", addr); fprintf(allocationLog, "%s %p\n", msg, addr);
fflush(allocationLog); fflush(allocationLog);
backtrace_symbols_fd(array, frames, fileno(allocationLog)); backtrace_symbols_fd(array, frames, fileno(allocationLog));
fflush(allocationLog); fflush(allocationLog);
@ -340,7 +345,19 @@ Class zombie_class;
static void runFinalize(void *addr, void *context) static void runFinalize(void *addr, void *context)
{ {
dump_stack("Freeing Object: ", addr);
id obj = addr; id obj = addr;
size_t size = (uintptr_t)context;
if ((canarySize > 0) &&
(*(uint32_t*)((char*)obj + size) != canary))
{
fprintf(stderr, "Something wrote past the end of %p\n", addr);
if (obj->isa != Nil)
{
fprintf(stderr, "Instance of %s\n", obj->isa->name);
}
abort();
}
//fprintf(stderr, "FINALIZING %p (%s)\n", addr, ((id)addr)->isa->name); //fprintf(stderr, "FINALIZING %p (%s)\n", addr, ((id)addr)->isa->name);
if (Nil == ((id)addr)->isa) { return; } if (Nil == ((id)addr)->isa) { return; }
struct objc_slot *slot = objc_get_slot(obj->isa, cxx_destruct); struct objc_slot *slot = objc_get_slot(obj->isa, cxx_destruct);
@ -413,25 +430,35 @@ static GC_descr descriptor_for_class(Class cls)
static id allocate_class(Class cls, size_t extra) static id allocate_class(Class cls, size_t extra)
{ {
size_t size = class_getInstanceSize(cls);
if (canarySize)
{
extra += 4;
}
id obj = 0; id obj = 0;
// If there are some extra bytes, they may contain pointers, so we ignore // If there are some extra bytes, they may contain pointers, so we ignore
// the type // the type
if (extra > 0) if (extra > 0)
{ {
size += extra;
// FIXME: Overflow checking! // FIXME: Overflow checking!
obj = GC_MALLOC(class_getInstanceSize(cls) + extra); obj = GC_MALLOC(size);
} }
else else
{ {
obj = GC_MALLOC_EXPLICITLY_TYPED(class_getInstanceSize(cls), obj = GC_MALLOC_EXPLICITLY_TYPED(size, descriptor_for_class(cls));
descriptor_for_class(cls));
} }
//fprintf(stderr, "Allocating %p (%s + %d). Base is %p\n", obj, cls->name, extra, GC_base(obj)); //fprintf(stderr, "Allocating %p (%s + %d). Base is %p\n", obj, cls->name, extra, GC_base(obj));
// It would be nice not to register a finaliser if the object didn't // It would be nice not to register a finaliser if the object didn't
// implement finalize or .cxx_destruct methods. Unfortunately, this is not // implement finalize or .cxx_destruct methods. Unfortunately, this is not
// possible, because a class may add a finalize method as it runs. // possible, because a class may add a finalize method as it runs.
GC_REGISTER_FINALIZER_NO_ORDER(obj, runFinalize, 0, 0, 0); GC_REGISTER_FINALIZER_NO_ORDER(obj, runFinalize,
dump_stack(obj); (void*)(uintptr_t)size-canarySize, 0, 0);
if (canarySize > 0)
{
*(uint32_t*)((char*)obj + size - canarySize) = canary;
}
dump_stack("Allocating object", obj);
return obj; return obj;
} }
@ -562,10 +589,10 @@ int objc_gc_retain_count(id object)
return (0 == refcount) ? 0 : refcount->refCount; return (0 == refcount) ? 0 : refcount->refCount;
} }
static uint32_t canary;
static void nuke_buffer(void *addr, void *s) static void nuke_buffer(void *addr, void *s)
{ {
dump_stack("Freeing allocation: ", addr);
uintptr_t size = (uintptr_t)s; uintptr_t size = (uintptr_t)s;
if (canary != *(uint32_t*)((char*)addr + size - 4)) if (canary != *(uint32_t*)((char*)addr + size - 4))
{ {
@ -596,7 +623,7 @@ void* objc_gc_allocate_collectable(size_t size, BOOL isScanned)
GC_REGISTER_FINALIZER_NO_ORDER(buffer, nuke_buffer, GC_REGISTER_FINALIZER_NO_ORDER(buffer, nuke_buffer,
(void*)(uintptr_t)size, 0, 0); (void*)(uintptr_t)size, 0, 0);
} }
dump_stack(buffer); dump_stack("Allocating memory", buffer);
return buffer; return buffer;
} }
void* objc_gc_reallocate_collectable(void *ptr, size_t size, BOOL isScanned) void* objc_gc_reallocate_collectable(void *ptr, size_t size, BOOL isScanned)
@ -615,7 +642,7 @@ void* objc_gc_reallocate_collectable(void *ptr, size_t size, BOOL isScanned)
} }
memcpy(new, ptr, size); memcpy(new, ptr, size);
} }
dump_stack(new); dump_stack("New allocation from realloc: ", new);
return new; return new;
} }

Loading…
Cancel
Save