Presentation is loading. Please wait.

Presentation is loading. Please wait.

Suan Hsi Yong University of Wisconsin – Madison

Similar presentations


Presentation on theme: "Suan Hsi Yong University of Wisconsin – Madison"— Presentation transcript:

1 Suan Hsi Yong University of Wisconsin – Madison
Efficient Runtime Monitoring of C Programs for 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 Runtime Type-Checking Conclusion
Description Experiments Runtime Type-Checking Conclusion

6 (Joint work with Susan Horwitz)
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) Our approach, which is targeted for the C language, but is applicable to related languages, is to dynamically detect invalid writes via pointers. When an invalid write is detected, we report an error and halt execution, so that no damage can be done. Our goal is to have the instrumentation be used in deployed code, so we want to be efficient, that is, to incur a low runtime overhead. We also want no false positives; since we’ll halt execution when an error is detected, we don’t want to halt the program on a false error. Finally, we want our technique to apply to legacy code, so we require no changes to the code by the programmer.

7 Memory Safety Violations
a[i] *(a+i) Pointer dereference to invalid target (‘direct’ memory accesses are OK) Each pointer has well-defined target objects 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 Writes to 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 To illustrate how stack smashing is usually accomplished, here’s an example which we will revisit throughout the talk. We have a buffer of size 12, and a pointer p which points to it. We have a loop which reads input and writes it into buf via *p, incrementing p until a null character is read. Notice that there is no bounds check, [click] so that p may go out of bounds, and eventually overwrite the return address on the stack. An attacker could thus write a suitable value into the return address, so that when the function returns, control is transferred to the attacker’s code. The goal of our tool is to detect invalid writes via pointer dereferences before damage can be done. 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 This is the approach often called fat pointers, where each pointer is associated with information about what it’s supposed to point to, so that at a pointer dereference, the pointer’s address is checked to see if it is within the bounds of the array or object it’s supposed to point to. Two tools that use fat pointers are CCured and Cyclone, which are two variants of the C language designed to be less prone to pointer-related errors and vulnerabilities. The advantage of this approach is that it detects all invalid pointer dereferences. There are several disadvantages, however: Using fat pointers, it’s difficult to detect uses of dangling pointers, so they effectively limit the programmer’s control of memory management; They require the use of garbage collection, or a region-based model, which is a major component of Cyclone These tools don’t handle all of C; that is, they reject some legal C programs, so programmer effort is necessary to port existing programs to these languages.

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 To demonstrate how fat pointers work, we revisit the example from earlier, with the pointer p pointing to buf, potentially going out of bounds and overwriting the return address. A fat pointer is a pointer with additional information associated with it: specifically, the address and size of the referent, or what the pointer is supposed to point to. So in this example, p is supposed to point to buf, so [click] we record this information here, saying that p is supposed to point to buf, which has size 12. Any dereference of p is checked against this information, [click] so an access within bounds is OK, while an out-of-bounds access is flagged as an error. return address

13 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 This brings us to our approach. What we do is, instead of associating information with the pointer, we associate information with the target. Specifically, we maintain a mirror of memory, with one bit for each byte, marking whether it is OK or bad to reference a location via a pointer: shown here, a green check mark indicates an OK location, while a red X indicates the location should not be accessed via a pointer. [click] So this in-bound dereference is OK because the mirror of buf is marked OK while the out-of-bounds dereference is flagged as an error, because the mirror is marked with an X 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?
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 An important question we must consider is: which locations should be marked ‘OK’? Naively, we can mark all user-defined variables, on the stack or the heap, as ‘OK’ when it is allocated, and ‘not OK’ when it is deallocated. In this program, we have two user-defined locations, buf and p, which we mark as ‘OK’. [click] In the first access, *p is marked OK, so the dereference is OK while in the second access, it’s marked not-OK, so the dereference is flagged as an error. Note that the mirror of the return address will always be marked ‘out-of-bounds’, so stack smashing attacks will always be detected. return address

