Presentation is loading. Please wait.

Presentation is loading. Please wait.

Runtime Monitoring of C Programs for Security and Correctness

Similar presentations


Presentation on theme: "Runtime Monitoring of C Programs for Security and Correctness"— Presentation transcript:

1 Runtime Monitoring of C Programs for Security and Correctness
Suan Hsi Yong University of Wisconsin – Madison Ph.D. Committee: Susan Horwitz (advisor), Thomas Reps, Charles Fischer, Somesh Jha, James Smith

2 Errors in Software Software errors are undesirable
may produce incorrect results may crash system may corrupt data may be vulnerable to attack Software errors are difficult to detect may be infrequently exercised may not noticeably alter observable output

3 Memory and Type Safety Memory safety: each dereference can only access ‘intended target’ spatial access errors (e.g., out-of-bounds array access) temporal access errors (e.g., stale pointer dereference) Type safety: operations can only be applied to values of certain types

4 Memory and Type Safety Useful for improving quality of software
Tradeoff between efficiency and flexibility If too general, incurs a high runtime overhead to enforce If too restrictive, limits expressiveness and utility of language C language mandates but does not enforce memory and type safety programmer’s responsibility, error prone

5 Approaches for Finding Errors
Static Approaches imprecise, not scalable Dynamic Approaches incomplete coverage, high runtime overhead This thesis: Dynamic approach, but use static analysis to improve overhead for testing/debugging, and for use in deployed software

6 This Thesis Explores… Runtime checking of memory and type safety in C programs Three manifestations Memory-Safety Enforcer (MSE): detects invalid dereferences Sensitive Location Checker (SLC): detects invalid writes to security-sensitive locations Runtime Type Checker (RTC): detects bugs manifested as type errors

7 Common Features Tagged memory Source-level instrumentation
each byte of memory is tagged at runtime with information used to detect errors Source-level instrumentation approach is portable, compatible with uninstrumented libraries Static analysis identifies and eliminates unnecessary instrumentation

8 Architecture C source file runtime system/ libraries
instru- mented C source file Instrumenter classifications Static Analysis C Compiler instru- mented exec- utable

9 Outline Introduction Memory-Safety Enforcer (MSE)
Sensitive-Location Checker (SLC) Runtime Type Checker (RTC) Related Work Conclusion

10 Memory Safety p = &a p’s valid target is a
*(p+i) valid only if it accesses a Spatial access error: out of bounds e.g., if i is negative or is too large Temporal access error: stale pointer e.g., if a had been freed prior to *(p+i)

11 Memory Safety Enforcer (MSE)
Debugging Tool invalid access  programming error e.g., buffer overrun, stale pointer dereference Security Tool most attacks require invalid write to succeed (e.g., stack smashing attacks) halt execution when violation detected

12 Control Transfer Attacks
Idea: overwrite sensitive location with address of malicious code Sensitive locations include return address (stack smashing) global offset table function pointers longjmp buffer exec call argument others…

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

14 Detecting Invalid Access
Fat Pointer Record information about what each pointer should point to Safe-C, CCured, Cyclone Tagged Memory (our approach) Record information about which locations may be valid targets of some pointer dereference also used by Purify

15 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

16 Tagged Memory associates information with target rather than pointer 
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  = valid target of unsafe dereference

17 Fat Pointer vs. Tagged Memory
Fat Pointers Guaranteed to catch all spatial errors Difficult to catch temporal errors efficiently e.g., CCured uses garbage collection Tagged Memory Can detect both spatial and temporal errors efficiently Guaranteed only to catch invalid accesses to non-user memory But can improve with static analysis

18 Improving MSE Which dereferences to check?
if static analysis can guarantee that *p is always valid, then *p need not be instrumented. classify dereferences into checked/unchecked Which locations to tag valid at runtime? if x can only be accessed directly or via unchecked dereference, then x need not be tagged valid classify locations into tracked/untracked

