Использование неправильно выровненного указателя с разыменованием std::shared_ptr<NSDate>
Я работаю в устаревшей кодовой базе с большим количеством Objective-C++, написанным с использованием ручного сохранения / выпуска. Память управляется с использованием большого количества C++ std::shared_ptr<NSMyCoolObjectiveCPointer>
, с подходящим удалителем, переданным на конструкцию, которая вызывает release
на содержавшемся объекте. Кажется, это прекрасно работает; однако при включении UBSan он жалуется на смещенные указатели, обычно при разыменовании shared_ptrs для выполнения некоторой работы.
Я искал подсказки и / или решения, но сложно найти техническое обсуждение входов и выходов указателей объектов 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>
Я подозреваю, что с использованием &*
"бросить" shared_ptr<NSDate>
чтобы NSDate*
, Я думаю, что я мог бы обойти эту проблему, используя .get()
вместо этого на shared_ptr, но мне искренне любопытно, что происходит. Спасибо за любые отзывы или советы!
1 ответ
Здесь были красные селедки: shared_ptr
, ручное сохранение / освобождение и т. д. Но я обнаружил, что даже этот очень простой код (с включенным ARC) вызывает "убсан":
#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++ может привести к неопределенному поведению.