Ошибка времени выполнения C (с использованием -fsanitize= не определено): объект имеет, возможно, недопустимый vptr: abs (смещение вверх) слишком большой

#c #g #runtime

#c #g #время выполнения

Вопрос:

Мой код:

 const int N = 1048569;
struct A { };
struct B : virtual public A {
    char space[N];
};
struct C : virtual public A {
    void foo() {};
};
struct D : public B, public C { };

D T;
int main() {
    T.foo();
    return 0;
}
  

Когда я компилирую свой код с -fsanitize=undefined помощью, я получаю ошибку времени выполнения ниже:

 a.cpp:13:10: runtime error: member call on address 0x560aead1e6a8 which does not point to an object of type 'C'
0x560aead1e6a8: note: object has a possibly invalid vptr: abs(offset to top) too big
 00 00 00 00  b0 dc c1 ea 0a 56 00 00  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  00 00 00 00
              ^~~~~~~~~~~~~~~~~~~~~~~
              possibly invalid vptr

  

Программа запустится успешно, если я не использую -fsanitize=undefined опцию.

Более того, если я установлю значение N равным 1048568 (просто уменьшите на 1), программа будет успешно запущена в обоих случаях.

Интересно, это моя вина или просто потому fsanitize , что не может справиться с таким большим размером

Комментарии:

1. Скорее всего, у вас заканчивается пространство стека, которое по умолчанию должно составлять 1 МБ = 1024 * 1024 B = 1048576 B. пространство символов [N] резервирует N 1 B памяти ( 1 для ‘ 0’), которое должно занимать 1048572 4 B. Кроме того, foo() занимаетдополнительные 4 B, следовательно, переполненный стек. Если N = 1048568, общая память, выделенная для структуры D = 1048568 4 4 = 1048576, следовательно, программа работает нормально.

2. Рекомендуется помещать большие массивы в кучу, что std::vector также выполняется автоматически, это позволяет перемещать семантику, которая часто может значительно повысить производительность.

Ответ №1:

Это произвольное ограничение размера, установленное дезинфицирующим средством Clang UndefinedBehaviour:

https://github.com/llvm/llvm-project/blob/5745eccef54ddd3caca278d1d292a88b2281528b/compiler-rt/lib/ubsan/ubsan_type_hash.h#L55-L57

 /// A sanity check for Vtable. Offsets to top must be reasonably small
/// numbers (by absolute value). It's a weak check for Vtable corruption.
const int VptrMaxOffsetToTop = 1<<20;
  

«смещение вверх» в Itanium ABI — ptrdiff_t это значение в таблице vtable для каждого объекта (и подобъекта базового класса), которое при добавлении к указателю дает указатель на наиболее производный объект (тип, который вы new выбрали или объявили как переменную). Это то, что используется dynamic_cast<void*>(pointer_to_base) . UBSan ожидает, что это будет относительно небольшим, поскольку ваши классы в любом случае не будут такими большими и смещенными. Это означает, что единственный случай, когда он будет настолько большим, — это если вы случайно переполните буфер и выполните запись в vtable напрямую.

Способ «исправить» это, если вам действительно нужно это сделать, — поместить большой класс в качестве второго базового класса, чтобы его смещение было относительно небольшим.

Комментарии:

1. Большое спасибо. Итак, это означает, что мой код не нарушает никаких правил, но большое смещение обычно означает, что что-то не так?