19 Checked Derefs/Tracked Locs
Checked Derefs/Tracked Locs naively: all dereferences are checked; all user-defined locations are tracked char buf[12]; char *p = &buf[0]; do { *p = getchar(); } while(*p++ != ‘\0’); p buf return address

20 Checked Derefs/Tracked Locs
Checked Derefs/Tracked Locs FN_PTR fp = &foo; char buf[12]; char *p = &buf[0]; do { *p = getchar(); } while(*p++ != ‘\0’); (*fp)(); p buf fp Static Analysis: identify fewer checked derefs and tracked locations

21 Checked Dereferences Writes Only vs. Read/Write
write-only checks catches most attacks; significantly improving overhead Flow-insensitive analysis: *p is checked 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 a[i]  *(a+i)

22 Example: Checked Dereferences
FN_PTR fp = &foo; char buf[12]; char *p = &buf[0]; do { *p = getchar(); } while(*p++ != ‘\0’); (*fp)(); dereferences: *p checked *fp unchecked

23 Tracked Locations locations that may be validly accessed via checked 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 checked dereference *p, all locations in p’s points-to set are tracked

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

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

26 Summary of MSE Coverage
Attack target MSE Unoptimized MSE + Static Analysis Return address yes Function pointer no likely exec argument maybe

27 Evaluation: Runtime Overhead

28 Flow-Sensitive Analyses
Redundant Checks Analysis *(p+i) = ...; *(p+i) = ...; //- don’t instrument Pointer Range Analysis track range of possible values for each pointer *p = ...; p  a:char[10], [3,7] if *p is definitely in-bounds, don’t instrument

29 Flow-Sensitive Analyses
remind ppl: lower is better

30 Flow-Sensitive Analyses

31 Analysis Time Slowdown

32 MSE Static Analysis Summary
Unoptimized MSE high runtime overhead only catches invalid access to non-user memory Flow-insensitive (Extended Points-To Analysis) low runtime overhead, scalable analysis Flow-sensitive Analyses 20% improvement, but analysis not scalable Write-only faster than read-write checking

33 Comparison with Other Tools (runtime overhead)

34 Summary of MSE 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 erroneous free

35 Outline Introduction Memory-Safety Enforcer (MSE)
Sensitive-Location Checker (SLC) Runtime Type Checker (RTC) Related Work Conclusion

36 Two Approaches to Security
MSE: Try to detect all invalid accesses including invalid accesses that are not vulnerable to attack SLC: Detect only invalid accesses to sensitive locations return address, function pointers, longjmp buffers, exec call arguments related work: StackGuard – protects only return address on the activation record

37 SLC vs MSE: classification
identify unsafe dereferences, then compute tracked locations dereferences locations w *p x *q y *r z (points-to edges)

38 SLC vs MSE: classification
identify sensitive locations, then compute unchecked dereferences dereferences locations w *p x *q y *r z (points-to edges)

39 Example char safe_buf[8]; char vuln_buf[8]; strcpy(vuln_buf, “ls”);
char safe_buf[8]; char vuln_buf[8]; strcpy(vuln_buf, “ls”); gets(safe_buf); system(vuln_buf); not sensitive safe _buf sensitive unchecked vuln _buf checked use same color blocks as previous… return address

40 SLC vs MSE: instrumentation
SLC must set/clear tag of sensitive locations, while MSE must set/clear tag of tracked locations In general, much fewer sensitive locations that tracked locations, so SLC is faster SLC must set/clear tag of return address on activation record may slow down SLC compared to MSE

41 Runtime Overhead: SLC vs MSE
Average: SLC=37.7%, MSE=54.1%

42 SLC: The Bad News In some of the benchmarks (ijpeg, li, perl, gap), over 90% of the dereferences were not checked i.e., they may point to a sensitive location due to imprecision of points-to analysis could be improved with better points-to analysis Good news: can tell from static analysis whether SLC will be effective for a given program

