Memory Model Safety of Programs Sebastian Burckhardt Madanlal Musuvathi Microsoft Research EC^2, July 7, 2008
Motivation: Memory Model Vulnerabilities Programmers do not always follow strict locking discipline in performance-critical code ◦ Ad-hoc synchronization with normal loads and stores or interlocked operations is faster Such code can break on relaxed memory models ◦ Most multicore machines are not sequentially consistent ◦ Both compilers and actual hardware can contribute to effect (we focus on hardware effects here) Vulnerabilities are hard to find, reproduce, and analyze ◦ May require specific hardware configuration and schedule 2
C# Example (found in production-level code) volatile bool isIdling; volatile bool hasWork; //Consumer thread void BlockOnIdle(){ lock (condVariable){ isIdling = true; if (!hasWork) Monitor.Wait(condVariable); isIdling = false; } //Producer thread void NotifyPotentialWork(){ hasWork = true; if (isIdling) lock (condVariable) { Monitor.Pulse(condVariable); } 3
Key pieces of code on previous slide: On x86, hardware may perform store late Bug: Producer thread does not notice waiting Consumer, does not send signal Store ii, 1 Example: Store Buffer Vulnerability Store ii, 1 volatile int ii = 0; volatile int hw = 0; Load hw, 0 Load ii, 1 Store hw, 1 ConsumerProducer 0 0 4
Abstract View of Memory Models 5 Given a program P, a memory model Y defines the subset T P,Y T of traces corresponding to some (partial or complete) execution of P on Y. T P, SC T T P, Y SC (sequential consistency) Is strongest memory model More executions may be possible on a relaxed memory model Y 5
Memory Model Safety Observation: Programmer writes code for SC ◦ Resorts to {fences, volatiles, interlocked operations} to maintain SC behavior where needed ◦ If program P exhibits non-SC behavior, it is most likely a bug Definition: A program P is Y-safe if T P,SC = T P,Y 6
Goal & Position 7 Goal: Find efficient methods to verify / falsify memory model safety of programs. Position: Memory models should be formulated in a manner that facilitates this goal. It helps if a memory model Y … … guarantees that data-race-free programs are Y-safe (but what about racy ones?) … guarantees borderline executions (to be defined next).
Borderline Executions Def.: A borderline execution for P, Y is an execution in T P,SC with a successor in T P,Y - T P,SC TP,YTP,Y T P,SC 8 Successor traces are traces with one more instruction.
Example: TSO Borderline Execution Store hw, Store ii, Load hw, Store hw, Load ii, Store ii, Load hw, Store hw, Load ii, Store ii, Load hw, 0 T P, SC T P, TSO Successor traces are traces with one more instruction.
Borderline Executions Def.: A borderline execution for P, Y is an execution in T P,SC with a successor in T P,Y - T P,SC Def.: A memory model Y guarantees borderline executions if the following property is true: A program P is Y -safe if and only if it has no borderline executions. TP,YTP,Y T P,SC 10
Borderline Executions Def.: A borderline execution for P, Y is an execution in T P,SC with a successor in T P,Y - T P,SC Def.: A memory model Y guarantees borderline executions if the following property is true: A program P is Y -safe if and only if it has no borderline executions. TP,YTP,Y T P,SC 11 We can verify / falsify this as a safety property of sequentially consistent executions!
When does a Memory Model Guarantee Borderline Executions? Simplest case: If each trace T P,Y has a predecessor in T P,Y, we can use simple induction (because empty trace is in T P,SC ). This is true for TSO, and we show in [ CAV08 ] paper how to exploit this to build a borderline monitor. 12 TP,YTP,Y T P,SC
How May a Memory Model Fail to Guarantee Borderline Executions? Sometimes, traces have no predecessors (i.e. we can not take away any one instruction). Example program: some memory models may allow this “circular” trace: 13 // Thread 1// Thread 2 if (y = 1) if (x = 1) x = 1; y = 1; Load y, 1 Store x, 1 Store y, 1 Load x, 1
Conclusions / Future Work With increasing use of multicores and little programmer regard for race-freedom, we expect more programs to exhibit failures caused by the memory model. Borderline executions provide a practical way to verify / falsify memory model safety for a general class of memory models Future work: how to deal with ◦ Memory models without borderline executions ◦ Memory models defined by compiler optimizations 14