未对齐的指针使用与 std::shared_ptr<NSDate> 取消引用



我正在使用一个遗留代码库,其中包含大量使用手动保留/发布编写的Objective-C++。内存使用大量的C++std::shared_ptr<NSMyCoolObjectiveCPointer>进行管理,在构造中传递一个合适的删除器,在包含的对象上调用release。这似乎效果很好;但是,在启用 UBSan 时,它抱怨指针未对齐,通常是在取消引用shared_ptrs以执行某些工作时。

我已经搜索了线索和/或解决方案,但很难找到关于Objective-C对象指针来龙去脉的技术讨论,甚至很难找到任何关于Objective-C++的讨论,所以我在这里。

这是一个完整的Objective-C++程序,演示了我的问题。当我使用 UBSan 在 Macbook 上运行它时,我在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;
}
}

我在给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>

怀疑使用&*shared_ptr<NSDate>"投射"到NSDate*有些不对劲.我想我可以通过在shared_ptr上使用.get()来解决这个问题,但我真的很好奇发生了什么。感谢您的任何反馈或提示!

这里有一些红鲱鱼:shared_ptr、手动保留/释放等。但我最终发现,即使是这个非常简单的代码(启用了 ARC(也会导致 ubsan 命中:

#import <Foundation/Foundation.h>
int main(int argc, const char * argv[]) {
@autoreleasepool {
NSDate& d = *[NSDate distantPast];
NSLog(@"%@", &d);
}
return 0;
}

这似乎只是[NSDate distantPast]的问题(顺便说一下,[NSDate distantFuture],但不是,例如,[NSDate date](。我的结论是,这些必须是在 Foundation 深处的某个地方粗略/未对齐地分配的单例对象,当您取消引用它们时,它会导致指针读取未对齐。

(请注意,当代码只是NSLog(@"%@", &*[NSDate distantPast])时,它不会发生。我认为这是因为编译器只是将原始指针上的&*折叠为无操作。它不适用于原始问题中的shared_ptr情况,因为shared_ptr过载operator*.鉴于此,我相信在纯 Objective-C 中没有简单的方法可以做到这一点,因为您无法将&操作与*操作分开,就像涉及C++引用时所做的那样[通过将*的临时结果存储在NSDate&中]。

您不应该使用"裸"NSDate类型。Objective-C 对象应始终与指向对象的指针类型一起使用(例如NSDate *(,并且您永远不应该获得"指针后面的类型"。

特别是在64位平台上,Objective-C对象指针有时可能不是有效的指针,而是"标记指针",它将对象的"值"存储在指针的某些位中,而不是作为实际分配的对象。你必须始终让 Objective-C 运行时机制处理 Objective-C 对象指针。将其取消引用为常规 C/C++ 指针可能会导致未定义的行为。

相关内容

最新更新