未对齐的指针与 std::shared_ptr<NSDate> 取消引用一起使用
Misaligned pointer use with std::shared_ptr<NSDate> dereference
我在遗留代码库中工作,其中有大量 Objective-C++ 使用手册 retain/release 编写。内存使用大量 C++ std::shared_ptr<NSMyCoolObjectiveCPointer>
进行管理,并在构建时传入一个合适的删除器,该删除器在包含的对象上调用 release
。这似乎很好用;然而,当启用 UBSan 时,它会抱怨指针未对齐,通常是在取消引用 shared_ptrs 来做一些工作时。
我已经搜索了线索and/or解决方案,但是很难找到Objective-C对象指针来龙去脉的技术讨论,更难找到任何关于Objective-C++的讨论,所以我来了。
这是一个完整的 Objective-C++ 程序,它演示了我的问题。当我 运行 在我的 Macbook 上使用 UBSan 时,我在 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>
我 怀疑 &*
到 "cast" 和 shared_ptr<NSDate>
到 NSDate*
的用法有问题.我想我可以通过在 shared_ptr 上使用 .get()
来解决这个问题,但我真的很好奇发生了什么。感谢您的任何反馈或提示!
这里有一些转移注意力的东西:shared_ptr
、手册 retain/release 等。但我最终发现即使是这个非常简单的代码(启用了 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 深处某处分配 sketchily/misaligned-ly 的单例对象,当您取消引用它们时,会导致指针读取未对齐。
(请注意,当代码只是 NSLog(@"%@", &*[NSDate distantPast])
时,它 不会 发生。我认为这是因为编译器只是在原始指针上折叠 &*
进入无操作。它不适用于原始问题中的 shared_ptr
情况,因为 shared_ptr
重载 operator*
。鉴于此,我相信没有简单的方法可以在纯 Objective-C,因为您不能将 &
操作与 *
操作分开,就像在涉及 C++ 引用时一样 [通过将 *
的临时结果存储在一个 NSDate&
].)
您不应该使用 "bare" NSDate
类型。 Objective-C 对象应始终与指向对象的指针类型一起使用(例如 NSDate *
),并且您永远不应该获得 "type behind the pointer".
特别是,在 64 位平台上,Objective-C 对象指针有时可能不是有效指针,而是 "tagged pointers" 将对象的 "value" 存储在某些位中的指针,而不是作为实际分配的对象。您必须始终让 Objective-C 运行时机制处理 Objective-C 对象指针。将其作为常规 C/C++ 指针取消引用会导致未定义的行为。
我在遗留代码库中工作,其中有大量 Objective-C++ 使用手册 retain/release 编写。内存使用大量 C++ std::shared_ptr<NSMyCoolObjectiveCPointer>
进行管理,并在构建时传入一个合适的删除器,该删除器在包含的对象上调用 release
。这似乎很好用;然而,当启用 UBSan 时,它会抱怨指针未对齐,通常是在取消引用 shared_ptrs 来做一些工作时。
我已经搜索了线索and/or解决方案,但是很难找到Objective-C对象指针来龙去脉的技术讨论,更难找到任何关于Objective-C++的讨论,所以我来了。
这是一个完整的 Objective-C++ 程序,它演示了我的问题。当我 运行 在我的 Macbook 上使用 UBSan 时,我在 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>
我 怀疑 &*
到 "cast" 和 shared_ptr<NSDate>
到 NSDate*
的用法有问题.我想我可以通过在 shared_ptr 上使用 .get()
来解决这个问题,但我真的很好奇发生了什么。感谢您的任何反馈或提示!
这里有一些转移注意力的东西:shared_ptr
、手册 retain/release 等。但我最终发现即使是这个非常简单的代码(启用了 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 深处某处分配 sketchily/misaligned-ly 的单例对象,当您取消引用它们时,会导致指针读取未对齐。
(请注意,当代码只是 NSLog(@"%@", &*[NSDate distantPast])
时,它 不会 发生。我认为这是因为编译器只是在原始指针上折叠 &*
进入无操作。它不适用于原始问题中的 shared_ptr
情况,因为 shared_ptr
重载 operator*
。鉴于此,我相信没有简单的方法可以在纯 Objective-C,因为您不能将 &
操作与 *
操作分开,就像在涉及 C++ 引用时一样 [通过将 *
的临时结果存储在一个 NSDate&
].)
您不应该使用 "bare" NSDate
类型。 Objective-C 对象应始终与指向对象的指针类型一起使用(例如 NSDate *
),并且您永远不应该获得 "type behind the pointer".
特别是,在 64 位平台上,Objective-C 对象指针有时可能不是有效指针,而是 "tagged pointers" 将对象的 "value" 存储在某些位中的指针,而不是作为实际分配的对象。您必须始终让 Objective-C 运行时机制处理 Objective-C 对象指针。将其作为常规 C/C++ 指针取消引用会导致未定义的行为。