Optimized fuzzing method and system for JavaScript JIT compiler guided by dry path
By optimizing the fuzzing method guided by the dry path, the problem of insufficient testing of optimization sub-strategies in the JavaScript JIT compiler was solved, improving testing efficiency and coverage, and uncovering more vulnerabilities.
Patent Information
- Authority / Receiving Office
- CN · China
- Patent Type
- Patents(China)
- Current Assignee / Owner
- INST OF COMPUTING TECH CHINESE ACAD OF SCI
- Filing Date
- 2023-12-13
- Publication Date
- 2026-07-31
AI Technical Summary
Existing JavaScript engine fuzzing methods are prone to encountering edge coverage bottlenecks when triggering the JavaScript JIT compiler's optimization sub-strategies, leading to decreased testing efficiency and difficulty in discovering vulnerabilities in complex optimization sub-strategies.
We employ an optimized dry path-guided fuzzing approach. By marking optimized dry paths in the JavaScript engine and combining edge coverage and path coverage, we prioritize testing of underexplored optimized dry paths. Furthermore, we generate effective test cases through pruning and scheduling strategies, thereby improving testing efficiency.
It effectively discovers under-tested optimization sub-strategies in the JavaScript JIT compiler, improves the vulnerability discovery rate, avoids path explosion problems, and improves the efficiency and coverage of fuzzing.
Smart Images

