Fully Automatic and Precise Detection of Thread Safety Violations
Total Page:16
File Type:pdf, Size:1020Kb
Fully Automatic and Precise Detection of Thread Safety Violations Michael Pradel and Thomas R. Gross Department of Computer Science ETH Zurich 1 Motivation Thread-safe classes: Building blocks for concurrent programs 2 Motivation Thread-safe classes: Building blocks for concurrent programs 2 Motivation Thread-safe classes: Building blocks for concurrent programs 2 Example from JDK StringBuffer b = new StringBuffer() b.append("abc") Thread 1 Thread 2 b.insert(1, b) b.deleteCharAt(1) 3 Example from JDK StringBuffer b = new StringBuffer() b.append("abc") Thread 1 Thread 2 b.insert(1, b) b.deleteCharAt(1) ! IndexOutOfBoundsException Confirmed as bug: Issue #7100996 3 Example from JDK StringBuffer b = new StringBuffer() b.append("abc") ThreadHow 1 toThread test 2 b.insert(1,thread b) b.deleteCharAt(1) safety? ! IndexOutOfBoundsException Confirmed as bug: Issue #7100996 3 Goal Automatic and precise bug detection Thread Class Tool safety violations 4 Goal Automatic and precise bug detection Tests Thread Class Tool safety violations Formal False specs positives 4 Goal Automatic and precise bug detection Thread Class Tool safety violations 4 Thread-Safe Classes “behaves correctly when accessed from multiple threads ... with no additional synchronization ... (in the) calling code” page 18 5 Thread-Safe Classes “behaves correctly when accessed from multiple threads ... with no additional synchronization ... (in the) calling code” page 18 5 Thread-Safe Classes “behaves correctly when accessed from multiple threads ... with no additional synchronization ... (in the) calling code” page 18 5 Thread-Safe Classes “behaves correctly when accessed from multiple threads ... with no additional synchronization ... (in the) calling code” page 18 “operations ... behave as if they occur in some serial order that is consistent with the order of the method calls made by each of the individual threads” StringBuffer API documentation, JDK 6 5 Thread-Safe Classes “behaves correctly when accessed from multiple threads ... with no additional synchronization ... (in the) calling code” page 18 “operations ... behave as if they occur in some serial order that is consistent with the order of the method calls made by each of the individual threads” StringBuffer API documentation, JDK 6 5 Example StringBuffer b = new StringBuffer() Thread 1 Thread 2 b.append("a") b.append("c") b.append("b") 6 Example StringBuffer b = new StringBuffer() Thread 1 Thread 2 b.append("a") b.append("c") b.append("b") "abc" 3 "cab" 3 "acb" 3 "ac" 7 6 Example StringBuffer b = new StringBuffer() Thread 1 Thread 2 b.append("a") b.append("c") b.append("b") "abc" 3 "cab" 3 "acb" 3 "ac" 7 6 Example StringBuffer b = new StringBuffer() Thread 1 Thread 2 b.append("a") b.append("c") b.append("b") "abc" 3 "cab" 3 "acb" 3 "ac" 7 6 Example StringBuffer b = new StringBuffer() Thread 1 Thread 2 b.append("a") b.append("c") b.append("b") "abc" 3 "cab" 3 "acb" 3 "ac" 7 6 Example StringBuffer b = new StringBuffer() Thread 1 Thread 2 b.append("a") b.append("c") b.append("b") "abc" 3 "cab" 3 "acb" 3 "ac" 7 6 Approach Class Generate a under concurrent test test (CUT) Execute Thread safety Bug oracle 7 Approach Class Generate a under concurrent test test (CUT) Execute Thread safety Bug oracle 7 Approach Class Generate a under concurrent test test (CUT) Execute Thread safety Bug oracle 7 Generating Concurrent Tests Example: StringBuffer b = new StringBuffer() b.append("abc") Thread 1 Thread 2 b.insert(1, b) b.deleteCharAt(1) 8 Generating Concurrent Tests Sequential prefix: Example: Create and set up CUT instance StringBuffer b = new StringBuffer() b.append("abc") Thread 1 Thread 2 b.insert(1, b) b.deleteCharAt(1) 8 Generating Concurrent Tests Example: StringBuffer b = new StringBuffer() b.append("abc") Thread 1 Thread 2 b.insert(1, b) b.deleteCharAt(1) Concurrent suffixes: Use shared CUT instance 8 Test Generation Algorithm 1. Create prefix Instantiate CUT Call methods 2. Create suffixes for prefix Call methods on shared CUT instance 3. Prefix + two suffixes = test 9 Creating a Prefix 1. Create prefix Instantiate CUT Call methods StringBuffer b = new StringBuffer() b.append("abc") Thread 1 Thread 2 b.insert(1, b) b.deleteCharAt(1) 10 Creating a Prefix 1. Create prefix Randomly Instantiate CUT select a constructor Call methods StringBuffer b = new StringBuffer() b.append("abc") Thread 1 Thread 2 b.insert(1, b) b.deleteCharAt(1) 10 Creating a Prefix 1. Create prefix Randomly Instantiate CUT select a constructor Call methods StringBuffer b = new StringBuffer() b.append("abc") Thread 1 Thread 2 b.insert(1, b) b.deleteCharAt(1) 10 Creating a Prefix 1. Create prefix After adding a call: Instantiate CUT Execute Call methods StringBuffer b = new StringBuffer() b.append("abc") Thread 1 Thread 2 b.insert(1, b) b.deleteCharAt(1) 10 Creating a Prefix 1. Create prefix After adding a call: Instantiate CUT Execute Call methods StringBuffer b = new StringBuffer() b.append("abc") Thread 1 Thread 2 3 b.insert(1, b) b.deleteCharAt(1) 10 Creating a Prefix 1. Create prefix Instantiate CUT Randomly Call methods select a method StringBuffer b = new StringBuffer() b.append("abc") Thread 1 Thread 2 b.insert(1, b) b.deleteCharAt(1) 10 Creating a Prefix 1. Create prefix Instantiate CUT Randomly Call methods select a method StringBuffer b = new StringBuffer() b.append(b.append("abc")/* String */) Thread 1 Thread 2 b.insert(1, b) b.deleteCharAt(1) 10 Creating a Prefix 1. Create prefix Arguments: a) Take available object Instantiate CUT b) Call method returning Call methods required type c) Random value StringBuffer b = new StringBuffer() b.append(b.append("abc")/* String */) Thread 1 Thread 2 b.insert(1, b) b.deleteCharAt(1) 10 Creating a Prefix 1. Create prefix Arguments: a) Take available object Instantiate CUT b) Call method returning Call methods required type c) Random value StringBuffer b = new StringBuffer() b.append("abc") Thread 1 Thread 2 b.insert(1, b) b.deleteCharAt(1) 10 Creating a Prefix 1. Create prefix After adding a call: Instantiate CUT Execute Call methods StringBuffer b = new StringBuffer() b.append("abc") Thread 1 Thread 2 b.insert(1, b) b.deleteCharAt(1) 10 Creating a Prefix 1. Create prefix After adding a call: Instantiate CUT Execute Call methods StringBuffer b = new StringBuffer() b.append("abc") Thread 1 Thread 2 3 b.insert(1, b) b.deleteCharAt(1) 10 Creating a Prefix 1. Create prefix Instantiate CUT Call methods StringBuffer b = new StringBuffer() b.append("abc") Thread 1 Thread 2 b.insert(1, b) b.deleteCharAt(1) 10 Creating Suffixes 2. Create suffixes for prefix Call methods on shared CUT instance 11 Creating Suffixes 2. Create suffixes for prefix Call methods on shared CUT instance StringBuffer b = new StringBuffer() b.append("abc") b.insert(1, b) 11 Creating Suffixes 2. Create suffixes for prefix Randomly Call methods on select a shared CUT instance method StringBuffer b = new StringBuffer() b.append("abc") b.insert(1, b) 11 Creating Suffixes 2. Create suffixes for prefix Randomly Call methods on select a shared CUT instance method StringBuffer b = new StringBuffer() b.append("abc") b.insert(1,b.insert(/* int b) */, /* CharSequence */) 11 Creating Suffixes Arguments: 2. Create suffixes a) Take available object for prefix b) Call method returning Call methods on required type shared CUT instance c) Random value StringBuffer b = new StringBuffer() b.append("abc") b.insert(1,b.insert(/* int b) */, /* CharSequence */) 11 Creating Suffixes Arguments: 2. Create suffixes a) Take available object for prefix b) Call method returning Call methods on required type shared CUT instance c) Random value StringBuffer b = new StringBuffer() b.append("abc") b.insert(1,b.insert(-5, b) b) 11 Creating Suffixes 2. Create suffixes After adding a call: for prefix Execute Call methods on shared CUT instance StringBuffer b = new StringBuffer() b.append("abc") b.insert(1,b.insert(-5, b) b) 11 Creating Suffixes 2. Create suffixes After adding a call: for prefix Execute Call methods on shared CUT instance StringBuffer b = new StringBuffer() b.append("abc") b.insert(1,b.insert(-5, b) b) ! 11 Creating Suffixes Arguments: 2. Create suffixes a) Take available object for prefix b) Call method returning Call methods on required type shared CUT instance c) Random value StringBuffer b = new StringBuffer() b.append("abc") b.insert(1,b.insert(/* int b) */, /* CharSequence */) 11 Creating Suffixes Arguments: 2. Create suffixes a) Take available object for prefix b) Call method returning Call methods on required type shared CUT instance c) Random value StringBuffer b = new StringBuffer() b.append("abc") b.insert(1, b) 11 Creating Suffixes 2. Create suffixes After adding a call: for prefix Execute Call methods on shared CUT instance StringBuffer b = new StringBuffer() b.append("abc") b.insert(1, b) 11 Creating Suffixes 2. Create suffixes After adding a call: for prefix Execute Call methods on shared CUT instance StringBuffer b = new StringBuffer() b.append("abc") b.insert(1, b) 3 11 Creating Suffixes 2. Create suffixes for prefix Call methods on shared CUT instance StringBuffer b = new StringBuffer() b.append("abc") b.insert(1, b) 11 Creating Suffixes 2. Create suffixes for prefix Call methods on shared CUT instance StringBuffer b = new StringBuffer() b.append("abc") b.insert(1, b) b.deleteCharAt(1) 11 Creating Suffixes 2. Create suffixes After adding a call: for prefix Execute Call methods on shared CUT instance StringBuffer b = new StringBuffer() b.append("abc") b.insert(1, b) b.deleteCharAt(1) 11 Creating Suffixes 2. Create suffixes After adding a call: for prefix Execute Call methods on shared CUT instance StringBuffer b = new StringBuffer() b.append("abc") b.insert(1, b) b.deleteCharAt(1) 3 11 Creating Suffixes 2. Create suffixes for prefix Call methods on shared CUT instance StringBuffer b = new StringBuffer() b.append("abc") b.insert(1, b) b.deleteCharAt(1) 11 Creating a Test 3.