43 SLC vs MSE Memory Safety Enforcer (MSE)
detects invalid accesses that may not be vulnerable to attack may prevent new as-yet-undiscovered methods of attack Sensitive Location Checker (SLC) targets specific locations known (a priori) to be vulnerable to attack better runtime overhead because of limited scope

44 Outline Introduction Memory-Safety Enforcer (MSE)
Sensitive-Location Checker (SLC) Runtime Type Checker (RTC) Related Work Conclusion

45 Runtime Type Checking Idea is to detect runtime type violations
value of one type is used in context of incompatible type Scalar types only (structs and arrays broken down into components) Debugging tool, for use during development/testing Higher overhead acceptable (~20x) Related tools: Purify, Insure++, Valgrind

46 Error Example 1: Union 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!

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

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

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

50 Example 3: Custom Allocator
int *intArray = (int *) myMalloc(10 * sizeof(int)); int **ptrArray = (int **) myMalloc(10 * sizeof(int *)); i User memory intArray ptrArray ORIGINAL myMemory i User memory myMemory PURIFY

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

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

53 Runtime Type-Checking
Track type of values in memory store in mirror of memory: 4 bits for each byte unalloc, uninit, integral, real, pointer, zero Warning when type of value assigned does not match expected type Error when bad runtime type is used

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

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

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

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

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

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

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

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

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

63 Runtime Type Checking Found errors in SPEC 95, SPEC 2000, and Solaris utilities But, overhead is high 4x-170x slowdown, average 43.5x Use Static Analysis Flow insensitive: Type Flow Analysis Flow sensitive: redundant checks, may-be-uninitialized

64 Type Flow Analysis Classify lvalue expressions into:
safe: runtime type always equals static type no instrumentation needed type-unsafe: always in-bounds, but runtime type may not equal static type must be instrumented to check runtime type mem-unsafe: may violate memory safety must be fully instrumented (including check that dereference does not access unalloc memory) maybe use examples to illustrate each classification

65 Type Flow Analysis Classify locations into:
untracked: runtime type always equals static type no instrumentation needed tracked: runtime type always equals static type, but may be accessed by unsafe dereference initialize mirror to static type, but type doesn’t change unsafe: may cause type-safety error at runtime initialize mirror to uninit; type may change at runtime

66 Type-Flow Analysis Points-to Analysis Build Assignment Graph
Compute Possible Types Classify Expressions and Locations >

67 Type-Flow Analysis Points-to Analysis Build Assignment Graph
Compute points-to set for each pointer Points-to Analysis Build Assignment Graph Compute Possible Types Classify Expressions and Locations pt p {f}

68 Type-Flow Analysis Points-to Analysis Build Assignment Graph
Compute Possible Types Classify Expressions and Locations x x 5.0 VALUEfloat VALUEinit &i VALUEvalid-ptr int nodes

69 Type-Flow Analysis Points-to Analysis Build Assignment Graph
Compute Possible Types Classify Expressions and Locations *p *p y+z VALUEint p+i VALUEptr &i VALUEvalid-ptr nodes

70 Type-Flow Analysis Points-to Analysis Build Assignment Graph
x = y; Points-to Analysis Build Assignment Graph Compute Possible Types Classify Expressions and Locations x y = *p = 5.0; VALUEfloat = *p x = y + z; VALUEint = x edges

71 Type-Flow Analysis Points-to Analysis Build Assignment Graph
T = “uninitialized” Points-to Analysis Build Assignment Graph Compute Possible Types Classify Expressions and Locations valid-ptr init pointer int char float  |  = “multiply-typed”

72 Type-Flow Analysis Points-to Analysis Build Assignment Graph
Compute Possible Types Classify Expressions and Locations float VALUEfloat T(x)  T(y) T(y) x y =

73 Type-Flow Analysis Points-to Analysis Build Assignment Graph
Compute Possible Types Classify Expressions and Locations T(*p)  T(k) {k} p pt *p y = *p {k} p pt k T(k)  T(y)

