\input{configpres} \title{\lq Debugging Tools\rq} \maketitle \subsection{Simple Debugging Tools} \begin{frame} \frametitle{strace} \begin{alertblock}{What is strace?} strace is a powerful diagnosing tool. It traces system calls and signals for a specified program. \end{alertblock} \end{frame} \begin{frame}[containsverbatim] \frametitle{strace Example} \begin{verbatim} $ strace /bin/ls execve("/bin/ls", ["/bin/ls"], [/* 32 vars */]) = 0 brk(0) = 0xa6a000 access("/etc/ld.so.nohwcap", F_OK) = -1 ENOENT (No such file or directory) mmap(NULL, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f6 access("/etc/ld.so.preload", R_OK) = -1 ENOENT (No such file or directory) open("/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3 fstat(3, {st_mode=S_IFREG|0644, st_size=73614, ...}) = 0 mmap(NULL, 73614, PROT_READ, MAP_PRIVATE, 3, 0) = 0x7f68e06d6000 [...] \end{verbatim} \end{frame} \begin{frame}[containsverbatim] \frametitle{Important strace Options} \begin{itemize} \item \textbf{-f}: Follow Forks \item \textbf{-v}: Verbose mode \item \textbf{-T}: Print out time which is spent in each syscall \item \textbf{-p} PID: Attach to PID \end{itemize} \end{frame} \subsection{The GNU Debugger: gdb} \begin{frame}[containsverbatim] \frametitle{Debug Test Program} build the program with debug symbols \begin{verbatim} gcc -g -ohello hello.c \end{verbatim} start the debugger \begin{verbatim} gdb ./hello \end{verbatim} \end{frame} \begin{frame}[containsverbatim] \frametitle{Important gdb Commands} run the program \begin{verbatim} (gdb) run Starting program: /home/devel/work/hello Hello, world! [Inferior 1 (process 935) exited normally] (gdb) \end{verbatim} \end{frame} \begin{frame}[containsverbatim] \frametitle{Important gdb Commands} set a breakpoint \begin{verbatim} (gdb) list 1 #include 2 3 int main(void) 4 { 5 printf("Hello, world!\n"); 6 return 0; 7 } (gdb) break 5 Breakpoint 1 at 0x400528: file hello.c, line 5. \end{verbatim} \end{frame} \begin{frame}[containsverbatim] \frametitle{Important gdb Commands} run the program again (now with breakpoint set) \begin{verbatim} (gdb) run Starting program: /home/jan/work/examples/hello Breakpoint 1, main () at hello.c:5 5 printf("Hello, world!\n"); \end{verbatim} \begin{verbatim} step over printf (gdb) next Hello, world! 6 return 0; \end{verbatim} let the program run to end \begin{verbatim} (gdb) continue Continuing. [...] \end{verbatim} \end{frame} \begin{frame}[containsverbatim] \frametitle{gdb Commands: Overview} \begin{tabular}{|c|c|p{5cm}|} \hline \textbf{Command} & \textbf{Short-Form} & \textbf{Description} \\ \hline run & r & start running program \\ \hline continue & c & continue running program \\ \hline break X & b & set a breakpoint at line X \\ \hline step & s & step \textbf{in} the current function \\ \hline next & n & step \textbf{over} the current function \\ \hline print V & -- & show the value of variable V \\ \hline display V & -- & show the value of variable V every time the program stops \\ \hline \end{tabular} \end{frame} \begin{frame}[containsverbatim] \frametitle{gdb Commands: Overview} \begin{tabular}{|c|c|p{5cm}|} \hline \textbf{Command} & \textbf{Short-Form} & \textbf{Description} \\ \hline backtrace & bt & display the current backtrace (stack) \\ \hline frame X & f & change display to frame X of the current stack \\ \hline quit & q & end gdb \\ \hline \end{tabular} \end{frame} \subsection{Post-Mortem Debugging (Core Files)} \begin{frame}[containsverbatim] \frametitle{Create a Crashing Program} \begin{verbatim} /* hello_crash.c */ #include int main(void) { char *p = NULL; printf("Hello, crash! %c\n", *p); return 0; } \end{verbatim} build the program with debug symbols \begin{verbatim} gcc -g -ohello_crash hello_crash.c \end{verbatim} \end{frame} \begin{frame}[containsverbatim] \frametitle{Run Program} enable core files \begin{verbatim} $ ulimit -c unlimited \end{verbatim} run program \begin{verbatim} $ ./hello_segfault Segmentation fault (core dumped) \end{verbatim} core file created \begin{verbatim} $ ls -l core -rw------- 1 devel devel 249856 Jan 1 00:00 core \end{verbatim} \end{frame} \begin{frame}[containsverbatim] \frametitle{Analyzing Core Files with gdb} start the debugger \begin{verbatim} $ gdb ./hello_crash ./core [...] Reading symbols from ./hello_crash...done. [New LWP 1239] Core was generated by `./hello_crash'. Program terminated with signal SIGSEGV, Segmentation fault. #0 0x000000000040051a in main () at hello_crash.c:7 7 printf("Hello, crash! %c\n", *p); \end{verbatim} look at the full backtrace \begin{verbatim} (gdb) bt #0 0x000000000040051a in main () at hello_crash.c:7 \end{verbatim} \end{frame} \begin{frame}[containsverbatim] \frametitle{Important Commands for Post-Mortem Debugging} activate core dumps and set a maximum size \begin{verbatim} ulimit -c N \end{verbatim} view the current filename pattern for core files \begin{verbatim} cat /proc/sys/kernel/core_pattern \end{verbatim} set the filename pattern for core files \begin{verbatim} echo core-%p > /proc/sys/kernel/core_pattern \end{verbatim} view the core dump with gdb \begin{verbatim} gdb ./exe ./corefile \end{verbatim} \end{frame} \begin{frame}[containsverbatim] \frametitle{Symbol Tables} \begin{alertblock}{What if release software may not contain debug information?} \end{alertblock} build the program with debug symbols \begin{verbatim} gcc -g -ohello_crash hello_crash.c \end{verbatim} copy out debug information \begin{verbatim} objcopy --only-keep-debug hello_crash hello_crash.dbg \end{verbatim} remove the debug symbols from the binary for release \begin{verbatim} strip --strip-all hello_crash \end{verbatim} \end{frame} \begin{frame}[containsverbatim] \frametitle{Symbol Tables} cannot debug using only the release binary \begin{verbatim} $ gdb ./hello_crash ./core [...] Reading symbols from ./hello_crash...(no debugging symbols found)...done. [New LWP 1555] Core was generated by `./hello_crash'. Program terminated with signal SIGSEGV, Segmentation fault. #0 0x00005645f239a6c4 in ?? () \end{verbatim} start gdb specifying symbol file \begin{verbatim} $ gdb --symbols=./hello_crash.dbg --exec=./hello_crash --core=./core [...] Reading symbols from ./hello_crash.dbg...done. [New LWP 1555] Core was generated by `./hello_crash'. Program terminated with signal SIGSEGV, Segmentation fault. #0 0x00005645f239a6c4 in main () at hello_crash.c:7 7 printf("Hello, crash! %c\n", *p); \end{verbatim} \end{frame} \begin{frame}[containsverbatim] \frametitle{Symbol Tables} link debug symbols to the binary \begin{verbatim} objcopy --add-gnu-debuglink=./hello_crash.dbg ./hello_crash \end{verbatim} now the binary can be debugged (symbol file still used!) \begin{verbatim} $ gdb ./hello_crash ./core [...] Reading symbols from ./hello_crash... Reading symbols from /home/devel/work/hello_crash.dbg...done. done. warning: exec file is newer than core file. [New LWP 1555] Core was generated by `./hello_crash'. Program terminated with signal SIGSEGV, Segmentation fault. #0 0x00005645f239a6c4 in main () at hello_crash.c:7 7 printf("Hello, crash! %c\n", *p); \end{verbatim} \end{frame} \begin{frame}[containsverbatim] \frametitle{Symbol Tables} \begin{alertblock}{Always keep the debug symbols for release binaries!} \end{alertblock} verify that the debug symbols match the executable \begin{verbatim} $ file hello_crash hello_crash.dbg hello_crash: ... BuildID[sha1]=9bc82e1d6627ba58492ea24462fd6658726c4fc0 hello_crash.dbg: ... BuildID[sha1]=9bc82e1d6627ba58492ea24462fd6658726c4fc0 \end{verbatim} \end{frame} \subsection{Cross Debugging} \begin{frame}[containsverbatim] \frametitle{Building for the Target System} build the program for armhf with debug symbols \begin{verbatim} $ arm-linux-gnueabihf-gcc -g -ohello hello.c \end{verbatim} check executable \begin{verbatim} $ file hello hello: ELF 32-bit LSB executable, ARM, EABI5 version 1 (SYSV),... \end{verbatim} \end{frame} \begin{frame} \frametitle{QEMU as a Tool for Cross Development} \begin{alertblock}{What is QEMU?} QEMU is a high performance emulation and virtualization environment for all common CPU architectures. gängigen CPU Architekturen. \end{alertblock} \end{frame} \begin{frame}[containsverbatim] \frametitle{Testing Executables using QEMU User Emulation} a system cannot directly execute foreign architecture executables \begin{verbatim} $ ./hello bash: ./hello: cannot execute binary file: Exec format error \end{verbatim} but it can emulate them \begin{verbatim} $ qemu-arm -L /opt/.../arm-linux-gnueabihf/libc ./hello Hello, world! \end{verbatim} \end{frame} \begin{frame}\frametitle{Remote Debugging} \begin{figure}[h] \centering \includegraphics[width=8cm]{images/remote_debug.png} \end{figure} \end{frame} \begin{frame}[containsverbatim] \frametitle{Remote Debugging Session} from the target: \begin{verbatim} $ gdbserver :2345 ./hello Process ./hello created; pid = 310 Listening on port 2345 \end{verbatim} from the host: \begin{verbatim} $ arm-linux-gnueabihf-gdb ./hello (gdb) set solib-absolute-prefix /opt/.../arm-linux-gnueabihf/libc (gdb) target remote 192.168.2.2:2345 Remote debugging using 192.168.2.2:2345 0x30016180 in _start() from /opt/.../libc/lib/ld-linux-armhf.so.3 (gdb) c \end{verbatim} \end{frame} \begin{frame}[containsverbatim] \frametitle{Automatically Execute gdb Commands} edit a gdb init script \begin{verbatim} vi gdbinit.txt \end{verbatim} add startup commands \begin{verbatim} set solib-absolute-prefix /opt/.../arm-linux-gnueabihf/libc target remote 192.168.2.2:2345 \end{verbatim} specify init script when starting gdb \begin{verbatim} arm-linux-gnueabihf-gdb -x gdbinit.txt ./hello \end{verbatim} \end{frame} \subsection{Memory Debugging} \begin{frame} \frametitle{Common Problems} \begin{itemize} \item writing/reading beyond memory regions \item memory leaks \item ''use after free()'' \end{itemize} \end{frame} \begin{frame}[containsverbatim] \frametitle{The glibc Mechanism: mtrace} add support for mtrace to a program \begin{verbatim} #include [...] int main(void) { mtrace(); [...] \end{verbatim} activate mtrace \begin{verbatim} MALLOC_TRACE=hello.trace ./hello \end{verbatim} view the mtrace results \begin{verbatim} mtrace ./hello.dbg hello.trace \end{verbatim} \end{frame} \begin{frame}[containsverbatim] \frametitle{The glibc Mechanism: mtrace} create a test program with a memory leak \begin{verbatim} /* mem_leak.c */ #include #include #include int main(void) { int i = 0; char *p = NULL; mtrace(); for(i = 0; i < 50; i++) p = malloc(sizeof(char)); free(p); } \end{verbatim} \end{frame} \begin{frame}[containsverbatim] \frametitle{The glibc Mechanism: mtrace} build the test program with debug symbols \begin{verbatim} $ gcc -fno-PIE -no-pie -g -omem_leak mem_leak.c \end{verbatim} run the test program with mtrace activated \begin{verbatim} $ MALLOC_TRACE=mem_leak.trace ./mem_leak \end{verbatim} view the trace results \begin{verbatim} $ mtrace ./mem_leak mem_leak.trace Memory not freed: ----------------- Address Size Caller 0x1536460 0x1 at /home/devel/work/mem_leak.c:13 0x1536480 0x1 at /home/devel/work/mem_leak.c:13 0x15364a0 0x1 at /home/devel/work/mem_leak.c:13 [...] \end{verbatim} \end{frame} \begin{frame}[containsverbatim] \frametitle{mtrace and PIE} With a patched mtrace, PIE can be traced by disabling ASLR. \begin{verbatim} $ gcc -fPIE -pie -g -omem_leak mem_leak.c $ setarch `uname -m` -R env MALLOC_TRACE=mem_leak.trace ./mem_leak $ setarch `uname -m` -R ./mtrace-patched ./mem_leak mem_leak.trace Memory not freed: ----------------- Address Size Caller 0x0000555555756450 0x1 at /home/devel/work/mem_leak.c:13 0x0000555555756470 0x1 at /home/devel/work/mem_leak.c:13 0x0000555555756490 0x1 at /home/devel/work/mem_leak.c:13 [...] \end{verbatim} \end{frame} \begin{frame}[containsverbatim] \frametitle{glibc Hooks for malloc()} \_\_malloc\_hook: \begin{verbatim} void *function (size_t size, const void *caller) \end{verbatim} \_\_realloc\_hook: \begin{verbatim} void *function (void *ptr, size_t size, const void *caller) \end{verbatim} \_\_free\_hook: \begin{verbatim} void *function (void *ptr, const void *caller) \end{verbatim} \_\_memalign\_hook: \begin{verbatim} void *function (size_t size, size_t alignment, const void *caller) \end{verbatim} Do not use them! Use mtrace instead! \end{frame} \begin{frame}[containsverbatim] \frametitle{libduma / electric fence} create a test program with memory access beyond a region \begin{verbatim} /* array_access.c */ #include #include int main(void) { int *a; int i; a = calloc(10, sizeof(*a)); for(i = 0; i < 11; i++) printf("%d ", a[i]); printf("\n"); return 0; } \end{verbatim} build and run the test program \begin{verbatim} $ gcc -g -oarray_access array_access.c $ ./array_access 0 0 0 0 0 0 0 0 0 0 135121 \end{verbatim} Invalid access and nobody cared! \end{frame} \begin{frame}[containsverbatim] \frametitle{libduma / electric fence} enable core dumps \begin{verbatim} $ ulimit -c unlimited \end{verbatim} run the program with libduma active \begin{verbatim} $ LD_PRELOAD=libduma.so.0 ./array_access [...] Segmentation fault (core dumped) \end{verbatim} investigate core dump \begin{verbatim} $ gdb ./array_access core [...] Core was generated by `./array_access'. Program terminated with signal SIGSEGV, Segmentation fault. #0 0x00000000004005ce in main () at array_access.c:13 13 printf("%d ", a[i]); (gdb) print i $1 = 10 \end{verbatim} \end{frame} \begin{frame} \frametitle{Valgrind} \begin{alertblock}{Advantages} \begin{itemize} \item high success rate \item lots of functionality and features \end{itemize} \end{alertblock} \begin{alertblock}{Disadvantage} \begin{itemize} \item runtime dramatically affected \end{itemize} \end{alertblock} \end{frame} \begin{frame}[containsverbatim] \frametitle{Valgrind} \begin{verbatim} $ valgrind --leak-check=full ./mem_leak [...] ==8457== HEAP SUMMARY: ==8457== in use at exit: 49 bytes in 49 blocks ==8457== total heap usage: 50 allocs, 1 frees, 50 bytes allocated ==8457== ==8457== 49 bytes in 49 blocks are definitely lost in loss record 1 of 1 ==8457== at 0x4C28C20: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so) ==8457== by 0x4005C4: main (mem_leak.c:14) ==8457== ==8457== LEAK SUMMARY: ==8457== definitely lost: 49 bytes in 49 blocks [...] \end{verbatim} \end{frame} \begin{frame}[containsverbatim] \frametitle{Valgrind} \begin{verbatim} $ valgrind --leak-check=full ./array_access [...] ==8493== Invalid read of size 4 ==8493== at 0x4005CE: main (array_access.c:13) ==8493== Address 0x51e0068 is 0 bytes after a block of size 40 alloc'd ==8493== at 0x4C2AD10: calloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so) ==8493== by 0x4005AC: main (array_access.c:10) ==8493== 0 0 0 0 0 0 0 0 0 0 0 ==8493== ==8493== HEAP SUMMARY: ==8493== in use at exit: 40 bytes in 1 blocks ==8493== total heap usage: 1 allocs, 0 frees, 40 bytes allocated ==8493== ==8493== 40 bytes in 1 blocks are definitely lost in loss record 1 of 1 ==8493== at 0x4C2AD10: calloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so) ==8493== by 0x4005AC: main (array_access.c:10) ==8493== ==8493== LEAK SUMMARY: ==8493== definitely lost: 40 bytes in 1 blocks [...] \end{verbatim} \end{frame} \begin{frame}{AddressSanitizer} \begin{block}{About AddressSanitizer} \begin{itemize} \item Fast memory error detector \item Based on compiler instrumentation and shadow memory \item Available for \textbf{gcc} and \textbf{clang} \end{itemize} \end{block} \begin{block}{Types of bugs} \begin{itemize} \item Buffer overflows (heap, stack, global) \item Use-after-free, dangling pointers, ... \end{itemize} \end{block} \end{frame} \begin{frame}[fragile]{AddressSanitizer Example} \begin{verbatim} $ gcc -o array_access -fsanitize=address array_access.c $ ./array_access ================================================================= ==5623==ERROR: AddressSanitizer: heap-buffer-overflow READ of size 4 at 0x60400000dff8 thread T0 #0 0x56525f56eabf in main #1 0x7fa413d042e0 in __libc_start_main #2 0x56525f56e949 in _start 0x60400000dff8 is located 0 bytes to the right of 40-byte region allocated by thread T0 here: #0 0x7fa414144ed0 in calloc #1 0x56525f56ea66 in main #2 0x7fa413d042e0 in __libc_start_main SUMMARY: AddressSanitizer: heap-buffer-overflow in main [...] \end{verbatim} \end{frame} \input{tailpres}