Part 2: Reachability analysis of stack-based systems.

Slides:



Advertisements
Similar presentations
CS 267: Automated Verification Lecture 8: Automata Theoretic Model Checking Instructor: Tevfik Bultan.
Advertisements

1 Model checking. 2 And now... the system How do we model a reactive system with an automaton ? It is convenient to model systems with Transition systems.
Automatic Verification Book: Chapter 6. What is verification? Traditionally, verification means proof of correctness automatic: model checking deductive:
Abstraction and Modular Reasoning for the Verification of Software Corina Pasareanu NASA Ames Research Center.
C O N T E X T - F R E E LANGUAGES ( use a grammar to describe a language) 1.
1 Symbolic Execution for Model Checking and Testing Corina Păsăreanu (Kestrel) Joint work with Sarfraz Khurshid (MIT) and Willem Visser (RIACS)
1/20 Generalized Symbolic Execution for Model Checking and Testing Charngki PSWLAB Generalized Symbolic Execution for Model Checking and Testing.
Bebop: A Symbolic Model Checker for Boolean Programs Thomas Ball Sriram K. Rajamani
Timed Automata.
CSCI 4325 / 6339 Theory of Computation Zhixiang Chen Department of Computer Science University of Texas-Pan American.
Background for “KISS: Keep It Simple and Sequential” cs264 Ras Bodik spring 2005.
CS 267: Automated Verification Lecture 10: Nested Depth First Search, Counter- Example Generation Revisited, Bit-State Hashing, On-The-Fly Model Checking.
Reducing Context-bounded Concurrent Reachability to Sequential Reachability Gennaro Parlato University of Illinois at Urbana-Champaign Salvatore La Torre.
Pushdown Systems Koushik Sen EECS, UC Berkeley Slide Source: Sanjit A. Seshia.
Iterative Context Bounding for Systematic Testing of Multithreaded Programs Madan Musuvathi Shaz Qadeer Microsoft Research.
CIS 540 Principles of Embedded Computation Spring Instructor: Rajeev Alur
The Language Theory of Bounded Context-Switching Gennaro Parlato (U. of Illinois, U.S.A.) Joint work with: Salvatore La Torre (U. of Salerno, Italy) P.
Summarizing Procedures in Concurrent Programs Shaz Qadeer Sriram K. Rajamani Jakob Rehof Microsoft Research.
Computability and Complexity 4-1 Existence of Undecidable Problems Computability and Complexity Andrei Bulatov.
CS21 Decidability and Tractability
Complexity 12-1 Complexity Andrei Bulatov Non-Deterministic Space.
Complexity 11-1 Complexity Andrei Bulatov Space Complexity.
Model Checking Lecture 5. Outline 1 Specifications: logic vs. automata, linear vs. branching, safety vs. liveness 2 Graph algorithms for model checking.
Static Analysis of Embedded C Code John Regehr University of Utah Joint work with Nathan Cooprider.
Context-bounded model checking of concurrent software Shaz Qadeer Microsoft Research Joint work with: Jakob Rehof, Microsoft Research Dinghao Wu, Princeton.
1 Linear Bounded Automata LBAs. 2 Linear Bounded Automata are like Turing Machines with a restriction: The working space of the tape is the space of the.
1 Today Another approach to “coverage” Cover “everything” – within a well-defined, feasible limit Bounded Exhaustive Testing.
1/25 Context-Bounded Analysis of Concurrent Queue Systems Gennaro Parlato University of Illinois at Urbana-Champaign Università degli Studi di Salerno.
Debugging Concurrent Software by Context-Bounded Analysis Shaz Qadeer Microsoft Research Joint work with: Jakob Rehof, Microsoft Research Dinghao Wu, Princeton.
A temporal logic for calls and returns P. Madhusudan University of Pennsylvania Joint work with Rajeev Alur and Kousha Etessami Talk at HCES 2004, Philadelphia.
Fall 2004COMP 3351 A Universal Turing Machine. Fall 2004COMP 3352 Turing Machines are “hardwired” they execute only one program A limitation of Turing.
Part II: Atomicity for Software Model Checking. Class Account { int balance; static int MIN = 0, MAX = 100; bool synchronized deposit(int n) { int t =
Grammars, Languages and Finite-state automata Languages are described by grammars We need an algorithm that takes as input grammar sentence And gives a.
Model Checking Lecture 5. Outline 1 Specifications: logic vs. automata, linear vs. branching, safety vs. liveness 2 Graph algorithms for model checking.
Final Exam Review Cummulative Chapters 0, 1, 2, 3, 4, 5 and 7.
Design and Analysis of Algorithms
272: Software Engineering Fall 2012 Instructor: Tevfik Bultan Lecture 4: SMT-based Bounded Model Checking of Concurrent Software.
Regular Model Checking Ahmed Bouajjani,Benget Jonsson, Marcus Nillson and Tayssir Touili Moran Ben Tulila
Verifying Concurrent Message- Passing C Programs with Recursive Calls Sagar Chaki, Edmund Clarke, Nicholas Kidd, Thomas Reps, and Tayssir Touili.
1 CD5560 FABER Formal Languages, Automata and Models of Computation Lecture 7 Mälardalen University 2010.
Languages of nested trees Swarat Chaudhuri University of Pennsylvania (with Rajeev Alur and P. Madhusudan)
1 Unit 1: Automata Theory and Formal Languages Readings 1, 2.2, 2.3.
February 18, 2015CS21 Lecture 181 CS21 Decidability and Tractability Lecture 18 February 18, 2015.
Scope-bounded Multistack Pushdown Systems: - fixed-point - sequentialization - tree-width 1 Salvatore La Torre Gennaro Parlato (U. Salerno, Italy) (U.
1 Models of Computation o Turing Machines o Finite store + Tape o transition function: o If in state S1 and current cell is a 0 (1) then write 1 (0) and.
Type Systems CS Definitions Program analysis Discovering facts about programs. Dynamic analysis Program analysis by using program executions.
Context-bounded model checking of concurrent software Shaz Qadeer Microsoft Research Joint work with: Jakob Rehof, Microsoft Research Dinghao Wu, Princeton.
Learning Symbolic Interfaces of Software Components Zvonimir Rakamarić.
1 Linear Bounded Automata LBAs. 2 Linear Bounded Automata (LBAs) are the same as Turing Machines with one difference: The input string tape space is the.
1 Turing’s Thesis. 2 Turing’s thesis: Any computation carried out by mechanical means can be performed by a Turing Machine (1930)
1Computer Sciences Department. Book: INTRODUCTION TO THE THEORY OF COMPUTATION, SECOND EDITION, by: MICHAEL SIPSER Reference 3Computer Sciences Department.
Weighted Automata and Concurrency Akash Lal Microsoft Research, India Tayssir Touili, Nicholas Kidd and Tom Reps ACTS II, Chennai Mathematical Institute.
TM Design Macro Language D and SD MA/CSSE 474 Theory of Computation.
/ PSWLAB Thread Modular Model Checking by Cormac Flanagan and Shaz Qadeer (published in Spin’03) Hong,Shin Thread Modular Model.
MOPS: an Infrastructure for Examining Security Properties of Software Authors Hao Chen and David Wagner Appears in ACM Conference on Computer and Communications.
1 Chapter Pushdown Automata. 2 Section 12.2 Pushdown Automata A pushdown automaton (PDA) is a finite automaton with a stack that has stack operations.
CIS 540 Principles of Embedded Computation Spring Instructor: Rajeev Alur
CMSC 330: Organization of Programming Languages Pushdown Automata Parsing.
Chapter 9 Turing Machines What would happen if we change the stack in Pushdown Automata into some other storage device? Truing Machines, which maintains.
KISS: K EEP I T S IMPLE AND S EQUENTIAL Guy Martin, OSLab, GNU09 Shaz Qadeer Microsoft Research One Microsoft Way Redmond, WA Dinghao Wu Department.
Recursively Enumerable and Recursive Languages. Definition: A language is recursively enumerable if some Turing machine accepts it.
Sequentializing Parameterized Programs
PDAs Accept Context-Free Languages
Automatic Verification
LIMITS OF ALGORITHMIC COMPUTATION
Turing Machines Acceptors; Enumerators
Over-Approximating Boolean Programs with Unbounded Thread Creation
Decidable Languages Costas Busch - LSU.
… NPDAs continued.
Presentation transcript:

Part 2: Reachability analysis of stack-based systems

From Finite to Infinite-State Systems So far, algorithms for systems with finite state spaces –semi-algorithms in the presence of recursion

Control Data AcyclicLoopingInfinite Finite Infinite YesYesYes YesNoNo Decidability of reachability analysis Single thread of control: Finite

Control Data AcyclicLoopingInfinite Finite Infinite YesYesNo YesNoNo Decidability of reachability analysis Multiple threads of control: Finite

Decidability vs. Expressiveness Unbounded state  Undecidable Is the unbounded system able to encode a Turing machine? –Single-counter machines? NO –Two-counter machines? YES –Single-stack machines? NO –Two-stack machines? YES

State representation Explicit representation infeasible Symbolic representation is the key –For the transition system –For the reachable states

Pushdown systems (G, L, g 0, l 0,  ) g, h  G : finite set of control states l, m  L : finite set of stack symbols g 0 : initial control state l 0 : initial stack symbol  : set of transitions

Remarks The classical definition of a pushdown system has, in addition, an alphabet I of input symbols. Each transition depends on the control state, the top of the stack, and the input symbol. The language L  I* of a classical pushdown system contains those input sequences for which there is an execution leading to the empty stack. We are only concerned with reachability analysis and will therefore ignore I.

Three kinds of transitions: (g, l)  (h, m) (step) (g, l)  (h, m n) (call) (g, l)  (h,  ) (return) Configuration: g, l l  h, m g, l  h, n m g, l  h,

Modeling sequential programs An element in G is a valuation to global variables An element in L is a valuation to local variables and –current instruction address for the frame at the top of the stack –return instruction address for the other frames

Example bool a = F; void main( ) { L1: a = T; L2: flip(a); L3: } void flip(bool x) { L4: a = !x; L5: } (F,  ) (F,  _, L3  ) (F,  _, L3   T, L5  ) (T,  _, L3   T, L4  ) (T,  _, L2  ) (F,  _, L1  ) (a,  x, pc  )

Reachability problem Given pushdown system (G, L, g 0, l 0,  ) and control state g, does there exist a stack ls  L* such that (g 0, l 0 )  * (g, ls)?

Naïve algorithm Add (g 0, l 0 ) to R (g, ls)  R (g, ls)  (g’, ls’) Add (g’, ls’) to R

R is unbounded so algorithm won’t terminate Two solutions: –Summary-based (a.k.a. interprocedural dataflow analysis) –Automata-based Problem with the naïve algorithm

Automata-based algorithm Add (g 0, l 0 ) to R (g, ls)  R (g, ls)  (g’, ls’) Add (g’, ls’) to R Key idea: Use a finite automaton to symbolically represent R

Symbolic representation Pushdown system (G, L, g 0, l 0,  ) Representation automaton (Q, L, T, G, F) - Q (  G) is the set of states - L is the alphabet - T is the transition relation - G is the set of initial states - F is the set of final states

gs1s1 s2s2 l l m h m Represents the set of configurations: { (h, m), (g, l m* l) } A set C of configurations is regular if it is representable by an automaton Theorem (Buchi) : The set of configurations reachable from a regular set is also regular.

Remarks Buchi’s theorem does not contradict the fact that pushdown systems can accept non-regular languages over the input alphabet I. The classical definition of a pushdown system has, in addition, an alphabet I of input symbols. The language of reachable stack configurations is a language over the alphabet L. The accepted language is a language over the alphabet I.

Pushdown system: (G, L, g 0, l 0,  ) - G = {g 0, g 1, g 2 } - L = {l 0, l 1, l 2 } - (g 0, l 0 )  (g 1, l 1 l 0 ) (g 1, l 1 )  (g 2, l 2 l 0 ) (g 2, l 2 )  (g 0, l 1 ) (g 0, l 1 )  (g 0,  ) g0g0 l0l0 s0s0

Pushdown system: (G, L, g 0, l 0,  ) - G = {g 0, g 1, g 2 } - L = {l 0, l 1, l 2 } - (g 0, l 0 )  (g 1, l 1 l 0 ) (g 1, l 1 )  (g 2, l 2 l 0 ) (g 2, l 2 )  (g 0, l 1 ) (g 0, l 1 )  (g 0,  ) g0g0 l0l0 s0s0 g1g1 g2g2

Pushdown system: (G, L, g 0, l 0,  ) - G = {g 0, g 1, g 2 } - L = {l 0, l 1, l 2 } - (g 0, l 0 )  (g 1, l 1 l 0 ) (g 1, l 1 )  (g 2, l 2 l 0 ) (g 2, l 2 )  (g 0, l 1 ) (g 0, l 1 )  (g 0,  ) g0g0 l0l0 s0s0 g1g1 g2g2 s 1,1 l1l1 s 2,2 l2l2

Pushdown system: (G, L, g 0, l 0,  ) - G = {g 0, g 1, g 2 } - L = {l 0, l 1, l 2 } - (g 0, l 0 )  (g 1, l 1 l 0 ) (g 1, l 1 )  (g 2, l 2 l 0 ) (g 2, l 2 )  (g 0, l 1 ) (g 0, l 1 )  (g 0,  ) g0g0 l0l0 s0s0 g1g1 g2g2 s 1,1 l0l0 l1l1 s 2,2 l2l2

Pushdown system: (G, L, g 0, l 0,  ) - G = {g 0, g 1, g 2 } - L = {l 0, l 1, l 2 } - (g 0, l 0 )  (g 1, l 1 l 0 ) (g 1, l 1 )  (g 2, l 2 l 0 ) (g 2, l 2 )  (g 0, l 1 ) (g 0, l 1 )  (g 0,  ) g0g0 l0l0 s0s0 g1g1 g2g2 s 1,1 l0l0 l1l1 s 2,2 l2l2 l0l0

Pushdown system: (G, L, g 0, l 0,  ) - G = {g 0, g 1, g 2 } - L = {l 0, l 1, l 2 } - (g 0, l 0 )  (g 1, l 1 l 0 ) (g 1, l 1 )  (g 2, l 2 l 0 ) (g 2, l 2 )  (g 0, l 1 ) (g 0, l 1 )  (g 0,  ) g0g0 l0l0 s0s0 g1g1 g2g2 s 1,1 l0l0 l1l1 s 2,2 l2l2 l0l0 l1l1

Pushdown system: (G, L, g 0, l 0,  ) - G = {g 0, g 1, g 2 } - L = {l 0, l 1, l 2 } - (g 0, l 0 )  (g 1, l 1 l 0 ) (g 1, l 1 )  (g 2, l 2 l 0 ) (g 2, l 2 )  (g 0, l 1 ) (g 0, l 1 )  (g 0,  ) g0g0 l0l0 s0s0 g1g1 g2g2 s 1,1 l0l0 l1l1 s 2,2 l2l2 l0l0 l1l1 

Pushdown system: (G, L, g 0, l 0,  ) - G = {g 0, g 1, g 2 } - L = {l 0, l 1, l 2 } - (g 0, l 0 )  (g 1, l 1 l 0 ) (g 1, l 1 )  (g 2, l 2 l 0 ) (g 2, l 2 )  (g 0, l 1 ) (g 0, l 1 )  (g 0,  ) g0g0 l0l0 s0s0 g1g1 g2g2 s 1,1 l0l0 l1l1 s 2,2 l2l2 l0l0 l1l1  l0l0

Pushdown system: (G, L, g 0, l 0,  ) - G = {g 0, g 1, g 2 } - L = {l 0, l 1, l 2 } - (g 0, l 0 )  (g 1, l 1 l 0 ) (g 1, l 1 )  (g 2, l 2 l 0 ) (g 2, l 2 )  (g 0, l 1 ) (g 0, l 1 )  (g 0,  ) g0g0 l0l0 s0s0 g1g1 g2g2 s 1,1 l0l0 l1l1 s 2,2 l2l2 l0l0 l1l1  l0l0 { (g 0, l 0  l 0 l 0 +  l 1 l 0 l 0 + ), (g 1, l 1 l 0 + ), (g 2, l 2 l 0 l 0 + ) }

Reachability analysis for concurrent pushdown systems Undecidable in general Three approaches –restrict computation model, e.g., Esparza- Podelski 00 –sound and imprecise approaches, e.g., Bouajjani-Esparza-Touili 03, Flanagan- Qadeer 03 –unsound but precise approaches

Context-bounded verification of concurrent software           Context Context switch Analyze all executions with small number of context switches ! Different from bounded-depth model checking no bound on the computation within each context

Many subtle concurrency errors are manifested in executions with a small number of context switches Context-bounded analysis can be performed efficiently Why context-bounded analysis?

KISS: a static analysis tool Technique to use any sequential checker to perform context-bounded concurrency analysis Found a number of concurrency errors in NT device drivers even with a context-switch bound of two

DriverKLOC# Fields# Races Tracedrv0.530 Moufiltr Kbfiltr Imca1.151 Startio1.190 Toaster/toastmon1.481 Diskperf diag vdev Fakemodem Toaster/bus Serenum Toaster/func Mouclass Kbdclass Mouser Fdc Total: 30 races

Zing: an explicit-state model checker Case study (Naik-Rehof 04): Concurrent transaction management code from Microsoft product group Analyzed by the Zing model checker after automatically translating to the Zing input language –Found three bugs each requiring between three and four context switches

Many subtle concurrency errors are manifested in executions with a small number of context switches Context-bounded analysis can be performed efficiently Why context-bounded analysis?

Polynomially-bounded executions Context bounding leads to polynomial bound on the number of executions –n threads, each executing k steps –total no. of executions =  ( n k ) –With context bound c, no. of executions = O( (n 2.k) c )

Reachability analysis Rechability analysis of finite-data concurrent programs is decidable for bounded number of context switches

Sequential program Q KISS Sequential Checker Concurrent program P  No error found Error in Q indicates error in P KISS: A static checker for concurrent software

Sequential program Q KISS Concurrent program P KISS: A static checker for concurrent software  No error found Error in Q indicates error in P SDV

KISS strategy Q encodes executions of P with small number of context switches –instrumentation introduces lots of extra paths to mimic context switches Leverage all-path analysis of sequential checkers Sequential program Q KISS Concurrent program P  SDV

PnpStop( ) { int t; de->stopping = T; t = AtomicDecr(& de->count); if (t == 0) SetEvent(& de->stopEvent); WaitEvent(& de->stopEvent); } DispatchRoutine( ) { int t; if (! de->stopping) { AtomicIncr(& de->count); // do useful work // … t = AtomicDecr(& de->count); if (t == 0) SetEvent(& de->stopEvent); }

DispatchRoutine( ) { int t; if (! de->stopping) { AtomicIncr(& de->count); // do useful work // … t = AtomicDecr(& de->count); if (t == 0) SetEvent(& de->stopEvent); } PnpStop( ) { int t; if ($) return; de->stopping = T; if ($) return; t = AtomicDecr(& de->count); if ($) return; if (t == 0) SetEvent(& de->stopEvent); if ($) return; WaitEvent(& de->stopEvent); }

PnpStop( ) { int t; if ($) return; de->stopping = T; if ($) return; t = AtomicDecr(& de->count); if ($) return; if (t == 0) SetEvent(& de->stopEvent); if ($) return; WaitEvent(& de->stopEvent); } DispatchRoutine( ) { int t; CODE; if (! de->stopping) { CODE; AtomicIncr(& de->count); // do useful work // … CODE; t = AtomicDecr(& de->count); CODE; if (t == 0) SetEvent(& de->stopEvent); CODE; } if ( !done ) { if ($) { done = T; PnpStop( ); } } CODE  bool done = F;

PnpStop( ) { int t; if ($) return; de->stopping = T; if ($) return; t = AtomicDecr(& de->count); if ($) return; if (t == 0) SetEvent(& de->stopEvent); if ($) return; WaitEvent(& de->stopEvent); } DispatchRoutine( ) { int t; CODE; if (! de->stopping) { CODE; AtomicIncr(& de->count); // do useful work // … CODE; t = AtomicDecr(& de->count); CODE; if (t == 0) SetEvent(& de->stopEvent); CODE; } if ( !done ) { if ($) { done = T; PnpStop( ); } } CODE  bool done = F; main( ) { DispatchRoutine( ); }

PnpStop( ) { int t; CODE; de->stopping = T; CODE; t = AtomicDecr(& de->count); CODE; if (t == 0) SetEvent(& de->stopEvent); CODE; WaitEvent(& de->stopEvent); CODE; } DispatchRoutine( ) { int t; if ($) return; if (! de->stopping) { if ($) return; AtomicIncr(& de->count); // do useful work // … if ($) return; t = AtomicDecr(& de->count); if ($) return; if (t == 0) SetEvent(& de->stopEvent); } if ( !done ) { if ($) { done = T; PnpStop( ); } } CODE  bool done = F; main( ) { PnpStop( ); }

KISS features (I) KISS trades off soundness for scalability Sound for event-driven systems –embedded software, TinyOS Unsoundness is precisely quantifiable for other systems –e.g., for 2-thread program, explores all executions with up to two context switches

KISS features (II) Cost of analyzing a concurrent program P = cost of analyzing a sequential program Q –Size of Q asymptotically same as size of P Allows any sequential checker to analyze concurrency

However… Hard limit on number of explored contexts –e.g., two context switches for concurrent program with two threads

Is a tuning knob possible? Given a concurrent boolean program P and a positive integer c, does P go wrong by failing an assertion via an execution with at most c contexts?

          Context Context switch Problem: Unbounded computation possible within each context! Unbounded execution depth and reachable state space Different from bounded-depth model checking

Global store g, valuation to global variables Local store l, valuation to local variables Stack s, sequence of local stores State (g, s) Sequential boolean program

Global store g, valuation to global variables Local store l, valuation to local variables Stack s, sequence of local stores State (g, s) Sequential boolean program Transition relation: (g, s)  (g’, s’)

Reachability problem for sequential boolean program Given (g, s), is there s’ such that (g, s)  * (error,s’)? Reach(g, s) = { (g’,s’) | (g, s)  * (g’, s’) }

Aggregate state Set of stacks ss Aggregate state (g, ss) = { (g,s) | s  ss } Reach(g, ss) = ∪ {Reach(g,s) | s  ss}

Aggregate transition relation Suppose G = { g’ 1,…, g’ n } There is a unique partition of Reach(g, ss) into aggregate states: (g’ 1, ss’ 1 )  …  (g’ n, ss’ n ) (g, ss)  (g’ 1, ss’ 1 ). (g, ss)  (g’ n, ss’ n ) iff Reach(g,ss) = (g’ 1, ss’ 1 )  …  (g’ n, ss’ n )

Theorem (Buchi, Schwoon00) If ss is regular and (g, ss)  (g’, ss’), then ss’ is regular. If ss is given as a finite automaton A, then a finite automaton A’ for ss’ can be constructed from A in polynomial time.

Algorithm Solution: Compute automaton for ss’ such that (g, {s})  (error, ss’) and check if ss’ is nonempty. Problem: Given (g, s), is there s’ such that (g, s)  * (error,s’)?

Global store g, valuation to global variables Local store l, valuation to local variables Stack s, sequence of local stores State (g, s 1, s 2 ) Concurrent boolean program Transition relation: (g, s 1 )  (g’, s’ 1 ) in thread 1 (g, s 1, s 2 )  1 (g’, s’ 1, s 2 ) (g, s 2 )  (g’, s’ 2 ) in thread 2 (g, s 1, s 2 )  2 (g’, s 1, s’ 2 )

Reachability problem for concurrent boolean program Given (g, s 1, s 2 ), are there s’ 1 and s’ 2 such that (g, s 1, s 2 ) reaches (error, s’ 1, s’ 2 ) via an execution with at most c contexts?

Aggregate transition relation (g, ss 1 )  (g’, ss’ 1 ) in thread 1 (g, ss 1, ss 2 )  1 (g’, ss’ 1, ss 2 ) (g, ss 1, ss 2 )  2 (g’, ss 1, ss’ 2 ) (g, ss 2 )  (g’, ss’ 2 ) in thread 2 (g, ss 1, ss 2 ) = { (g, s 1, s 2 ) | s 1  ss 1, s 2  ss 2 }

Algorithm: 2 threads, c contexts   12   12   12 Depth c (g, {s 1 }, {s 2 }) Compute the set of reachable aggregate states. Report an error if (g, ss 1, ss 2 ) is reachable and g = error, ss 1 is nonempty, and ss 2 is nonempty.

Complexity: 2 threads, c contexts   12   12   12 Depth of tree = context bound c Branching factor bounded by G  2 (G = # of global stores) Number of edges bounded by (G  2) (c+1) Each edge computable in polynomial time Depth c (g, {s 1 }, {s 2 })

Results Algorithm for checking if a concurrent boolean program P fails an assertion via an execution with at most c contexts Algorithm for checking if a concurrent boolean program P with unbounded fork- join parallelism fails an assertion via an execution with at most c contexts