74 } Type-Flow Analysis Points-to Analysis Build Assignment Graph
Compute Possible Types Classify Expressions and Locations poss-type(x)  static-type(x)  x type-unsafe int k; float f = 2.3; int *p = &f; k = *p; } poss-type(k) = float k type-unsafe static-type(k) = int

75 } Type-Flow Analysis Points-to Analysis Build Assignment Graph
Compute Possible Types Classify Expressions and Locations poss-type(x)  static-type(x)  x type-unsafe int k; float f = 2.3; int *p = &f; k = *p; } poss-type(*p) = float *p type-unsafe static-type(*p) = int

76 Type-Flow Analysis Points-to Analysis Build Assignment Graph
Compute Possible Types Classify Expressions and Locations 2. poss-type(p) ≠ valid-ptr  *p mem-unsafe int *p = &k; p = p + 1; *p = 5;  *p mem-unsafe

77 } Type-Flow Analysis Points-to Analysis Build Assignment Graph
Compute Possible Types Classify Expressions and Locations 3.*p unsafe,  x tracked {x} p pt int k; float f = 2.3; int *p = &f; k = *p; } *p unsafe f tracked p may-point-to f

78 Type-Flow Analysis: Example
int k; float f; int *p; f = 2.3; p = (int *)&f; k = *p; print_int( k ); tracked f safe p type-unsafe k type-unsafe *p Instrumentation: w/o using type-flow analysis

79 Type-Flow Analysis: Example
int k; float f; int *p; f = 2.3; p = (int *)&f; k = *p; print_int( k ); tracked f safe p type-unsafe k type-unsafe *p Instrumentation: using type-flow analysis

80 RTC Runtime Overhead

81 Flow-sensitive Refinements
May-be-uninitialized analysis needed to detect uses of uninitialized data analysis and runtime overhead both slow not worthwhile: better to use unoptimized Redundant checks analysis for each use of x, if error is reported, tag of x is corrected to prevent cascading errors y = x + 1; z = x + 2; //check of x is redundant >

82 RTC Runtime Overhead

83 Analysis Time

84 Outline Introduction Memory-Safety Enforcer (MSE)
Sensitive-Location Checker (SLC) Runtime Type Checker (RTC) Related Work Conclusion

85 Related Work Run-time Error Detection Reducing Instrumentation
Purify, Insure++, Valgrind Safe C, CCured, Cyclone Safe languages (Java), dynamically-typed languages (Lisp) Reducing Instrumentation Java (remove array-bounds checks) Lisp and Scheme (remove tag-handling operations, e.g., Henglein) CCured (remove pointer checks: Necula et al) more detailed slides on, e.g., range analysis

86 Conclusion Finding errors and vulnerabilities is difficult
Three related runtime monitoring approaches Sensitive location checker: efficient, security-oriented Memory-safety enforcer: efficient, security or debugging Runtime type-checker: slow, for debugging Effective in detecting errors Static analysis to improve runtime overhead Flow-insensitive: scalable, significant improvements Flow-sensitive: modest improvements, not scalable

87 Future Work Better static analysis: fewer unsafe and tracked locations
escape analysis / scope analysis better points-to analysis Different mechanism for tagging e.g. hash lookup Apply ideas to other languages and environments e.g. absorb overhead on multiprocessor

88 Runtime Monitoring of C Programs for Security and Correctness
END Suan Hsi Yong University of Wisconsin – Madison Ph.D. Committee: Susan Horwitz (advisor), Thomas Reps, Charles Fischer, Somesh Jha, James Smith

89 Index Start / Mem & Type Safety / Architecture
MSE / Ctl-xfer Attack / Fat Pointers / Classification / Flow-sensitive / Results SLC / Example / Results RTC / Union / MyMalloc / Inheritance / Example / Type-Flow / Results Related / Conclusion / Future MSE example / CCured wild / Type-Flow example / MBU

