Just ran into this very strange bug. I'm getting crashes due to memory errors after adding a new shared_ptr
instance variable in an existing containing class.
When I run with the AddressSanitizer in Xcode, it reports a heap buffer overflow on the new iVar, when an object of the containing class is instantiated (i.e., at its CTOR).
I can't replicate this in a simple system, and this is proprietary code (so I can't show it fully here). However, I can trigger the error simply by declaring a new shared_ptr instance variable, of a simple type (int).
My classes are basically
// MyContainingClass.h
#include <memory>
// Forward
class HelperObject;
class MyContainingClass {
public:
MyContainingClass();
...
private:
std::shared_ptr<HelperObject> _helperObject;
};
//MyContainingClass.cpp
#include "HelperObject.h"
...
MyContainingClass::MyContainingClass() {
_helperObject = std::make_shared<HelperObject>();
}
...
and that runs fine. But when I try this (adding one line: the new iVar)
// MyContainingClass.h
#include <memory>
// Forwards
class HelperObject;
class DifferentHelperObject;
class MyContainingClass {
public:
MyContainingClass();
...
private:
std::shared_ptr<HelperObject> _helperObject;
std::shared_ptr<DifferentHelperObject> _differentHelperObject; // <- NEW LINE
};
I get a heap buffer overflow at the MyContainingClass CTOR, even without trying to allocate the DifferentHelperObject.. (I get the same error if I do create the smart pointer in the CTOR.)
If I change the order of declaration, AddressSanitizer reports the error at HelperObject
instead of DifferentHelperObject
(i.e., whichever one is declared second).
DifferentHelperObject is a fairly simple type; it contains only simple POD members and methods (no pointers, arrays, smart pointers, etc.) So I tried replacing it with something really simple: a std::shared_ptr<int>
:
std::shared_ptr<HelperObject> _helperObject;
std::shared_ptr<int> _anInt; //Adding just this line to otherwise working code causes a crash
and I now get the heap buffer overflow on the shared_ptr for _anInt.
This is what the AddressSanitizer output looks like for the last case:
SUMMARY: AddressSanitizer: heap-buffer-overflow (/private/var/containers/Bundle/Application/XXXXXXX/MyApp.app/MyApp:arm64+0x10aa0e120) in std::__1::shared_ptr<int>::shared_ptr()+0x4c
Shadow bytes around the buggy address:
0x0002a56ea260: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
0x0002a56ea270: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
0x0002a56ea280: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
0x0002a56ea290: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x0002a56ea2a0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
=>0x0002a56ea2b0: 00 00 00 00 00 00 00 00 00 00[fa]fa fa fa fa fa
0x0002a56ea2c0: fa fa fa fa fa fa fa fa fd fd fd fd fd fd fd fd
0x0002a56ea2d0: fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd
0x0002a56ea2e0: fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd
0x0002a56ea2f0: fd fd fa fa fa fa fa fa fa fa fa fa fa fa fa fa
0x0002a56ea300: fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd
Shadow byte legend (one shadow byte represents 8 application bytes):
Addressable: 00
Partially addressable: 01 02 03 04 05 06 07
Heap left redzone: fa
AddressSanitizer report breakpoint hit. Use 'thread info -s' to get extended information about the report.
(lldb) thread info -s
thread #1: tid = 0xb9794, 0x000000012446b328 libclang_rt.asan_ios_dynamic.dylib`__asan::AsanDie(), queue = 'com.apple.main-thread', stop reason = Heap buffer overflow
{
"access_size": 8,
"access_type": 1,
"address": 5023012304,
"description": "heap-buffer-overflow",
"instrumentation_class": "AddressSanitizer",
"pc": 4543308068,
"stop_type": "fatal_error"
}
(lldb)
and the call stack looks like this:
Thread 1 Queue : com.apple.main-thread (serial)
#0 0x000000012446b328 in __asan::AsanDie() ()
#1 0x000000012448096c in __sanitizer::Die() ()
#2 0x000000012446890c in __asan::ScopedInErrorReport::~ScopedInErrorReport() ()
#3 0x000000012446813c in __asan::ReportGenericError(unsigned long, unsigned long, unsigned long, unsigned long, bool, unsigned long, unsigned int, bool) ()
#4 0x000000012446943c in __asan_report_store8 ()
#5 0x000000010ecd6124 in std::__1::shared_ptr<int>::shared_ptr() at /Applications/Xcode_13.2.1.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS15.2.sdk/usr/include/c++/v1/memory:2983
#6 0x000000010ecd119c in std::__1::shared_ptr<int>::shared_ptr() at /Applications/Xcode_13.2.1.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS15.2.sdk/usr/include/c++/v1/memory:2985
#7 0x000000010ecd0f08 in MyContainingClass::MyContainingClass() at /Users/xxx/MyContainingClass.cpp:22
#8 0x000000010ecd1870 in MyContainingClass::MyContainingClass() at /Users/xxx/MyContainingClass.cpp:22
I've been trying to understand the AddressSanitizer output; the shadow memory addresses don't seem to map correctly onto actual program memory (mutliplying by 8, there is still an offset. So any help understanding how the AddressSanitizer output above could point me to the actual problem (or suggestions what the actual problem might be) would be greatly appreciated.
I had the same issue. In my case, as @user17732522 mentioned, it was a symptom, not the problem itself.
I had static library and executable linked with the library. Both library and executable included the same header file:
class Config
{
public:
bool foo;
bool bar;
#if defined(DEFINITION)
bool baz;
#endif
};
DEFINITION
was defined in library and not defined in the executable
So the following class:
class Class {
// ...
private:
Config config_;
std::shared_ptr<int> obj1_;
std::shared_ptr<HelperObject> obj2_;
}
produced the same ASan error:
=================================================================
==7202==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x60c000000180 at pc 0x000000c33b5d bp 0x7ffc29bc1da0 sp 0x7ffc29bc1d98
WRITE of size 8 at 0x60c000000180 thread T0
#0 0xc33b5c in std::__1::shared_ptr<HelperObject>::shared_ptr() /usr/local/include/c++/v1/memory:4047:7