- Research Article
14
- 10.1016/j.scico.2017.04.008
Concolic testing for functional languages
- May 12, 2017
- Science of Computer Programming
- Aggelos Giantsios + 2 more +2
Concolic testing for functional languages
Recently, symbolic execution has been witnessed great success in applying symbolic execution to automatically generate high coverage test inputs for programs testing. While path explosion still a primary challenge in scaling concolic testing to large programs, meanwhile constraint solving is fundamental to concolic testing as a constraint solver is continuously invoked during analysis, while the main roadblock to performance of concolic testing engines is the time spent in constraint solving. To address this issue and achieve the purpose of selecting the path with simplest constraints to solve, this paper presents a new heuristic search strategy which determines the path priority based on the complexity of path constraints. We have implemented the strategy in angr, a binary-oriented concolic testing tools implemented by Shellphish, and evaluated this strategy on CTF binaries provided by famous institution. The results show that our strategy achieves significantly greater testing efficiency in larger programs while no apparent effect in minor programs.
Concolic testing for functional languages
Concolic testing for functional languages
GuideSE: Annotations for Guiding Concolic Testing
Dynamic symbolic execution or concolic testing has been proposed recently to effectively generate test inputs for real-world programs. Unfortunately, dynamic symbolic execution techniques do not scale well for large realistic programs, because often the number of feasible execution paths of a program increases exponentially with the increase in the length of an execution path. We believe that programmers usually have a good understanding of programs they write and could help concolic testing to guide path exploration effectively so that concolic testing can achieve the desired testing goal quickly. In this paper, we propose a simple annotation mechanism, called Guide SE, that enables a programmer to guide concolic testing by annotating the program under test. We have currently identified three mechanisms for providing these annotations: control annotations, equivalence annotations, and data annotations. Controls annotations allow a programmer to specify the paths of interest using regular expressions. Equivalence annotations enable a programmer to prune paths that result in user defined equivalent states. Data annotations allow a programmer to abstract portions of the program under test and to explore the reduced path space of the abstract program.
Read moreScalable Concolic Testing of RTL Models
Simulation is widely used for validation of Register-Transfer-Level (RTL) models. While simulating with millions of random or constrained-random tests can cover majority of the functional scenarios, the number of remaining scenarios can still be huge (hundreds or thousands) in case of today's industrial designs. Hard-to-activate branches are one of the major contributors for such remaining/untested scenarios. While directed test generation techniques using formal methods are promising in activating branches, it is infeasible to apply them on large designs due to state space explosion. In this article, we propose a fully automated and scalable approach to cover the hard-to-activate branches using concolic testing of RTL models. While application of concolic testing on hardware designs has shown some promising results in improving the overall coverage, they are not designed to activate specific targets such as uncovered corner cases and rare scenarios. In other words, existing concolic testing approaches address state space explosion problem but leads to path explosion problem while searching for the uncovered targets. Our proposed approach maps directed test generation problem to target search problem while avoiding overlapping searches involving multiple targets. This article makes two important contributions. (1) We propose a directed test generation technique to activate a target by effective utilization of concolic testing on RTL models. (2) We develop efficient learning and clustering techniques to minimize the overlapping searches across targets to drastically reduce the overall test generation effort. Experimental results demonstrate that our approach significantly outperforms the state-of-the-art methods in terms of test generation time (up to 205X, 69X on average) as well as memory requirements (up to 31X, 7X on average).
Read moreAccelerating Symbolic Analysis for Android Apps
While tools based on symbolic execution are commonly used to analyze mobile applications, these tools can suffer from path explosion when real-world applications have more paths than available computing resources can handle. However, many of the paths are unsatisfiable, that is, no input exists that can satisfy all the path constraints and cause the path to execute. Unfortunately, analysis tools cannot determine this without constraint collection and constraint solving, which are expensive to perform. As a result, analysis tools waste valuable computational resources on unsatisfiable paths. In this work, we demonstrate that machine learning classifiers can predict unsatisfiable paths, resulting in a savings of computational resources. Our classifiers take path-level statistical features as input, and model inference can run immediately after a path is found. This saves analysis time spent on both constraint collection and constraint solving for unsatisfiable paths. We enhance the TIRO Android application analysis tool to avoid paths that are predicted to be unsatisfiable and show that a Random Forest model can achieve 95 % balanced predication accuracy in Android applications. We also show that modified TIRO is able to avoid analyzing 51 % of paths as they are unsatisfiable, resulting in a savings of 14 % of the analysis time.
Read moreHybrid Concolic Testing
We present hybrid concolic testing, an algorithm that interleaves random testing with concolic execution to obtain both a deep and a wide exploration of program state space. Our algorithm generates test inputs automatically by interleaving random testing until saturation with bounded exhaustive symbolic exploration of program points. It thus combines the ability of random search to reach deep program states quickly together with the ability of concolic testing to explore states in a neighborhood exhaustively. We have implemented our algorithm on top of CUTE and applied it to obtain better branch coverage for an editor implementation (VIM 5.7, 150 K lines of code) as well as a data structure implementation in C. Our experiments suggest that hybrid concolic testing can handle large programs and provide, for the same testing budget, almost 4times the branch coverage than random testing and almost 2times that of concolic testing.
Read morePartial Solution Based Constraint Solving Cache in Symbolic Execution
Constraint solving is one of the main challenges for symbolic execution. Caching is an effective mechanism to reduce the number of the solver invocations in symbolic execution and is adopted by many mainstream symbolic execution engines. However, caching can not perform well on all programs. How to improve caching’s effectiveness is challenging in general. In this work, we propose a partial solution-based caching method for improving caching’s effectiveness. Our key idea is to utilize the partial solutions inside the constraint solving to generate more cache entries. A partial solution may satisfy other constraints of symbolic execution. Hence, our partial solution-based caching method naturally improves the rate of cache hits. We have implemented our method on two mainstream symbolic executors (KLEE and Symbolic PathFinder) and two SMT solvers (STP and Z3). The results of extensive experiments on real-world benchmarks demonstrate that our method effectively increases the number of the explored paths in symbolic execution. Our caching method achieves 1.07x to 2.3x speedups for exploring the same amount of paths on different benchmarks.
Read moreQuantum Concolic Testing
This paper presents the first concolic testing framework explicitly designed for quantum programs. The framework introduces quantum constraint generation methods for quantum control statements that quantify quantum states and offers a symbolization method for quantum variables. Based on this framework, we generate path constraints for each concrete execution path of a quantum program. These constraints guide the exploration of new paths, with a quantum constraint solver determining outcomes to create novel input samples, thereby enhancing branch coverage. Our framework has been implemented in Python and integrated with Qiskit for practical evaluation. Experimental results show that our concolic testing framework improves branch coverage, generates high-quality quantum input samples, and detects bugs, demonstrating its effectiveness and efficiency in quantum programming and bug detection. Regarding branch coverage, our framework achieves more than 74.27% on quantum programs with under 5 qubits.
Read moreScaling symbolic execution using ranged analysis
This paper introduces a novel approach to scale symbolic execution --- a program analysis technique for systematic exploration of bounded execution paths---for test input generation. While the foundations of symbolic execution were developed over three decades ago, recent years have seen a real resurgence of the technique, specifically for systematic bug finding. However, scaling symbolic execution remains a primary technical challenge due to the inherent complexity of the path-based exploration that lies at core of the technique. Our key insight is that the state of the analysis can be represented highly compactly: a test input is all that is needed to effectively encode the state of a symbolic execution run. We present ranged symbolic execution, which embodies this insight and uses two test inputs to define a range, i.e., the beginning and end, for a symbolic execution run. As an application of our approach, we show how it enables scalability by distributing the path exploration---both in a sequential setting with a single worker node and in a parallel setting with multiple workers. As an enabling technology, we leverage the open-source, state-of-the-art symbolic execution tool KLEE. Experimental results using 71 programs chosen from the widely deployed GNU Coreutils set of Unix utilities show that our approach provides a significant speedup over KLEE. For example, using 10 worker cores, we achieve an average speed-up of 6.6X for the 71 programs.
Read moreBinary-oriented hybrid fuzz testing
In software security testing, fuzz testing and symbolic execution are two main testing techniques. Fuzz testing finds program bugs by executing the target program with random inputs it generates while monitoring the execution for abnormal behaviors. Though fuzz testing is able to explore deep into a program's state space efficiently, it usually cannot guarantee the code coverage ratio in many situations. Symbolic execution treats a program's input as symbols and executes the program with such symbolic input. Symbolic execution tries to explore the whole state space of a program by analyzing all of the program's paths. Symbolic execution always guarantees the code coverage ratio in theory, but for real programs it has many problems such as path explosion. Our new method, hybrid fuzz testing, combines the benefits of fuzz testing and symbolic execution. Symbolic execution will start to work and to generate new program input when fuzz testing cannot increase the code coverage ratio. The experiment result implies that our hybrid fuzz testing method can obviously increase the code coverage ratio efficiently in reasonable resources using.
Read moreEliminating Path Redundancy via Postconditioned Symbolic Execution
Symbolic execution is emerging as a powerful technique for generating test inputs systematically to achieve exhaustive path coverage of a bounded depth. However, its practical use is often limited by path explosion because the number of paths of a program can be exponential in the number of branch conditions encountered during the execution. To mitigate the path explosion problem, we propose a new redundancy removal method called postconditioned symbolic execution. At each branching location, in addition to determine whether a particular branch is feasible as in traditional symbolic execution, our approach checks whether the branch is subsumed by previous explorations. This is enabled by summarizing previously explored paths by weakest precondition computations. Postconditioned symbolic execution can identify path suffixes shared by multiple runs and eliminate them during test generation when they are redundant. Pruning away such redundant paths can lead to a potentially exponential reduction in the number of explored paths. Since the new approach is computationally expensive, we also propose several heuristics to reduce its cost. We have implemented our method in the symbolic execution engine KLEE [1] and conducted experiments on a large set of programs from the GNU Coreutils suite. Our results confirm that redundancy due to common path suffix is both abundant and widespread in real-world applications.
Read moreMultiSE: multi-path symbolic execution using value summaries
Dynamic symbolic execution (DSE) has been proposed to effectively generate test inputs for real-world programs. Unfortunately, DSE techniques do not scale well for large realistic programs, because often the number of feasible execution paths of a program increases exponentially with the increase in the length of an execution path. In this paper, we propose MultiSE, a new technique for merging states incrementally during symbolic execution, without using auxiliary variables. The key idea of MultiSE is based on an alternative representation of the state, where we map each variable, including the program counter, to a set of guarded symbolic expressions called a value summary. MultiSE has several advantages over conventional DSE and conventional state merging techniques: value summaries enable sharing of symbolic expressions and path constraints along multiple paths and thus avoid redundant execution. MultiSE does not introduce auxiliary symbolic variables, which enables it to 1) make progress even when merging values not supported by the constraint solver, 2) avoid expensive constraint solver calls when resolving function calls and jumps, and 3) carry out most operations concretely. Moreover, MultiSE updates value summaries incrementally at every assignment instruction, which makes it unnecessary to identify the join points and to keep track of variables to merge at join points. We have implemented MultiSE for JavaScript programs in a publicly available open-source tool. Our evaluation of MultiSE on several programs shows that 1) value summaries are an eective technique to take advantage of the sharing of value along multiple execution path, that 2) MultiSE can run significantly faster than traditional dynamic symbolic execution and, 3) MultiSE saves a substantial number of state merges compared to conventional state-merging techniques.
Read moreLazy symbolic execution for test data generation
In the context of test data generation, symbolic execution gets more attention as computing power increases continuously. Experiments show that test generation tools based on symbolic execution can get high coverage and find bugs on real applications. However, symbolic execution still has limitations in handling some complex program structures such as pointers, arrays and library functions. To address the problem, this study proposes a technique called lazy symbolic execution, which combines symbolic execution with a lazy evaluation strategy. The authors approach is motivated by the observation that some program structures can be reasoned about symbolically and the others have to be evaluated concretely. Traditional symbolic execution can cope with the former well, whereas lazy symbolic evaluation is used to handle the latter. However, lazy symbolic evaluation introduces intermediate variables into path constraints. To eliminate those variables, concrete values for some input variables are first obtained by constraint solving or searching processes. Then, the given path is executed again using inputs consisting of concrete and symbolic values. The procedure is repeated until all intermediate variables are wiped out. The authors have implemented a prototype tool and performed some experiments. The empirical results show the effectiveness of their approach.
Read moreDiscrete Optimization on Train Rescheduling on Single-Track Railway: Clustering Hierarchy and Heuristic Search
This paper focuses on discrete dynamic optimization on train rescheduling on single-track railway with the consideration of train punctuality and station satisfaction degree. A discrete dynamic system is firstly described to mimic train rescheduling, and a state transition function is specially designed according to the train departure event. The purpose of this function is to improve simulation efficiency by directly confirming the next discrete time. After the construction and analysis of optimization models to discrete dynamic system, a two-stage heuristic search strategy is developed, by using clustering hierarchy theory and stochastic search strategy, to obtain train departure time and arrival time before each state transition of the system. Finally, a numerical experiment is conducted to verify the proposed system, models, and the heuristic search strategy. The result shows that the discrete dynamic system, together with the state transition function and heuristic search strategy, shows better performance in simulation efficiency and solution quality.
Read moreOptimizing Symbolic Execution Path Exploration with a Transfer Learning-Based Strategy
aSymbolic execution is an important software analysis technique, but it faces challenges such as path explosion, which leads to a reduction in efficiency. Existing path exploration strategies, such as Random State Search, typically exhibit poor adaptability to real-world programs and lack effective path selection strategies. To address these challenges, this paper proposes a Transfer Learning-based Symbolic Execution Path Exploration Strategy, TLS (Transfer Learning Search). We adopt a transfer learning method based on functional classification to optimize existing symbolic execution strategies. Specifically, real-world programs are classified according to their functional characteristics, and transfer learning is applied by freezing partial layers of existing neural networks with training sets from each program family that better reflect its features. Multiple models are trained based on different training sets to adapt to various program families. Experimental results show that this strategy solves the problem of insufficient training data for real-world programs. Compared to traditional heuristic methods such as random-path (rps) and random-state (rss) strategies, this approach significantly improves instruction coverage and branch coverage on specific program families. For example, in the Grep program test, branch coverage increased by approximately fifteen percentage points, generating more test cases. This approach provides a new and effective solution to the adaptability problem of symbolic execution for complex programs.
Read moreInvasive Software Testing: Mutating Target Programs to Diversify Test Exploration for High Test Coverage
Software testing techniques have advanced significantly over several decades; however, most of current techniques still test a target program as it is, and fail to utilize valuable information of diverse test executions on many variants of the original program in test generation. This paper proposes a new direction for software testing - Invasive Software Testing (IST). IST first generates a set of target program variants m <sub xmlns:mml="http://www.w3.org/1998/Math/MathML" xmlns:xlink="http://www.w3.org/1999/xlink">1</sub> , ⋯, m <sub xmlns:mml="http://www.w3.org/1998/Math/MathML" xmlns:xlink="http://www.w3.org/1999/xlink">n</sub> from an original target program p by applying mutation operations <sub xmlns:mml="http://www.w3.org/1998/Math/MathML" xmlns:xlink="http://www.w3.org/1999/xlink">1</sub> , ⋯, μ <sub xmlns:mml="http://www.w3.org/1998/Math/MathML" xmlns:xlink="http://www.w3.org/1999/xlink">n</sub> . Second, given a test suite T, IST executes m <sub xmlns:mml="http://www.w3.org/1998/Math/MathML" xmlns:xlink="http://www.w3.org/1999/xlink">1</sub> , ⋯, m <sub xmlns:mml="http://www.w3.org/1998/Math/MathML" xmlns:xlink="http://www.w3.org/1999/xlink">n</sub> with T and records the test runs which increase test coverage compared to p with T. Based on the recorded information, IST generates guideposts for automated test generation on p toward high test coverage. Finally, IST generates test inputs on p with the guideposts and achieves higher test coverage. We developed DEMINER which implements IST for C programs through software mutation and concolic testing. Further, we showed the effectiveness of DEMINER on three real-world target programs Busybox-ls, Busybox-printf, and GNU-find. The experiment results show that the amount of the improved branch coverage by DEMINER is 24.7% relatively larger than those of the conventional concolic testing techniques on average.
Read more