1 relay: static race detection on millions of lines of code jan voung, ranjit jhala, and sorin...
Post on 21-Dec-2015
239 views
TRANSCRIPT
1
RELAY: Static Race Detectionon Millions of Lines of Code
Jan Voung, Ranjit Jhala, and Sorin LernerUC San Diego
speaker
2
Definition of data race
It is an event between 2 threads, where there are unordered accesses to the same memory location and at least one of the accesses is a write
…
temp = g;
…
…
g = V;
…
thread 1 thread 2
3
Bugs from races
Example bug: incorrect resource bounds
…
temp = size;
…
…; free();
size = size - n;
…
thread 1 thread 2
4
RELAY against data races
RELAY finds data races RELAY is a static tool:
analyzes the program before it runs
RELAY is scalable: analyzed the Linux kernel (4.5 million LOC) in 5 hours on 32 cpus
Found 53 races in a sample of 149 warnings
5
Introduction Computing locksets & recording accesses
Relative locksets Guarded accesses
Experiments with Linux Categorizing warnings: false positives Filters targeting categories
Outline
6
locks held
Checking with Locksets
lock(l);
temp = g;
unlock(l);
… read(g);
lock(l);
g = 0;
…
thread 1 thread 2
Locks are a mechanism for mutual exclusion Only one thread holds a particular lock at a time. No race if the same lock must have been acquired
for each of two different shared accesses
locks held
l l
common
l ll
7
A more realistic (but simple) example
read/write with lock
write without lock
work (void *d) {
o = d->priv;
lock(o->lock);
read_stats(o);
}
read_stats(x) {
if (x->f1++ < 10) {
unlock(x->lock);
x->stats = ...;
} else
unlock(x->lock);
}
No race
Race
8
Key components of RELAY
work (void *d) {
o = d->priv;
lock(o->lock);
read_stats(o);
}
read_stats(x) {
if (x->f1++ < 10) {
unlock(x->lock);
x->stats = ...;
} else
unlock(x->lock);
}
GOAL: scalabilityKEY: modularity
work ( ){ … }
read_stats ( ){…}
read_stats
work
9
L+ = {}, L- = {}
Key components of RELAY
work (void *d) {
o = d->priv;
lock(o->lock);
read_stats(o);
}
read_stats(x) {
if (x->f1++ < 10) {
unlock(x->lock);
x->stats = ...;
} else
unlock(x->lock);
}
1) Relative locksets: locks acq./rel. in function – caller handles locks before
2) Guarded accesses: pair accesses with relative
locksets to catch races
L+ = {}, L- = {x->lock}
L+: MUST have been acq.L-: MAY have been rel.
L+ = {d->priv->lock}, L- = {}
L+ = {}, L- = {x->lock} 3) Summaries
4) Symbolic execution: what is the “same” memory location?
Normalize to globals and formals.
10
How RELAY runs
work (void *d) {work (void *d) {
o = d->priv;o = d->priv;
lock(o->lock);lock(o->lock);
read_stats(o);read_stats(o);
}}
read_stats(x) {
if (x->f1++ < 10) {
unlock(x->lock);
x->stats = ...;
} else
unlock(x->lock);
}
L+ = {}, L- = {}
L+ = {}, L- = {x->lock}
L+ = {}, L- = {x->lock} x->stats: L+ = {}, L- = {x->lock}
x->f1: L+ = {}, L- = {}
L+ = {}, L- = {x->lock}
summary:
1) Assume symbolic execution ran.
2) Compute relative locksets
3) Compute guarded accesses
x->stats: L+ = {}, L- = {x->lock}
x->f1: L+ = {}, L- = {}
11
x->stats: L+ = {}, L- = {x->lock}
How RELAY runs
work (void *d) {work (void *d) {
o = d->priv;o = d->priv;
lock(o->lock);lock(o->lock);
read_stats(o);read_stats(o);
}}
read_stats(x)
x->stats: L+ = {}, L- = {x->lock}
x->f1: L+ = {}, L- = {}
L+ = {}, L- = {x->lock}
summary:x->f1: L+ = {}, L- = {}
L+ = {}, L- = {x->lock}
summary:
12
x->stats: L+ = {}, L- = {x->lock}
Applying summaries
work (void *d) {
o = d->priv;
lock(o->lock);
read_stats(o);
}
read_stats(x)
L+ = {d->priv->lock}, L- = {}
L+ = {}, L- = {}
x->f1: L+ = {}, L- = {}
L+ = {}, L- = {x->lock}
summary:
L+ = {}, L- = {d->priv->lock}
L+ = {}, L- = {d->priv->lock}
summary:
BEFORE
DIFFERENCE
AFTER
13
Applying summaries
work (void *d) {
o = d->priv;
lock(o->lock);
read_stats(o);
}
read_stats(x) {
L+ = {d->priv->lock}, L- = {}
L+ = {}, L- = {}
L+ = {}, L- = {d->priv->lock}
d->priv->stats: L+ = {}, L- = {d->priv->lock}
d->priv->f1: L+ = {d->priv->lock}, L- = {}
L+ = {}, L- = {d->priv->lock}
d->priv: L+ = {}, L- = {}
summary:
x->stats: L+ = {}, L- = {x->lock}
x->f1: L+ = {}, L- = {}
L+ = {}, L- = {x->lock}
summary:
L+ = {d->priv->lock}, L- = {}
d->priv->f1: L+ = {d->priv->lock}, L- = {}
BEFORE
DIFFERENCE
AFTER
14
Checking for Races
d->priv->stats: L+ = {}, L- = {d->priv->lock}
d->priv->f1: L+ = {d->priv->lock}, L- = {}
L+ = {}, L- = {d->priv->lock}
d->priv: L+ = {}, L- = {}
summary:
work (void *d) {
o = d->priv;
lock(o->lock);
read_stats(o);
}
L+ = {d->priv->lock}, L- = {}
L+ = {}, L- = {}
L+ = {}, L- = {d->priv->lock}
L+ = {d->priv->lock}, L- = {}
work (void *d)
d->priv->stats: L+ = {}, L- = {d->priv->lock}
d->priv->f1: L+ = {d->priv->lock}, L- = {}
d->priv: L+ = {}, L- = {}
summary:
d->priv->stats: L+ = {}, L- = {d->priv->lock}
d->priv->f1: L+ = {d->priv->lock}, L- = {}
d->priv: L+ = {}, L- = {}
summary:
row 1: reads only => no racerow 2: common lock => no racerow 3: no common lock => race
15
Modular Unsoundness
Pointer-arithmetic corner cases Accesses in assembly code Function pointers Not enforcing must-alias for lockset intersection Filters
Revisit eachand improve
16
Introduction Computing locksets & recording accesses
Relative locksets Guarded accesses
Experiments with Linux Categorizing warnings: false positives Filters targeting categories
Outline
17
Linux experiments
5000+ warnings Sample 90 and categorize Design and apply filters to zoom-in on races
18
Categories of false positives
Initialization: thread allocates object and initializes it before sharing
Aliasing: mixed up different data structures
Unsharing: objects removed from shared structures
Recursive locks: “Big kernel lock”
Non-lock synchronization: spawn, wait, signal, etc.
Conditional locking: locking correlated with return value, conditionals, etc.
19
Example filter: Thread “ownership”
To reduce initialization false positives: remove accesses originating from the thread that
allocated the object.
x = malloc()init(x)share(x)update(x)
x = get()update(x)
x = get()update(x)
thread 1 thread 2 thread 3
filtered
23
Related Work
Dynamic techniques Locksets and extensions [Savage et al. 97, Choi et al. 02,
Yu et al. 05, Elmas et al. 07] Atomicity [Flanagan et al. 04, Wang et al. 06] Benign vs. harmful [Narayanasamy et al. 07]
Static techniques for Java Type systems [Flanagan et al. 99, Boyapati et al. 02] Aliasing, must-not aliasing [Naik et al. 06, 07]
Static techniques for C Scalability, ranking [Engler et al. 03] Aliasing and sharing [Pratikakis et al. 06, Kahlon et al. 07]
24
Summary
Relative locksets: Modular summary-based analysis
Can analyze 46K functions of Linux kernel modular => parallelizable on a grid of 32 cpus: approx. 5 hours
Modular unsoundness finds 53 races (or 25 after all filters) future work: better analyses, better filters
whether races are benign or not, is another question!