Использование неправильно выровненного указателя с разыменованием 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++ может привести к неопределенному поведению.

Другие вопросы по тегам