On the Impact of Exception Handling Compatibility on Binary Instrumentation†
Total Page:16
File Type:pdf, Size:1020Kb
On the Impact of Exception Handling Compatibility on Binary Instrumentation† Soumyakant Priyadarshan Huan Nguyen R. Sekar Stony Brook University Stony Brook University Stony Brook University Stony Brook, NY, USA Stony Brook, NY, USA Stony Brook, NY, USA [email protected] [email protected] [email protected] Abstract overheads, but has been held back by challenges in accurate dis- assembly and code pointer identification. With the emergence of To support C++ exception handling, compilers generate metadata position-independent (or relocatable) binaries as the dominant for- that is a rich source of information about the code layout. On mat in recent years, researchers have been able to address these Linux, this metadata is also used to support stack tracing, thread challenges, e.g., in Egalito [41], RetroWrite [11] and SBR[28, 29] cleanup and other functions. For this reason, Linux binaries contain systems. code-layout-revealing metadata for C-code as well. Even hand- written assembly in low-level system libraries is covered by such Despite recent advances, deployability of binary instrumentation metadata. We investigate the implications of this metadata in this continues to face significant challenges. One of the major concerns paper, and show that it can be used to (a) improve accuracy of is compatibility. In particular, existing static binary instrumentation disassembly, (b) achieve significantly better accuracy at function tools tend to break stack tracing (for C and C++) as well as C++ boundary identification as compared to previous research, and(c) exception handling. While compatibility with these features may as a rich source of information for defeating fine-grained code not be important for proof-of-concept instrumentations, it is hardly randomization. a viable option for any software meant for wide deployment. Although there have been a few research efforts in exception- ACM Reference Format: compatible binary instrumentation (e.g., Zipr++ [15]), most con- Soumyakant Priyadarshan, Huan Nguyen, and R. Sekar. 2020. On the Im- temporary works [11, 40, 41] tend to dismiss off these compatibility pact of Exception Handling Compatibility on Binary Instrumentation[2]. In issues as engineering problems. However, we show in this paper 2020 Workshop on Forming an Ecosystem Around Software Transformation that the impact of stack-tracing and exception compatibility is much (FEAST’20), November 13, 2020, Virtual Event, USA. ACM, New York, NY, more fundamental. This is because the metadata required to support USA, 6 pages. https://doi.org/10.1145/3411502.3418428 these features is a rich source of information about the binaries. This information can significantly simplify some aspects of instru- 1 Introduction mentation (e.g., disassembly and function identification), while Binary instrumentation [6, 11, 21, 34, 39, 41, 45] is a well-established introducing new challenges in other aspects (e.g., fine-grained code technique for security hardening, application monitoring and de- randomization). We analyze these impacts in this paper, and make bugging, profiling, and so on. Binary instrumentation is more de- the following contributions: sirable than source-code instrumentation because the vast major- • We show how disassembly can be a challenge for some complex ity of today’s software, including most open-source software, is binaries, and discuss how exception handling metadata can help. distributed in binary form. Furthermore, the use of binary-only • There have been many recent papers on accurate function bound- third-party libraries and hand-written assembly in large software ary identification [2, 3, 30, 32]. Many of them rely on complex packages make source-based approaches incomplete. Instrumenting machine learning or static analysis techniques, yet suffer from all parts of code is critical for many applications, especially those in significant error rates. In contrast, we show how exception- security, such as CFI [1, 43, 46], SFI [22, 36, 42], program hardening handling metadata can provide a simple yet far more accurate [9, 18, 26, 31, 47], code randomization [5, 14, 25, 29, 38, 40, 44], etc. solution. In particular we can achieve an F1-score of 0.96 by Binary instrumentation techniques fall into two broad categories: just using the EH metadata, and improve it further to 1.0 with a dynamic [6, 21] and static [20]. Dynamic instrumentation tools have simple analysis. proved to be robust, but they incur high performance overheads • We point out how code randomization techniques can be signifi- for many applications. Static instrumentation incurs much lower cantly degraded by exception-handling metadata since it reveals information such as function boundaries, as well as the location Permission to make digital or hard copies of all or part of this work for personal or of some key instructions that are often targeted in ROP attacks. classroom use is granted without fee provided that copies are not made or distributed for profit or commercial advantage and that copies bear this notice and the full citation Our study is focused on the Linux/x86 platform, with implementa- on the first page. Copyrights for components of this work owned by others than the tion results obtained on 64-bit (x86_64) binaries. author(s) must be honored. Abstracting with credit is permitted. To copy otherwise, or republish, to post on servers or to redistribute to lists, requires prior specific permission and/or a fee. Request permissions from [email protected]. FEAST’20, November 13, 2020, Virtual Event, USA © 2020 Copyright held by the owner/author(s). Publication rights licensed to ACM. ACM ISBN 978-1-4503-8089-8/20/11...$15.00 †This work was supported by ONR (N00014-17-1-2891) and in part by NSF (CNS- https://doi.org/10.1145/3411502.3418428 1918667). FEAST’20, November 13, 2020, Virtual Event, USA Soumyakant Priyadarshan, Huan Nguyen, and R. Sekar 2 C++ Exception Handling and Stack Tracing Application Library with data Library within code size In C++, exception handling is implemented using try/catch blocks. Firefox libxul.so 126 MB Catch blocks immediately follow a try block and contain handlers Chromium, gedit libffi.so 31 KB for one or more exceptions that may arise within the try block. If LibreOffice, gedit libgnutls.so 1.4 MB an exception arises outside of a try-block (or if that exception is libgcrypt.so 2.3 MB not handled in the catch blocks associated with the current try- gimp, vlc, ssh, libgcrypt.so 2.3 MB block) then the C++ runtime looks for a try-block in the caller of evince, apt-get the current function, or its caller, and so on. Thus, the C++ runtime needs to “walk up the stack” from a callee function to its callers. Table 1: A few packages with data in the midst of code The C++ compiler generates exception-handling (EH) metadata [24] that is used to perform this stack unwinding step. • Pointers to destructor routines of objects created on stack, Stack unwinding may also be needed for generating stack traces • Start address of each unwinding block, and when programs crash or experience unrecoverable errors, or when • Arithmetic or load/store operations needed to restore callee threads exit. On Linux, the same EH-metadata is used to support saved registers and stack pointers. these features as well. For this reason, EH-metadata is present 1 in Linux for all code , not just C++. In fact, most hand-written 3 Disassembly assembly code in low-level libraries such as glibc include stack unwinding information, put in place using the GNU assembler’s Accurate disassembly of stripped binaries is a challenging problem .cfi directive (which stands for call frame information). for variable-length instruction sets such as those of the Intel x86 On Linux, EH metadata is stored in the sections eh_frame, architecture. There are two basic techniques for disassembly: linear eh_frame_hdr and gcc_except_table. Only the first two are needed disassembly and recursive disassembly. Linear disassembly is in for stack-unwinding, so the third table is present only for C++ func- general unsound, i.e., it can misclassify data as code. Recursive tions. Unlike debugging information that may be present in a binary disassembly can be sound, but is incomplete: it tends to miss code but not loaded into a process memory, all these sections must be that is only reached via indirect transfers. A number of researchers loaded into readable regions of process memory. have proposed heuristics to overcome these drawbacks, but these The eh_frame_hdr section is a binary search table that maps a heuristics are not always successful, and cannot guarantee accurate function to its FDE (Frame description entry), a descriptor for its disassembly in general. Another alternative is exhaustive disassem- stack frame. These FDE records are present in eh_frame section. FDE bly that treats every possible offset as an instruction. Multiverse records contain special instructions describing how to restore callee [4] develops such an approach, and has been further improved by Miller et al [23]. However, there is significant overhead in terms of saved registers and the stack pointer. These instructions effectively 2 partition the function body into smaller blocks called unwinding code size as well as runtime overhead, about 60% on SPEC CPU . blocks. Each unwinding block comprises of a set of contiguous Unsoundness of linear disassembly stems from the presence of instructions that share the same state of the callee-saved registers data or padding in the midst of code. Modern compilers such as and the stack pointer. GCC and Clang have come to avoid inclusion of data in the midst Consider an instruction that pushes a callee-saved register on of code, and to use NOPs for padding. This enabled recent binary the stack. It requires a corresponding restoration operation that will instrumentation efforts [11, 29, 41] to rely on linear disassembly load that register from the stack and then restore the original stack in their systems.