Static Detection of Eventbased Races in Android App
Static Detection of Event-based Races in Android App Yongjian Hu Univ. of California, Riverside Iulian Neamtiu NJIT
Motivation • Rise of Event-Driven Systems Mobile apps Web apps Data centers • Concurrency is a Serious Issue • 66% of high-severity Android bugs are due to concurrency [Zhou et al. , EASE’ 15] • 84% of Android race bugs are event-driven races [Bielik et al. , OOPSLA’ 15; Hsial et al. PLDI’ 14; Maiya et al. , PLDI’ 14]
Concurrency Error Example not ordered class Main. Activity { Data. Base m. DB; Broadcast. Receiver recv = new Broadcast. Receiver() { void on. Receive(Context ctx, Intent i) { Bundle b = i. get. Extras(); m. DB. update(b); } } void on. Create(. . . ) { m. DB = new Data. Base(); register. Receiver(recv, . . . ); } void on. Start() { m. DB. open(); } void on. Stop() { m. DB. close(); } void on. Destroy() { unregister. Receiver(recv); m. DB = null; }} Broadcast Receiver Main Thread regis ter on. Create() on. Start() update on. Receive() on. Stop() er t s i reg un on. Destroy() update() before close() OK
Concurrency Error Example not ordered class Main. Activity { Data. Base m. DB; Broadcast. Receiver recv = new Broadcast. Receiver() { void on. Receive(Context ctx, Intent i) { Bundle b = i. get. Extras(); m. DB. update(b); } } void on. Create(. . . ) { m. DB = new Data. Base(); register. Receiver(recv, . . . ); } void on. Start() { m. DB. open(); } void on. Stop() { m. DB. close(); } void on. Destroy() { unregister. Receiver(recv); m. DB = null; }} Broadcast Receiver Main Thread regis ter on. Create() on. Start() on. Stop() te a upd on. Receive() er t s i reg un on. Destroy() update() after close() Exception!
State-of-the-art in (Android) Race Detection • Prior Android race detectors: all are dynamic • Droid. Racer [Maiya et al. , PLDI’ 14]; CAFA [Hsiao et al. , PLDI’ 14]; Event. Racer [Bielik et al. , OOPSLA’ 15] • Limitations • Coverage issues → False negatives; “no run - no see” • False positives (imprecise Android modeling) • Static race detection is hard [Hong and Kim, STVR’ 15] • Only 7 out of 43 detectors are static • “the accuracy of [static] models is often low because of the imprecision inherent to static analysis methods”
Our Approach • SIERRA: the first static, sound event-driven race detector for Android apps • Highly effective • Finds 29 true races per app vs. 4 (best dynamic detector) • Efficient • Analyzes an app in 31 minutes on average
SIERRA: Stat. Ic Event-based Race detecto. R for Android App Analysis booster Hybrid Context Selector PA Refiner Call Graph Builder Pointer Analysis WALA Race Reporting Race Refinement Building HB Graph Static HB Graph Path/Context Sensitivity Refutation THRESHER Race <rw 1, rw 1’> Race <rw 2, rw 2’> Race Prioritization . . . Race Reports
Event Race Definition • Action = context-sensitive event handling • Novel abstraction, reifies • Callbacks (lifecycle, GUI callbacks, etc. ) • Thread, Async. Task, Executor • Handler’s Send. Message and Post. Runnable • Happens-before (HB): A 1 ≺ A 2 • A 1 is completed before A 2 • Event race = “racy” pair of memory accesses • • Access same location: α(x) = α(y) At least one access is write: α(x): Write � α(y): Write Access from two actions: θ(x) = A 1 � θ(y) = A 2 � A 1 ≠ A 2 No HB between two actions: A 1 ⊀ A 2 � A 2 ⊀ A 1
“Boosting” Static Analysis with Fixpointbased Callgraph Construction • Manifest. xml lists all the activities on. Create on. Item. Click thread on. Start Runnable 1 on. Long. Press … … on. Resume on. Click Async. Task Runnable 2 … … • For each activity: 1. Find lifecycle callbacks, e. g. , on. Create, on. Start 2. Find XML registered callbacks, e. g. , on. Click 3. Find system callbacks, e. g. , on. Low. Memory 4. Build call graph for found actions a) Find add to work list registered UI callbacks, e. g. , on. Item. Click, on. Long. Press b) Added to work list if new actions found 5. Go back to step 4. Iterate until fixpoint on. Post Execution on. Create Options. Menu on. Low Memory msg 1. handle. Message
Action Sensitivity: When Context Sensitivity is Insufficient • Hybrid context sensitivity [Kastrinis et al. , PLDI’ 13] • K object sensitivity for dispatch invocations • K call-site sensitivity for static invocations • a Action 2 Action 1 c 1: Base 64. encode. Bytes (byte[] source) c 1’: Base 64. encode. Bytes (byte[] source) K=2 c 2: Base 64. encode. Bytes (byte[] source, int offset, int length, int options) c 3: Base 64. encode. Bytes. To. Bytes(byte[] source, int offset, int length, int options) { …… os = new Output. Stream(…); os. write(…); } Points-to(<[c 2: : c 3], os>) = {<[c 2: : c 3], new Output. Stream>} Event 1: WRITE: <[c 2: : c 3], os> READ: <[c 2: : c 3], os> Points-to(<[c 2: : c 3], os>) = {<[c 2: : c 3], new Output. Stream>} Event 2: WRITE: <[c 2: : c 3], os> READ: <[c 2: : c 3], os> Conflated → Imprecise → False positive
Action Sensitivity: When Context Sensitivity is Insufficient • Hybrid context sensitivity [Kastrinis et al. , PLDI’ 13] • K object sensitivity for dispatch invocations • K call-site sensitivity for static invocations • We add Action sensitivity Action 1 c 1: Base 64. encode. Bytes (byte[] source) c 2: Base 64. encode. Bytes (byte[] source, int offset, int length, int options) c 3: Base 64. encode. Bytes. To. Bytes(byte[] source, int offset, int length, int options) { …… os = new Output. Stream(…); os. write(…); } Points-to (<[c 2: : c 3@a 1], os>) = {<[c 2: : c 3@a 1], new Output. Stream>} Event 1: WRITE: <[c 2: : c 3@a 1], os> READ: <[c 2: : c 3@a 1], os> Action 2 No race c 1’: Base 64. encode. Bytes (byte[] source) c 2: Base 64. encode. Bytes (byte[] source, int offset, int length, int options) c 3: Base 64. encode. Bytes. To. Bytes(byte[] source, int offset, int length, int options) { …… os = new Output. Stream(…); os. write(…); } Points-to(<[c 2: : c 3@a 2], os>) = {<[c 2: : c 3@a 2], new Output. Stream>} Event 2: WRITE: <[c 2: : c 3@a 2], os> READ: <[c 2: : c 3@a 2], os>
≺ on me ≺o n. C lic k 1 on. Resume() on. R esu 3 Re su m e * ck 2 Cli on. Click 1() on. Click 2() ≺o n. C lick on on. Click 3() on 1. Action invocation rule (sender ≺ receiver) 2. Component lifecycle rule 3. GUI layout/object order 4. Intra-procedural domination 5. Intra-action, inter-procedural domination 6. Inter-action transitivity 7. Transitivity Clic k 2 Happens-before Rules on. Pause() Method M e 1 Δe 1 <dom e 2 A 1 Δe 2 A 1≺A 2
Symbolic Execution-based Refutation • So far, the analysis is… • Context sensitive • Field sensitive • Path insensitive • …and discovers 22% of all possible actions orders • Which is not bad! • Next, we use symbolic execution-based refutation to further order accesses • Bonus: on-demand path sensitivity!
Open. Sudoku app Example Path void stop(){// action B if (m. Is. Running) { m. Is. Running = false; m. Accum. Time=. . . // �� B } } Method entry if (m. Is. Running) m. Accum. Time = … Backward symbolic execution Timer. Runnable runner = { void run() {//action A if (m. Is. Running) { m. Accum. Time=. . . // �� A if (*) {. . . post. Delayed(runner, . . . ); } else m. Is. Running=false; } }} Constraints m. Is. Running = false if (*) Post Delayed m. Is. Running = false Method exit m. Is. Running = true if (m. Is. Running) true m. Is. Running = false m. Accum. Time = … Can �� A occur before �� B? Yes!
Example Path Timer. Runnable runner = { void run() {//action A if (m. Is. Running) { m. Accum. Time=. . . // �� A if (*) {. . . post. Delayed(runner, . . . ); } else m. Is. Running=false; } }} Method entry m. Is. Running = false if (m. Is. Running) true Backward symbolic execution void stop(){// action B if (m. Is. Running) { m. Is. Running = false; m. Accum. Time=. . . // �� B } } Constraints m. Is. Running = false m. Accum. Time = … m. Is. Running = false Method exit m. Is. Running = true if (m. Is. Running) m. Accum. Time = … Race Refuted! Can �� B occur before �� A? No!
Evaluation Effectiveness: races found App Installs (millions) Actions HB edges Racy pairs After action sensitivity After refutation True races False positives Event Racer Barcode Scanner > 100 136 2, 756 (30%) 64 24 15 11 4 7 VLC > 100 151 2, 349 (20%) 202 78 35 32 3 0 > 10 259 4, 710 (14%) 836 285 106 93 13 5 17 1 FB Reader ≫ K-9 > 5 312 5, 725 (12%) 1, 347 370 89 72 NPR > 1 490 10, 673 (9%) 607 132 21 21 0 3 160 2, 755 (22%) 431 80 33 29 4 4 Across 20 apps Best prior work (dynamic) Efficiency (median time, in seconds) Basic static analysis (CG, PA) 1, 310 HB construction 29 Symbolic Execution Total 560 1, 899 Dataset: 194 open-source apps; 20 analyzed manually Limitation: bound #paths to 5, 000 while still sound (report race, might be false positives)
Conclusions • Event-based races: most prevalent concurrency errors in Android • Prior approaches: all dynamic • Low coverage, false negatives, false positives • Our approach • • • First fully static event-based race detector Novel action sensitive context abstraction Static happens-before relation Static backward symbolic execution to refute false races Highly effective; efficient
Backup
Context Sensitivity(cont’d) • Object insensitive = imprecision void on. Touch. Event(Motion. Event e 1, Motion. Event e 2) { …… if (e 1. get. X() – e 2. get. X() > 120. 0 f …) { Message msg = new Message(); msg. what = FLING; …… ED G this. handler. send. Message(msg); R E } else { M Message msg = new Message(); msg. what = LONG_PRESS; …… this. handler. send. Message(msg); } } O 1: Handler handler = new Handler() { public void handle. Message(Message msg) { …… switch (msg. what) { case FLING: …… case LONG_PRESS: …… } } }
Context Sensitivity(cont’d) • Object sensitivity in SIERRA = precision void on. Touch. Event(Motion. Event e 1, Motion. Event e 2) { …… 1> if (e 1. get. X() – e 2. get. X() > 120. 0 f …) { o h, c < : Message msg = new Message(); x ct msg. what = FLING; …… this. handler. send. Message(msg); C 1: } else { Message msg = new Message(); msg. what = LONG_PRESS; …… this. handler. send. Message(msg); ctx: <o C 2: } h , c 2> } K-obj + 1 -call-site for event posting and message passing Oh: Handler handler = new Handler() { public void handle. Message(Message msg) { …… switch (msg. what) { case FLING: …… case LONG_PRESS: …… } } }
Happens-before Rules • Rule 1: Action invocation rule • The sender action happens-before the recipient • Rule 2: Component lifecycle rule • Activity. on. Create < Activity. on. Start • Activity. on. Start < Activity. on. Stop • …… • Rule 3: GUI layout/object order • Activity. on. Resume < View. on. Click < Activity. on. Pause
Happens-before Rules (cont’d) • Method M e 1 Δe 1 <dom e 2 A 1 <HB Δe 2 A 2
Context Sensitivity(cont’d) • Object insensitive = imprecision void on. Touch. Event(Motion. Event e 1, Motion. Event e 2) { …… if (e 1. get. X() – e 2. get. X() > 120. 0 f …) { Message msg = new Message(); msg. what = FLING; …… ED G this. handler. send. Message(msg); R E } else { M Message msg = new Message(); msg. what = LONG_PRESS; …… this. handler. send. Message(msg); } } O 1: Handler handler = new Handler() { public void handle. Message(Message msg) { …… switch (msg. what) { case FLING: …… case LONG_PRESS: …… } } }
Context Sensitivity(cont’d) • Object sensitivity in SIERRA = precision void on. Touch. Event(Motion. Event e 1, Motion. Event e 2) { …… 1> if (e 1. get. X() – e 2. get. X() > 120. 0 f …) { o h, c < : Message msg = new Message(); x ct msg. what = FLING; …… this. handler. send. Message(msg); C 1: } else { Message msg = new Message(); msg. what = LONG_PRESS; …… this. handler. send. Message(msg); ctx: <o C 2: } h , c 2> } K-obj + 1 -call-site for event posting and message passing Oh: Handler handler = new Handler() { public void handle. Message(Message msg) { …… switch (msg. what) { case FLING: …… case LONG_PRESS: …… } } }
Happens-before Rules • Rule 1: Action invocation rule • The sender action happens-before the recipient • Rule 2: Component lifecycle rule • Activity. on. Create < Activity. on. Start • Activity. on. Start < Activity. on. Stop • …… • Rule 3: GUI layout/object order • Activity. on. Resume < View. on. Click < Activity. on. Pause
Happens-before Rules (cont’d) • Method M e 1 Δe 1 <dom e 2 A 1 <HB Δe 2 A 2
Concurrency Error, Example 1 • Intra-component Race Image overridden!
Concurrency Error, Example 2 • Inter-component Race update() before close(): OK
Concurrency Error, Example 2 • Inter-component Race update() after close(): Exception!
“Boosting” Static Analysis with Fixpoint-based Callgraph Construction • Manifest. xml lists all the activities on. Create thread on. Start on. Resume on. Click Async. Task Runnable 2 … … a) Find add to work list registered UI callbacks, e. g. , on. Item. Click, on. Long. Press b) Added to work list if new actions found on. Post Execution on. Create Options. Menu 5. Go back to step 4. Iterate until fixpoint on. Create Runnable 1 on. Long. Press … … • For each activity: 1. Find lifecycle callbacks, e. g. , on. Create, on. Start 2. Find XML registered callbacks, e. g. , on. Click 3. Find system callbacks, e. g. , on. Low. Memory 4. Build call graph for found actions on. Item. Click on. Low Memory msg 1. handle. Message Prior Android static analyses: imprecise on. Start * on. Resume Async. Task on. Item. Click on. Low Memory on. Create Options. Menu msg 1. handle. Message on. Post Execution * on. Click thread on. Long. Press Runnable 2 Runnable 1
- Slides: 30