780 likes | 797 Views
AFix detects and fixes single-variable atomicity violations efficiently, reducing deadlock risks and improving code readability /
E N D
Automated Atomicity-Violation Fixing Jeremy Bo Tanima Lingmei
Problem Fixing concurrency bugs is challenging Root cause Error-prone patches Serialization bottlenecks Many ideas for detection, but few for fixing
Related Work Identification Force interleaving to identify problems Bug Fixing Concurrent program synthesis Infer synchronization based on user input Steer execution to avoid failure Hardware watch points
Contributions Implement tool AFix which detects and fixes single variable atomicity violations Improve code readability Fix bugs without large performance degradation Minimal deadlock risk
Atomicity Violation: Example void buf_write(…){ int tmp = buf_len+str_len; if(tmp > MAX) return memcpy(buf[buf_len+1],str,str_len); buf_len=tmp; }
Atomicity Violation: Example Buffer void buf_write(…){ int tmp = buf_len+str_len; if(tmp > MAX) return memcpy(buf[buf_len+1], str, str_len); buf_len=tmp; } buf_len = 2 MAX = 7
Atomicity Violation: Example Buffer Input: str = JOE str_len = 3 void buf_write(…){ int tmp = buf_len+str_len; if(tmp > MAX) return memcpy(buf[buf_len+1], str, str_len); buf_len=tmp; } buf_len = 2 MAX = 7
Atomicity Violation: Example Buffer Input: str = JOE str_len = 3 void buf_write(…){ int tmp = buf_len+str_len; if(tmp > MAX) return memcpy(buf[buf_len+1], str, str_len); buf_len=tmp; } buf_len = 2 MAX = 7 tmp = 5
Atomicity Violation: Example Buffer Input: str = JOE str_len = 3 void buf_write(…){ int tmp = buf_len+str_len; if(tmp > MAX) return memcpy(buf[buf_len+1], str, str_len); buf_len=tmp; } buf_len = 2 MAX = 7 tmp = 5
Atomicity Violation: Example Buffer Input: str = JOE str_len = 3 void buf_write(…){ int tmp = buf_len+str_len; if(tmp > MAX) return memcpy(buf[buf_len+1], str, str_len); buf_len=tmp; } buf_len = 2 MAX = 7 tmp = 5
Atomicity Violation: Example Buffer Input: str = JOE str_len = 3 void buf_write(…){ int tmp = buf_len+str_len; if(tmp > MAX) return memcpy(buf[buf_len+1], str,str_len); buf_len=tmp; } buf_len = 5 MAX = 7 tmp = 5
Atomicity Violation: Example void buf_write(…){ int tmp = buf_len+str_len; if(tmp > MAX) return memcpy(buf[buf_len+1], str, str_len); buf_len=tmp; }
Atomicity Violation: Example Buffer void buf_write(…){ int tmp = buf_len+str_len; if(tmp > MAX) return memcpy(buf[buf_len+1], str, str_len); buf_len=tmp; } buf_len = 2 MAX = 7
Atomicity Violation: Example Buffer Input: str = JOE str_len = 3 void buf_write(…){ int tmp = buf_len+str_len; if(tmp > MAX) return memcpy(buf[buf_len+1], str, str_len); buf_len=tmp; } buf_len = 2 MAX = 7
Atomicity Violation: Example Buffer Input: str = JOE str_len = 3 void buf_write(…){ int tmp = buf_len+str_len; if(tmp > MAX) return memcpy(buf[buf_len+1], str, str_len); buf_len=tmp; } buf_len = 2 MAX = 7 tmp = 5
Atomicity Violation: Example Buffer Input: str = JOE str_len = 3 void buf_write(…){ int tmp = buf_len+str_len; if(tmp > MAX) return memcpy(buf[buf_len+1], str, str_len); buf_len=tmp; } buf_len = 2 MAX = 7 tmp = 5
Atomicity Violation: Example void buf_write(…){ int tmp = buf_len+str_len; if(tmp > MAX) return memcpy(buf[buf_len+1], str, str_len); buf_len=tmp; } void buf_write(…){ int tmp = buf_len+str_len; if(tmp > MAX) return memcpy(buf[buf_len+1], str, str_len); buf_len=tmp; } Input: str = JOE str_len = 3 buf_len = 2 MAX = 7 Input: str = SAM str_len = 3
Atomicity Violation: Example Buffer Input: str = SAM str_len = 3 void buf_write(…){ int tmp = buf_len+str_len; if(tmp > MAX) return memcpy(buf[buf_len+1], str, str_len); buf_len=tmp; } buf_len = 2 MAX = 7 tmp = 5
Atomicity Violation: Example Buffer Input: str = SAM str_len = 3 void buf_write(…){ int tmp = buf_len+str_len; if(tmp > MAX) return memcpy(buf[buf_len+1], str, str_len); buf_len=tmp; } buf_len = 2 MAX = 7 tmp = 5
Atomicity Violation: Example Buffer Input: str = SAM str_len = 3 void buf_write(…){ int tmp = buf_len+str_len; if(tmp > MAX) return memcpy(buf[buf_len+1], str, str_len); buf_len=tmp; } buf_len = 2 MAX = 7 tmp = 5
Atomicity Violation: Example Buffer Input: str = SAM str_len = 3 void buf_write(…){ int tmp = buf_len+str_len; if(tmp > MAX) return memcpy(buf[buf_len+1], str, str_len); buf_len=tmp; } buf_len = 5 MAX = 7 tmp = 5
Atomicity Violation: Example void buf_write(…){ int tmp = buf_len+str_len; if(tmp > MAX) return memcpy(buf[buf_len+1],str,str_len); buf_len=tmp; } void buf_write(…){ int tmp = buf_len+str_len; if(tmp > MAX) return memcpy(buf[buf_len+1], str, str_len); buf_len=tmp; } Input: str = JOE str_len = 3 buf_len = 5 MAX = 7 Input: str = SAM str_len = 3
Atomicity Violation: Example Buffer Input: str = JOE str_len = 3 void buf_write(…){ int tmp = buf_len+str_len; if(tmp > MAX) return memcpy(buf[buf_len+1], str, str_len); buf_len=tmp; } buf_len = 5 MAX = 7 tmp = 5 ERROR
Atomicity Violation: Example void buf_write(…){ int tmp = buf_len+str_len; if(tmp > MAX) return memcpy(buf[buf_len+1], str, str_len); buf_len=tmp; }
Atomicity Violation: Example Buffer Input: str = JOE str_len = 3 void buf_write(…){ int tmp = buf_len+str_len; if(tmp > MAX) return memcpy(buf[buf_len+1], str, str_len); buf_len=tmp; } buf_len = 2 MAX = 7 tmp = 5
Atomicity Violation: Example void buf_write(…){ int tmp = buf_len+str_len; if(tmp > MAX) return memcpy(buf[buf_len+1], str, str_len); buf_len=tmp; } void buf_write(…){ int tmp = buf_len+str_len; if(tmp > MAX) return memcpy(buf[buf_len+1], str, str_len); buf_len=tmp; } Input: str = JOE str_len = 3 buf_len = 2 MAX = 7 Input: str = HI str_len = 2
Atomicity Violation: Example Buffer Input: str = HI str_len = 2 void buf_write(…){ int tmp = buf_len+str_len; if(tmp > MAX) return memcpy(buf[buf_len+1], str, str_len); buf_len=tmp; } buf_len = 4 MAX = 7 tmp = 4
Atomicity Violation: Example void buf_write(…){ int tmp = buf_len+str_len; if(tmp > MAX) return memcpy(buf[buf_len+1], str, str_len); buf_len=tmp; } void buf_write(…){ int tmp = buf_len+str_len; if(tmp > MAX) return memcpy(buf[buf_len+1], str, str_len); buf_len=tmp; } Input: str = JOE str_len = 3 buf_len = 4 MAX = 7 Input: str = HI str_len = 3
Atomicity Violation: Example Buffer Input: str = HI str_len = 2 void buf_write(…){ int tmp = buf_len+str_len; if(tmp > MAX) return memcpy(buf[buf_len], str, str_len); buf_len=tmp; } buf_len = 5 MAX = 7 tmp = 5
Background: CTrigger In-house tool to detect potential single variable atomicity violations Identifies potential violations via static analysis Prunes infeasible interleaving Ranks possible violations based on probability of occurrence Attempt to expose bugs via delays at specific points
Background: CTrigger Terminology (p, c, r) triple: Preceding, current, remote two consecutive accesses (p, c) to the same variable in the same thread are interleaved by a remote access (r) by another thread Causes execution to differ from serial execution
Background: CTrigger Modifications Output all possible combinations for c instructions Return complete call stack for p, c, and r Limitations Exposed bugs should not always use separate patches May not expose the root problem
Naïve Fix Lock Require lock before p and r, release after c and r Lock Unlock Unlock
Naïve Fix void buf_write(…){ int tmp = buf_len+str_len; if(tmp > MAX) return memcpy(buf[buf_len+1], str, str_len); buf_len=tmp; }
Naïve Fix void buf_write(…){ Lock int tmp = buf_len+str_len; if(tmp > MAX) return memcpy(buf[buf_len+1], str, str_len); Unlock Lock buf_len=tmp; Unlock }
Naïve Fix void buf_write(…){ int tmp = buf_len+str_len; if(tmp > MAX) return memcpy(buf[buf_len+1], str, str_len); buf_len=tmp; }
Naïve Fix void buf_write(…){ int tmp = buf_len+str_len; if(tmp > MAX) return Lock memcpy(buf[buf_len+1], str, str_len); Lock buf_len=tmp; Unlock Unlock }
Fixing Strategy • Fix one bug report • a single atomicity violation • Fix multiple bug report • multiple bugs reported by bug detectors
How AFix changes the code critical region 1 critical region 2 p c r avoid introducing new bugs? mutually exclusive
m p c n q Single-Function Operation • Condition 1: assume the function is not recursive. • the set of protected nodes = • P∩C={p,c,m,q}∩{p,c,m,n,q}={p,c,m,q} • (2) Search backword from c, for each searching path, let out-degree(p)≤1 and in-degree(p)=0 • C={p,c,m,n,q} • (1) Search forward from p, for each searching path, let out-degree(c)=0 and in-degree(c)≤1 • P = {p,c,m,q}
a b unreleased lock / potential deadlock lock; if(gPtr){ puts(gPtr); unlock; } else{ unlock; ... } unlock; insert unlock from protected nodes to unprotected nodes
a a double lock and unlock without lock • while(...){ • lock; • ptr = aPtr; • } • puts(ptr); • unlock; lock; while(...){ insert lock from unprotected nodes to protected nodes
Deadlock Analysis and Avoidance critical region pthread_mutex_timelock to acquire locks AFix statically analysis acuqire lock after maximum delay? Yes potentially-blocking operations? Yes No No pthread_mutex_lock to acquire locks continue program time out
Single-Function Operation Condition 2: the function is recursive. • void foo(){ • lock; • ptr = aPtr; • if (...) foo(); • puts(ptr); • unlock; • } reentrant_lock; reentrant_unlock; reentrant lock avoids double lock mutex.owner: thread-ID mutex.count: nesting level
Multiple-Function Operation • when p and c come from different functions • Problems: no detection? • Solution: • substitute p and c with the real call node in the innermost function.
Harmonizing two critical regions-mutually exclusive critical region 2 critical region 1 p ... (r) ... c r if any of the calls in f (a function containing p&c) which lead to r are themselves inside the p-c critical region, then the lock operation of r is redundant and should be removed