90 Example: Allocation FN_PTR fp = &foo; char buf[12]; char *p = &buf[0];
Example: Allocation FN_PTR fp = &foo; char buf[12]; char *p = &buf[0]; do { *p = getchar(); } while(*p++ != ‘\0’); (*fp)(); p buf fp locations: untracked tracked pointers: unsafe safe p fp

91 Example: Allocation FN_PTR fp = &foo; char buf[12]; char *p = &buf[0];
Example: Allocation FN_PTR fp = &foo; char buf[12]; char *p = &buf[0]; do { *p = getchar(); } while(*p++ != ‘\0’); (*fp)(); fp p buf fp locations: untracked tracked pointers: unsafe safe p fp

92 Example: Allocation FN_PTR fp = &foo; char buf[12]; char *p = &buf[0];
Example: Allocation FN_PTR fp = &foo; char buf[12]; char *p = &buf[0]; do { *p = getchar(); } while(*p++ != ‘\0’); (*fp)(); buf fp p buf fp locations: untracked tracked pointers: unsafe safe p fp

93 Example: Allocation FN_PTR fp = &foo; char buf[12]; char *p = &buf[0];
Example: Allocation FN_PTR fp = &foo; char buf[12]; char *p = &buf[0]; do { *p = getchar(); } while(*p++ != ‘\0’); (*fp)(); p buf fp p buf fp locations: untracked tracked pointers: unsafe safe p fp

94 Example: Checking Writes
Example: Checking Writes FN_PTR fp = &foo; char buf[12]; char *p = &buf[0]; do { *p = getchar(); } while(*p++ != ‘\0’); (*fp)(); p buf fp p buf fp locations: untracked tracked pointers: unsafe safe p fp

95 Example: Checking Writes
Example: Checking Writes FN_PTR fp = &foo; char buf[12]; char *p = &buf[0]; do { *p = getchar(); } while(*p++ != ‘\0’); (*fp)(); p buf fp p buf fp locations: untracked tracked pointers: unsafe safe p fp

96 Example: Checking Writes
Example: Checking Writes FN_PTR fp = &foo; char buf[12]; char *p = &buf[0]; do { *p = getchar(); } while(*p++ != ‘\0’); (*fp)(); p buf fp p buf fp locations: untracked tracked pointers: unsafe safe p fp

97 Example: Checking Writes
Example: Checking Writes FN_PTR fp = &foo; char buf[12]; char *p = &buf[0]; do { *p = getchar(); } while(*p++ != ‘\0’); (*fp)(); p buf fp p buf fp locations: untracked tracked pointers: unsafe safe p fp

98 Back

99 } CCured WILD Pointers int i; int *p; int *q1, *q2; q1 = i;
q1 = (int)&p; q2 = &i; } q1 WILD => q2 WILD

100 Back

101 Type-Flow Analysis: Example
int k; float f; int *p; f = 2.3; p = (int *)&f; k = *p; print_int( k ); {f} p pt 1. Points-to analysis

102 Type-Flow Analysis: Example
int k; float f; int *p; f = 2.3; p = (int *)&f; k = *p; print_int( k ); VALUEfloat f = VALUEvalid-ptr p = *p k = {f} p pt 2. Assignment graph

103 Type-Flow Analysis: Example
int k; float f; int *p; f = 2.3; p = (int *)&f; k = *p; print_int( k ); float float VALUEfloat f = valid-ptr valid-ptr VALUEvalid-ptr p = float float *p k = T(*p)  T(f) {f} p pt 3. Runtime types

104 Type-Flow Analysis: Example
int k; float f; int *p; f = 2.3; p = (int *)&f; k = *p; print_int( k ); float tracked f valid-ptr safe p float type-unsafe k float type-unsafe *p {f} p pt 4. Type-safety levels

105 Back

106 May-be-uninitialized Analysis

107 May-be-uninitialized Analysis

108 May-be-uninitialized Analysis

109 Back


Download ppt "Runtime Monitoring of C Programs for Security and Correctness"

Similar presentations


Ads by Google