Null pointer static detection system and method for java programs
Patent Information
- Application Number
- CN202611127381.9
- Authority / Receiving Office
- CN · China
- Patent Type
- Applications(China)
- Current Assignee / Owner
- Filing Date
- 2026-07-28
- Publication Date
- 2026-10-16
AI Technical Summary
然而,在处理大规模工业级Java工程时,现有技术普遍面临路径敏感性不足及分析效率低下的瓶颈
[0008] Compared with existing technologies, this application provides a static null pointer detection system and method for Java programs. By extracting path guards and mapping them to a binary decision graph, it reduces the complexity of path constraint solving to an efficient graph traversal process. This allows the analyzer to directly carry and compute the null value logic state of variables under specific branches along the data flow path. This significantly enhances the path sensitivity of the analysis. Utilizing fine-grained logical filters and global conflict resolution techniques, it proactively removes spurious states mutually exclusive with the actual execution logic, eliminating spurious null paths caused by semantic loss at the source and greatly reducing the false alarm rate.
Smart Images

Figure CN122926426A_ABST
Abstract
Description
Technical Field
[0001] This application relates to the field of intelligent detection, and more specifically, to a static null pointer detection system and method for Java programs. Background Technology
[0002] Java is widely used in enterprise-level development due to its cross-platform compatibility and security. However, in practice, NullPointerException remains one of the main causes of program crashes and system instability. To effectively identify and eliminate such risks early in the software development cycle, building a high-precision static null pointer detection solution has become a core requirement for ensuring the security of complex software systems.
[0003] Current static null pointer detection schemes typically encompass a variety of techniques, ranging from lightweight type checking to high-precision symbolic execution, aiming to locate potential null reference defects through static scanning of source code. However, when dealing with large-scale industrial Java projects, existing technologies generally face bottlenecks such as insufficient path sensitivity and low analysis efficiency. Because many existing schemes cannot effectively identify the protective effect of explicit checkpoints on subsequent variable states, their analysis mechanisms often suffer from significant loss of path context semantics. During execution state aggregation, such tools typically perform only simple Boolean union operations, failing to recognize the non-null assertions already included in path guards. This leads to the analyzer constructing logically invalid false null paths even if variables have passed validation due to a lack of semantic awareness, resulting in numerous false positives. Furthermore, when dealing with complex loops, recursion, or deeply nested branches, existing analysis frameworks often continuously trigger state updates due to combinations of logically mutually exclusive and unreachable null constraints, due to a lack of effective pruning for logical satisfiability. This not only caused severe performance oscillations during the fixed-point iteration process, making it difficult for the analysis process to converge, but also made the contradiction between accuracy and speed in large-scale engineering scanning increasingly prominent.
[0004] Therefore, an optimized static null pointer detection method for Java programs is needed. Summary of the Invention
[0005] To address the aforementioned technical issues, this application provides a static null pointer detection system and method for Java programs.
[0006] According to one aspect of this application, a static null pointer detection method for Java programs is provided, comprising: Obtain the Java source code to be tested; Lightweight name pre-analysis is performed on the Java source code to be tested to obtain the control flow graph and an initial set of aliases; Construct a sparse value flow graph based on the control flow graph and the initial alias set; Path guard extraction and binary decision graph mapping are performed on the control flow graph to obtain the guard mapping table; Based on the guard mapping table, null flow analysis based on guard propagation is performed on the sparse value flow graph to obtain the potential defect set.
[0007] According to another aspect of this application, a static null pointer detection system for Java programs is provided, comprising: The source code acquisition module is used to acquire the Java source code to be tested. The lightweight name pre-analysis module is used to perform lightweight name pre-analysis on the Java source code to be tested in order to obtain the control flow graph and the initial alias set; The sparse value flow graph construction module is used to construct sparse value flow graphs based on the control flow graph and the initial alias set; The path guard extraction and binary decision graph mapping module is used to extract path guards and map binary decision graphs to the control flow graph to obtain a guard mapping table. The null value flow analysis module is used to perform null value flow analysis on sparse value flow graphs based on guard propagation, based on the guard mapping table, to obtain a set of potential defects.
[0008] Compared with existing technologies, this application provides a static null pointer detection system and method for Java programs. By extracting path guards and mapping them to a binary decision graph, it reduces the complexity of path constraint solving to an efficient graph traversal process. This allows the analyzer to directly carry and compute the null value logic state of variables under specific branches along the data flow path. This significantly enhances the path sensitivity of the analysis. Utilizing fine-grained logical filters and global conflict resolution techniques, it proactively removes spurious states mutually exclusive with the actual execution logic, eliminating spurious null paths caused by semantic loss at the source and greatly reducing the false alarm rate. Attached Figure Description
[0009] The above and other objects, features, and advantages of this application will become more apparent from the more detailed description of the embodiments of this application in conjunction with the accompanying drawings. The drawings are provided to further illustrate the embodiments of this application and form part of the specification. They are used together with the embodiments of this application to explain this application and do not constitute a limitation thereof. In the drawings, the same reference numerals generally represent the same components or steps.
[0010] Figure 1 This is a flowchart of a static null pointer detection method for Java programs according to an embodiment of this application; Figure 2This is a schematic diagram of the data flow of a static null pointer detection method for Java programs according to an embodiment of this application; Figure 3 This is a block diagram of a static null pointer detection system for Java programs according to an embodiment of this application. Detailed Implementation
[0011] Hereinafter, exemplary embodiments according to this application will be described in detail with reference to the accompanying drawings. Obviously, the described embodiments are merely some embodiments of this application, and not all embodiments of this application. It should be understood that this application is not limited to the exemplary embodiments described herein.
[0012] As indicated in this application and claims, unless the context clearly indicates otherwise, the words "a," "an," "an," and / or "the" are not specifically singular and may include plural forms. Generally speaking, the terms "comprising" and "including" only indicate the inclusion of explicitly identified steps and elements, which do not constitute an exclusive list, and the method or apparatus may also include other steps or elements.
[0013] While this application makes various references to certain modules of the systems according to embodiments of this application, any number of different modules can be used and run on user terminals and / or servers. The modules described are merely illustrative, and different aspects of the systems and methods may use different modules.
[0014] Flowcharts are used in this application to illustrate the operations performed by the system according to embodiments of this application. It should be understood that the preceding or following operations are not necessarily performed in exact order. Instead, various steps can be processed in reverse order or simultaneously as needed. Furthermore, other operations can be added to these processes, or one or more steps can be removed from them.
[0015] The technical solution of this application proposes a static null pointer detection method for Java programs. Figure 1 This is a flowchart of a static null pointer detection method for Java programs according to an embodiment of this application. Figure 2 This is a system architecture diagram of a static null pointer detection method for Java programs according to an embodiment of this application. Figure 1 and Figure 2As shown, the static null pointer detection method for Java programs according to an embodiment of this application includes the following steps: S1, obtaining the Java source code to be detected; S2, performing lightweight name pre-analysis on the Java source code to be detected to obtain a control flow graph and an initial alias set; S3, constructing a sparse value flow graph based on the control flow graph and the initial alias set; S4, performing path guard extraction and binary decision graph mapping on the control flow graph to obtain a guard mapping table; S5, performing null flow analysis based on guard propagation on the sparse value flow graph based on the guard mapping table to obtain a potential defect set.
[0016] Specifically, S1 involves acquiring the Java source code to be tested. It should be understood that the Java source code is the original carrier for null pointer exception analysis. By acquiring the source code, the system can effectively identify and eliminate the risk of null pointer exceptions that may cause program crashes and system instability through deep static scanning of the code logic in the early stages of the software development cycle, thereby meeting the core requirement of ensuring the security and stability of complex industrial-grade software systems. The Java source code to be tested refers to the Java language program text written by the developer but not compiled into bytecode. It is the sole data source for subsequent construction of control flow graphs, sparse value flow graphs, and path sensitivity analysis, determining the semantic breadth and logical depth that the detection system can perceive.
[0017] In practice, the first step is to locate the source code's storage location, typically achieved by configuring the project's root directory path. Specifically, the system receives the root directory path of the Java project to be tested, either from user input or a configuration file. Based on this path, the system launches a recursive file system traverser. This traverser visits every file and subdirectory under the root directory until the entire directory tree has been traversed, recording the absolute paths of all files. This step forms the basis for subsequent file filtering.
[0018] Next, all files found during the scan are filtered to identify Java source files with the ".java" extension. Specifically, the list of all file paths obtained from the recursive scan is filtered. The filtering rule is based on an exact match of the file extension; the system checks whether each file's extension is ".java". Only files with an exact match are retained and considered valid Java source code files, while other types of files (such as ".class" bytecode files, text files, or configuration files) are excluded at this stage.
[0019] Next, the contents of these identified Java source files are read one by one. Specifically, for each Java source file path obtained after filtering, the system uses standard file I / O operations (e.g., the Files.readAllBytes or Files.readAllLines methods in Java NIO) to read the file contents completely into memory in binary or character stream form. To ensure correct code parsing, the system usually needs to specify or detect the file's character encoding (such as UTF-8). This step converts the source code on disk into a string or character sequence in memory, preparing for subsequent syntax analysis.
[0020] Subsequently, the contents of all read files are integrated to form a complete source code model that can be processed by subsequent analysis modules. Specifically, the contents of all read Java source files are organized and managed to form a unified and logically complete collection of project source code. This model is an in-memory data structure (e.g., a Map or a custom ProjectModel object) containing all file paths and their corresponding source code contents, ensuring that subsequent intermediate code conversion modules can access the complete contents of any source file as needed.
[0021] Specifically, in step S2, a lightweight name pre-analysis is performed on the Java source code to be detected to obtain a control flow graph and an initial alias set. It should be understood that the security of industrial-grade Java code often relies on a combination of complex path constraints and data dependencies, while traditional static analysis often separates control flow analysis and data flow analysis, resulting in insufficient path sensitivity. To achieve high-precision path-sensitive analysis while ensuring industrial-grade scanning speed, the technical solution of this application constructs the basic topology structure and alias relationship mapping required for subsequent analysis through lightweight preprocessing methods, thereby avoiding the construction of a large number of logically invalid false paths due to a lack of semantic awareness.
[0022] In practice, the Java source code to be tested first undergoes intermediate code transformation to obtain an intermediate representation in three-address code form. Specifically, a lexical analyzer and a syntax analyzer are used to parse the obtained Java source code, generating an abstract syntax tree (AST). Then, the AST is traversed and transformed into a simpler, more standardized intermediate representation—three-address code. This transformation process eliminates syntactic sugar and complex structures in the source code, providing a unified and simplified operational basis for constructing control flow graphs and performing pointer analysis.
[0023] Next, a control flow graph is constructed based on the intermediate representation in three-address code form. The control flow graph is a graphical representation of all possible paths during the execution of the program to be detected, and it forms the basis for subsequent path guard extraction. Its construction is based on basic blocks. Specifically, first, the system scans the three-address code instruction sequence, dividing the instructions into multiple basic blocks. Each basic block is the largest instruction sequence that satisfies the following conditions: only the first instruction can be jumped to, and only the last instruction can be a jump instruction. Then, the system analyzes the jump relationships between basic blocks (such as conditional branches, unconditional jumps, method calls, and returns), and connects these basic blocks with directed edges to form a directed graph, i.e., the control flow graph. Nodes in the graph represent basic blocks, and edges represent possible control flow paths.
[0024] Next, pointer analysis is performed on the intermediate representation in three-address code form to obtain a set of pointer relationships. Pointer analysis aims to determine which memory objects (i.e., heap-allocated instances) each pointer variable (i.e., object references in Java) might point to. Specifically, a lightweight, stream-insensitive analysis algorithm (such as type-based or allocation-point-based analysis) is used to traverse the three-address code. For each variable that might point to heap memory (e.g., allocated via the `new` keyword), the analyzer records a pointer relationship. The set of pointer relationships can be formally represented as a series of tuples (p, o), where p is a pointer variable and o is an abstract memory object. For example, for the code `String s = new String("hello");`, the analysis will generate a pointer relationship (s, O1), where O1 represents the object created by this specific `new String` operation.
[0025] Subsequently, an initial alias set is generated based on the set of pointer relationships. This initial alias set refers to the set of different variable pairs pointing to the same memory address in the program. It should be understood that the original set of pointer relationships is centered on a single variable, while alias relationships are relationships between variable pairs. Therefore, in the technical solution of this application, a systematic transformation process is further used to derive the potential alias relationships between variables from the set of pointer relationships. The generated initial alias set clearly represents which variable groups may have alias relationships, which is crucial for accurately determining the scope of influence of values and avoiding false positives and false negatives in subsequent data flow analysis.
[0026] Specifically, firstly, the pointer relation variable pairs are extracted from the pointer relation set to obtain a sequence of variable pairs to be verified. That is, by traversing the memory mapping graph obtained through pointer analysis, variables pointing to the same or intersecting memory regions are paired to obtain the sequence of variable pairs to be verified. Secondly, a parallel pointer set intersection operation is performed on the sequence of variable pairs to obtain a pointer overlap mapping table. That is, a parallel pointer set intersection operation is performed on the sequence of variable pairs to obtain a pointer overlap mapping table. Bit vector calculation techniques are used to quickly determine the intersection of the pointers of each variable pair to determine whether there is a collision at the physical memory level. Next, alias predicates are used to determine the pointer overlap mapping table to obtain an alias relation matrix. That is, alias predicates are used to determine the pointer overlap mapping table. Combined with Java language type safety constraints (such as subclass compatibility) and variable scope constraints, the overlapping pointers are evaluated to determine whether they constitute a real alias relationship in the program logic, thus obtaining an alias relation matrix. Finally, the alias relation matrix is subjected to set unification based on a disjoint-set data structure to obtain an initial alias set. In other words, by utilizing the lookup and merge properties of disjoint-set data structures, transitive alias relationships (such as A and B being aliases of each other, and B and C being aliases of each other, then A, B, and C are merged) are efficiently clustered to obtain an accurate initial set of aliases.
[0027] Specifically, S3 involves constructing a sparse value flow graph based on the control flow graph and the initial alias set. It should be understood that traditional static analysis often separates control flow analysis from data flow analysis, leading to a serious loss of path context semantics when processing large-scale Java programs, resulting in numerous spurious null paths and false alarms. Therefore, in the technical solution of this application, a sparse value flow graph is constructed to simplify complex program execution logic into a relational graph containing only key data flow directions. This allows the analyzer to directly carry logical formulas along the flow paths of variables, solving the problem of insufficient path sensitivity and avoiding the computational complexity explosion that may be caused by full path exploration through sparsification. This improves the convergence efficiency of the fixed-point iteration process while maintaining industrial-grade scanning speed. The sparse value flow graph is a graph structure that retains only nodes and data flow edges related to pointers, variable definitions, and references in the program, used to accurately depict the possible propagation trajectories of null values in the program.
[0028] In practice, the first step is to identify key points and instantiate nodes for each instruction in the control flow graph to obtain the initial SVFG node set. Specifically, this involves traversing each instruction within each basic block of the control flow graph and identifying key points related to value definition and usage. These key points primarily include: definition points, which are statements that assign values to variables or instantiate objects (e.g., x = new MyClass(), y = null, z = a + b); and usage points, which are statements that use variable values (e.g., method call x.foo(), field read a = y.field, return value return z). For each identified key point, the system instantiates a corresponding SVFG node. For example, an assignment statement x = y will generate two nodes: one representing the use of the value of y (usage point), and the other representing the definition of x (definition point). Ultimately, all instantiated nodes constitute the initial SVFG node set, establishing the most basic node elements for the value flow graph.
[0029] Next, based on the SVFG initial node set and the control flow graph, an intermediate graph containing local data flows is constructed. Specifically, data flow edges are added within the SVFG initial node set. The direction of the data flow edge represents the direction of value flow, i.e., from the value's generation point (definition point) to its consumption point (use point). In this process, direct data flow relationships are first established within the procedure (i.e., within a single method) according to the order of the control flow graph and the variable definition-use chain. For example, within the same basic block, if instruction I1: x = ... defines a value, and subsequent instruction I2: ... = x uses this value, then a data flow edge is added between the definition point node corresponding to I1 and the use point node corresponding to I2. This process constructs an intermediate graph containing local data flows.
[0030] Furthermore, cross-process data flow edges are connected to the intermediate graph containing local data flows to obtain a fully connected graph. To establish a complete data flow, the calling relationships between methods must be considered. Specifically, through inter-process analysis, the values of actual parameters are connected to the formal parameters, and the return values of methods are connected to the variables that receive the return values. For example, for a method call point (e.g., `call foo(a)`), firstly, a data flow edge is added between the actual parameter (e.g., the usage node of `a`) and the definition node of the formal parameter of the called method `foo`; secondly, a data flow edge is added between the usage node of the return statement of the called method `foo` and the variable that receives the return value at the call point (e.g., the definition node of `b` in `b = foo(a)`). In this way, the local data flow graphs within each method are connected, forming a fully connected inter-process graph that captures all possible value flow paths in the program.
[0031] Subsequently, the fully connected graph is refined with aliased data flow edges to obtain a refined full value flow graph. Due to the existence of alias relationships (e.g., p and q may point to the same object), modifications to an object via p may affect reads of that object via q. The system iterates through each pair of alias relationships (i.e., pairs of variables belonging to the same alias equivalence class) in the initial alias set. For each pair of variables that may have aliases, the system checks the fully connected graph for a potential data flow from the definition point of one variable to the usage point of the other. If such a path exists, and the data flow is due to sharing the same memory object (alias), the system adds an explicit aliased data flow edge to directly represent this indirect data dependency. For example, if p and q are aliases, then there is a data flow through the alias relationship between the statements p.setField(10) (definition point) and int x = q.getField() (usage point). In this way, the value flow graph can more accurately reflect the behavior of pointer programs.
[0032] Finally, the refined full value flow graph is subjected to graph sparsification to obtain a sparse value flow graph. Specifically, the system identifies all nodes related to the generation, propagation, and sensitive use (such as dereferencing) of null values, called critical nodes. Then, starting from these critical nodes, all value flow paths that can affect these nodes are traced backward (or propagated forward). Only the nodes and edges on these paths are retained, while other irrelevant nodes and edges are removed. The final result is a sparse value flow graph that preserves the complete null value propagation path but is much smaller in scale than the full value flow graph, thus greatly improving the efficiency of subsequent null value flow analysis.
[0033] Specifically, in step S4, path guard extraction and binary decision graph mapping are performed on the control flow graph to obtain a guard mapping table. It should be understood that in the static analysis of Java programs, conventional data flow propagation mechanisms often suffer from significant loss of path context semantics. This context blind spot causes the analyzer to only perform simple Boolean union operations during execution state aggregation, failing to recognize the special non-null assertions that may be included in the path guard conditions of the target node itself. When nullable data flows pass through explicit checkpoints such as "if (obj != null)", if the system lacks semantic awareness, it will still forcibly accumulate the nullable conditions that have passed the check to subsequent nodes, thereby constructing a large number of logically invalid false nullable paths and generating false alarms. Therefore, to overcome the above defects and achieve higher accuracy detection, the technical solution of this application constructs a logical model capable of deeply perceiving complex Java syntax features such as branches and loops through path guard extraction and binary decision graph mapping, thereby realizing dynamic state transitions based on guard constraint conflict resolution. Among them, the binary decision graph is a normalized data structure used to represent Boolean functions. It has efficient logical operations and simplification characteristics. It can convert complex logical formulas into efficient BDD index sets through binary decision graph mapping. The final generated guard mapping table is an index table that stores the mapping relationship between binary decision graph indexes and sparse value flow graph nodes. It is the direct basis for subsequent guard propagation and state refinement.
[0034] In practice, the first step is to perform control dependency calculation on the control flow graph to obtain the control dependency set. This step aims to determine the conditional branch nodes controlled by each node (basic block) in the program. If node B's control depends on node A, it means that a conditional branch decision of node A directly determines whether node B will be executed. The system uses standard control dependency calculation algorithms (such as calculation methods based on the frontier of the dominant node) to analyze the control flow graph. The final generated control dependency set records, in a mapping form, the branch nodes and their corresponding branch conditions on which each control flow graph node (the dependent node) depends for control. This dependency set reveals the source of constraints on the program execution path.
[0035] Next, node location, dependency backtracking, and conjunction operations are performed on the control dependency set and the sparse value flow graph to obtain the node logic formula set. This step aims to compute the path guard for each node in the sparse value flow graph (SVFG). Specifically, first, since SVFG nodes originate from instructions in the CFG, the system associates the nodes in the SVFG back to their corresponding CFG nodes (or instructions). Second, for each SVFG node, the system backtracks all branch nodes to which it has direct and indirect control dependencies, based on the control dependency set. Finally, all branch conditions encountered during the backtracking process are logically conjunctiond (AND operation) to form the complete path conditions to reach the SVFG node. For each SVFG node, the system generates such a logic formula; the logic formulas of all nodes constitute the node logic formula set, which precisely describes the common premises of all possible paths to each data flow node using Boolean logic.
[0036] Furthermore, a BDD index transformation is performed on the set of node logic formulas to obtain a BDD index set. It should be understood that while the set of node logic formulas expresses path conditions, the formulas themselves may be complex and redundant, hindering efficient computer processing. A Binary Decision Graph (BDD) is a standardized data structure that uniquely represents a Boolean function and supports efficient logical operations and equivalence checks. Therefore, a BDD index transformation is performed on the set of node logic formulas to obtain a BDD index set. Specifically, each logic formula in the set of node logic formulas is converted into a BDD. This process includes: first, identifying all Boolean variables appearing in the formula (usually corresponding to conditional judgments in the program, such as x>0, obj==null, etc.); second, recursively constructing a shared, simplified BDD graph according to a fixed variable order. Each unique BDD structure is identified by a root node index; finally, the path guard formula of each SVFG node is converted (or mapped) to a specific BDD index. The BDD indices of all nodes constitute the BDD index set. This process transforms the semantic equivalence judgment of logical formulas into a simple equality judgment of BDD indexes, which greatly improves the efficiency of subsequent logical operations.
[0037] Subsequently, a mapping relationship is established between the BDD index set and the sparse value flow graph to obtain the guard mapping table. Specifically, a mapping table data structure (e.g., a hash table or an array) is created where the keys are unique identifiers of nodes in the sparse value flow graph (such as node IDs), and the values are the BDD indices of the path guards corresponding to those nodes. By traversing the BDD index set, the system creates a record in the guard mapping table for each SVFG node, associating the node ID with the BDD index of its path guard. The resulting guard mapping table allows for the rapid retrieval of the BDD representation of the path guards for any given SVFG node, thus providing readily available logical context for subsequent guard propagation analysis.
[0038] Specifically, S5, based on the guard mapping table, performs null value flow analysis on the sparse value flow graph using guard propagation to obtain a set of potential defects. That is, through actual analysis operations, the abstract model is transformed into a concrete defect report that can guide code repair. Specifically, based on guard propagation, null value states are bound to path conditions (guards) for propagation, ensuring that the analysis is path-sensitive. This means that the analyzer can understand that a variable may be null in one branch but not in another, thus avoiding false positives for protected operations within a branch, significantly reducing the false positive rate. The final set of potential defects is the final output of this static analysis, providing developers with a list of code locations that require focused attention and repair.
[0039] In practice, the first step is to initialize the sparse value flow graph (SVFG) with null states to obtain a node state table and a list of pending tasks. Specifically, two core data structures are initialized first. The first is the node state table, which assigns and records a null state for each node in the SVFG. This null state is typically a Boolean formula (represented by BDD in subsequent steps). Initially, except for nodes that explicitly generate null values (e.g., nodes directly assigned x = null, uninitialized fields, etc.), the null states of most nodes are initialized to "always false" (i.e., logical zeros of BDD, indicating that it is impossible to be null). The null states of nodes that explicitly generate null values are initialized as their path guards (obtained from the guard mapping table), indicating that their value is null under the path conditions leading to that node. The second is the list of pending tasks, which is a queue or set used to store data flow edges to be processed. During initialization, all data flow edges originating from nodes with initial null states are added to the list of pending tasks.
[0040] Next, based on the guard mapping table, guard gating logic is calculated for the currently processed edges in the list of tasks to be processed to obtain effective flow conditions. Specifically, a data flow edge that needs to be processed is taken from the list of tasks to be processed, denoted as u→v, where u is the source node and v is the target node. This step aims to calculate the path conditions that must be satisfied for null states to propagate from u to v. First, the path guard Guard(u) of the source node u and the path guard Guard(v) of the target node v are queried from the guard mapping table. An effective flow condition means that an execution path must be able to reach the source node u and also be able to execute from u to the target node v. This is usually achieved through a logical conjunction operation, that is, the effective flow condition is a logical combination of the reachability constraints of the source node guard, the control flow guard from u to v, and the target node guard.
[0041] Furthermore, based on valid transition conditions, conditional state transitions are performed on the current null states of the source node and the target node in the node state table to obtain the updated null state of the target node. It should be understood that in static analysis of Java programs, traditional conditional state transition mechanisms suffer from significant loss of path context semantics. This context blind spot causes the analyzer to perform only simple Boolean union operations when performing state aggregation, failing to identify the non-null assertion special relationships that may be included in the target node's own path guard conditions. When the null data stream passes through explicit checkpoints such as `if (obj != null)`, the traditional conditional state transition mechanism suffers from significant loss of path context semantics. This context blind spot causes the analyzer to perform only simple Boolean union operations when performing state aggregation, failing to identify the non-null assertion special relationships that may be included in the target node's own path guard conditions. When nullable data flows pass through explicit checkpoints such as `if (obj != null)`, traditional mechanisms, lacking semantic awareness, still forcibly accumulate nullable conditions that have passed the check to subsequent nodes, thus constructing a large number of logically invalid false nullable paths. Furthermore, due to the lack of pruning for logical satisfiability, the analyzer often experiences severe performance oscillations in the fixed-point iteration process when handling complex loops, recursions, or deeply nested branches because combinations of logically mutually exclusive and unreachable null value constraints continuously trigger state updates, preventing convergence within a finite time. To overcome these shortcomings and achieve higher accuracy detection, this application employs a dynamic state transition method based on guard constraint conflict resolution.
[0042] In this process, firstly, based on the valid flow conditions, the nullability condition of the source node and the non-nullability assertion guard of the target node are semantically refined to obtain the refined incremental nullability condition. It should be understood that the conventional data flow propagation process often ignores the defensive semantics already possessed by the target node, leading to the ineffective nullability risk permeating downstream. In the specific execution process, the non-nullability assertion guards controlled by the target node under the current control flow path are first identified and used as logical filters in the propagation calculation. That is, the nullability condition of the source node is joined with the valid flow conditions, and then joined with the logical NOT of the non-nullability guard to generate the refined incremental nullability condition. This process is expressed by the formula:
[0043] in, This indicates that after refining, it starts from the source node. Flow to target node Incremental null conditions; Indicates the source node The initial void-causing condition carried; This indicates the valid transition conditions of the current data flow edge; Represents the logical AND operation; Represents the logical NOT operation; Represents the target node The current location already satisfies the non-empty verification logic set. This step utilizes a logic compensation mechanism to actively strip away filtered paths at the propagation source. This not only intercepts abnormal states mutually exclusive with the target path, but also reduces the scale of false alarm paths generated at the source by simplifying the null semantics in the incremental stage.
[0044] Secondly, based on the refined incremental voiding condition, the current voiding condition of the target node and the global path context semantics are correlated and aggregated to obtain the aggregated voiding condition after conflict resolution. That is, the refined incremental state generated in the previous steps is logically ORed with the existing voiding state of the target node, and then the merged result is uniformly projected into the global path context space of the target node. Specifically, utilizing the normalization characteristics of the binary decision graph (BDD), a simplification operator is invoked to resolve conflicts in the projected result. This process is expressed by the following formula:
[0045] in, Indicates the target node after conflict resolution. The final null condition; Normalized simplified functions representing BDD structures; Represents the target node The original null-causing condition BDD held before aggregation; Represents a logical OR operation; Represents the target node The program operates within a global path semantic context (BDD) in the overall program graph. This allows for the establishment of a conjugate resolution mechanism for path semantics, ensuring that every remaining null-risk path possesses genuine satisfiability within the global semantic framework. Consequently, by eliminating cross-branch logical conflicts, precise location of nullable states is achieved, ensuring a high degree of consistency between the detection results and the actual execution flow semantics of the program.
[0046] Furthermore, based on the nullable condition after the aggregation conflict is resolved, dynamic enqueue control driven by satisfiability is applied to the current nullable condition of the target node to obtain the updated nullable state of the target node. It should be understood that traditional simple value comparison update mechanisms are prone to ineffective iterative oscillations. In the technical solution of this application, a deep consistency comparison is performed on the nullable condition states before and after aggregation, and a non-constant falsehood criterion is introduced as a prerequisite for update triggering. Only when the nullable condition after aggregation is not logically equivalent to the original state, and the path corresponding to the condition is still logically satisfiable (i.e., not a logical zero), is an update signal triggered and the node and its successors allowed to re-enter the analysis queue. This process is expressed by the formula:
[0047] in, This is a control signal indicating whether to re-add the node and its associated paths to the analysis work list; a value of 1 indicates an update, and a value of 0 indicates a mask. This indicates the non-equivalence decision between two sets of logical formulas in the BDD structure; 00 indicates that the logical constant is false, representing a dead path that is semantically unreachable. Thus, an iterative criterion based on content validity is constructed. By forcibly pruning updates to dead paths, this not only avoids the waste of computational resources on logical singularities but also significantly improves the fixed-point convergence efficiency during large-scale Java project scanning, effectively balancing the trade-off between accuracy and speed in industrial-grade analysis.
[0048] In summary, by integrating the aforementioned improved mechanism based on guard constraint conflict resolution, the long-standing path semantic loss interference in Java static analysis was successfully resolved. Specifically, by utilizing fine-grained logical filters and global conflict prediction, false defect reports caused by misanalysis of controlled branches were significantly reduced. Furthermore, a satisfiability-driven pruning strategy was used to dynamically block invalid propagation paths. Ultimately, an industry-grade static analysis model was constructed that can deeply perceive complex Java syntax features such as polymorphism and branching while maintaining high convergence performance, providing highly reliable null pointer safety guarantees with high industrial responsiveness for complex Java software systems.
[0049] Subsequently, checkpoint probing and defect collection are performed on the updated sparse value flow graph and the updated node state table to obtain a set of potential defects. It should be understood that after the iterative propagation process ends, the node state table records the path conditions under which each SVFG node might be null. At this point, the system begins collecting defects. Specifically, the sparse value flow graph is traversed to find specific checkpoints, i.e., operation nodes that may cause null pointer exceptions, mainly including: instance method call points (such as obj.method()), field access points (such as obj.field), etc. For each such checkpoint node, the system performs a probing operation: first, it obtains the final null condition of the node from the node state table; second, it obtains the path guards of the node itself from the guard map table. The logical condition for the existence of a potential defect is: there exists an execution path that can reach the checkpoint, and the variable at the checkpoint may be null. Therefore, the condition for the existence of a defect is that the conjunction of the two BDDs is not false. If the calculated BDD is not equal to logically false, it means that under some real path context, the checkpoint may indeed have a null pointer dereference. The system records the checkpoint node, its corresponding source code location, and the path conditions that led to the defect, forming a defect report. All such defect reports constitute the final set of potential defects.
[0050] In summary, the static null pointer detection method for Java programs according to the embodiments of this application is explained. It extracts path guards and maps them to a binary decision graph, reducing the complexity of path constraint solving to an efficient graph traversal process. This allows the analyzer to directly carry and compute the null value logic state of variables under specific branches along the data flow path. In this way, the path sensitivity of the analysis is significantly enhanced. By utilizing fine-grained logical filters and global conflict resolution techniques, it actively removes spurious states mutually exclusive with the actual execution logic, eliminating spurious null paths caused by semantic loss at the source and greatly reducing the false alarm rate.
[0051] Furthermore, a static null pointer detection system for Java programs is also provided.
[0052] Figure 3 This is a block diagram of a static null pointer detection system for Java programs according to an embodiment of this application. Figure 3As shown, the static null pointer detection system 300 for Java programs according to an embodiment of this application includes: a source code acquisition module 310, used to acquire the Java source code to be detected; a lightweight name pre-analysis module 320, used to perform lightweight name pre-analysis on the Java source code to be detected to obtain a control flow graph and an initial alias set; a sparse value flow graph construction module 330, used to construct a sparse value flow graph based on the control flow graph and the initial alias set; a path guard extraction and binary decision graph mapping module 340, used to perform path guard extraction and binary decision graph mapping on the control flow graph to obtain a guard mapping table; and a null value flow analysis module 350, used to perform null value flow analysis based on guard propagation on the sparse value flow graph based on the guard mapping table to obtain a potential defect set.
[0053] As described above, the static null pointer detection system 300 for Java programs according to embodiments of this application can be implemented in various wireless terminals, such as servers with static null pointer detection algorithms for Java programs. In one possible implementation, the static null pointer detection system 300 for Java programs according to embodiments of this application can be integrated into the wireless terminal as a software module and / or a hardware module. For example, the static null pointer detection system 300 for Java programs can be a software module in the operating system of the wireless terminal, or it can be an application developed for the wireless terminal; of course, the static null pointer detection system 300 for Java programs can also be one of many hardware modules of the wireless terminal.
[0054] Alternatively, in another example, the null pointer static detection system 300 for Java programs and the wireless terminal can also be separate devices, and the null pointer static detection system 300 for Java programs can be connected to the wireless terminal via wired and / or wireless networks, and transmit interactive information according to an agreed data format.
[0055] The various embodiments of this disclosure have been described above. These descriptions are exemplary and not exhaustive, nor are they limited to the disclosed embodiments. Many modifications and variations will be apparent to those skilled in the art without departing from the scope and spirit of the described embodiments. The terminology used herein is chosen to best explain the principles, practical application, or improvement of the technology in the market, or to enable others skilled in the art to understand the embodiments disclosed herein.
Claims
1. A static null pointer detection method for Java programs, characterized in that, include: Obtain the Java source code to be tested; Lightweight name pre-analysis is performed on the Java source code to be tested to obtain the control flow graph and an initial set of aliases; Construct a sparse value flow graph based on the control flow graph and the initial alias set; Path guard extraction and binary decision graph mapping are performed on the control flow graph to obtain the guard mapping table; Based on the guard mapping table, null flow analysis based on guard propagation is performed on the sparse value flow graph to obtain the potential defect set.
2. The static null pointer detection method for Java programs according to claim 1, characterized in that, Lightweight name pre-analysis is performed on the Java source code to be tested to obtain the control flow graph and an initial set of aliases, including: The Java source code to be tested is converted into an intermediate code to obtain an intermediate representation in the form of a three-address code; A control flow graph is constructed based on the intermediate representation in the form of a three-address code. Pointer analysis is performed on the intermediate representation in the three-address code form to obtain the set of pointer relationships; Generate an initial set of aliases based on the set of pointer relationships.
3. The static null pointer detection method for Java programs according to claim 2, characterized in that, Based on the set of pointer relationships, an initial set of aliases is generated, including: Extract the variable pairs of the pointer relation set to obtain the sequence of variable pairs to be verified. To obtain a pointer-overlapping mapping table, parallel operations are performed on the pointer set intersection of the sequence for the variable to be verified. Alias predicate determination is performed on the overlapping mapping table to obtain the alias relation matrix; The alias relation matrix is subjected to set unification based on disjoint-set data structure to obtain the initial alias set.
4. The static null pointer detection method for Java programs according to claim 1, characterized in that, Based on the control flow graph and the initial alias set, a sparse value flow graph is constructed, including: Key point identification and node instantiation are performed for each instruction in the control flow graph to obtain the initial node set of SVFG; Based on the initial node set and control flow graph of SVFG, an intermediate graph containing local data flows is constructed; Connect the intermediate graph containing local data flows with cross-process data flow edges to obtain a fully connected graph; The fully connected graph is refined by aliasing data flow edges to obtain a refined full-valued flow graph; The refined full-value flow graph is subjected to graph sparsification to obtain a sparse-value flow graph.
5. The static null pointer detection method for Java programs according to claim 1, characterized in that, Path guard extraction and binary decision graph mapping are performed on the control flow graph to obtain a guard mapping table, including: Perform control dependency calculations on the control flow graph to obtain the control dependency set; Perform node location, dependency backtracking, and conjunction operations on the control dependency set and sparse value flow graph to obtain the node logic formula set; Perform BDD index transformation on the node logic formula set to obtain the BDD index set; The BDD index set and the sparse value flow graph are mapped and stored to obtain the guard mapping table.
6. The static null pointer detection method for Java programs according to claim 1, characterized in that, Based on the guard mapping table, null flow analysis based on guard propagation is performed on the sparse value flow graph to obtain a set of potential defects, including: Null state initialization is performed on the sparse value flow graph to obtain the node state table and the list of pending tasks; Based on the guard mapping table, the guard gate logic is calculated on the edge currently being processed in the work list to obtain the valid flow conditions. Based on the effective transition conditions, conditional state transitions are performed on the current null state of the source node and the current null state of the target node in the node state table to obtain the updated null state of the target node.
7. The static null pointer detection method for Java programs according to claim 6, characterized in that, Based on the guard mapping table, null flow analysis based on guard propagation is performed on the sparse value flow graph to obtain a set of potential defects. It also includes: checkpoint detection and defect collection on the updated sparse value flow graph and the updated node state table to obtain a set of potential defects.
8. The static null pointer detection method for Java programs according to claim 6, characterized in that, Based on valid transition conditions, conditional state transitions are performed on the current null state of the source node and the current null state of the target node in the node state table to obtain the updated null state of the target node, including: Based on the effective flow conditions, the nullability condition of the source node and the non-nullability assertion guard of the target node are semantically refined to obtain the refined incremental nullability condition. Based on the refined incremental voiding condition, the current voiding condition of the target node and the global path context semantics are associated with conflict resolution and aggregated to obtain the voiding condition after aggregated conflict resolution. Based on the nullability condition after the aggregation conflict is resolved, dynamic enqueue control driven by satisfiability is applied to the current nullability condition of the target node to obtain the updated null value state of the target node.
9. A static null pointer detection system for Java programs, characterized in that, include: The source code acquisition module is used to acquire the Java source code to be tested. The lightweight name pre-analysis module is used to perform lightweight name pre-analysis on the Java source code to be tested in order to obtain the control flow graph and the initial alias set; The sparse value flow graph construction module is used to construct sparse value flow graphs based on the control flow graph and the initial alias set; The path guard extraction and binary decision graph mapping module is used to extract path guards and map binary decision graphs to the control flow graph to obtain a guard mapping table. The null value flow analysis module is used to perform null value flow analysis on sparse value flow graphs based on guard propagation, based on the guard mapping table, to obtain a set of potential defects.