16 Which locations are valid?
Which locations are valid? FN_PTR fp = &foo; char buf[12]; char *p = &buf[0]; do { *p = getchar(); } while(*p++ != ‘\0’); (*fp)(); p buf However, consider a modified version of the running example. Here we have a function pointer fp pointing to foo; the middle portion is the same, and after the loop there is a function call via fp In this program, we have three user-defined locations, fp buf and p, which we mark as ‘OK’. [click] Note that if *p goes out of bounds and writes into fp, we will still consider it OK, and not report an error. This is an undesirable behavior, since if an attacker writes a suitable value here, the subsequent call to fp can transfer control to malicious code. [click] So, we want fp to be marked with an X rather than OK. [click] Therefore, we perform static analysis, to reduce the number of locations that we mark as OK. 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) So we introduce the notion of an ‘unsafe pointer’, which is a pointer that may point to invalid memory. That is, a pointer p is unsafe if either p is assigned a non-pointer value, like an integer or a value from input, or p is the result of pointer arithmetic. Recall that we treat an array access a[i] as equivalent to *(a+i), so effectively all array accesses are treated as unsafe pointers. Or, p is unsafe if it may be a dangling pointer, that is, if it may point to freed or deallocated memory. The idea is that only writes via unsafe pointers will be checked at runtime. A dereference of a safe pointer will not be checked, since it can never point to invalid memory. Also, for efficiency, we don’t check reads: We found this to be a good policy, as the most malicious attacks need to write into inappropriate memory. Since there are typically many more reads than writes, this allows our approach to be more efficient, without sacrificing protection.

19 Example: Unsafe Dereferences
FN_PTR fp = &foo; char buf[12]; char *p = &buf[0]; do { *p = getchar(); } while(*p++ != ‘\0’); (*fp)(); Let’s see how our static analysis will work in the running example. In this program, there are two pointers, fp and p. Since p is involved in pointer arithmetic, p++ here, we mark p as an unsafe pointer. fp is safe, since it can only point to a valid location, that is, the function foo. dereferences: *p unsafe *fp safe

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 Example: Tracked Locations
FN_PTR fp = &foo; char buf[12]; char *p = &buf[0]; do { *p = getchar(); } while(*p++ != ‘\0’); (*fp)(); So, going back to the example, recall that we have p as an unsafe pointer, and fp a safe pointer. There are three locations in this program: fp, buf, and p. The points-to analysis is trivial: as shown here, p may point to buf, and fp may point to foo. A tracked location is a location that may be pointed to by an unsafe pointer; since p is the only unsafe pointer, and p may point to buf, but is the only tracked location. p and fp can be untracked. p buf fp locations: untracked tracked p buf points-to graph: foo fp dereferences: *p unsafe *fp safe

22 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 So, going back to the example, recall that we have p as an unsafe pointer, and fp a safe pointer. There are three locations in this program: fp, buf, and p. The points-to analysis is trivial: as shown here, p may point to buf, and fp may point to foo. A tracked location is a location that may be pointed to by an unsafe pointer; since p is the only unsafe pointer, and p may point to buf, but is the only tracked location. p and fp can be untracked. fp p buf fp locations: untracked tracked p buf points-to graph: foo fp dereferences: *p unsafe *fp safe

23 Tool Overview C source file instru- mented C library Static Analysis
Instrumenter C Compiler So here is an overview of our system. We begin with a C source file, and first perform static analysis to identify unsafe pointers and tracked locations. Next, the instrumenter takes the C source file and information about unsafe pointers and tracked variables, and generates the instrumented program. The instrumented code is in C, and is compiled by a regular C compiler to generate the instrumented executable. [click] For accurate coverage, the instrumented program needs to be linked with instrumented versions of the C library function. unsafe dereferences tracked variables instru- mented C source file instru- mented exec- utable

24 Outline Introduction Security Tool Runtime Type-Checking Conclusion
Description Experiments Runtime Type-Checking 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 As a first experiment, we downloaded two Linux programs with known root exploits, and simulated attacks to gain root access The first is the c-finger daemon, which has a standard buffer overrun attack. The second is a sophisticated attack on traceroute, which has a multiple-free bug: that is, the program attempts to free a location that has already been freed. With a very clever method the attack program uses the erroneous call to free to modify an entry in the global offset table, which stores the address of library calls, so that a subsequent library function call will transfer control to the attacker’s code, rather than to the expected library function. Both of the attacks failed when running our instrumented version of the programs, with the invalid pointer dereference being reported, and the program halting.

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

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

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

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

