2015-06-12 4 views
24

Последние версии функций GCC и Clang Undefined Behavior Sanitizer (UBSan), который является флагом компиляции (-fsanitize=undefined), который добавляет код инструментария во время выполнения. На ошибках, предупреждения, такие, как этот показано:Как я могу разбить отчеты UBSan в gdb и продолжить?

packet-ber.c:1917:23: runtime error: left shift of 54645397829836991 by 8 places cannot be represented in type 'long int'

Теперь я хотел бы отладить это и сделать перерыв отладки на указанной линии. Для Address Sanitizer (ASAN) есть ASAN_OPTIONS=abort_on_error=1, что приводит к фатальной ошибке, которая является захватывающей. Единственный вариант UBSan, который кажется полезным, - UBSAN_OPTIONS=print_stacktrace=1, что приводит к дампу трассировки вызовов для отчетов. Однако это не позволяет мне проверять локальные переменные, а затем продолжить программу. Использование -fsanitize-undefined-trap-on-error поэтому невозможно.

Как мне разбить gdb на отчеты UBSan? В то время как break __sanitizer::SharedPrintfCode работает, имя выглядит довольно внутренним.

+4

Я думаю, что до тех пор, пока API не будет реализован и не задокументирован, хороший способ поймать вызов библиотеки времени выполнения UBSan с целью продолжения вашей программы - это сделать 'rbreak^__ ubsan_handle_', что остановит выполнение до того, как библиотеки на территорию C++, где он выделяет экземпляры класса Diag. Подсуньте все, что хотите, затем введите 'return', чтобы продолжить свою программу. –

+6

Для дальнейшего использования, 'abort_on_error' кажется нереализованным для UBSAN. Используйте это вместо: 'UBSAN_OPTIONS = print_stacktrace = 1: halt_on_error = 1' – Lekensteyn

+1

