1 / 86

Efficient Runtime Monitoring of C Programs for Security and Correctness

Efficient Runtime Monitoring of C Programs for Security and Correctness. Suan Hsi Yong University of Wisconsin – Madison. Program Errors. incorrect results, crash system, corrupt data security vulnerabilities buffer overrun, stale pointers, format strings difficult to detect

ash
Download Presentation

Efficient Runtime Monitoring of C Programs for Security and Correctness

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. Efficient Runtime Monitoringof C Programsfor Security and Correctness Suan Hsi Yong University of Wisconsin – Madison

  2. Program Errors • incorrect results, crash system, corrupt data • security vulnerabilities • buffer overrun, stale pointers, format strings • difficult to detect • Static Tools: slow, imprecise • Testing/Debugging: incomplete coverage • Runtime Detection: high overhead

  3. Incorrect Behavior in C Programs • C specifications defines ‘correct’ and ‘incorrect’ behaviors • syntax and type system not strong enough to statically ensure correct behavior • easy to write program with incorrect behavior • Ensuring correct behavior at runtime is complicated: • expensive • restricts flexibility (e.g. ‘well-typed’) • changes expected behavior (e.g. garbage collection)

  4. Efficient Runtime Monitoring • Security tool [FSE’03] • Detect invalid pointer dereferences • Efficient: for use in deployed code • Preserves flexibility: can apply to legacy code • Runtime Type-Checking [FASE’01, RV’02, FMSD’04] • Report type errors • Heavyweight debugging tool

  5. Outline • Introduction • Security Tool • Description • Experiments • Runtime Type-Checking • Description • Experiments • Conclusion

  6. Security Tool (Joint work with Susan Horwitz) • Enforce memory safety at runtime • halt execution when detected • Efficient (low overhead) • No false positives • No source code modification needed • Portable (source level instrumentation)

  7. Memory Safety Violations a[i]  *(a+i) • Pointer dereference to invalid target • (‘direct’ memory accesses are OK) • Each pointer has well-defined targetobjects it can validly point to • Example violations: buffer overrun, stale pointer • Invalid write more dangerous than read • Write exploits: can corrupt data, gain control • Read exploits: obtain confidential data • For efficiency: by default we only check writes; but can check reads also

  8. Exploiting Invalid Writesto gain control of program • Idea: overwrite vulnerable location with address of malicious code • Vulnerable locations include • return address (stack smashing) • function pointer • setjmp buffer • global offset table • others…

  9. Stack Smashing char buf[12]; char *p = &buf[0]; do { *p = getchar(); } while(*p++ != ‘\0’); p buf return address

  10. Preventing Attack • Protect vulnerable location(s) only • StackGuard: return address only • efficient, automatic • platform dependent, doesn’t protect other locations • Prevent all invalid accesses • Check each dereference to see if target is valid • Fat Pointers • Tagged Memory (our approach)

  11. Fat Pointers • Record information about what a pointer should point to • CCured, Cyclone • detects all invalid accesses • less flexibility in memory management:requires garbage collection(or region-based model, for Cyclone) • doesn’t handle all of C(rejects some legal C programs) • requires programmer changes to source

  12. Fat Pointers • associate information with pointer: address and size of referent p buf 12 char buf[12]; char *p = &buf[0]; do { *p = getchar(); } while(*p++ != ‘\0’);  buf  return address

  13.                                             Tagged Memory • associates information with target rather than pointer p char buf[12]; char *p = &buf[0]; do { *p = getchar(); } while(*p++ != ‘\0’);  buf  return address

  14. Fat Pointer vs. Tagged Memory • Fat Pointers • Guaranteed to catch all invalid accesses • Difficult to implement flexibly and efficiently • Casting; memory management • Tagged Memory • Can be flexible and efficient • Guaranteed only to catch invalid accesses to non-user memory • May catch more invalid accesses with better static analysis

  15.                                             Which locations are valid? • naively: all user-defined locations are valid char buf[12]; char *p = &buf[0]; do { *p = getchar(); } while(*p++ != ‘\0’); p buf   return address

  16.                                               Which locations are valid? FN_PTRfp= &foo; char buf[12]; char *p = &buf[0]; do { *p = getchar(); } while(*p++ != ‘\0’); (*fp)(); p buf   fp • Static Analysis, to mark fewer locations as ‘valid’

  17. Static Analysis • Unsafe dereferences: may refer to invalid memory • only unsafe dereferences are checked at runtime • safe dereferences are not checked • Tracked locations: may be validly accesssed via unsafe dereference • mirror is marked valid () when allocated,and invalid () when freed. • untracked locations always invalid ():never a target of unsafe dereference

  18. Unsafe Dereferences • Writes Only vs. Read/Write • Flow-insensitive analysis: • *p is unsafe if: • p is assigned a non-pointer value, or • p is the result of pointer arithmetic, or • p may point to stack or freed heap location • Flow-sensitive (dataflow) analysis • Redundant Checks • Array Range (Interval) Analysis a[i] *(a+i)

  19. dereferences: *p unsafe *fp safe Example: Unsafe Dereferences FN_PTR fp = &foo; char buf[12]; char *p = &buf[0]; do { *p = getchar(); } while(*p++ != ‘\0’); (*fp)();

  20. Tracked Locations • locations that may be validly accessed via unsafe dereference • fewer tracked locations means better performance and coverage • less overhead to mark and clear ‘valid’ tag • increase likelihood of catching invalid access • identify with points-to analysis [Das’00] • for each unsafe dereference *p,all locations in p’s points-to set are tracked

  21. locations: dereferences: points-to graph: untracked *p unsafe p p fp untracked *fp safe fp tracked buf foo buf Example: Tracked Locations FN_PTR fp = &foo; char buf[12]; char *p = &buf[0]; do { *p = getchar(); } while(*p++ != ‘\0’); (*fp)();

  22.                         locations:  dereferences: points-to graph:     untracked   *p unsafe p   p fp   untracked   *fp safe fp    tracked  buf foo buf    Example: Tracked Locations FN_PTR fp = &foo; char buf[12]; char *p = &buf[0]; do { *p = getchar(); } while(*p++ != ‘\0’); (*fp)(); p buf   fp

  23. Tool Overview Csourcefile instru-mentedClibrary Static Analysis Instrumenter CCompiler unsafe dereferences tracked variables instru-mentedC sourcefile instru-mentedexec-utable

  24. Outline • Introduction • Security Tool • Description • Experiments • Runtime Type-Checking • Description • Experiments • Conclusion

  25. Experiments (Checking Writes) • Tool successfully detected two simulated attacks via known vulnerabilities • cfingerd: buffer overrun attack • traceroute: modifying Global Offset Table entry via multiple-free bug

  26. Runtime Overhead Flow-insensitive: 57% Flow-sensitive: 51% (340%) Cyclone Olden Spec 95 Spec 2000

  27. increasing size (LOC) Runtime Overhead Flow-insensitive: 57% Flow-sensitive: 51% (340%)

  28. increasing size (LOC) Analysis/Instrument/Compile Time • Slowdown vs. Compile Time Flow-sensitive: 18.5x Flow-insensitive: 3.4x 98x 380x 16x

  29. Checking Reads and Writes Runtime overhead: 2.6x slowdown Cyclone Olden Spec 95 Spec 2000

  30. Comparing with CCured/Cyclone

  31. Summary: Security Tool • Tool for detecting invalid pointer dereferences that • has low runtime overhead • does not report false positives • is portable, and does not require programmer changes to source code • protects against a wide range of vulnerabilities, including stack smashing and using freed memory

  32. Outline • Introduction • Security Tool • Description • Experiments • Runtime Type-Checking • Description • Experiments • Conclusion

  33. Runtime Type Checking (Joint work with Susan Horwitz, Alexey Loginov, Tom Reps) • Debugging tool, for use during development/testing • Higher overhead acceptable (~20x) • Related tools: Purify, Insure++, Valgrind • Goal is to detect runtime type violations • value of one type is used in context of incompatible type • Scalar types only (Structs and array broken down into components)

  34. Example 1: Unions union U { int u1; int *u2; } u; int *p; u.u1 = 20; // write int into u.u1 p = u.u2; // copy int from u.u2 -- suspicious! *p = 0; // bad pointer deref -- error!

  35. Example 2: Bad Pointer Access int *intArray = (int *) malloc(15 * sizeof(int)); int **ptrArray = (int **) malloc(15 * sizeof(int *)); User memory intArray ptrArray

  36. intArray ptrArray i intArray ptrArray padding PURIFY Example 2: Bad Pointer Access int *i, sumEven = 0; for(i = intArray; ...; i += 2)sumEven += *i; i User memory ORIGINAL User memory

  37. Example 3: User Memory Management char * myMalloc(size_t size) { static char *myMemory, *current; ... if(first_time){ myMemory = (char *) malloc(BLKSIZE); } ... return &myMemory[current += size]; }

  38. i i PURIFY myMemory Example 3: User Memory Management int *intArray = (int *) myMalloc(10 * sizeof(int)); int **ptrArray = (int **) myMalloc(10 * sizeof(int *)); User memory intArray ptrArray ORIGINAL myMemory User memory

  39. Example 4: Simulated Inheritance struct Base { int a1; int a2; } base; struct Sub { int b1; int b2; char b3; } sub; void f(struct Base *s) { s->a1 = ... s->a2 = ... } : f(&base); f(&sub); :

  40. Example 4: Simulated Inheritance struct Base { int a1; int a2; } base; struct Sub { int b1; float f1; int b2; char b3; } sub; void f(struct Base *s) { s->a1 = ... s->a2 = ... } : f(&base); f(&sub); :

  41. Runtime Type-Checking • Track type of values in memory • unalloc, uninit, integral, real, pointer, zero • store in mirror of memory: 4 bits for each byte • Warning when bad runtime type is written into a location • Error when bad runtime type is used

  42. (memory) (mirror) k . . . unalloc f . . . unalloc p . . . unalloc Runtime Type Checking Tool int k; float f; int *p; f = 2.3; p = (int *)&f; k = *p; print_int( k );

  43. k . . . unalloc f . . . unalloc p . . . unalloc Runtime Type Checking Tool (memory) (mirror) int k; float f; int *p; f = 2.3; p = (int *)&f; k = *p; print_int( k ); uninit instrument a declaration

  44. k . . . unalloc uninit f . . . unalloc p . . . unalloc uninit Runtime Type Checking Tool (memory) (mirror) int k; float f; int *p; f = 2.3; p = (int *)&f; k = *p; print_int( k ); uninit instrument a declaration

  45. k . . . unalloc f . . . unalloc p . . . unalloc Runtime Type Checking Tool (memory) (mirror) int k; float f; int *p; f = 2.3; p = (int *)&f; k = *p; print_int( k ); uninit uninit float 2.3 uninit instrument an assignment

  46. k . . . unalloc f . . . unalloc p . . . unalloc Runtime Type Checking Tool (memory) (mirror) int k; float f; int *p; f = 2.3; p = (int *)&f; k = *p; print_int( k ); uninit uninit float 2.3 ( &f ) uninit pointer instrument an assignment

  47. k . . . unalloc f . . . unalloc p . . . unalloc OK Runtime Type Checking Tool (memory) (mirror) int k; float f; int *p; f = 2.3; p = (int *)&f; k = *p; print_int( k ); uninit uninit float 2.3 ( &f ) uninit pointer instrument a use (of a pointer)

  48. k . . . unalloc f . . . unalloc p . . . unalloc OK Runtime Type Checking Tool (memory) (mirror) int k; float f; int *p; f = 2.3; p = (int *)&f; k = *p; print_int( k ); uninit uninit float 2.3 ( &f ) uninit pointer instrument a pointer dereference

  49. k . . . unalloc f . . . unalloc p . . . unalloc warning! Runtime Type Checking Tool (memory) (mirror) int k; float f; int *p; f = 2.3; p = (int *)&f; k = *p; print_int( k ); 2.3 float uninit uninit float 2.3 ( &f ) uninit pointer instrument an assignment

  50. k . . . unalloc error! f . . . unalloc p . . . unalloc Runtime Type Checking Tool (memory) (mirror) int k; float f; int *p; f = 2.3; p = (int *)&f; k = *p; print_int( k ); 2.3 uninit float uninit float 2.3 ( &f ) uninit pointer instrument a use (of an int)

More Related