Debugging via Run-Time Type Checking Title Page Alexey Loginov, Suan Yong, Susan Horwitz, Thomas...

Post on 19-Jan-2016

225 views 0 download

Tags:

Transcript of Debugging via Run-Time Type Checking Title Page Alexey Loginov, Suan Yong, Susan Horwitz, Thomas...

Debuggingvia

Run-TimeType Checking

Title Page

Alexey Loginov, Suan Yong,Susan Horwitz, Thomas Reps

University of Wisconsin - Madison

Overview 1 - C

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

Run-Time Type Checking

Overview 2 - goal

• Goal: help programmers find these bugs via run-time type checking– flag errors

– track down their cause(fault localization)

Run-Time Type Checking

Overview - Approach

• 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

Approach: Track Types Dynamically

Tracking run-time types: Use

• 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

Tracking run-time types: Assignment

• 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

Tracking Run-Time Types

Purify, Safe C ...

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

Where we fit in

• Introduction• Examples• Implementation• Results from Initial Tests• Future Work• Conclusion

Talk Overview

union U { int u1; int *u2; } u;

int *p;

u.u1 = 8;

p = u.u2;

*p = 0;

(0x08)

(u)(p)

(tags):

::

::

unalloc

uninituninit

Ex1: Unions 1

Example 1: Unions

(memory):

::

::

FE232F10

00000000

00000000

int00000008

union U { int u1; int *u2; } u;

int *p;

u.u1 = 8;

p = u.u2;

*p = 0;

(0x08)

(u)(p)

(tags):

::

::

unalloc

uninituninit

Ex1: Unions 2

Example 1: Unions

(memory):

::

::

FE232F10

00000008

00000000

int00000008 int

warning

union U { int u1; int *u2; } u;

int *p;

u.u1 = 8;

p = u.u2;

*p = 0;

(0x08)

(u)(p)

(tags):

::

::

unalloc

uninituninit

Ex1: Unions 3

Example 1: Unions

(memory):

::

::

FE232F10

00000008

00000008

intint

error!

union U { int u1; int *u2; } u;

int *p;

u.u1 = 8;

p = u.u2;

*p = 0;

(0x08)

(u)(p)

(tags):

::

::

unalloc

uninituninit

Ex1: Unions 4

Example 1: Unions

(memory):

::

::

FE232F10

00000008

00000008

intint

error!

Ex 2: Array (heap) 1

int *intArray = (int *)malloc(15 * sizeof(int));

int **ptrArray = (int **)malloc(15 * sizeof(int *));

Example 2: Bad Pointer Access

intArray ptrArray memory

Ex 2: Array (heap) 2

int *i, sumEven = 0;for(i = intArray; ...; i += 2)sumEven += *i;

DT

C

iintArray ptrArray

intArray ptrArraypadding

PU

RIF

Y

i

memory

Example 2: Bad Pointer Access

Ex 3: Array (stack) 1

Example 3: Bad Pointer Access (stack)

int intArray[10];int *ptrArray[10];for(i = intArray; ...; i += 2) ...

intArray ptrArray stack

PU

RIF

YD

TC

intArray ptrArray stack

i

i

Ex 4: Sim Inherit 1

struct Sub { int b1; int b2; char b3;} sub;

struct Sup { int a1; int a2;} sup;

Example 4: Simulated Inheritance

a1a2

intint

b1b2b3

intintchar

subsup

Ex 4: Sim Inherit 2

Example 4: Simulated Inheritance

a1a2

intint

b1b2b3

intintchar

subsup

void f(struct Sup *s)

{

printInt(s->a1);

printInt(s->a2);

}

f(&sup);

s

f(&sub);

s

Ex 4: Sim Inherit 3

struct Sub { int b1; float f1; int b2; char b3;} sub;

struct Sup { int a1; int a2;} sup;

Example 4: Simulated Inheritance

a1a2

intint

b1f1b2b3

floatintchar

int

Ex 4: Sim Inherit 4

Example 4: Simulated Inheritance

a1a2

intint

b1f1b2b3

floatintchar

int

void f(struct Sup *s)

{

printInt(s->a1);

printInt(s->a2);

}

f(&sub);

s

f(&sup);

serror!

• Introduction• Examples• Implementation• Results from Initial Tests• Future Work• Conclusion

Talk Overview

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

Overview of Tool

Impl: Mirror and Tags

0 1 00 0 1 01

continuation bits

type bits:{unalloc, uninit,

integral, real, pointer}

size bits (log2)

00 0 01 0 0 01

(currently unused bits)

US

ER

ME

MM

IRR

OR

Mirror and Tags

Impl: Tags (examples)

integral0 integral0

integral0 size=21 0

real0 size=41 00 0 01 0 0 01

uninit0 unalloc0 0

integral0 0

char

short

float

char [3] (third element uninitialized)

Mirror and Tags

Source Level X-lation: C to C

• 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

Source Level Translation

Instr Example: x = *p (1)

x = *p

*(tmp2 = &x)

*

*(tmp3 = (verifyTag(&p, ptr_type), p), verifyPtr(tmp3, sizeof(int)), tmp3)

(verifyTag(&p, ptr_type), p)

int x;int *p;

Instr Example: x = *p (2)

*(tmp2 = &x)

*(tmp3 = (verifyTag(&p, ptr_type), p), verifyPtr(tmp3, sizeof(int)), tmp3)

*(tmp2 = &x) = *(tmp3 = (verifyTag(&p, ptr_type), p), verifyPtr(tmp3, sizeof(int)), tmp3)

x = *p

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)

• Introduction• Examples• Implementation• Results from Initial Tests• Future Work• Conclusion

Talk Overview

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

Effectiveness of Tool

Sample bug descriptions

• 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

Sample Bugs and Errors Reported

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

Alterations in Behavior

Performance – by slowdown (table)

programlines of C code

uninstr-umente

instru-mented slowdown

ul 468 0.33 1.87 5.6plot 326 1.02 5.90 5.8tsp 567 12.78 83.64 6.5units 457 0.39 3.19 8.2bisort 570 7.60 70.86 9.3col 502 1.47 29.39 20.0perimeter 389 2.22 49.13 22.1mst 493 3.25 83.10 25.6compress 1,491 19.87 695.83 35.0go 26,917 12.04 654.86 54.4li 6,272 5.47 320.99 58.7nroff 11,018 0.82 53.01 64.6bh 1,049 8.97 910.02 101.4gcc 151,531 11.08 1288.64 116.3vortex 52,624 12.37 1596.02 129.1

Performancerun-time (sec)

Solaris utils

Olden

SPECint95

Mean =43.8

Median =23.9

Areas for Improvement: overhead, spurious errors

• 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[])

Areas for Improvement

Optimization: no intervening write

• Remove redundant checks– when there are no intervening writes

(of a different type) to a given location

int x, y, z;

if (x < 0){

y = (x > 5) ? x : 0;

}

x += 10;

Planned Optimizations

Optimization: static analysis

• 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

Planned Optimizations

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