Также см. [Как разбить отладчик, когда -fsanitize = undefined печатает что-то?] (Http://clang-developers.42468.n3.nabble.com/ How-to-break-in-debugger-when-fsanitize-undefined-prints-something-td4032345.html) в списке рассылки Clang Dev. – jww

ответ

14

При нарушении функций обнаружения (как описано в статьях @Mark Plotnick и @Iwillnotexist Idonotexist) является одним из вариантов, более эффективный подход ломает функции, сообщающие об этих проблемах после обнаружения. Этот подход также используется для ASAN, где можно было бы разбить __asan_report_error.

Резюме: Вы можете остановиться на отчете ubsan через точку останова на __ubsan::ScopedReport::~ScopedReport или __ubsan::Diag::~Diag. Это частные детали реализации, которые могут измениться в будущем. Протестировано с помощью GCC 4.9, 5.1.0, 5.2.0 и Clang 3.3, 3.4, 3.6.2.

Для GCC 4.9.2 от ppa:ubuntu-toolchain-r/test вам необходимо libubsan0-dbg, чтобы сделать вышеуказанные точки останова доступными. Ubuntu 14.04 с Clang 3.3 и 3.4 не поддерживает точки останова __ubsan::ScopedReport::~ScopedReport, поэтому вы можете перерыв только до печати сообщения с помощью __ubsan::Diag::~Diag.

Пример глючит исходный код и сеанс GDB:

$ cat undef.c 
int main(void) { return 1 << 1000; } 
$ clang --version 
clang version 3.6.2 (tags/RELEASE_362/final) 
Target: x86_64-unknown-linux-gnu 
Thread model: posix 
$ clang -w -fsanitize=undefined undef.c -g 
$ gdb -q -ex break\ __ubsan::ScopedReport::~ScopedReport -ex r ./a.out 
Reading symbols from ./a.out...done. 
Breakpoint 1 at 0x428fb0 
Starting program: ./a.out 
undef.c:1:27: runtime error: shift exponent 1000 is too large for 32-bit type 'int' 

Breakpoint 1, 0x0000000000428fb0 in __ubsan::ScopedReport::~ScopedReport()() 
(gdb) bt 
#0 0x0000000000428fb0 in __ubsan::ScopedReport::~ScopedReport()() 
#1 0x000000000042affb in handleShiftOutOfBoundsImpl(__ubsan::ShiftOutOfBoundsData*, unsigned long, unsigned long, __ubsan::ReportOptions)() 
#2 0x000000000042a952 in __ubsan_handle_shift_out_of_bounds() 
#3 0x000000000042d057 in main() at undef.c:1 

анализ Детального следующим образом. Обратите внимание, что как ASAN, так и ubsan оба происходят из проекта LLVM, compiler-rt. Это используется Clang и заканчивается в GCC. Ссылки в следующих разделах указывают на код проекта компилятора-rt, выпуск 3.6.

ASAN внесло свою внутреннюю __asan_report_error часть documented public interface. Эта функция вызывается всякий раз, когда нарушение обнаруживается, его поток продолжается в lib/asan/asan_report.c:938:

void __asan_report_error(uptr pc, uptr bp, uptr sp, uptr addr, int is_write, 
         uptr access_size) { 
    // Determine the error type. 
    const char *bug_descr = "unknown-crash"; 
    ... 

    ReportData report = { pc, sp, bp, addr, (bool)is_write, access_size, 
         bug_descr }; 
    ScopedInErrorReport in_report(&report); 

    Decorator d; 
    Printf("%s", d.Warning()); 
    Report("ERROR: AddressSanitizer: %s on address " 
      "%p at pc %p bp %p sp %p\n", 
      bug_descr, (void*)addr, pc, bp, sp); 
    Printf("%s", d.EndWarning()); 

    u32 curr_tid = GetCurrentTidOrInvalid(); 
    char tname[128]; 
    Printf("%s%s of size %zu at %p thread T%d%s%s\n", 
     d.Access(), 
     access_size ? (is_write ? "WRITE" : "READ") : "ACCESS", 
     access_size, (void*)addr, curr_tid, 
     ThreadNameWithParenthesis(curr_tid, tname, sizeof(tname)), 
     d.EndAccess()); 

    GET_STACK_TRACE_FATAL(pc, bp); 
    stack.Print(); 

    DescribeAddress(addr, access_size); 
    ReportErrorSummary(bug_descr, &stack); 
    PrintShadowMemoryForAddress(addr); 
} 

ubsan с другой стороны, не имеет открытый интерфейс, но его текущая реализация также намного проще и ограничен (меньше опций). При ошибках стоп-трасса может быть напечатана, когда задана переменная среды UBSAN_OPTIONS=print_stacktrace=1. Таким образом, с помощью функции поиска исходного кода print_stacktrace, один находит функцию MaybePrintStackTrace, которая называется хотя ScopedReport destructor:

ScopedReport::~ScopedReport() { 
    MaybePrintStackTrace(Opts.pc, Opts.bp); 
    MaybeReportErrorSummary(SummaryLoc); 
    CommonSanitizerReportMutex.Unlock(); 
    if (Opts.DieAfterReport || flags()->halt_on_error) 
    Die(); 
} 

Как вы можете видеть, есть способ, чтобы убить программу на ошибки, но, к сожалению, нет встроенной механизм запуска ловушки отладчика. Тогда найдем подходящую точку останова.

Команда info functions <function name> GDB позволила определить функцию MaybePrintStackTrace как функцию, на которой можно установить точку останова. Выполнение команды info functions ScopedReport::~ScopedReport дало еще одну функцию: __ubsan::ScopedReport::~ScopedReport. Если ни одна из этих функций не отображается (даже с установленными символами отладки), вы можете попробовать info functions ubsan или info functions sanitizer, чтобы получить все функции (UndefinedBehavior) Sanitizer.

+0

+1. Мне кажется, что ваш вопрос вызывает необходимость, и эта потребность была бы удовлетворена, если бы у дезинфицирующих средств была сила-no-'inline', 'extern'-linkage,' void'-возвращающая пустая внутренняя функция, которую можно было бы остановить отладчик, и эту функцию вызывать с аргументами, полезными для отладчика при сообщении о безумии. Что-то [похоже на интерфейс регистрации JIT] (https://sourceware.org/gdb/onlinedocs/gdb/Registering-Code.html#Registering-Code). –

+0

Этот ответ был бы более полезен, если бы ответ на фактический вопрос стал более заметным. – xaxxon

+0

@xaxxon Я открыт для предложений, не могли бы вы уточнить, какая часть нуждается в улучшении? – Lekensteyn

11

Как @Mark Plotnick points out, способ сделать это в точке останова на обработчиках U12an .

UBSan имеет несколько обработчиков или точек входа в магическую функцию, которые вызываются для неопределенного поведения. Код инструментов компилятора, введя соответствующие проверки; Если код проверки обнаруживает UB, он вызывает эти обработчики. Все они начинаются с __ubsan_handle_ и определены в libsanitizer/ubsan/ubsan_handlers.h. Вот link to GCC's copy of ubsan_handlers.h.

Вот соответствующие биты заголовка UBSan (точки останова на любом из них):

#define UNRECOVERABLE(checkname, ...) \ 
    extern "C" SANITIZER_INTERFACE_ATTRIBUTE NORETURN \ 
    void __ubsan_handle_ ## checkname(__VA_ARGS__); 

#define RECOVERABLE(checkname, ...) \ 
    extern "C" SANITIZER_INTERFACE_ATTRIBUTE \ 
    void __ubsan_handle_ ## checkname(__VA_ARGS__); \ 
    extern "C" SANITIZER_INTERFACE_ATTRIBUTE NORETURN \ 
    void __ubsan_handle_ ## checkname ## _abort(__VA_ARGS__); 

/// \brief Handle a runtime type check failure, caused by either a misaligned 
/// pointer, a null pointer, or a pointer to insufficient storage for the 
/// type. 
RECOVERABLE(type_mismatch, TypeMismatchData *Data, ValueHandle Pointer) 

/// \brief Handle an integer addition overflow. 
RECOVERABLE(add_overflow, OverflowData *Data, ValueHandle LHS, ValueHandle RHS) 

/// \brief Handle an integer subtraction overflow. 
RECOVERABLE(sub_overflow, OverflowData *Data, ValueHandle LHS, ValueHandle RHS) 

/// \brief Handle an integer multiplication overflow. 
RECOVERABLE(mul_overflow, OverflowData *Data, ValueHandle LHS, ValueHandle RHS) 

/// \brief Handle a signed integer overflow for a unary negate operator. 
RECOVERABLE(negate_overflow, OverflowData *Data, ValueHandle OldVal) 

/// \brief Handle an INT_MIN/-1 overflow or division by zero. 
RECOVERABLE(divrem_overflow, OverflowData *Data, 
      ValueHandle LHS, ValueHandle RHS) 

/// \brief Handle a shift where the RHS is out of bounds or a left shift where 
/// the LHS is negative or overflows. 
RECOVERABLE(shift_out_of_bounds, ShiftOutOfBoundsData *Data, 
      ValueHandle LHS, ValueHandle RHS) 

/// \brief Handle an array index out of bounds error. 
RECOVERABLE(out_of_bounds, OutOfBoundsData *Data, ValueHandle Index) 

/// \brief Handle a __builtin_unreachable which is reached. 
UNRECOVERABLE(builtin_unreachable, UnreachableData *Data) 
/// \brief Handle reaching the end of a value-returning function. 
UNRECOVERABLE(missing_return, UnreachableData *Data) 

/// \brief Handle a VLA with a non-positive bound. 
RECOVERABLE(vla_bound_not_positive, VLABoundData *Data, ValueHandle Bound) 

/// \brief Handle overflow in a conversion to or from a floating-point type. 
RECOVERABLE(float_cast_overflow, FloatCastOverflowData *Data, ValueHandle From) 

/// \brief Handle a load of an invalid value for the type. 
RECOVERABLE(load_invalid_value, InvalidValueData *Data, ValueHandle Val) 

RECOVERABLE(function_type_mismatch, 
      FunctionTypeMismatchData *Data, 
      ValueHandle Val) 

/// \brief Handle returning null from function with returns_nonnull attribute. 
RECOVERABLE(nonnull_return, NonNullReturnData *Data) 

/// \brief Handle passing null pointer to function with nonnull attribute. 
RECOVERABLE(nonnull_arg, NonNullArgData *Data) 

Асан еще проще. Если вы посмотрите в libsanitizer/include/sanitizer/asan_interface.h, которые вы должны просматривать here, вы можете прочитать мертвый поддавки комментарий:

// This is an internal function that is called to report an error. 
    // However it is still a part of the interface because users may want to 
    // set a breakpoint on this function in a debugger. 
    void __asan_report_error(void *pc, void *bp, void *sp, 
          void *addr, int is_write, size_t access_size); 

Многие другие функции в этом заголовке явно комментировали как будто они были опубликованы, чтобы быть отозваны из отладчик.

Я определенно советую вам изучить другие заголовки libsanitizer/include/sanitizerhere. Там есть много полезных вещей.


Breakpoints для UBSan и асан можно добавить следующим образом:

(gdb) rbreak ^__ubsan_handle_ __asan_report_error 
(gdb) commands 
(gdb) finish 
(gdb) end 

Это останова на обработчики и finish сразу же после этого. Это позволяет печатать отчет, но отладчик получает контроль сразу после его печати.

+3

Я надеялся, что для этого существует одна точка останова или переменная среды. В любом случае, можете ли вы показать пример использования этой функции с помощью 'gdb'? Было бы здорово иметь подход (или простой макрос 'gdbinit'), который действует как ASAN. То есть, отобразите сообщение и перерыв. – Lekensteyn

+0

@Lekensteyn Переменные среды не могут вводить точки останова; и для существования одной доступной функции, UBSan должен будет использовать одну, мультиплексирующую, varargs функцию (которая не обязательно хорошая конструкция, и теряет способность эффективно останавливать точку только на некоторых типах UB). Что касается 'gdb' /' gdbinit', уверен, я добавлю это через пару часов. –

+0

@Lekensteyn Похоже на '(gdb) rbreak __asan_report_error' и' (gdb) rbreak^__ ubsan' тупо прерывать все функции, соответствующие этим регулярным выражениям. Нет функции, называемой после возвращения '__asan_report_error', так что мне кажется, что вам нужно как-то сразу запрограммировать' gdb' на '' gdb' после попадания на одну из этих точек останова. –

 Смежные вопросы

  • Нет связанных вопросов^_^