360 likes | 480 Vues
This paper presents an innovative approach to debugging C programs through run-time type checking, aimed at helping programmers identify common errors resulting from weak static typing. By dynamically tracking types and verifying them during memory operations, the methodology flags type violations and supports fault localization. Examples illustrate how erroneous operations (such as incorrect pointer use and unions) can be detected, significantly improving the reliability of C applications. Initial testing results showcase the effectiveness of this technique in addressing logical errors and enhancing developer productivity.
E N D
Title Page Debugging via Run-Time Type Checking Alexey Loginov, Suan Yong, Susan Horwitz, Thomas Reps University of Wisconsin - Madison
Overview 1 - C Run-Time Type Checking • C: weak static typing permits bugs • unions (write one field, read another) • casting (pointer with non-pointer value, pointer points to object with wrong type) • pointer arithmetic (array out-of-bounds)
Overview 2 - goal Run-Time Type Checking • Goal: help programmers find these bugs via run-time type checking • flag errors • track down their cause(fault localization)
Overview - Approach Approach: Track Types Dynamically • Tag each memory location with its dynamic type • in ‘mirror’ of memory • On use, verify that current dynamic type is appropriate • On assignment, verify that assigned type is consistent with static type
Tracking run-time types: Use Tracking Run-Time Types • Use of a location: dynamic type verified • wrong type: generate error message • Example uses: • int *p; • *p + 5; => error if p not a pointer => error if *p not an integer
Tracking run-time types: Assignment Tracking Run-Time Types • Assignment: dynamic type propagated. • different type: generate warning message • helpful in tracking down root cause • Example assignment: • int x, *q; • x = *q; => tag of x set to tag of *q => warn if *q not an integer
Purify, Safe C ... Where we fit in • Purify [Hastings+] • array out-of-bounds, bad pointer dereferences, use of uninitialized data • memory leaks • Safe C [Austin+] • stale pointer uses, unions • Our Approach • run-time type violations • some ability to track down logical errors(not just flag symptoms)
Talk Overview • Introduction • Examples • Implementation • Results from Initial Tests • Future Work • Conclusion
Ex1: Unions 1 (memory) (tags) : : FE232F10 unalloc : : : : 00000000 uninit 00000000 uninit : : : : Example 1: Unions union U { int u1; int *u2; } u; int *p; u.u1 = 8; p = u.u2; *p = 0; (0x08) (u) 00000008 int (p)
Ex1: Unions 2 (memory) (tags) : : FE232F10 unalloc : : : : 00000008 uninit 00000000 uninit : : : : warning Example 1: Unions union U { int u1; int *u2; } u; int *p; u.u1 = 8; p = u.u2; *p = 0; (0x08) (u) int (p) 00000008 int
Ex1: Unions 3 (memory) (tags) : : FE232F10 unalloc : : : : error! 00000008 uninit 00000008 uninit : : : : Example 1: Unions union U { int u1; int *u2; } u; int *p; u.u1 = 8; p = u.u2; *p = 0; (0x08) (u) int (p) int
Ex1: Unions 4 (memory) (tags) : : FE232F10 unalloc : : : : error! 00000008 uninit 00000008 uninit : : : : Example 1: Unions union U { int u1; int *u2; } u; int *p; u.u1 = 8; p = u.u2; *p = 0; (0x08) (u) int (p) int
Ex 2: Array (heap) 1 Example 2: Bad Pointer Access int *intArray = (int *) malloc(15 * sizeof(int)); int **ptrArray = (int **) malloc(15 * sizeof(int *)); memory intArray ptrArray
Ex 2: Array (heap) 2 i intArray ptrArray padding PURIFY Example 2: Bad Pointer Access int *i, sumEven = 0; for(i = intArray; ...; i += 2)sumEven += *i; i memory intArray ptrArray DTC
Ex 3: Array (stack) 1 i i Example 3: Bad Pointer Access (stack) int intArray[10]; int *ptrArray[10]; for(i = intArray; ...; i += 2) ... stack intArray ptrArray DTC stack intArray ptrArray PURIFY
Ex 4: Sim Inherit 1 Example 4: Simulated Inheritance struct Sup { int a1; int a2; } sup; struct Sub { int b1; int b2; char b3; } sub; sup sub a1 a2 b1 b2 b3 int int int int char
Ex 4: Sim Inherit 2 s s f(&sup); f(&sub); Example 4: Simulated Inheritance void f(struct Sup *s) { printInt(s->a1); printInt(s->a2); } sup sub a1 a2 b1 b2 b3 int int int int char
Ex 4: Sim Inherit 3 Example 4: Simulated Inheritance struct Sub { int b1; float f1; int b2; char b3; } sub; struct Sup { int a1; int a2; } sup; a1 a2 b1 f1 b2 b3 int int int float int char
Ex 4: Sim Inherit 4 s s error! f(&sup); f(&sub); Example 4: Simulated Inheritance void f(struct Sup *s) { printInt(s->a1); printInt(s->a2); } a1 a2 b1 f1 b2 b3 int int int float int char
Talk Overview • Introduction • Examples • Implementation • Results from Initial Tests • Future Work • Conclusion
Overview of Tool Overview of Tool • Instrumentation • preprocessed source file instrumented C file • Compilation • Execution • tracks types in “mirror” of memory • writes error/warning messages, sends signal • signal can be intercepted by GDB for further debugging
Impl: Mirror and Tags 0 0 1 0 1 1 1 0 0 0 0 0 0 0 1 0 Mirror and Tags USER MEM MIRROR continuation bits (currently unused bits) 0 type bits: {unalloc, uninit, integral, real, pointer} size bits (log2)
Impl: Tags (examples) 0 0 0 0 0 integral uninit integral integral real 1 1 0 1 0 1 integral size=2 0 0 0 0 0 0 unalloc size=4 Mirror and Tags char 0 short 0 float 0 char [3](third element uninitialized) 0
Source Level X-lation: C to C Source Level Translation • C source C source • Using Lucent’s Ckit front end • syntax-directed transformations • instruments statements and expressions to set, verify, and propagate tags • preserves expression values, types, side-effects • makes extensive use of “comma” operator • introduces many temporary variables • separate (partial) instrumentation
Instr Example: x = *p (1) *(tmp2 = &x) * (verifyTag(&p, ptr_type), p) *(tmp3 =(verifyTag(&p, ptr_type), p), verifyPtr(tmp3, sizeof(int)), tmp3) int x; int *p; x = *p
Instr Example: x = *p (2) *(tmp2 = &x) = *(tmp3 =(verifyTag(&p, ptr_type), p), verifyPtr(tmp3, sizeof(int)), tmp3) *(tmp2 = &x) x = *p *(tmp3 =(verifyTag(&p, ptr_type), p), verifyPtr(tmp3, sizeof(int)), tmp3)
Instr Example: x = *p (3) x = *p *(tmp2 = &x) = *(tmp3 =(verifyTag(&p, ptr_type), p), verifyPtr(tmp3, sizeof(int)), tmp3) (tmp1 = copyTag(tmp2, tmp3, int_type), tmp1)
Talk Overview • Introduction • Examples • Implementation • Results from Initial Tests • Future Work • Conclusion
Effectiveness of Tool Effectiveness of Tool • Bugs identified in: • Solaris utilities (nroff, plot, ul, ...) • Olden benchmarks (health, voronoi) • Output usually succinct • Error messages pinpoint bug symptoms • Warning messages help track down logical error
Sample bug descriptions Sample Bugs and Errors Reported • stray pointer corrupts return address on stack • error: pointer dereference incorrectly typed • stray pointer corrupts _iob array (stdin, stdout, stderr) • error: referencing unallocated memory • treats malloc’ed memory as zero-initialized • error: use of uninitialized memory
Alterations in Behavior Alterations in Behavior • Hard to preserve behavior of non-portable programs • Purify affects go, nroff, others • modified memory layout • Our tool affects ul, units, col • local variable addition • register variable demotion • Behavior sometimes altered but cause of error is the same
Performance – by slowdown (table) Performance run-time (sec) Mean = 43.8 Median = 23.9 Solaris utils Olden SPECint95
Areas for Improvement: overhead, spurious errors Areas for Improvement • Overhead • current slowdown: 6x - 130x • can improve instrumentation and macro definitions • no attempt yet made to identify and remove unnecessary checks • Spurious errors and warnings • abundant in a few benchmarks • due to memset, calloc, etc. • incomplete extern types (__ctype[])
Optimization: no intervening write Planned Optimizations • Remove redundant checks • when there are no intervening writes (of a different type) to a given location x += 10; int x, y, z; if (x < 0){ y = (x > 5) ? x : 0; }
Optimization: static analysis Planned Optimizations • Remove redundant instrumentation • use static analysis • to find variables that need no instrumentation • i.e. variables that can be statically determined to be type-safe • has potential to drastically reduce overhead
Conclusion Conclusion • Run-time type checking works • identified pointer and array access errors • warnings help find root cause of error • Potential for finding subtler type-bugs • no “real” examples found in preliminary testing • Slowdown is relatively high • potential for significant speedup untapped