From 72156a402f54ea4be9dc3e3e9704c612f7d9ad16 Mon Sep 17 00:00:00 2001 From: Michael Lotz Date: Mon, 31 Oct 2011 21:58:00 +0000 Subject: [PATCH] bonefish+mmlr: * Introduce "paranoid" malloc/free into the slab allocator (initializing allocated memory to 0xcc and setting freed memory to 0xdeadbeef). * Allow for optional stack traces for slab object cache tracing. git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@43046 a95241bf-73f2-0310-859d-f6bbb57e9c96 --- build/config_headers/tracing_config.h | 1 + headers/private/kernel/slab/ObjectDepot.h | 4 ++ src/system/kernel/slab/MemoryManager.cpp | 8 +++- src/system/kernel/slab/ObjectCache.cpp | 29 ++++++++++++ src/system/kernel/slab/ObjectCache.h | 4 ++ src/system/kernel/slab/ObjectDepot.cpp | 54 +++++++++++++++++++++++ src/system/kernel/slab/Slab.cpp | 27 ++++++++++-- src/system/kernel/slab/slab_private.h | 37 ++++++++++++++++ 8 files changed, 159 insertions(+), 5 deletions(-) diff --git a/build/config_headers/tracing_config.h b/build/config_headers/tracing_config.h index 926b69e1d5..5f1da44232 100644 --- a/build/config_headers/tracing_config.h +++ b/build/config_headers/tracing_config.h @@ -48,6 +48,7 @@ #define SIGNAL_TRACING 0 #define SLAB_MEMORY_MANAGER_TRACING 0 #define SLAB_OBJECT_CACHE_TRACING 0 +#define SLAB_OBJECT_CACHE_TRACING_STACK_TRACE 0 /* stack trace depth */ #define SWAP_TRACING 0 #define SYSCALL_TRACING 0 #define SYSCALL_TRACING_IGNORE_KTRACE_OUTPUT 1 diff --git a/headers/private/kernel/slab/ObjectDepot.h b/headers/private/kernel/slab/ObjectDepot.h index 4ed0591606..456038aed2 100644 --- a/headers/private/kernel/slab/ObjectDepot.h +++ b/headers/private/kernel/slab/ObjectDepot.h @@ -45,6 +45,10 @@ void object_depot_store(object_depot* depot, void* object, uint32 flags); void object_depot_make_empty(object_depot* depot, uint32 flags); +#if PARANOID_KERNEL_FREE +bool object_depot_contains_object(object_depot* depot, void* object); +#endif + #ifdef __cplusplus } #endif diff --git a/src/system/kernel/slab/MemoryManager.cpp b/src/system/kernel/slab/MemoryManager.cpp index b59a2a82a4..0c8dcdfdb2 100644 --- a/src/system/kernel/slab/MemoryManager.cpp +++ b/src/system/kernel/slab/MemoryManager.cpp @@ -617,7 +617,11 @@ MemoryManager::AllocateRaw(size_t size, uint32 flags, void*& _pages) ? CREATE_AREA_DONT_WAIT : 0) | CREATE_AREA_DONT_CLEAR, &virtualRestrictions, &physicalRestrictions, &_pages); - return area >= 0 ? B_OK : area; + + status_t result = area >= 0 ? B_OK : area; + if (result == B_OK) + fill_allocated_block(_pages, size); + return result; } // determine chunk size (small or medium) @@ -656,6 +660,8 @@ MemoryManager::AllocateRaw(size_t size, uint32 flags, void*& _pages) chunk->reference = (addr_t)chunkAddress + size - 1; _pages = (void*)chunkAddress; + fill_allocated_block(_pages, size); + TRACE("MemoryManager::AllocateRaw() done: %p (meta chunk: %d, chunk %d)\n", _pages, int(metaChunk - area->metaChunks), int(chunk - metaChunk->chunks)); diff --git a/src/system/kernel/slab/ObjectCache.cpp b/src/system/kernel/slab/ObjectCache.cpp index 7a3bb3b258..8f1822a4aa 100644 --- a/src/system/kernel/slab/ObjectCache.cpp +++ b/src/system/kernel/slab/ObjectCache.cpp @@ -238,3 +238,32 @@ ObjectCache::ReturnObjectToSlab(slab* source, void* object, uint32 flags) partial.Add(source); } } + + +#if PARANOID_KERNEL_FREE + +bool +ObjectCache::AssertObjectNotFreed(void* object) +{ + MutexLocker locker(lock); + + slab* source = ObjectSlab(object); + if (!partial.Contains(source) && !full.Contains(source)) { + panic("object_cache: to be freed object slab not part of cache!"); + return false; + } + + object_link* link = object_to_link(object, object_size); + for (object_link* freeLink = source->free; freeLink != NULL; + freeLink = freeLink->next) { + if (freeLink == link) { + panic("object_cache: double free of %p (slab %p, cache %p)", + object, source, this); + return false; + } + } + + return true; +} + +#endif // PARANOID_KERNEL_FREE diff --git a/src/system/kernel/slab/ObjectCache.h b/src/system/kernel/slab/ObjectCache.h index f2d36b5caf..498256edad 100644 --- a/src/system/kernel/slab/ObjectCache.h +++ b/src/system/kernel/slab/ObjectCache.h @@ -107,6 +107,10 @@ public: void FreePages(void* pages); status_t EarlyAllocatePages(void** pages, uint32 flags); void EarlyFreePages(void* pages); + +#if PARANOID_KERNEL_FREE + bool AssertObjectNotFreed(void* object); +#endif }; diff --git a/src/system/kernel/slab/ObjectDepot.cpp b/src/system/kernel/slab/ObjectDepot.cpp index 60bab902c3..e8a890edbc 100644 --- a/src/system/kernel/slab/ObjectDepot.cpp +++ b/src/system/kernel/slab/ObjectDepot.cpp @@ -31,6 +31,10 @@ public: inline void* Pop(); inline bool Push(void* object); + +#if PARANOID_KERNEL_FREE + bool ContainsObject(void* object) const; +#endif }; @@ -72,6 +76,22 @@ DepotMagazine::Push(void* object) } +#if PARANOID_KERNEL_FREE + +bool +DepotMagazine::ContainsObject(void* object) const +{ + for (uint16 i = 0; i < current_round; i++) { + if (rounds[i] == object) + return true; + } + + return false; +} + +#endif // PARANOID_KERNEL_FREE + + // #pragma mark - @@ -352,6 +372,40 @@ object_depot_make_empty(object_depot* depot, uint32 flags) } +#if PARANOID_KERNEL_FREE + +bool +object_depot_contains_object(object_depot* depot, void* object) +{ + WriteLocker writeLocker(depot->outer_lock); + + int cpuCount = smp_get_num_cpus(); + for (int i = 0; i < cpuCount; i++) { + depot_cpu_store& store = depot->stores[i]; + + if (store.loaded != NULL && !store.loaded->IsEmpty()) { + if (store.loaded->ContainsObject(object)) + return true; + } + + if (store.previous != NULL && !store.previous->IsEmpty()) { + if (store.previous->ContainsObject(object)) + return true; + } + } + + for (DepotMagazine* magazine = depot->full; magazine != NULL; + magazine = magazine->next) { + if (magazine->ContainsObject(object)) + return true; + } + + return false; +} + +#endif // PARANOID_KERNEL_FREE + + // #pragma mark - private kernel API diff --git a/src/system/kernel/slab/Slab.cpp b/src/system/kernel/slab/Slab.cpp index 402d9718cc..12f02f6d3d 100644 --- a/src/system/kernel/slab/Slab.cpp +++ b/src/system/kernel/slab/Slab.cpp @@ -54,10 +54,12 @@ static ConditionVariable sMaintenanceCondition; namespace SlabObjectCacheTracing { -class ObjectCacheTraceEntry : public AbstractTraceEntry { +class ObjectCacheTraceEntry + : public TRACE_ENTRY_SELECTOR(SLAB_OBJECT_CACHE_TRACING_STACK_TRACE) { public: ObjectCacheTraceEntry(ObjectCache* cache) : + TraceEntryBase(SLAB_OBJECT_CACHE_TRACING_STACK_TRACE, 0, true), fCache(cache) { } @@ -668,7 +670,7 @@ object_cache_alloc(object_cache* cache, uint32 flags) void* object = object_depot_obtain(&cache->depot); if (object) { T(Alloc(cache, flags, object)); - return object; + return fill_allocated_block(object, cache->object_size); } } @@ -717,7 +719,7 @@ object_cache_alloc(object_cache* cache, uint32 flags) void* object = link_to_object(link, cache->object_size); T(Alloc(cache, flags, object)); - return object; + return fill_allocated_block(object, cache->object_size); } @@ -729,7 +731,24 @@ object_cache_free(object_cache* cache, void* object, uint32 flags) T(Free(cache, object)); - if (!(cache->flags & CACHE_NO_DEPOT)) { +#if PARANOID_KERNEL_FREE + // TODO: allow forcing the check even if we don't find deadbeef + if (*(uint32*)object == 0xdeadbeef) { + if (!cache->AssertObjectNotFreed(object)) + return; + + if ((cache->flags & CACHE_NO_DEPOT) == 0) { + if (object_depot_contains_object(&cache->depot, object)) { + panic("object_cache: object %p is already freed", object); + return; + } + } + } + + fill_freed_block(object, cache->object_size); +#endif + + if ((cache->flags & CACHE_NO_DEPOT) == 0) { object_depot_store(&cache->depot, object, flags); return; } diff --git a/src/system/kernel/slab/slab_private.h b/src/system/kernel/slab/slab_private.h index 6eba3fcf69..9c0e1e2b08 100644 --- a/src/system/kernel/slab/slab_private.h +++ b/src/system/kernel/slab/slab_private.h @@ -84,4 +84,41 @@ slab_internal_free(void* buffer, uint32 flags) } +#if PARANOID_KERNEL_MALLOC || PARANOID_KERNEL_FREE +static inline void* +fill_block(void* buffer, size_t size, uint32 pattern) +{ + if (buffer == NULL) + return NULL; + + size &= ~(sizeof(pattern) - 1); + for (size_t i = 0; i < size / sizeof(pattern); i++) + ((uint32*)buffer)[i] = pattern; + + return buffer; +} +#endif + + +static inline void* +fill_allocated_block(void* buffer, size_t size) +{ +#if PARANOID_KERNEL_MALLOC + return fill_block(buffer, size, 0xcccccccc); +#else + return buffer; +#endif +} + + +static inline void* +fill_freed_block(void* buffer, size_t size) +{ +#if PARANOID_KERNEL_FREE + return fill_block(buffer, size, 0xdeadbeef); +#else + return buffer; +#endif +} + #endif // SLAB_PRIVATE_H