Automated Concolic Testing of Smartphone Apps Saswat Anand Mayur Naik Stanford Univ. Georgia Tech. Hongseok Yang Mary Jean Harrold Univ. of Oxford Georgia Tech. Motivation Motivation Problems with Smartphone Apps Problem Automatically generate test inputs for bounded exhaustive testing of smartphone apps Test Inputs for Apps • • • Whole-program testing Test input is a sequence of events e1, e2…, en Types of events: a tap on the screen, change in geo-location, arrival of a SMS message, etc. Bounded Exhaustive Testing of Apps S, the set of all event sequences* s.t. each sequence takes a unique path Goal: cover these Set of covered branches *of bounded-length Two subproblems 1. Generate individual events 2. Generate sequences of events Generating Individual Events • An event is associated with data X & Y coordinates of a tap event o geo-location of a change-in-geo-location event o content of an incoming SMS event o etc. o • Data determine which program path is taken Challenge: Generate the “right” data for events Example: Music Player App Play Pause Rewind Skip Stop Eject Example: Music Player App tap(136, 351) public void onClick(View target) { if (target == play) startService(new Intent(ACTION_PLAY)); else if (target == pause) startService(new Intent(ACTION_PAUSE)); else if (target == skip) startService(new Intent(ACTION_SKIP)); else if (target == rewind) startService(new Intent(ACTION_REWIND)); else if (target == stop) startService(new Intent(ACTION_STOP)); else if (target == eject) showUrlDialog(); } Example: Music Player App tap(248, 351) public void onClick(View target) { if (target == play) startService(new Intent(ACTION_PLAY)); else if (target == pause) startService(new Intent(ACTION_PAUSE)); else if (target == skip) startService(new Intent(ACTION_SKIP)); else if (target == rewind) startService(new Intent(ACTION_REWIND)); else if (target == stop) startService(new Intent(ACTION_STOP)); else if (target == eject) showUrlDialog(); } Example: Music Player App tap(360, 351) public void onClick(View target) { if (target == play) startService(new Intent(ACTION_PLAY)); else if (target == pause) startService(new Intent(ACTION_PAUSE)); else if (target == skip) startService(new Intent(ACTION_SKIP)); else if (target == rewind) startService(new Intent(ACTION_REWIND)); else if (target == stop) startService(new Intent(ACTION_STOP)); else if (target == eject) showUrlDialog(); } Example: Music Player App tap(24, 351) public void onClick(View target) { if (target == play) startService(new Intent(ACTION_PLAY)); else if (target == pause) startService(new Intent(ACTION_PAUSE)); else if (target == skip) startService(new Intent(ACTION_SKIP)); else if (target == rewind) startService(new Intent(ACTION_REWIND)); else if (target == stop) startService(new Intent(ACTION_STOP)); else if (target == eject) showUrlDialog(); } Example: Music Player App tap(136, 493) public void onClick(View target) { if (target == play) startService(new Intent(ACTION_PLAY)); else if (target == pause) startService(new Intent(ACTION_PAUSE)); else if (target == skip) startService(new Intent(ACTION_SKIP)); else if (target == rewind) startService(new Intent(ACTION_REWIND)); else if (target == stop) startService(new Intent(ACTION_STOP)); else if (target == eject) showUrlDialog(); } Example: Music Player App tap(305, 544) public void onClick(View target) { if (target == play) startService(new Intent(ACTION_PLAY)); else if (target == pause) startService(new Intent(ACTION_PAUSE)); else if (target == skip) startService(new Intent(ACTION_SKIP)); else if (target == rewind) startService(new Intent(ACTION_REWIND)); else if (target == stop) startService(new Intent(ACTION_STOP)); else if (target == eject) showUrlDialog(); } Generating Individual Events Existing alternatives • Random Testing oCannot perform systematic/exhaustive testing • Platform-specific tools (e.g., hierarchy viewer in Android) oLimited to GUI Events oCannot handle third-party GUI widgets Generating Individual Events Our solution Use concolic execution to generate data associated with events Generating Individual Tap Events 1 2 3 4 5 6 7 tap(int x, int y){ if (x>2 && x<4){ if (y>1 && y<3) W1_clicked(); else W2_clicked(); }else W3_clicked(); } x>2 && x<4 T y>1 && y<3 1 2 T 3 F 5 F 7 Generating Individual Tap Events x>2 && x<4 T y>1 && y<3 1 2 T 3 tap(1, 5) F 7 F 5 Generating Individual Tap Events x>2 && x<4 T y>1 && y<3 1 2 T 3 F 7 F 5 (x>2 && x<4) tap(1, 5) F1 !(x>2 && x<4) W3_clicked() tap(3, 5) Generating Individual Tap Events x>2 && x<4 T y>1 && y<3 1 2 T 3 tap(1, 5) F 7 F 5 (x>2 && x<4) (x>2 && x<4) (y>1 && y<3) tap(3, 5) tap(3, 2) T1 (x>2 && x<4) F2 !(y>1 && y<3) W2_clicked() Generating Individual Tap Events x>2 && x<4 T y>1 && y<3 1 2 T 3 tap(1, 5) F 7 F 5 (x>2 && x<4) (y>1 && y<3) tap(3, 5) tap(3, 2) T1 T2 (x>2 && x<4) (y>1 && y<3) W1_clicked() Example: Music Player App ❖ ❖ ❖ ❖ ❖ ❖ ❖ ❖ ❖ ❖ ❖ Two subproblems 1. Generate individual events 2. Generate sequences of events Generating Sequences of Events Concatenate individual events generated by concolic execution. Baseline Algorithm Baseline algorithm Goal: cover these S, Set of all event sequences s.t. each sequence takes a unique path Set of covered branches Baseline Algorithm Suffers from Path Explosion 25000 20000 15000 10000 5000 0 1 2 3 4 Number of sequences generated for Music Player app by baseline algorithm ACTEve Algorithm ACTEve: Automated Concolic Testing of Event-driven programs ACTEve Algorithm Baseline algorithm S, Set of all event sequences s.t. each sequence takes a unique path ACTEve algorithm R s.t. R ⊆ S Goal: cover these Set of covered branches ACTEve is relatively sound Path Subsumption Program state in concolic execution Maps memory location to values (symbolic or concrete) < γ, C > Path constraint Path Subsumption Note γ - memory map C – path constraint Program entry Path 𝑝1 < γ1 , 𝐶1 > 𝑝1 subsumes 𝑝2 1. 𝐶2 ⇒ 𝐶1 2. 𝛾1 = 𝛾2 Path 𝑝2 < γ2 , 𝐶2 > Path Subsumption Note γ - memory map C – path constraint Program entry Path 𝑝1 < γ1 , 𝐶1 > Path 𝑝2 < γ2 , 𝐶2 > 𝑝1 subsumes 𝑝2 1. 𝐶2 ⇒generate 𝐶1 - Don’t test corresponding to 2. 𝛾 = 𝛾 1 2 any path that is an extension of 𝑝 2 - Only generate tests corresponding to paths that are extension of 𝑝1 Path Subsumption • Checking path subsumption is very expensive in general o Constraint implication check o Matching memory map • But, path subsumption can be checked cheaply in special cases o Read-only events o Events whose mutual ordering does not matter o etc. Read-only Events Program Entry event 𝑒 is does not 𝑛 𝑞 corresponds to write to any memory 𝑒1 , … , 𝑒𝑛−1 location. corresponds to 𝑒𝑛 Path 𝑝 executed for event sequence 𝑒1 , … , 𝑒𝑛 𝑝 is subsumed by q Read-only Events ❖ ❖ ❖ ❖ ❖ ❖ ❖ ❖ ❖ ❖ ❖ Read-only events are represented as ❖ ACTEve System Architecture Empirical Study • Apply ACTEve and baseline algorithms o event sequences of length up to 4 o 16 concurrently running emulators o time budget of 12 hours • Measured three metrics o running time o number of feasible paths o number of satisfiability checks Empirical Results Future Work Widget Explosion Main Contributions 1. Concolic execution to generate individual events 2. ACTEve: an efficient algorithm for bounded exhaustive testing of eventdriven programs o Requires only a small fraction (5-36%) of time compared to baseline algorithm 3. Implementation for Android Backup slides Read-only Events Program Entry ′ ⇒𝐶 1. 𝐶 ∧ 𝐶 1 1 corresponds to ′ 2. 𝛾 = 𝛾 because 𝑝 1 2 event sequence 𝑒1 , … , 𝑒𝑛−1 < γ1, 𝐶1 > corresponds to 𝑒𝑛 in 𝑒1 , … , 𝑒𝑛−1 , 𝑒𝑛 < γ2, 𝐶1 ∧ 𝐶 ′ > Path 𝑝 executed for input event sequence 𝑒1 , … , 𝑒𝑛 does not write to any memory location. A Solution: Use Platform-specific Knowlege Output of Android’s “Hierarchy Viewer” tool A Solution: Use Platform-specific Knowlege Output of Android’s “Hierarchy Viewer” tool void onTouchEvent(MotionEvent e) { int rawX = (int) e.getX(); int rawY = (int) e.getY(); int x = (rawX – MARGIN) / SIZE; int y = (rawY – MARGIN) / SIZE; if (x >= 0 && x < 3 && y >= 0 & y < 3) { int cell = x + 3 * y; … } Path Subsumption Program Entry Program Entry same program location Path 𝑝1 {𝑝′ 𝑠. 𝑡. 𝑝1 ; 𝑝′ is feasible} Covered branches Path 𝑝2 {𝑝′ 𝑠. 𝑡. 𝑝2 ; 𝑝′ is feasible} Covered branches Path Subsumption Program Entry Program Entry same program location Path 𝑝1 ′ ′ {𝑝 𝑠. 𝑡. 𝑝1 ; 𝑝 is feasible} Covered branches Path 𝑝2 if we′ explore all paths ′ 𝑠. 𝑡. 𝑝2 ; 𝑝 is feasible} that{𝑝 extends 𝑝1 , then no need to explore any path that extends 𝑝2 because no additional branch coverage will be obtained. Covered branches Example: Music Player App Path constraint when PAUSE button is tapped on