¿Cómo puedo descifrar los informes de UBSan en gdb y continuar?
sanitizer address-sanitizer (2)
Como @Mark Plotnick señala , la manera de hacerlo es irrumpir en los manejadores de UBSan.
UBSan tiene una cantidad de manejadores, o puntos de entrada de funciones mágicas, que se llaman por comportamiento indefinido. El compilador instruye el código inyectando cheques según sea apropiado; Si el código de verificación detecta UB, llama a estos controladores. Todos comienzan con __ubsan_handle_
y se definen en libsanitizer/ubsan/ubsan_handlers.h
. Aquí hay un enlace a la copia de GCC de ubsan_handlers.h
.
Aquí están los bits relevantes del encabezado UBSan (punto de interrupción en cualquiera de estos):
#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)
ASAN es aún más fácil. Si buscas en libsanitizer/include/sanitizer/asan_interface.h
, que deberías buscar here , puedes leer un obsequio de un comentario:
// 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);
Muchas otras funciones en este encabezado se comentan explícitamente como hechas públicas para poder ser llamadas desde un depurador.
Definitivamente te aconsejo que explores otros encabezados de libsanitizer/include/sanitizer
here . Hay numerosas golosinas que se tendrán allí.
Los puntos de interrupción para UBSan y ASan se pueden agregar de la siguiente manera:
(gdb) rbreak ^__ubsan_handle_ __asan_report_error
(gdb) commands
(gdb) finish
(gdb) end
Esto interrumpirá los manejadores y finish
inmediatamente después. Esto permite que se imprima el informe, pero el depurador obtiene el control justo después de que se imprime.
Las versiones recientes de GCC y Clang presentan un Desinfectante de comportamiento indefinido (UBSan) que es un indicador de compilación ( -fsanitize=undefined
) que agrega código de instrumentación de tiempo de ejecución. En caso de error, se muestra una advertencia como esta:
packet-ber.c: 1917: 23: error en tiempo de ejecución: el desplazamiento a la izquierda de 54645397829836991 por 8 lugares no se puede representar en el tipo ''long int''
Ahora me gustaría depurar esto y obtener una interrupción de depuración en dicha línea. Para Address Sanitizer (ASAN) hay ASAN_OPTIONS=abort_on_error=1
que resulta en un error fatal que es detectable. La única opción de UBSan que parece utilizable es UBSAN_OPTIONS=print_stacktrace=1
que resulta en un volcado de seguimiento de llamadas para los informes. Sin embargo, esto no me permite inspeccionar las variables locales y luego continuar con el programa. Por lo tanto, no es posible el uso de -fsanitize-undefined-trap-on-error
.
¿Cómo debo entrar en gdb en los informes de UBSan? Mientras que el break __sanitizer::SharedPrintfCode
parece funcionar, el nombre parece bastante interno.
Si bien romper con las funciones de detección (como lo describen @Mark Plotnick y @Iwillnotexist Idonotexist ) es una opción, un mejor enfoque es romper con las funciones que reportan estos problemas después de la detección. Este enfoque también se utiliza para ASAN, donde se rompería en __asan_report_error
.
Resumen: puede detenerse en un informe de ubsan a través de un punto de interrupción en __ubsan::ScopedReport::~ScopedReport
o __ubsan::Diag::~Diag
. Estos son detalles privados de implementación que podrían cambiar en el futuro. Probado con GCC 4.9, 5.1.0, 5.2.0 y Clang 3.3, 3.4, 3.6.2.
Para GCC 4.9.2 de ppa:ubuntu-toolchain-r/test , necesita libubsan0-dbg
para que los puntos de interrupción anteriores estén disponibles. Ubuntu 14.04 con Clang 3.3 y 3.4 no admite los __ubsan::ScopedReport::~ScopedReport
, por lo que solo puede interrumpir la impresión del mensaje usando __ubsan::Diag::~Diag
.
Ejemplo de código fuente con errores y una sesión 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
El análisis detallado sigue. Tenga en cuenta que tanto ASAN como ubsan se originan en un proyecto LLVM, compiler-rt . Esto es usado por Clang y termina en GCC también. Los enlaces en las siguientes secciones apuntan al código del proyecto compilador-rt, versión 3.6.
ASAN ha hecho que su __asan_report_error
interno __asan_report_error
parte de la interfaz pública documentada . Esta función se llama cada vez que se detecta una violación, su flujo continúa en 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);
}
Por otro lado, ubsan no tiene una interfaz pública, pero su implementación actual también es mucho más simple y limitada (menos opciones). En caso de errores, se puede imprimir un stacktrace cuando se establece la variable de entorno UBSAN_OPTIONS=print_stacktrace=1
. Por lo tanto, al buscar el código fuente para print_stacktrace
, se encuentra la función MaybePrintStackTrace que se llama a través del destructor ScopedReport :
ScopedReport::~ScopedReport() {
MaybePrintStackTrace(Opts.pc, Opts.bp);
MaybeReportErrorSummary(SummaryLoc);
CommonSanitizerReportMutex.Unlock();
if (Opts.DieAfterReport || flags()->halt_on_error)
Die();
}
Como puede ver, hay un método para eliminar el programa en caso de errores, pero desafortunadamente no hay un mecanismo incorporado para desencadenar una trampa de depuración. Encontremos un punto de interrupción adecuado entonces.
Las info functions <function name>
comando de GDB info functions <function name>
hicieron posible identificar MaybePrintStackTrace
como función en la que se puede establecer un punto de interrupción. La ejecución de las info functions ScopedReport::~ScopedReport
de info functions ScopedReport::~ScopedReport
dio otra función: __ubsan::ScopedReport::~ScopedReport
. Si ninguna de estas funciones parece estar disponible (incluso con los símbolos de depuración instalados), puede probar las info functions ubsan
o las info functions sanitizer
para obtener todas las funciones relacionadas con Desinfectante ( info functions ubsan
indefinido).