1 / 39

Introduction to Runtime Analysis

Introduction to Runtime Analysis. Ben Livshits. Based in part of Stanford class slides from http://www.stanford.edu/class/cs295 / and slides from Ben Zorn’s talk slides. My Lectures. reliability & bug finding. performance. security. Lecture 1 : Introduction to static analysis

andrew
Download Presentation

Introduction to Runtime Analysis

An Image/Link below is provided (as is) to download presentation Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author. Content is provided to you AS IS for your information and personal use only. Download presentation by click this link. While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server. During download, if you can't get a presentation, the file might be deleted by the publisher.

E N D

Presentation Transcript


  1. Introduction to Runtime Analysis Ben Livshits Based in part of Stanford class slides from http://www.stanford.edu/class/cs295/ and slides from Ben Zorn’s talk slides

  2. My Lectures reliability & bug finding performance security Lecture 1: Introduction to staticanalysis Lecture 2: Introduction to runtimeanalysis Lecture 3: Applicationsof static and runtime analysis

  3. Primary Focus: Runtime Monitoring • Purify for finding memory errors [1992] • Detecting memory leaks with Purify and GC [1992] • Detecting dangling pointers & buffer overruns with DieHard [2006] • Detecting buffer overruns with StackGard [1997]

  4. Memory Errors in C/C++ Dangling pointers: If the program mistakenly frees a live object, the allocator may overwrite its contents with a new object or heap metadata. Buffer overflows: Out-of-bound writes can corrupt the contents of live objects on the heap. Heap metadata overwrites: If heap metadata is stored near heap objects, an out-of-bound write can corrupt it. Uninitialized reads: Reading values from newly-allocated or unallocated memory leads to undefined behavior. Invalid frees: Passing illegal addresses to free can corrupt the heap or lead to undefined behavior. Double frees: Repeated calls to free of objects that have already been freed cause freelist-based allocators to fail.

  5. Purify FINDING MEMORY ERRORS IN C/C++ PROGRAMS

  6. Purify • C/C++ are not memory-safe • Neither the compiler nor the runtime system enforces type abstractions • What is memory-safe vs. type-safe? • Possible to read or write outside of your intended data structure • Among other bad behaviors • What else is possible that you can’t do in Java or Scheme or ML or F#?

  7. Memory State • Each byte of memory is in one of 3 states: • Unallocated: cannot be read or written • Allocatedbut uninitialized: cannot be read • Allocated and initialized: anything goes

  8. Memory State: Automaton per Byte • Check the state of each byte on each access • Binary instrumentation • Add code before each load and store • Represent states as giant array • 2 bits per byte of memory • What is the memory overhead? • 25%!! • Catches byte-level errors • Won’t catch bit-level errors

  9. What Can We Detect? • We can only detect bad accesses if they are to unallocated or uninitialized memory • Try to make all bad accesses be of those two forms • We can make this part of our custom memory allocator

  10. Red Zones and Aging Freed Memory • Red Zones • Leave buffer space between allocated objects that is never allocated • Guarantees that walking off the end of an array accesses unallocated memory • Aging Freed Memory • When memory is freed, do not reallocate immediately • Helps catch dangling pointer errors

  11. Summary of Purify • One of the first commercially successful runtime tools • Was an independent company that got bought by IBM Rational • Overhead can vary from 25% to 40x

  12. Memory Checking Tools Compared • This is where buffer overruns come from! • Why can’t we catch them?

  13. Memory leak detection PROGRESS & OPEN PROBLEMS

  14. Memory Leak Detection • Memory leaks are at least as serious as memory corruption errors • Also very difficult to find • Manifest only over hours, days, weeks • Often persist in production code • Managed languages such as Java and C# don’t really help

  15. How Do We Find Memory Leaks? • We can find many memory leaks using techniques borrowed from garbage collection • Any memory with no pointers to it is leaked • There is no way to free this memory • Run a garbage collector • But don’t free any garbage • Just detect the garbage • Any inaccessible memory is leaked memory • Can we do this in C/C++ at all? Sort of…

  16. But How Do We Do This in C/C++? • It is sometimes hard to tell what is accessible in a C/C++ program? • Cases • No pointers to a malloc’dblock: definitely garbage • No pointers to head of a malloc’dblock: maybe garbage • Pointers to the head of a malloc’dblock: not garbage by usual definition

  17. How Do We Do Detection? • From time to time, run a garbage collector • Use mark and sweep • Report areas of memory that are definitely or probably garbage • No type safety ==> no memory safety • Is this as easy as in Java? • Bookkeeping • Need to report who malloc’d the blocks originally • Store this information in the red zone between objects • Used in Purify, but watch out for memory overhead

  18. But This Only Works for C/C++ • A Limitation • Only finds leaks to unreachable objects • Doesn’t cover leaks in languages with GC • Retaining data structures longer than needed • In practice, also a serious source of leaks, especially in Java . . .

  19. Memory Leaks in Java • Look for objects not accessed for a “long time”. For each object • Track it from the moment it is allocated • Record the time of the last access (read or write) • Discard information when object is de-allocated • Periodically • Scan all objects • Warn about objects unused for a “long time”

  20. DieHard TOLERATING MEMORY ERRORS AT RUNTIME

  21. Focus on Heap Memory Errors • Buffer overflowchar *c = malloc(100);c[101] = ‘a’; • Dangling referencechar *p1 = malloc(100);char *p2 = p1;free(p1);p2[0] = ‘x’; c a 0 99 p1 p2 x 0 99

  22. Research Vision • Increase robustness of installed code base • Potentially improve millions of lines of code • Minimize effort – ideally no source mods, no recompilation • Reduce requirement to patch • Patches are expensive (detect, write, deploy) • Patches may introduce new errors • Trade resources for robustness • E.g., more memory implies higher reliability • Make deployment easy • Change the allocator DLL, no changes to code needed • Make existing programs more fault tolerant • Define semantics of programs with errors • Programs complete with correct result despite errors

  23. DieHard: Probabilistic Memory Safety • Emery D. Berger and Benjamin G. Zorn,  "DieHard: Probabilistic Memory Safety for Unsafe Languages", PLDI’06 • DieHard: correct execution in face of errors with high probability • Plug-compatible replacement for malloc/free in C lib • Define “infinite heap semantics” • Programs execute as if each object allocated with unbounded memory • All frees ignored • Approximating infinite heaps: 3 key ideas • Overprovisioning • Randomization • Replication • Allows analytic/probabilistic reasoning about safety

  24. Overprovisioning, Randomization Expand size requests by a factor of M (e.g., M=2) 1 2 3 4 5 Pr(write corrupts) = ½ ? 1 2 3 4 5 Randomize object placement 4 2 3 1 5 Pr(write corrupts) = ½ !

  25. Replication (optional) Replicate process with different randomization seeds P1 1 3 2 5 4 P2 input 4 3 1 5 2 P3 5 2 1 4 3 Broadcast input to all replicas Voter Compare outputs of replicas, kill when replica disagrees

  26. DieHard Implementation Details • Allocation • Segregate objects by size (log2), bitmap allocator • Within size class, place objects randomly in address space • Separate metadata from user data • Fill objects with random values – for detecting uninitialized reads • De-allocation • Expansion factor => frees deferred • Extra checks for illegal free

  27. DieHard CPU Performance (no replication)

  28. Correctness Results • Synthetic: • Tolerates high rate of synthetically injected errors in SPEC programs • Spec benchmarks: • Detected two previously unreported benign bugs (197.parser and espresso) • Avoiding real errors: • Successfully hides buffer overflow error in Squid web cache server (v 2.3s5) • Avoids dangling pointer error in Mozilla • DoSin glibc & Windows

  29. StackGuard AVOIDING SECURITY EXPLOITS

  30. Aleph One Fires The Opening Shot • “Smashing the Stack for Fun and Profit” • Aleph One (AKA Elias Levy), Phrack 49, August 1996 • It is a cook book for how to create exploits for “stack smashing” attacks • Prior to this paper, buffer overflow attacks were known, but not widely exploited • “Validate all input parameters” is a security principle going back to the 1960s • After this paper, attacks became rampant • Stack smashing vulns are massively common, easy to discover, and easy to exploit

  31. What is a “Stack Smash”? • Buffer overflow: • Program accepts string input, placing it in a buffer • Program fails to correctly check the length of the input • Attacker gets to overwrite adjacent state, corrupting it • Stack Smash: • Special case of a buffer overflow that corrupts the activation record

  32. What is a “Stack Smash”? • Return address • Overflow changes it to point somewhere else • “Shell Code” • Point to exploit code that was encoded as CPU instructions in the attacker’s string • That code does exec(“/bin/sh”) hence “shell code”

  33. Why? Et pourquoipas? • Why are we so vulnerable to something so trivial? • Because C chose to represent strings as null terminated instead of (base, bound) tuples • Because strings grow up and stacks grow down • Because we use Von Neumann architectures that store code and data in the same memory • But these things are hard to change … mostly • Try to move away from Von Neumann architecture by making key regions of memory be non-executable • Problem: x86 memory architecture does not distinguish between “readable” and “executable” per page

  34. Non-Executable Stack, 1997 • “Solar Designer” introduces the Linux non-executable stack patch • Fun with x86 segmentation registers maps the stack differently from the heap and static data • Results in a non-executable stack • Effective against naïve Stack Smash attacks • Bypassable: • Inject your shell code into the heap (still executable) • Point return address at your shell code in the heap

  35. StackGuard, 1998 • Compile in integrity checks for activation records • Insert a “canary word” (after the Welsh miner’s canary) • If the canary word is damaged, then your stack is corrupted • Instead of jumping to attacker code, abort the program • Log the intrusion attempt

  36. StackGuard Prototype • Written in a few days by one intern • Less than 100 lines of code patch to GCC • Helped a lot that the GCC function preamble and function post amble code generator routines were nicely isolated • First canary was hardcoded 0xDEADBEEF • Easily spoofable, but worked for proof of concept

  37. Canary Spoof Resistance • The random canary: • Pull a random integer from the OS /dev/random at process startup time • Simple in concept, but in practice it is very painful to make reading from /dev/random work while still inside crt0.o • Made it work, but motivated us to seek something simpler • “Terminator” canary: • CR, LF, 00, -1: the symbols that terminate various string library functions • Rationale: will cause all the standard string mashers to terminate while trying to write the canary  cannot spoof the canary and successfully write beyond it • Still vulnerable to attacks against poorly used memcpy() code, but buffer overflows thought to be rare

  38. XOR Random Canary • 1999, “Emsi” creates the frame pointer attack • Frame pointer stored below the canary  corruptible • Change FP to point to a fake activation record constructed on the heap • Function return code will believe FP, interpret the fake activation record, and jump to shell code • Bypasses both Terminator and Random Canaries • XOR Random Canary • XOR the correct return address with the random canary • Integrity check must match both the random number, and the correct return address

  39. L3: Putting These Ideas Together • Focus on malware detection and prevention • Much harder to “fix” than stack-based buffer overruns • Externally exploitable bugs • Nozzle • Runtime detector for heap spraying attacks • False positive rates: 10-6 • Overhead: 5-10% • Zozzle • Static/statistical detector • False positive rates: 10-6 • Overhead: very small

More Related