30 Comparing with CCured/Cyclone
Next, we compare the performance of our tool with CCured and Cyclone, the two tools most closely related to ours that use fat pointers. We ran these on some programs from the Cyclone benchmark. On average, our tool ran about twice as slow, CCured almost 5 times, and Cyclone about 1½ times. Since this is far from an exhaustive comparison, the thing to take away from this graph is that our performance is reasonably comparable to these other tools, and we have the advantage of supporting legacy C code with, while both CCured and Cyclone required some programmer intervention to compile. In fact, the Cyclone bars are from the Cyclone versions of the programs they distribute, which has been manually converted to Cyclone from C, which may account for its better performance.

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 In conclusion, then, We have implemented a tool for detecting invalid pointer dereferences that has a low runtime overhead; it doesn’t report false positives; it is portable, and doesn’t require programmer changes to source code; and it guards against a wide range of vulnerabilities: stack smashing, accessing freed memory, and others as well.

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

33 (Joint work with Susan Horwitz, Alexey Loginov, Tom Reps)
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 Example 2: Bad Pointer Access
int *i, sumEven = 0; for(i = intArray; ...; i += 2) sumEven += *i; i User memory intArray ptrArray ORIGINAL we find same error as purify, in different way i intArray ptrArray padding PURIFY 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 Example 3: User Memory Management
int *intArray = (int *) myMalloc(10 * sizeof(int)); int **ptrArray = (int **) myMalloc(10 * sizeof(int *)); i User memory intArray ptrArray ORIGINAL we find bug that purify doesn’t myMemory i User memory myMemory PURIFY

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 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 );

43 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

44 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

45 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

46 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

47 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)

48 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

49 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

50 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)

51 Runtime Type Checking Found errors in SPEC 95 and Olden benchmarks, and Solaris utilities But, overhead is high 3x-80x slowdown, average 37x Use Static Analysis Flow insensitive: Type Safety Levels Flow sensitive: redundant checks, may-be-uninitialized

52 Type Safety Levels Analysis
Classify program expressions into: safe: runtime type always equals static type no instrumentation needed unsafe: runtime type may not equal static type must be fully instrumented tracked: runtime type always equals static type, but may be pointed-to by unsafe dereference must initialize mirror to static type

53 Determining Type-Safety Levels
Points-to Analysis Build Assignment Graph Compute Runtime Types Compute Type-Safety Levels > >>

54 Determining Type-Safety Levels
Compute points-to set for each pointer Points-to Analysis Build Assignment Graph Compute Runtime Types Compute Type-Safety Levels pt p {f}

55 Determining Type-Safety Levels
Points-to Analysis Build Assignment Graph Compute Runtime Types Compute Type-Safety Levels x x 5.0 VALUEfloat VALUEzero &i VALUEvalid-ptr int nodes

56 Determining Type-Safety Levels
Points-to Analysis Build Assignment Graph Compute Runtime Types Compute Type-Safety Levels *p *p y+z VALUEint p+i VALUEptr Assume nomalized code: only *p, not **p. Distinguish between “ptr” and “valid-ptr”. &i VALUEvalid-ptr nodes

57 Determining Type-Safety Levels
x = y; Points-to Analysis Build Assignment Graph Compute Runtime Types Compute Type-Safety Levels x y = *p = 5.0; VALUEfloat = *p x = y + z; VALUEint = x edges

58 Determining Type-Safety Levels
T = “uninitialized” Points-to Analysis Build Assignment Graph Compute Runtime Types Compute Type-Safety Levels valid-pointer zero pointer int char float  |  = “multiply-typed”

59 Determining Type-Safety Levels
Points-to Analysis Build Assignment Graph Compute Runtime Types Compute Type-Safety Levels float VALUEfloat T(x) T(y) T(y) x y =

60 Determining Type-Safety Levels
Points-to Analysis Build Assignment Graph Compute Runtime Types Compute Type-Safety Levels T(*p) T(k) {k} p pt *p y = *p {k} p pt k T(k) T(y)

61 Determining Type-Safety Levels
Points-to Analysis Build Assignment Graph Compute Runtime Types Compute Type-Safety Levels runtime-type(x) static-type(x)  x unsafe int k; float f = 2.3; int *p = &f; k = *p; } runtime-type(k) = float k unsafe static-type(k) = int

62 Determining Type-Safety Levels
Points-to Analysis Build Assignment Graph Compute Runtime Types Compute Type-Safety Levels runtime-type(x) static-type(x)  x unsafe int k; float f = 2.3; int *p = &f; k = *p; } runtime-type(*p) = float *p unsafe static-type(*p) = int

