I am working in a legacy codebase with a large amount of Objective-C++ written using manual retain/release. Memory is managed using lots of C++ std::shared_ptr<NSMyCoolObjectiveCPointer>
, with a suitable deleter passed in on construction that calls release
on the contained object. This seems to work great; however, when enabling UBSan, it complains about misaligned pointers, usually when dereferencing the shared_ptrs to do some work.
I've searched for clues and/or solutions, but it's difficult to find technical discussion of the ins and outs of Objective-C object pointers, and even more difficult to find any discussion about Objective-C++, so here I am.
Here is a full Objective-C++ program that demonstrates my problem. When I run this on my Macbook with UBSan, I get a misaligned pointer issue in shared_ptr::operator*
:
#import <Foundation/Foundation.h>
#import <memory>
class DateImpl {
public:
DateImpl(NSDate* date) : _date{[date retain], [](NSDate* date) { [date release]; }} {}
NSString* description() const { return [&*_date description]; }
private:
std::shared_ptr<NSDate> _date;
};
int main(int argc, const char * argv[]) {
@autoreleasepool {
DateImpl date{[NSDate distantPast]};
NSLog(@"%@", date.description());
return 0;
}
}
I get this in the call to DateImpl::description
:
runtime error: reference binding to misaligned address 0xe2b7fda734fc266f for type 'std::__1::shared_ptr<NSDate>::element_type' (aka 'NSDate'), which requires 8 byte alignment
0xe2b7fda734fc266f: note: pointer points here
<memory cannot be printed>
I suspect that there is something awry with the usage of &*
to "cast" the shared_ptr<NSDate>
to an NSDate*
. I think I could probably work around this issue by using .get()
on the shared_ptr instead, but I am genuinely curious about what is going on. Thanks for any feedback or hints!
There were some red herrings here:
shared_ptr
, manual retain/release, etc. But I ended up discovering that even this very simple code (with ARC enabled) causes the ubsan hit:It seems to simply be an issue with
[NSDate distantPast]
(and, incidentally,[NSDate distantFuture]
, but not, for instance,[NSDate date]
). I conclude that these must be singleton objects allocated sketchily/misaligned-ly somewhere in the depths of Foundation, and when you dereference them it causes a misaligned pointer read.(Note it does not happen when the code is simply
NSLog(@"%@", &*[NSDate distantPast])
. I assume this is because the compiler simply collapses&*
on a raw pointer into a no-op. It doesn't for theshared_ptr
case in the original question becauseshared_ptr
overloadsoperator*
. Given this, I believe there is no easy way to make this happen in pure Objective-C, since you can't separate the&
operation from the*
operation, like you can when C++ references are involved [by storing the temporary result of*
in anNSDate&
].)