Sequentialization has been shown to be an effective symbolic verification technique for safety properties in multi-threaded C programs using POSIX threads. The tool Lazy-CSeq, which applies a lazy sequentialization scheme, demonstrated its efficiency by ranking top places within the concurrency division of the Competitions on Software Verification in recent years. This lazy sequentialization scheme encodes all thread schedules up to a given exploration bound into a single non-deterministic sequential C program. Another particularly effective technique to tackle the state explosion of multi-threaded programs is Partial Order Reduction (POR). It works by limiting the exploration of redundant thread execution orders (i.e., schedules) by exploiting independence. Integrating POR into lazy sequentialization can further significantly improve its scalability. However, a combination of both techniques can lead to unsoundness, i.e., reachability of states is not preserved within the exploration bound. The reason is that POR is not aware of the exploration bound; therefore, POR can prune a schedule which will reach a bug within the exploration bound and instead keep another (functionally) equivalent schedule which requires a larger exploration bound to reach the bug. This paper presents a novel implementation of lazy sequentialization, which integrates symbolic POR into the encoding to prune redundant schedules. An efficient pruning check is employed to make the POR technique aware of the exploration bound and thus avoid unsoundness, while still providing significant state space reductions. On the other hand, the integration of POR entails some additional encoding overhead that can also result in a performance slowdown. Such a case is avoided by a practical heuristic that only employs POR if the encoding overhead of POR is reasonable (i.e., less than 50%). Experimental evaluation shows that our proposed approach can provide considerable speed-ups.
Read full abstract