Thread Safety Violations Fully Automatic and Precise Detection...
Transcript of Thread Safety Violations Fully Automatic and Precise Detection...
1
Fully Automatic and Precise Detection ofThread Safety Violations
Michael Pradel and Thomas R. Gross
Department of Computer ScienceETH Zurich
2
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
3
Example from JDK
StringBuffer b = new StringBuffer()
b.append("abc")
b.insert(1, b) b.deleteCharAt(1)
Thread 1 Thread 2
3
Example from JDK
StringBuffer b = new StringBuffer()
b.append("abc")
b.insert(1, b) b.deleteCharAt(1)
Thread 1 Thread 2
IndexOutOfBoundsException
Confirmed as bug: Issue #7100996
!
3
Example from JDK
StringBuffer b = new StringBuffer()
b.append("abc")
b.insert(1, b) b.deleteCharAt(1)
Thread 1 Thread 2
IndexOutOfBoundsException
Confirmed as bug: Issue #7100996
!
How to testthread safety?
4
Goal
Automatic and precise bug detection
ClassThreadsafetyviolations
Tool
4
Goal
Automatic and precise bug detection
ClassThreadsafetyviolations
Tests
Falsepositives
Formalspecs
Tool
4
Goal
Automatic and precise bug detection
ClassThreadsafetyviolations
Tool
5
Thread-Safe Classes
“behaves correctly when accessedfrom multiple threads ... with noadditional synchronization ... (inthe) calling code” page 18
5
Thread-Safe Classes
“behaves correctly when accessedfrom multiple threads ... with noadditional synchronization ... (inthe) calling code” page 18
5
Thread-Safe Classes
“behaves correctly when accessedfrom multiple threads ... with noadditional synchronization ... (inthe) calling code” page 18
5
Thread-Safe Classes
“behaves correctly when accessedfrom multiple threads ... with noadditional synchronization ... (inthe) calling code”
“operations ... behave as if they occurin some serial order that is consistentwith the order of the method callsmade by each of the individualthreads”
page 18
StringBuffer API documentation, JDK 6
5
Thread-Safe Classes
“behaves correctly when accessedfrom multiple threads ... with noadditional synchronization ... (inthe) calling code”
“operations ... behave as if they occurin some serial order that is consistentwith the order of the method callsmade by each of the individualthreads”
page 18
StringBuffer API documentation, JDK 6
6
Example
StringBuffer b = new StringBuffer()
b.append("a")
b.append("b")
b.append("c")
Thread 1 Thread 2
6
Example
StringBuffer b = new StringBuffer()
b.append("a")
b.append("b")
b.append("c")
Thread 1 Thread 2
"abc" 3 "cab" 3 "acb" 3 "ac" 7
6
Example
StringBuffer b = new StringBuffer()
b.append("a")
b.append("b")
b.append("c")
Thread 1 Thread 2
"abc" 3 "cab" 3 "acb" 3 "ac" 7
6
Example
StringBuffer b = new StringBuffer()
b.append("a")
b.append("b")
b.append("c")
Thread 1 Thread 2
"abc" 3 "cab" 3 "acb" 3 "ac" 7
6
Example
StringBuffer b = new StringBuffer()
b.append("a")
b.append("b")
b.append("c")
Thread 1 Thread 2
"abc" 3 "cab" 3 "acb" 3 "ac" 7
6
Example
StringBuffer b = new StringBuffer()
b.append("a")
b.append("b")
b.append("c")
Thread 1 Thread 2
"abc" 3 "cab" 3 "acb" 3 "ac" 7
7
Approach
Bug
Classundertest(CUT)
Execute
Thread safetyoracle
Generate aconcurrent test
7
Approach
Bug
Classundertest(CUT)
Execute
Thread safetyoracle
Generate aconcurrent test
7
Approach
Bug
Classundertest(CUT)
Execute
Thread safetyoracle
Generate aconcurrent test
8
Generating Concurrent Tests
StringBuffer b = new StringBuffer()
b.append("abc")
b.insert(1, b) b.deleteCharAt(1)
Thread 1 Thread 2
Example:
8
Generating Concurrent Tests
StringBuffer b = new StringBuffer()
b.append("abc")
b.insert(1, b) b.deleteCharAt(1)
Thread 1 Thread 2
Sequential prefix:
Create and set upCUT instance
Example:
8
Generating Concurrent Tests
StringBuffer b = new StringBuffer()
b.append("abc")
b.insert(1, b) b.deleteCharAt(1)
Thread 1 Thread 2
Concurrent suffixes:
Use shared CUTinstance
Example:
9
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
10
Creating a Prefix
1. Create prefix� Instantiate CUT
� Call methods
StringBuffer b = new StringBuffer()
b.append("abc")
b.insert(1, b) b.deleteCharAt(1)
Thread 1 Thread 2
10
Creating a Prefix
1. Create prefix� Instantiate CUT
� Call methods
StringBuffer b = new StringBuffer()
b.append("abc")
b.insert(1, b) b.deleteCharAt(1)
Thread 1 Thread 2
Randomlyselect aconstructor
10
Creating a Prefix
1. Create prefix� Instantiate CUT
� Call methods
StringBuffer b = new StringBuffer()
b.append("abc")
b.insert(1, b) b.deleteCharAt(1)
Thread 1 Thread 2
Randomlyselect aconstructor
10
Creating a Prefix
1. Create prefix� Instantiate CUT
� Call methods
StringBuffer b = new StringBuffer()
b.append("abc")
b.insert(1, b) b.deleteCharAt(1)
Thread 1 Thread 2
After adding a call:Execute
10
Creating a Prefix
1. Create prefix� Instantiate CUT
� Call methods
StringBuffer b = new StringBuffer()
b.append("abc")
b.insert(1, b) b.deleteCharAt(1)
Thread 1 Thread 2
After adding a call:Execute
3
10
Creating a Prefix
1. Create prefix� Instantiate CUT
� Call methods
StringBuffer b = new StringBuffer()
b.append("abc")
b.insert(1, b) b.deleteCharAt(1)
Thread 1 Thread 2
Randomlyselect amethod
10
Creating a Prefix
1. Create prefix� Instantiate CUT
� Call methods
StringBuffer b = new StringBuffer()
b.append("abc")
b.insert(1, b) b.deleteCharAt(1)
Thread 1 Thread 2
Randomlyselect amethod
b.append(/* String */)
10
Creating a Prefix
1. Create prefix� Instantiate CUT
� Call methods
StringBuffer b = new StringBuffer()
b.append("abc")
b.insert(1, b) b.deleteCharAt(1)
Thread 1 Thread 2
b.append(/* String */)
Arguments:a) Take available objectb) Call method returning
required typec) Random value
10
Creating a Prefix
1. Create prefix� Instantiate CUT
� Call methods
StringBuffer b = new StringBuffer()
b.append("abc")
b.insert(1, b) b.deleteCharAt(1)
Thread 1 Thread 2
Arguments:a) Take available objectb) Call method returning
required typec) Random value
10
Creating a Prefix
1. Create prefix� Instantiate CUT
� Call methods
StringBuffer b = new StringBuffer()
b.append("abc")
b.insert(1, b) b.deleteCharAt(1)
Thread 1 Thread 2
After adding a call:Execute
10
Creating a Prefix
1. Create prefix� Instantiate CUT
� Call methods
StringBuffer b = new StringBuffer()
b.append("abc")
b.insert(1, b) b.deleteCharAt(1)
Thread 1 Thread 2
After adding a call:Execute
3
10
Creating a Prefix
1. Create prefix� Instantiate CUT
� Call methods
StringBuffer b = new StringBuffer()
b.append("abc")
b.insert(1, b) b.deleteCharAt(1)
Thread 1 Thread 2
11
Creating Suffixes
2. Create suffixesfor prefix
� Call methods onshared CUT instance
11
Creating Suffixes
2. Create suffixesfor prefix
� Call methods onshared CUT instance
StringBuffer b = new StringBuffer()
b.append("abc")
b.insert(1, b)
11
Creating Suffixes
2. Create suffixesfor prefix
� Call methods onshared CUT instance
StringBuffer b = new StringBuffer()
b.append("abc")
b.insert(1, b)
Randomlyselect amethod
11
Creating Suffixes
2. Create suffixesfor prefix
� Call methods onshared CUT instance
StringBuffer b = new StringBuffer()
b.append("abc")
b.insert(1, b)
Randomlyselect amethod
b.insert(/* int */, /* CharSequence */)
11
Creating Suffixes
2. Create suffixesfor prefix
� Call methods onshared CUT instance
StringBuffer b = new StringBuffer()
b.append("abc")
b.insert(1, b)b.insert(/* int */, /* CharSequence */)
Arguments:a) Take available objectb) Call method returning
required typec) Random value
11
Creating Suffixes
2. Create suffixesfor prefix
� Call methods onshared CUT instance
StringBuffer b = new StringBuffer()
b.append("abc")
b.insert(1, b)b.insert(-5, b)
Arguments:a) Take available objectb) Call method returning
required typec) Random value
11
Creating Suffixes
2. Create suffixesfor prefix
� Call methods onshared CUT instance
StringBuffer b = new StringBuffer()
b.append("abc")
b.insert(1, b)b.insert(-5, b)
After adding a call:Execute
11
Creating Suffixes
2. Create suffixesfor prefix
� Call methods onshared CUT instance
StringBuffer b = new StringBuffer()
b.append("abc")
b.insert(1, b)b.insert(-5, b)
After adding a call:Execute
!
11
Creating Suffixes
2. Create suffixesfor prefix
� Call methods onshared CUT instance
StringBuffer b = new StringBuffer()
b.append("abc")
b.insert(1, b)b.insert(/* int */, /* CharSequence */)
Arguments:a) Take available objectb) Call method returning
required typec) Random value
11
Creating Suffixes
2. Create suffixesfor prefix
� Call methods onshared CUT instance
StringBuffer b = new StringBuffer()
b.append("abc")
b.insert(1, b)
Arguments:a) Take available objectb) Call method returning
required typec) Random value
11
Creating Suffixes
2. Create suffixesfor prefix
� Call methods onshared CUT instance
StringBuffer b = new StringBuffer()
b.append("abc")
b.insert(1, b)
After adding a call:Execute
11
Creating Suffixes
2. Create suffixesfor prefix
� Call methods onshared CUT instance
StringBuffer b = new StringBuffer()
b.append("abc")
b.insert(1, b)
After adding a call:Execute
3
11
Creating Suffixes
2. Create suffixesfor prefix
� Call methods onshared CUT instance
StringBuffer b = new StringBuffer()
b.append("abc")
b.insert(1, b)
11
Creating Suffixes
2. Create suffixesfor prefix
� Call methods onshared CUT instance
StringBuffer b = new StringBuffer()
b.append("abc")
b.insert(1, b) b.deleteCharAt(1)
11
Creating Suffixes
2. Create suffixesfor prefix
� Call methods onshared CUT instance
StringBuffer b = new StringBuffer()
b.append("abc")
b.insert(1, b) b.deleteCharAt(1)
After adding a call:Execute
11
Creating Suffixes
2. Create suffixesfor prefix
� Call methods onshared CUT instance
StringBuffer b = new StringBuffer()
b.append("abc")
b.insert(1, b) b.deleteCharAt(1)
After adding a call:Execute
3
11
Creating Suffixes
2. Create suffixesfor prefix
� Call methods onshared CUT instance
StringBuffer b = new StringBuffer()
b.append("abc")
b.insert(1, b) b.deleteCharAt(1)
12
Creating a Test
3. Prefix + two suffixes = test
12
Creating a Test
3. Prefix + two suffixes = test
StringBuffer b = new StringBuffer()
b.append("abc")
b.insert(1, b) b.deleteCharAt(1)
12
Creating a Test
3. Prefix + two suffixes = test
StringBuffer b = new StringBuffer()
b.append("abc")
b.insert(1, b) b.deleteCharAt(1)
Thread 1 Thread 2
Spawn new threadfor each suffix
13
Approach
Bug
Classundertest(CUT)
Execute
Thread safetyoracle
Generate aconcurrent test
13
Approach
Bug
Classundertest(CUT)
Execute
Thread safetyoracle
Generate aconcurrent test
14
Thread Safety Oracle
Does the test executionexpose a thread safetyviolation?
� Focus on exceptionsand deadlocks
� Compare concurrentexecution tolinearizations
15
Assumptions
Concurrency-only crashes are undesired� Matches definition of thread safety
Control over all input to tests� Sequential execution: Deterministic
16
Linearizations
� Put all calls into one thread� Preserve order of calls within a thread
16
Linearizations
213
2
13
21
3
� Put all calls into one thread� Preserve order of calls within a thread
21 3
17
The Oracle
Exception ordeadlock?
Execute concurrently
No
Yes
Yes
No Thread safetyviolation
Samefailure?
Execute linearization All linearizationschecked
17
The Oracle
Exception ordeadlock?
Execute concurrently
No
Yes
Yes
No Thread safetyviolation
Samefailure?
Execute linearization All linearizationschecked
Execute concurrently
17
The Oracle
Exception ordeadlock?
Execute concurrently
No
Yes
Yes
No Thread safetyviolation
Samefailure?
Execute linearization All linearizationschecked
Exception ordeadlock?
17
The Oracle
Exception ordeadlock?
Execute concurrently
No
Yes
Yes
No Thread safetyviolation
Samefailure?
Execute linearization All linearizationschecked
Execute linearization
17
The Oracle
Exception ordeadlock?
Execute concurrently
No
Yes
Yes
No Thread safetyviolation
Samefailure?
Execute linearization All linearizationschecked
Samefailure?
17
The Oracle
Exception ordeadlock?
Execute concurrently
No
Yes
Yes
No Thread safetyviolation
Samefailure?
Execute linearization All linearizationschecked
Execute linearization
17
The Oracle
Exception ordeadlock?
Execute concurrently
No
Yes
Yes
No Thread safetyviolation
Samefailure?
Execute linearization All linearizationschecked
Samefailure?
17
The Oracle
Exception ordeadlock?
Execute concurrently
No
Yes
Yes
No Thread safetyviolation
Samefailure?
Execute linearization All linearizationschecked
Thread safetyviolation
18
Example
StringBuffer b = new StringBuffer()
b.append("abc")
b.insert(1, b) b.deleteCharAt(1)
Thread 1 Thread 2
18
Example
StringBuffer b = new StringBuffer()
b.append("abc")
b.insert(1, b) b.deleteCharAt(1)
Thread 1 Thread 2
!
18
Example
StringBuffer b = new StringBuffer()
b.append("abc")
b.insert(1, b) b.deleteCharAt(1)
StringBuffer b = ..
b.append("abc")
b.insert(1, b)
b.deleteCharAt(1) 3
Thread 1 Thread 2
!
18
Example
StringBuffer b = new StringBuffer()
b.append("abc")
b.insert(1, b) b.deleteCharAt(1)
StringBuffer b = ..
b.append("abc")
b.insert(1, b)
b.deleteCharAt(1) 3
Thread 1 Thread 2
StringBuffer b = ..
b.append("abc")
b.deleteCharAt(1)
b.insert(1, b) 3
!
18
Example
StringBuffer b = new StringBuffer()
b.append("abc")
b.insert(1, b) b.deleteCharAt(1)
StringBuffer b = ..
b.append("abc")
b.insert(1, b)
b.deleteCharAt(1) 3
Thread 1 Thread 2
StringBuffer b = ..
b.append("abc")
b.deleteCharAt(1)
b.insert(1, b) 3
!Thread safety violation
19
Properties of the Oracle
Sound but incomplete *
� All reported violations are real� Cannot guarantee thread safety
Independent of bug type� Data races� Atomicity violations� Deadlocks
* with respect to incorrectness
20
Implementation
21
Evaluation
1. Effectiveness in finding bugs
2. Performance
Setup:
� Thread-safe classes from six Javalibraries (e.g., JDK, Apache DBCP)
� Intel Xeon (8x3GHz)
22
Bugs
Found 15 bugs and 0 false positives
� 9 known bugs
� 6 previously unknown bugs� E.g., in JDK and Apache DBCP
23
Example: Apache DBCP
DataSource ds = new DataSource()
ds.setDataSourceName("a") ds.close()
Thread 1 Thread 2
23
Example: Apache DBCP
DataSource ds = new DataSource()
ds.setDataSourceName("a") ds.close()
Thread 1 Thread 2
ConcurrentModificationException
Reason: Unsynchronized use ofthread-unsafe collection
Confirmed as bug: Issue #369
!
24
Kinds of Failures
12 of 15 failures are implicit (VM or JDK)
Most common:
� NullPointerException� ConcurrentModificationException
25
Performance
25
Performance
8 hours
26
Conclusion
Concurrency: More and more important
Need tools to test thread-safe classes
This work:� Fully automatic testing� Only real bugs reported
27
Thank you!
Try it:
Fully Automatic and Precise Detection of Thread Safety ViolationsMichael Pradel and Thomas R. Gross, ETH Zurich