Figure CN117687915B_ABST
Abstract
Description
Technical Field
[0001] This invention belongs to the field of network security technology, and specifically relates to a method and system for fuzzing JavaScript JIT compilers that optimizes dry path guidance. Background Technology
[0002] JavaScript engines support the execution of JavaScript scripts and are widely used, including web browsers, PDF readers for office work, Node.js for website development, and React Native for mobile applications. To improve execution efficiency, major manufacturers have incorporated Just-In-Time (JIT) compilers into their JavaScript engines. These compilers compile frequently accessed code during runtime, generating high-quality native code. JavaScript is a dynamically typed language; variable types are determined at runtime and may change as the code executes. This significantly increases the complexity of JIT compiler optimizations, making the highly complex JIT compiler itself a major vulnerability area for JavaScript engines. In recent years, JIT compiler-related vulnerabilities have accounted for a vast majority of JavaScript engine vulnerabilities.
[0003] Fuzzing is a primary method for software vulnerability discovery. JavaScript engines are interpreter-based programs, and only syntactically correct test cases can test the functional code within the engine. To improve syntactic pass rates, Jsfunfuzz generates test cases based on syntactic rules. LangFuzz maintains a pool of code snippets during fuzzing, combining and mutating these snippets in the form of an abstract syntax tree. Skyfire builds a probabilistic model based on a corpus, learns the syntactic rules of test cases, and uses this model to construct syntactically valid test cases. Superion's initial seeds come from a pre-prepared corpus, while Nautilus generates initial seeds based on context-free grammars. Both mutate and prune seeds on an abstract syntax tree to generate new test cases.
[0004] Beyond improving the syntax pass rate of test cases, enhancing the semantic effectiveness and richness of test cases is equally important for deep testing of JavaScript engine code. CodeAlChemist generates test cases by combining different code snippets. Each code snippet generates combination constraints based on variable definitions and usage; different snippets can only be combined if these constraints are met. DIE performs mutation on an abstract syntax tree with type information, preserving some structural and type information of the seed during the mutation process. Montage transforms the abstract syntax tree of JavaScript test cases into a series of subtrees, uses these subtrees to train a neural network language model (NNLM), and replaces the original subtrees with the abstract syntax subtrees generated by NNLM to generate new test cases. Favocado tests the binding layer of the JavaScript runtime, generating semantically correct test cases by using pre-extracted semantic information and dynamic context information. Sofi uses fine-grained static and dynamic program analysis techniques to identify variable types and performs type-related mutations during the mutation phase. Furthermore, SoFi uses rule-based semantic repair methods to fix semantic errors introduced by mutations and leverages JavaScript's reflection mechanism to improve the semantic richness of test cases.
[0005] Recent work has shifted the focus of fuzzing to a core component of JavaScript engines—the JIT compiler. Fuzzilli proposes a novel intermediate representation on which syntactic and semantic mutations are performed. To trigger JIT, Fuzzilli designs a series of generation templates and mutation rules. FuzzJIT uses pre-designed templates to increase the probability of test cases triggering JIT and implements differential testing between interpreted and JIT execution at the JavaScript language level. JIT-picking implements an inspector by modifying the engine's source code, uncovering defects in the engine by comparing the results of interpreted and JIT execution.
[0006] The methods described above improve fuzzing from different levels and have all discovered a certain number of new vulnerabilities. Among these methods, jsfunfuzz, LangFuzz, CodeAlChemist, Montage, and Favocado do not use feedback information, while the others use edge coverage as feedback information to guide seed selection (Superion uses basic block coverage for some test targets). JinghanWang et al. conducted experimental demonstrations on edge coverage and path coverage with lengths of 2, 4, and 8, pointing out that longer path coverage is not very meaningful for vulnerability discovery, and using longer paths as feedback information will encounter the path explosion problem, which leads to a sharp increase in the number of seeds, making seed selection meaningless. Currently, fuzzing for JavaScript engines still uses edge coverage as the main feedback information. As fuzzing progresses, the growth rate of edge coverage slows down, and the number of newly added edges decreases. When edge coverage reaches a bottleneck, fuzzing finds it difficult to select seeds that trigger new edges. At this point, gray-box testing degenerates into black-box testing, and how to continue exploring bugs in the target software becomes an important issue for fuzzing. Summary of the Invention
[0007] To address the aforementioned problems, this invention proposes a fuzzing method for JavaScript JIT compilers guided by optimized dry paths, comprising: an initialization step, marking optimized dry paths in the JavaScript engine, where the optimized dry paths are the paths corresponding to the optimization sub-strategies during JIT compiler optimization operations; a first testing step, performing fuzzing on the JavaScript engine using edge coverage and generating seeds to construct a seed set; repeating the first testing step, and executing a second testing step when the edge coverage growth reaches zero; the second testing step, selecting seeds from the seed set for mutation to generate test cases, and performing fuzzing on the JavaScript engine using both edge coverage and path coverage methods; if the execution of a test case triggers a new edge or a new optimized dry path, adding the test case as a seed to the seed set; repeating the second testing step until the fuzzing of the JavaScript engine is completed.
[0008] In the JavaScript JIT compiler fuzzing method of this invention, in the second testing step, when the execution of the test case triggers a new edge or a new optimized trunk path, useless statements in the test case are pruned to generate a simplest seed; if the simplest seed can trigger the new edge, the simplest seed is added to the edge seed queue of the seed set; if the simplest seed can trigger the new optimized trunk path, the simplest seed is added to the path seed queue of the seed set.
[0009] In the JavaScript JIT compiler fuzzing method of the present invention, in the second testing step, for the seeds in the path seed queue, the cumulative number of times all optimized trunk paths are triggered by all current test cases is recorded, and the seeds generated by the test cases corresponding to the optimized trunk paths whose cumulative number of triggers is less than the test threshold are selected for the next round of testing steps; for the seeds in the edge seed queue, a seed is randomly selected for the next round of testing steps.
[0010] The JavaScript JIT compiler fuzzing method of the present invention identifies optimized dry paths in the JavaScript engine during the initialization step and operates the JavaScript engine to insert collection code, so as to collect information of the optimized dry paths during the execution of the JavaScript engine.
[0011] This invention also proposes a JavaScript JIT compiler fuzzing system guided by optimized dry paths, comprising: an initialization module for marking optimized dry paths in the JavaScript engine; the optimized dry path is the path corresponding to the optimization sub-strategy when the JIT compiler performs optimization operations; a first testing module for performing edge coverage fuzzing on the JavaScript engine and generating seeds to construct a seed set; a second testing module for selecting seeds from the seed set for mutation to generate test cases, and performing edge coverage and path coverage fuzzing on the JavaScript engine; if the execution of the test case triggers a new edge or a new optimized dry path, the test case is added as a seed to the seed set; when performing fuzzing on the JavaScript engine, the first testing module is repeatedly called until the edge coverage rate increases to zero, and then the second testing module is repeatedly called until the fuzzing of the JavaScript engine is completed.
[0012] The JavaScript JIT compiler fuzz testing system of the present invention includes a second testing module comprising a seed selection module for selecting test cases as seeds to be added to the seed set; wherein, when the execution of the test case triggers a new edge or a new optimized trunk path, useless statements in the test case are pruned to generate a simplest seed; if the simplest seed can trigger the new edge, the simplest seed is added to the edge seed queue of the seed set; if the simplest seed can trigger the new optimized trunk path, the simplest seed is added to the path seed queue of the seed set.
[0013] The JavaScript JIT compiler fuzzing system of the present invention further includes a scheduling module in the second testing module, which is used to select seeds to perform fuzzing tests on the JavaScript engine. Specifically, for seeds in the path seed queue, the cumulative number of times all optimized trunk paths are triggered by all current test cases is recorded. Seeds generated by test cases corresponding to optimized trunk paths with a cumulative number of triggers less than a test threshold are selected for use in the current testing step. For seeds in the edge seed queue, seeds are randomly selected for use in the current testing step.
[0014] The JavaScript JIT compiler fuzzing system of the present invention includes an initialization module comprising an instrumentation module for identifying optimized dry paths in the JavaScript engine and operating the JavaScript engine to insert collection code, thereby collecting information of the optimized dry paths during the execution of the JavaScript engine.
[0015] The present invention also proposes a computer-readable storage medium storing computer-executable instructions, characterized in that, when the computer-executable instructions are executed, the JavaScript JIT compiler fuzzing method with optimized dry path guidance as described above is implemented.
[0016] The present invention also proposes a data processing apparatus, including the computer-readable storage medium as described above, wherein when the processor of the data processing apparatus retrieves and executes the computer-executable instructions in the computer-readable storage medium, it implements fuzz testing of the JavaScript JIT compiler with optimized dry path guidance. Attached Figure Description
[0017] Figure 1 This is a schematic diagram of the framework of the optimized dry path-guided JavaScript JIT compiler fuzzing method of the present invention.
[0018] Figure 2 This is a schematic diagram of the data processing device of the present invention. Detailed Implementation
[0019] To make the objectives, technical solutions, and advantages of this invention clearer, the invention will be further described in detail below with reference to the accompanying drawings. It should be understood that the specific embodiments described herein are merely illustrative of the invention and are not intended to limit the invention.
[0020] To further investigate vulnerabilities in the JIT compiler of JavaScript engines, the inventors analyzed three representative JavaScript engines: Google's V8, Apple's JSC, and Mozilla's SpiderMonkey, and summarized the following three new insights:
[0021] Understanding 1: The JIT compiler consists of numerous optimizations, such as control flow simplification, common subexpression elimination, and loop-invariant code hoisting. When the execution frequency of a piece of JavaScript code reaches the threshold required for optimization, the JIT compiler processes the code using a set of optimizations. Each optimization uses code analysis to determine whether the code meets a certain optimization condition. Only when the optimization condition is met will the JIT compiler actually transform the code. Errors in optimization can lead to security vulnerabilities in the JIT native code. For example, incorrect type inference may cause necessary type checking to be mistakenly eliminated, resulting in type confusion vulnerabilities; incorrect code hoisting or incorrect integer range analysis may lead to array out-of-bounds vulnerabilities.
[0022] Insight 2: Even within the same optimization, when transforming code, different optimization sub-strategies must be selected based on the specific characteristics of the code, such as control flow structure, data flow relationships, instruction types, and called built-in functions. According to the inventors, most optimizations involve approximately 1 to 20 optimization sub-strategies, and some optimizations may have even more. Different optimization sub-strategies within the same optimization can reuse code. The inventors discovered that the execution of some optimization sub-strategies can completely cover the jump edges on the paths of other optimization sub-strategies. This means that in fuzzing tests where edge coverage is the feedback, even if a certain input file triggers a new optimization sub-strategy, it will not be selected into the seed queue because the edge coverage has not increased.
[0023] Insight 3: In the same optimization process, each sub-strategy has its own preconditions. Sub-strategies with preconditions that are difficult to satisfy are less likely to be adequately tested. Sub-strategies that are not adequately tested are more likely to have vulnerabilities or flaws. To discover potential vulnerabilities or flaws, sub-strategies with difficult-to-satisfy preconditions should be tested more extensively. However, current fuzzing methods spend most of their computational resources on sub-strategies with easily satisfyable preconditions.
[0024] As we can see from the above, the JavaScript JIT compiler contains numerous optimization stages. Each optimization stage has an outer loop used to analyze and optimize the intermediate representation of the JavaScript program. Only when optimizations are triggered can vulnerabilities or defects in the JIT compiler be detected. Each optimization contains multiple optimization sub-strategies; for those sub-strategies that are difficult to test or have relatively stringent preconditions, more testing resources should be allocated to them.
[0025] The inventors conducted an in-depth analysis of the optimization process and discovered that each optimization sub-strategy corresponds to a path in the optimization process. This invention refers to this path as the optimization dry path. The optimization dry path is a path in the JIT compiler optimization, used to approximate different optimization sub-strategies in the optimization. The starting point of the optimization dry path is the entry point of the outer loop iteration, and the ending point is the tail code of the outer loop iteration. When a JavaScript program is compiled using JIT, execution from the starting point to the ending point forms a path. Excluding nested loops on this path, the optimization dry path is formed.
[0026] Based on this, the present invention proposes a fuzzing method for JavaScript JIT compilers guided by optimized dry path coverage, which allocates computing power to newly emerging optimized dry paths or optimized dry paths with low testing frequency in order to discover vulnerabilities or defects in the JavaScript engine JIT compiler.
[0027] Fuzzing a JIT compiler involves not only testing different optimizations but also testing as many sub-optimization strategies as possible, while allocating more testing resources to the less frequently tested sub-optimization strategies. However, finding the code corresponding to the sub-optimization strategies is a challenge in fuzzing.
[0028] The technical solution of the optimized dry path-guided JavaScript JIT compiler fuzzing method of the present invention includes the following key technical points:
[0029] 1. Use optimized trunk paths to approximate the optimization of sub-policies.
[0030] The reason why multiple sub-policies exist in an optimization is that each sub-policy has different preconditions. When a precondition is met, the optimization executes the code corresponding to that sub-policy, and these codes form a path. From this perspective, an optimization sub-policy can actually be approximated using the path in the optimization.
[0031] Previous work has explored seed selection methods based on path feedback, where a path is defined as a series of consecutive edges. The path length represents the number of edges contained within the path. In path feedback-based seed selection, a test case is considered interesting if it differs from previous paths. Research by Jinghan Wang et al. shows that using path feedback does not offer a significant advantage in exploring new code, and when the path length exceeds 8, it causes an explosion in the number of seeds, leading to a loss of target for testing. Using optimized paths to approximate sub-strategies also faces the path explosion problem. To address this issue, this invention proposes the concept of optimizing dry paths.
[0032] Based on the analysis of optimizations in the JavaScript JIT compiler, the inventors discovered that optimizations in the JIT compiler typically involve an outer loop. This loop scans the intermediate representation corresponding to a function or a piece of code, analyzing and processing it. From an optimization design perspective, whether it's simple instruction-level optimization, complex loop optimization, or function-level optimization based on control flow structures and data flow relationships, it's necessary to traverse the intermediate representation. Inside the outer loop is the code used to analyze and process the intermediate representation. This part of the code often contains multiple branch conditions, and the code after each branch condition is the specific implementation of the optimization sub-strategy. This part of the code typically exhibits two patterns:
[0033] Pattern 1: The loop contains multiple paths, each executed based on different preconditions, optimizing different types of code, and avoiding code reuse between paths. In this pattern, each path is actually an optimization sub-strategy.
[0034] Mode 2: A circular memory contains multiple modules, each with several paths. These paths are combined to form an optimization sub-strategy. In this mode, different optimization sub-strategies reuse some code, and a path within a module may belong to different optimization sub-strategies.
[0035] The paths in the two modes mentioned above often contain nested loops. After analysis, the code of these nested loops is also part of the current optimization sub-strategy.
[0036] In the first mode, the triggering of new optimization sub-policies is often accompanied by the triggering of new edges, and different optimization sub-policies can be described using jump edges. Current fuzz testing based on edge coverage feedback actually focuses on the optimization sub-policies in this mode during the testing process.
[0037] For the second mode, because different optimization sub-strategies reuse code, after some optimization sub-strategies are triggered, the transition edges of the remaining optimization sub-strategies may be completely overwritten. Using edge overwriting cannot describe the optimization sub-strategies in this mode. In this mode, the combination of different branches between modules is actually a path composed of several continuous edges. Therefore, the path within the outer loop can be used to describe the optimization sub-strategy. Different loop iterations of the same loop structure may generate different paths to represent different optimization sub-strategies depending on the intermediate representations processed, the control flow structure, and the data flow relationships.
[0038] In both the first and second modes, nested loops may exist within the optimization sub-policy. Different iterations within these nested loops generate different paths, which are contained within the path of a single iteration of the outer loop. Therefore, using paths from the outer loop to describe the optimization sub-policy may lead to path explosion. In reality, different iteration counts within nested loops do not necessarily imply different optimization sub-policies. Therefore, when using paths from the outer loop to describe the optimization sub-policy, the paths generated by executing nested loops can be excluded.
[0039] Based on the discussion of the two modes above, this invention proposes the concept of an optimized dry path. The starting point of the optimized dry path is the entry point of the outer loop iteration, and the ending point of the path is the tail code of the loop iteration. If there are nested loops in the loop iteration, the iterations in the nested loops are not included in the optimized dry path.
[0040] Based on an understanding of optimization sub-strategies in JIT compiler optimization, this invention uses an optimization trunk path approximation optimization sub-strategy to limit each optimization trunk path to one loop iteration, thereby avoiding the path explosion problem.
[0041] 2. Seed selection based on optimized trunk path
[0042] This invention records the optimized dry paths executed by the JavaScript engine when executing test cases during fuzz testing. If a test case triggers a new optimized dry path, it is selected as a seed and enters the next round of mutation testing.
[0043] When the JavaScript engine reaches the beginning of an optimized trunk path, it begins recording the path. When it reaches the end of the optimized trunk path, it stops recording and performs a hash operation on each edge of the currently recorded path in sequence, obtaining a hash value. Different optimizations, or the same optimization but different optimized trunk paths, will inevitably contain different sequences of basic blocks, producing different hash values without hash collisions. Fuzz testing records these hash values and, after the test cases have finished executing, checks for the presence of new hash values to determine whether the test cases have reached a new optimized trunk path.
[0044] Numerous studies have demonstrated the crucial role of edge-cover feedback in fuzzing for exploring new code within a target program. Therefore, this invention combines edge-cover feedback with optimized dry path feedback. Edge-cover feedback explores new code, uncovering more optimizations, while optimized dry path feedback enables in-depth testing of optimization sub-strategies. Seeds selected through edge-cover and optimized dry path feedback are placed in separate seed queues. In the initial stages of fuzzing, most seeds are selected through edge-cover; to quickly explore more code, these seeds are primarily used for mutation. As fuzzing progresses, the number of seeds selected through optimized dry path feedback gradually increases. Once the number of seeds reaches a certain threshold, optimized dry path feedback is then activated. Subsequently, fuzzing alternates between edge-cover and optimized dry path feedback.
[0045] The selected seeds may introduce code unrelated to the new edge or optimized trunk path due to mutations. To reduce the impact of this code, test cases need to be pruned. Pruning is based on the premise that the pruned test case can still reach the new edge or optimized trunk path. If a test case reaches both the new edge and the optimized trunk path, depending on the pruning criteria, this test case will generate two different seeds, one entering the seed queue for triggering the new edge and the other for triggering the optimized path. Pruning a test case into two seeds ensures that edge coverage seeding and optimized trunk path seeding are orthogonal.
[0046] 3. Seed scheduling strategy based on optimized trunk path
[0047] During fuzz testing, optimized trunk paths with easily satisfyable preconditions receive more testing opportunities, while those with less favorable preconditions are tested far less frequently. Seed scheduling based on optimized trunk paths aims to give those less frequently tested optimized trunk paths more testing opportunities.
[0048] To select optimized trunk paths with fewer test runs, this invention records the cumulative number of times each optimized trunk path has been tested, as well as the test cases that triggered that optimized trunk path. Each time the JavaScript engine executes a test case, it records the optimized trunk paths touched by that test case and updates the cumulative number of tests for each optimized trunk path accordingly.
[0049] The present invention also designs a two-level seed queue. When a test case triggers a new optimized dry path, the test case is selected as a seed, and after being pruned, it is placed in the first-level seed queue.
[0050] A secondary queue is used to store the seeds that should be prioritized. Specifically, based on the number of times the optimized trunk path has been tested, n seeds are selected from the primary queue and stored in the secondary queue. Next, seeds are sequentially retrieved from the secondary queue for mutation testing. After all seeds in the secondary queue have been dequeued, seeds are reselected from the primary queue based on the number of times the optimized trunk path has been tested and added to the secondary queue.
[0051] The JIT compiler fuzzing method of the present invention will be described in detail below with reference to the accompanying drawings.
[0052] Figure 1 This is a schematic diagram of the framework of the optimized dry path-guided JavaScript JIT compiler fuzzing method of the present invention. Figure 1 As shown, the JavaScript JIT compiler fuzzing method of the present invention includes:
[0053] Step S1, Offline instrumentation
[0054] The JavaScript engine under test is analyzed to identify the optimized dry paths in the engine. Based on the information obtained from the analysis, the JavaScript engine is instrumented. The purpose of instrumentation is to insert necessary code into the JavaScript engine to collect information about optimized dry paths during the execution of the JavaScript engine.
[0055] The stake driving process consists of two sub-steps.
[0056] Step S11: Identify various basic blocks in the optimized trunk path; This invention implements an LLVM Pass named OptPathAnalysis. LLVM's LoopInfoWrapperPass can analyze all loop information in the target program. OptPathAnalysis obtains each loop structure and loop nesting relationship in the JavaScript engine's JIT optimization module. The entry basic block of the outer loop is marked as the starting point of an optimized trunk path; the exit basic block of the outer loop is marked as the ending point of the optimized trunk path; the basic blocks between the entry and exit are marked as basic blocks of the optimized trunk path. To avoid path explosion caused by nested loops, basic blocks within nested loops are excluded.
[0057] Step S12: Utilizing LLVM's Sanitizer Coverage Pass, the JavaScript execution engine collects edge and optimized trunk path coverage information by inserting a call to `trace_pc_guard(uint32*guard, uint32 flag)`. `trace_pc_guard(uint32*guard, uint32 flag)` is a function added to the JavaScript execution engine's source code for collecting edge and optimized trunk path information. To facilitate the collection of basic block information for optimized trunk paths, each basic block is assigned a unique number. The first parameter, `guard`, receives the basic block number. The second parameter, `flag`, receives the attribute information of the basic block analyzed by `OptPathAnalysis`: 0 indicates a normal basic block, 1 indicates the basic block is the starting point of the optimized trunk path, 2 indicates the basic block is the ending point of the optimized trunk path, and 3 indicates the basic block is a basic block within a nested loop in the optimized trunk path.
[0058] `start` is a global variable, initially set to `false`, indicating that no optimized trunk path start point has been encountered yet. At this point, the variable `path`, used to record optimized trunk path information, is empty. After entering the `__sanitizer_cov_trace_pc_guard` function, it first records the edge coverage information of the test case execution based on the edge bitmap indexed by the basic block number. (The SanitizerCoverage Pass breaks critical edges before instrumentation, ensuring that each instrumented basic block corresponds to one edge, thus allowing basic block coverage to approximate edge coverage.) Next, it collects optimized trunk path information based on the basic block's `flag` attribute. If the current basic block's `flag` is 1, it means that the current execution has reached the start point of an optimized trunk path, so `start` is set to `true`, indicating that recording the optimized trunk path begins, and the currently executed basic block number is recorded in the optimized trunk path. If `flag` is 0 and `start` is `true`, it means that the basic block belongs to the current optimized trunk path, and its number is added to the optimized trunk path. If flag is 2, it means the current basic block is the end point of an optimized trunk path. In this case, the number of the current basic block is added to the optimized trunk path, a hash value is calculated for all basic blocks in the optimized trunk path, and the path bitmap used to record optimized trunk path information is updated based on the hash value. Then, start is set to false, and the optimized trunk path is cleared. If flag is 3, it means the currently executed basic block belongs to a nested loop, and this basic block will not be recorded in the optimized trunk path.
[0059] Step S2, Dynamic Fuzz Test
[0060] Seed selection is achieved by using edge coverage and optimized dry path coverage. New code is explored through edge coverage, and errors in the JIT code are triggered through optimized dry path coverage. Seeds are also scheduled according to the degree to which optimized dry paths are tested, with priority given to testing optimized dry paths that have been tested less frequently.
[0061] The dynamic fuzzing test process includes sub-steps such as mutation, execution, seed selection evaluation, pruning, and scheduling.
[0062] Step S21, mutation
[0063] Similar to other fuzzing methods targeting JavaScript script execution engines, the mutation sub-steps of this invention include mutation strategies such as operator mutation, operand mutation, script concatenation, function call parameter mutation, and expression substitution. By applying these strategies to the seed, new test cases are generated.
[0064] Step S22, Execute
[0065] Start the instrumented JavaScript execution engine and hand over the mutated test cases to it for execution. During execution, the JavaScript engine updates the edge bitmap and path bitmap, which record the bitmap information corresponding to the edges and optimized paths reached by the test cases, respectively.
[0066] The execution process first employs edge coverage fuzzing. When the edge coverage rate no longer increases, a hybrid edge coverage and path coverage fuzzing method is used. Specifically, when performing edge coverage fuzzing, if the edge coverage rate no longer increases in a specified number of rounds (e.g., 100 rounds), a hybrid edge coverage and path coverage fuzzing method is then performed.
[0067] Step S23, Seed Selection and Evaluation
[0068] This includes: 1) Determining whether the current test case is interesting. The specific method is to observe the edge bitmap and path bitmap information. If the test case executes to a new edge or a new optimized trunk path, then the test case is considered interesting, and the relationship between the new optimized trunk path and the test case is established.<hash(path),inputfile> Record it in the seed mapping table. 2) The counting bitmap is a data structure used to record the number of times each optimized trunk path is tested. Regardless of whether the test case triggers a new edge or a new optimized trunk path, the counting bitmap will be updated according to the path bitmap to accumulate the number of times each optimized trunk path is tested.
[0069] Step S24, Cutting
[0070] Remove useless statements from interesting test cases. Specifically, delete some statements and test whether the test case still triggers new edges or optimized trunk paths, until the test case is reduced to its simplest form. Statements that trigger new edges after pruning are added to the edge seed queue, and statements that trigger optimized trunk paths are added to the path seed queue.
[0071] Step S25, Scheduling
[0072] The module schedules seeds in the path seed queue and the edge seed queue. For the path seed queue, this module determines the optimized trunk path with the fewest tests based on the counting bitmap, finds the corresponding seeds for these optimized trunk paths according to the seed mapping table, and prioritizes the selection of these seeds for mutation; for the edge seed queue, seeds are randomly selected for mutation.
[0073] Fuzzilli is a fuzzing tool developed by Google for JavaScript engines. Fuzzilli uses an intermediate representation to describe JavaScript programs and mutates this representation, resulting in test cases with high syntax and semantic pass rates. Fuzzilli includes modules for mutation, execution, seed selection and evaluation, pruning, and a seed queue. Its mutation module includes different granularity mutation strategies such as combination mutation, input mutation, operation mutation, and parameter mutation. Combination mutation randomly selects a seed for slicing and concatenates the resulting fragments to another seed; input mutation randomly selects instructions from the intermediate representation of the seed and mutates the operands of those instructions; operation mutation mutates the operators of the instructions; and parameter mutation calls functions in the seed with different parameters. Furthermore, Fuzzilli predefines templates that can trigger JIT (Just-In-Time) compilers, enabling faster execution of code within the JIT compiler. Fuzzilli communicates with the JavaScript engine under test via pipelines, implementing an efficient execution module. Fuzzilli performs seed selection, evaluation, pruning, and scheduling based on edge coverage. Fuzzilli's evaluation module determines whether a test case triggers a new edge and passes the triggered test cases to the pruning module. Fuzzilli's pruning module includes various pruning strategies, such as instruction pruning, basic block pruning, simplification pruning, and inline pruning. Instruction pruning prunes test cases statement by statement in the intermediate representation; basic block pruning prunes at the basic block level; simplification pruning simplifies complex operations in test cases; and inline pruning reduces function calls in test cases through function inlining. Fuzzilli uses these pruning strategies sequentially to prune test cases that trigger new edges, ensuring that pruned test cases can still trigger new edges. Fuzzilli's seed queue module stores seeds and schedules seed selection.
[0074] Since the structure of Fuzzilli is highly compatible with the technical solution of this invention, the functionality of this invention can be achieved by modifying Fuzzilli. To implement the seed selection strategy based on optimized trunk paths, four data structures can be added to the evaluation module of Fuzzilli: a path bitmap, a global path bitmap, a count bitmap, and a seed mapping table. The path bitmap records the bitmap information of the optimized trunk paths executed by a test case. The global path bitmap records the bitmap information of the optimized trunk paths executed by all test cases. The count bitmap records the number of times each optimized trunk path is tested. The key of the count bitmap is the hash value of the optimized trunk path, and the value corresponding to the key is an integer representing the number of test cases that can be executed on that optimized trunk path. The seed mapping table records the relationship between seeds and newly executed optimized trunk paths. The key of the seed mapping table is the hash value of the optimized trunk path, and the value corresponding to the key is the seed that was executed first on that optimized trunk path.
[0075] Before each test case is executed, the path bitmap is cleared to zero. After the test case is executed, the path bitmap records the bitmap information of the optimized dry path executed by the test case. The count bitmap is updated according to the path bitmap, and the path bitmap is compared with the global path bitmap to determine whether the current test case has executed a new optimized dry path. If a new optimized dry path is executed, the test case is handed over to the pruning module, and the global path bitmap is updated to record the new optimized dry path. The seed mapping table is updated, and a mapping relationship between the new optimized dry path hash value and the test case is established.
[0076] This invention reuses Fuzzilli's pruning strategy and adds pruning criteria for optimizing the trunk path. For test cases that trigger a new optimized trunk path, it ensures that the pruned test cases can still trigger this new optimized trunk path. To this end, the pruning and evaluation modules in Fuzzilli are modified. In the pruning module, new pruning criteria are added to each pruning strategy, and in the evaluation module, the pruning strategy based on the optimized trunk path is called. For test cases after edge pruning, those related to the new edge are added to the edge seed queue, and those related to the new optimized trunk path are added to the path seed queue.
[0077] To implement a seed scheduling strategy based on optimized trunk paths, this invention adds a two-level seed queue to the Fuzzilli seed queue. The first-level queue, the path seed queue, stores all seeds that trigger new optimized trunk paths. Seeds are selected from the first-level queue and added to the second-level queue based on a counting bitmap and a seed mapping table. The counting bitmap records the number of times currently explored optimized trunk paths have been tested. This module sorts the optimized trunk paths in ascending order based on this, taking the top 10% as the optimized trunk paths with the fewest tests. Seeds that can trigger these optimized trunk paths are found according to the seed mapping table and added to the second-level queue. When selecting seeds for mutation, selection is prioritized from the second-level queue. When the second-level queue is empty, the counting bitmap is re-sorted, and seeds are selected based on this re-sorting. For the edge-cover seed queue, the Fuzzilli seed scheduling strategy is followed, randomly selecting seeds from the queue for mutation. During fuzz testing, seeds selected from optimized trunk paths and seeds selected from edge covers are used alternately, each with a 50% probability to be mutated by the mutation submodule.
[0078] Figure 2 This is a schematic diagram of the data processing apparatus of the present invention. Figure 2 As shown, embodiments of the present invention also provide a computer-readable storage medium and a data processing apparatus. The computer-readable storage medium of the present invention stores computer-executable instructions. When these computer-executable instructions are executed by the processor of the data processing apparatus, they implement fuzz testing of a JavaScript JIT compiler with optimized dry path guidance. Those skilled in the art will understand that all or part of the steps in the above methods can be implemented by a program instructing related hardware (e.g., processor, FPGA, ASIC, etc.), and the program can be stored in a readable storage medium, such as a read-only memory, a disk, or an optical disk. All or part of the steps in the above embodiments can also be implemented using one or more integrated circuits. Accordingly, each module in the above embodiments can be implemented in hardware, for example, by implementing its corresponding function through an integrated circuit, or it can be implemented in the form of a software functional module, for example, by a processor executing a program / instruction stored in memory to implement its corresponding function. Embodiments of the present invention are not limited to any particular combination of hardware and software.
[0079] The above embodiments are only used to illustrate the present invention and are not intended to limit the present invention. Those skilled in the art can make various changes and modifications without departing from the spirit and scope of the present invention. Therefore, all equivalent technical solutions also fall within the scope of the present invention, and the patent protection scope of the present invention should be defined by the claims.
Claims
1. A method for optimizing a dry-path guided JavaScript JIT compiler fuzzing, characterized in that, include: The initialization step marks the optimized dry path in the JavaScript engine. This optimized dry path is the path corresponding to the optimization sub-strategy when the JIT compiler performs optimization operations. The starting point of this optimized dry path is the entry point of the outer loop iteration, and the ending point of this optimized dry path is the tail code of the outer loop iteration. When the JavaScript program is compiled in just-in-time, the path formed from this starting point to this ending point is executed, excluding nested loops on this path, to form the optimized dry path. The first test step involves performing a fuzz test on the JavaScript engine using the edge coverage method and generating seeds to construct a seed set. This first test step is repeated until the edge coverage rate increases to zero, at which point the second test step is executed. The second testing step involves selecting seeds from the seed set for mutation to generate test cases, and performing fuzz testing on the JavaScript engine using both edge coverage and optimized dry path coverage methods. If the execution of the test case triggers a new edge or a new optimized trunk path, the test case is added as a seed to the seed set; the second test step is repeated until the fuzz test of the JavaScript engine is completed; for the seeds in the path seed queue, the cumulative number of times all optimized trunk paths are triggered by all current test cases is recorded, and the seeds generated by the test cases corresponding to the optimized trunk paths whose cumulative number of triggers is less than the test threshold are selected for this round of testing; for the seeds in the edge seed queue, a seed is randomly selected for this round of testing.
2. The JavaScript JIT compiler fuzz testing method of claim 1, wherein, In the second testing step, when the execution of the test case triggers a new edge or a new optimized trunk path, useless statements in the test case are removed to generate the simplest seed; if the simplest seed can trigger the new edge, the simplest seed is added to the edge seed queue of the seed set; if the simplest seed can trigger the new optimized trunk path, the simplest seed is added to the path seed queue of the seed set.
3. The JavaScript JIT compiler fuzz testing method of claim 1, wherein, In this initialization step, the optimized dry path in the JavaScript engine is identified, and the JavaScript engine is manipulated to insert collection code, so that the information of the optimized dry path is collected during the execution of the JavaScript engine.
4. An optimized dry-path guided JavaScript JIT compiler fuzzing system, characterized in that, include: The initialization module is used to mark optimized dry paths in the JavaScript engine; This optimized dry path is the path corresponding to the optimization sub-strategy when the JIT compiler performs optimization operations; the starting point of this optimized dry path is the entry point of the outer loop iteration, and the ending point of this optimized dry path is the tail code of the outer loop iteration; when the JavaScript program is compiled just-in-time, the path formed from this starting point to this ending point is executed, excluding nested loops on this path, to form this optimized dry path; The first testing module is used to perform edge-coverage fuzz testing on the JavaScript engine and generate seeds to build a seed set. The second testing module is used to select seeds from the seed set for mutation to generate test cases, and to perform fuzz testing on the JavaScript engine using edge coverage and optimized dry path coverage methods. If the execution of the test case triggers a new edge or a new optimized trunk path, the test case is added to the seed set as a seed. Specifically, for the seeds in the path seed queue, the cumulative number of times all optimized trunk paths are triggered by all current test cases is recorded, and the seeds generated by the test cases corresponding to the optimized trunk paths whose cumulative number of triggers is less than the test threshold are selected for this round of testing. For the seeds in the edge seed queue, a seed is randomly selected for this round of testing. When performing fuzz testing on the JavaScript engine, the first test module is called repeatedly until the edge coverage increases to zero. Then, the second test module is called repeatedly until the fuzz testing of the JavaScript engine is completed.
5. The JavaScript JIT compiler fuzz testing system of claim 4, wherein, The second testing module includes a seed selection module, which selects test cases as seeds to add to the seed set. When the execution of a test case triggers a new edge or a new optimized trunk path, useless statements in the test case are removed to generate the simplest seed. If the simplest seed can trigger the new edge, the simplest seed is added to the edge seed queue of the seed set. If the simplest seed can trigger the new optimized trunk path, the simplest seed is added to the path seed queue of the seed set.
6. The JavaScript JIT compiler fuzz testing system of claim 4, wherein, The initialization module includes an instrumentation module for identifying optimized dry paths in the JavaScript engine and manipulating the JavaScript engine to insert data collection code, enabling the collection of information about the optimized dry paths during the execution of the JavaScript engine.
7. A computer-readable storage medium storing computer-executable instructions, characterized in that, When the computer-executable instructions are executed, the JavaScript JIT compiler fuzzing method with optimized dry path guidance as described in any one of claims 1 to 3 is implemented.
8. A data processing apparatus comprising the computer-readable storage medium of claim 7, wherein when a processor of the data processing apparatus retrieves and executes computer-executable instructions in the computer-readable storage medium, it implements fuzz testing of a JavaScript JIT compiler with optimized dry path guidance.