63 Determining Type-Safety Levels
Points-to Analysis Build Assignment Graph Compute Runtime Types Compute Type-Safety Levels 2. runtime-type(p) ≠ valid-ptr  *p unsafe int *p = &k; p = p + 1; *p = 5;  *p unsafe

64 Determining Type-Safety Levels
Points-to Analysis Build Assignment Graph Compute Runtime Types Compute Type-Safety Levels 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

65 Type-Safety Levels: 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 >>

66 Type-Safety Levels: Example
int k; float f; int *p; f = 2.3; p = (int *)&f; k = *p; print_int( k ); VALfloat f = VALvalid-ptr p = *p k = {f} p pt 2. Assignment graph

67 Type-Safety Levels: Example
int k; float f; int *p; f = 2.3; p = (int *)&f; k = *p; print_int( k ); float float VALfloat f = valid-ptr valid-ptr VALvalid-ptr p = float float *p k = T(*p) T(f) {f} p pt 3. Runtime types

68 Type-Safety Levels: 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 unsafe k float unsafe *p {f} p pt 4. Type-safety levels

69 Type-Safety Levels: Example
int k; float f; int *p; f = 2.3; p = (int *)&f; k = *p; print_int( k ); tracked f safe p unsafe k unsafe *p Instrumentation: w/o using type-safety levels

70 Type-Safety Levels: Example
int k; float f; int *p; f = 2.3; p = (int *)&f; k = *p; print_int( k ); tracked f safe p unsafe k unsafe *p Instrumentation: using type-safety levels

71 Outline Introduction Security Tool Runtime Type-Checking Conclusion
Description Experiments Runtime Type-Checking Conclusion

72 Runtime Overhead Unoptimized (37x) Optimized (23x)

73 Outline Introduction Security Tool Runtime Type-Checking Conclusion
Description Experiments Runtime Type-Checking Flow-insensitive Analysis Flow-sensitive Analyses Results Conclusion Here’s an outline of my talk. I’ve given a brief introduction. I’ll now describe some related approaches, which will lead into a description of our approach. I’ll then give some experimental results before concluding.

74 Conclusion Two applications of runtime monitoring
Security tool: efficient Runtime type-checking: for debugging Effective in detecting errors Static analysis to improve runtime overhead Flow-insensitive: type-safety levels Flow-sensitive refinements

75 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

76 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)

77 Suan Hsi Yong University of Wisconsin – Madison
Efficient Runtime Monitoring of C Programs for Security and Correctness END Suan Hsi Yong University of Wisconsin – Madison Good Afternoon. My name is Suan Yong, and I’ll be presenting our work on “Protecting C Programs from Attacks via Invalid Pointer Dereferences”, which is joint work with Susan Horwitz at the University of Wisconsin – Madison.

78 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)(); So I’ll now demonstrate what our tool would do when running this program. Initially, the mirror of all memory is implicitly marked not-OK. This is how our tool works: by default a location is not-OK unless it is explicitly marked OK. p buf fp locations: untracked tracked pointers: unsafe safe p fp

79 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)(); In the first line, when fp is allocated, since fp is untracked, we leave the mirror of fp as ‘bad’. Note that this is OK because fp should never be accessed via an unsafe pointer dereference. fp p buf fp locations: untracked tracked pointers: unsafe safe p fp

80 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 - In the second line, when buf is allocated, since buf is tracked, we mark its mirror as OK. fp p buf fp locations: untracked tracked pointers: unsafe safe p fp

81 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 - Next, when p is allocated, since p is untracked, its mirror is left as not-OK. fp p buf fp locations: untracked tracked pointers: unsafe safe p fp

82 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 - The first time through the loop, since we’re writing into an unsafe pointer dereference *p, we check its mirror and find it to be OK. fp p buf fp locations: untracked tracked pointers: unsafe safe p fp

83 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 We increment p… fp p buf fp locations: untracked tracked pointers: unsafe safe p fp

84 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 … and again find *p to be marked OK. fp p buf fp locations: untracked tracked pointers: unsafe safe p fp

85 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 This process continues until p goes out of bounds and points to fp. Now, when trying to write to fp, we find that the mirror of *p is marked ‘bad’, so we flag this as an error, and halt the program, to prevent the potential exploit from doing damage. fp p buf fp locations: untracked tracked pointers: unsafe safe p fp

86 Back


Download ppt "Suan Hsi Yong University of Wisconsin – Madison"

Similar presentations


Ads by Google