Index: lib/scudo/standalone/combined.h =================================================================== --- lib/scudo/standalone/combined.h +++ lib/scudo/standalone/combined.h @@ -375,7 +375,7 @@ const uptr From = Base; const uptr To = Base + Size; auto Lambda = [this, From, To, Callback, Arg](uptr Block) { - if (Block < From || Block > To) + if (Block < From || Block >= To) return; uptr ChunkSize; const uptr ChunkBase = getChunkFromBlock(Block, &ChunkSize); Index: lib/scudo/standalone/primary64.h =================================================================== --- lib/scudo/standalone/primary64.h +++ lib/scudo/standalone/primary64.h @@ -36,7 +36,7 @@ // freelist to the thread specific freelist, and back. // // The memory used by this allocator is never unmapped, but can be partially -// released it the platform allows for it. +// released if the platform allows for it. template class SizeClassAllocator64 { public: @@ -135,7 +135,9 @@ } template void iterateOverBlocks(F Callback) const { - for (uptr I = 1; I < NumClasses; I++) { + for (uptr I = 0; I < NumClasses; I++) { + if (I == SizeClassMap::BatchClassId) + continue; const RegionInfo *Region = getRegionInfo(I); const uptr BlockSize = getSizeByClassId(I); const uptr From = Region->RegionBeg; Index: lib/scudo/standalone/tests/wrappers_c_test.cpp =================================================================== --- lib/scudo/standalone/tests/wrappers_c_test.cpp +++ lib/scudo/standalone/tests/wrappers_c_test.cpp @@ -14,6 +14,14 @@ #include #include +extern "C" { +void malloc_enable(void); +void malloc_disable(void); +int malloc_iterate(uintptr_t base, size_t size, + void (*callback)(uintptr_t base, size_t size, void *arg), + void *arg); +} + // Note that every C allocation function in the test binary will be fulfilled // by Scudo (this includes the gtest APIs, etc.), which is a test by itself. // But this might also lead to unexpected side-effects, since the allocation and @@ -239,3 +247,43 @@ MI = mallinfo(); EXPECT_GE(static_cast(MI.fordblks), Free + BypassQuarantineSize); } + +static uintptr_t BoundaryP = 0U; +static size_t Counter = 0U; + +static void callback(uintptr_t Base, size_t Size, void *Arg) { + if (Base == BoundaryP) + Counter++; +} + +// Verify that a block located on an iteration boundary is not mis-accounted. +TEST(ScudoWrappersCTest, MallocIterateBoundary) { + const size_t BlockDelta = FIRST_32_SECOND_64(8U, 16U); + const size_t SpecialSize = 128U - BlockDelta; + const size_t PageSize = sysconf(_SC_PAGESIZE); + + std::vector V; + while (V.size() < 512U) { + void *P = malloc(SpecialSize); + EXPECT_NE(P, nullptr); + V.push_back(P); + const uintptr_t Block = reinterpret_cast(P) - BlockDelta; + if ((Block & (PageSize - 1)) == 0) { + BoundaryP = reinterpret_cast(P); + break; + } + } + EXPECT_NE(BoundaryP, 0U); + + const uintptr_t Boundary = BoundaryP - BlockDelta; + malloc_disable(); + malloc_iterate(Boundary - PageSize, PageSize, callback, nullptr); + malloc_iterate(Boundary, PageSize, callback, nullptr); + malloc_enable(); + EXPECT_EQ(Counter, 1U); + + while (!V.empty()) { + free(V.back()); + V.pop_back(); + } +}