## 1 Introduction  
Fuzzing [1] is a software testing method that randomly mutates the inputs, trying to reach the target execution paths. It is widely used in the software security domain, especially in finding vulnerabilities and increasing program coverage. Random testing lacks efficiency since the search space of random mutation is too large to reach full coverage. If we try to fuzz a program with the input length as long as 10 bytes, the search space will be \(2^{80}\) (10 bytes, each byte with eight bits). The problem can be even worse when the length grows longer. Therefore, different fuzzing methods based on various assumptions have come out to satisfy different goals. Despite the diverse methods and techniques, the core ideas of these approaches are similar: reduce the size of the search space and reach the target path as fast as possible. Based on the method of mutating inputs, there are three different methods: mutation-based fuzzing, grammar-based fuzzing, and hybrid fuzzing. Mutation-based fuzzing [2; 3; 4; 5] uses different heuristics based on program analysis to reduce the search space for efficiency. The disadvantage of this method is that it is difficult to pass complicated format checks. Grammar-based fuzzing [6; 7; 8] solves this problem to some extent by inferring the structure of the inputs to pass the input format checks. However, this method needs extra work to build input file grammar before fuzzing. In recent years, hybrid fuzzing [9; 10; 11; 12; 13; 14] has been widely researched and discussed in the security domain, which utilizes the effectiveness of concolic execution [15; 16], as well as the efficiency of fuzzing. Concolic execution is a mixed method of symbolic execution [17] and concrete execution, which builds logical constraints of branches in the program and generates new test cases by negating and solving the branches with the help of Satisfiability Modulo Theories (SMT) solvers [18]. The advantage of hybrid fuzzing is that for complicated checks that are hard to find by mutation-based fuzzing, concolic execution can solve those checks and make program execution deeper into the longer paths. Meanwhile, fuzzing can find shallow paths with simple checks quickly and compensate for the larger resources spent by concolic execution. Existing works [9; 10; 15; 19] show that hybrid fuzzing can achieve more program execution coverage and find more bugs compared with mutation-based fuzzing. There are three implementation methods of applying hybrid fuzzing: demand launch, optimal switch, and synchronization. [PERSON] [9] and other works [15] used the demand launch strategy, which only triggers the concolic execution engine when fuzzing gets stuck. The core idea of this method seems reasonable since concolic execution is good at solving complicated checks, which fuzzing fails to pass; however, it is hard to define what a \"stuck\" situation looks like. The previous work [11] showed that for most evaluations using Driller, the concolic execution engine is never triggered in the limited time because fuzzing never reaches a \"stuck\" state. MDPC [19] uses the optimal switch method, which computes the costs of solving every branch by fuzzing and concolic execution, respectively, and then chooses the more economic method for each branch. DigFuzz [11] extends this method by a step forward to make optimal decisions more efficient. The demand launch method and optical switch method share one important feature in common: the fuzzer and the concolic execution engine are triggered sequentially instead of in parallel. It may seem practical with single-core CPUs, which have limited computation capability; however, with modern multi-core CPUs and large memory space, the power of parallel computation is not utilized enough. Thus, the third method by synchronization comes out, which runs the concolic execution engine in parallel with the fuzzer and synchronizes the newly generated test cases with the fuzzer from time to time.