Memory safety runtime detection acceleration method incorporating static pointer analysis and media

By combining static pointer analysis with a runtime memory safety detection method, and utilizing compiler preprocessing and shadow stack mechanisms to insert out-of-bounds detection code, the problem of high time overhead in memory safety detection tools is solved, achieving efficient memory safety detection.

CN119903505BActive Publication Date: 2025-11-25NANJING UNIV OF AERONAUTICS & ASTRONAUTICS
View PDF 2 Cites 0 Cited by

Patent Information

Application Number
CN202411910962.0
Authority / Receiving Office
CN · China
Patent Type
Patents(China)
Current Assignee / Owner
Filing Date
2024-12-24
Publication Date
2025-11-25
Estimated Expiration
2044-12-24

AI Technical Summary

Technical Problem

Existing memory safety runtime detection tools in C language have excessive time overhead, which slows down program execution. Furthermore, existing methods cannot effectively reduce detection overhead without modifying the code, and they also suffer from high false negative and false positive rates.

Method used

This memory-safe runtime detection method combines static pointer analysis. It uses compiler preprocessing and metadata insertion, leverages the shadow stack mechanism to pass pointer metadata across processes, inserts code snippets for out-of-bounds detection, rewrites array subscript expressions and inserts runtime detection code, and generates an executable file for detection.

Benefits of technology

Without modifying existing code, it effectively reduces the time overhead of runtime detection tools, avoids missed and false alarms, and ensures the timely detection of memory errors.

✦ Generated by Eureka AI based on patent content.

Smart Images

  • Figure CN119903505B_ABST
    Figure CN119903505B_ABST
Patent Text Reader

Abstract

The application discloses a memory safety runtime detection acceleration method combined with static pointer analysis and a medium, and the method comprises the following steps: preprocessing and analyzing the source code of a program to be detected by using a compiler, collecting metadata of array variables, performing intra-procedure alias analysis and inter-procedure alias analysis, inserting a code segment for transferring metadata across procedures, rewriting array subscript expressions in the code, inserting a code segment for out-of-bound detection, inserting runtime detection code, compiling and linking the source code after the plugging by using the compiler and the linker, and generating an executable file; and running the executable file and outputting the result of the memory safety runtime detection. The application uses lightweight Boolean expression calculation instead of heavy runtime detection, can effectively reduce the time overhead of the runtime detection tool without changing the existing code, introducing false negatives and false positives, and guaranteeing the timing of error reporting and memory errors, and has practical application value.
Need to check novelty before this filing date? Find Prior Art

Description

Technical Field

[0001] This invention belongs to the field of electronic digital data processing, specifically relating to a method and medium for accelerating memory security runtime detection by combining static pointer analysis. Background Technology

[0002] The C programming language is widely used in developing various embedded systems and performance-sensitive software due to its direct and efficient control over low-level memory. However, its control over low-level memory can easily introduce memory safety issues. Among these, buffer overflows caused by array out-of-bounds access are one of the most frequent memory safety errors, and often lead to serious consequences.

[0003] Memory-safe runtime detection technology is a method for solving memory safety issues in the C language. This technology inserts additional code into the source code of the program under test to record metadata for each memory region at runtime and perform security checks based on the metadata before accessing memory. However, the intensive security checks incur time overhead, so current memory-safe runtime detection tools slow down program execution speed by up to tens of times, which seriously reduces the efficiency and practicality of such tools in checking memory safety issues.

[0004] Currently, an increasing number of technologies focus on reducing the time overhead of runtime memory safety detection tools, which can be divided into the following four categories:

[0005] 1) Static analysis: This involves analyzing the program without actually running it. Classic methods include model checking, data flow analysis, and abstract interpretation. Empirical studies show that simple static analysis, with its incomplete contextual semantic modeling, often results in poor detection capabilities for complex defects, high false positive and false negative rates, and limited practicality.

[0006] 2) A combination of manual annotation and runtime inspection: This method extracts semantic information from annotations at compile time to prove the memory safety of parts of the code, thereby reducing the overhead of memory safety inspection at runtime. This type of method requires manual modification of existing code, cannot achieve automated annotation, is inefficient, and is prone to incorrect annotation.

[0007] 3) A combination of static analysis and runtime detection: By analyzing the time cost of each runtime detection operation, more time-consuming operations are discarded, thus enabling as many checks as possible within a given time limit. However, this method may miss memory errors.

[0008] 4) Parallel runtime detection: This method parallelizes memory safety checks and program execution, separating the checking operation from the main thread of the program. However, this approach cannot guarantee that memory errors occur before memory access and cannot guarantee memory safety in real time.

[0009] The basic principle of existing memory safety runtime detection tools is to check for memory errors by calling memory safety check functions inserted into the source code. These check functions first query the metadata of the memory region and then perform the checks. This process occurs frequently and incurs significant overhead. However, safe memory accesses account for the vast majority of program memory access behavior, while dangerous memory accesses are only a small minority. In particular, access to arrays allocated on the stack is mostly safe. Therefore, most of the checks are redundant, leading to significant unnecessary runtime overhead. Summary of the Invention

[0010] Purpose of the invention: To solve the above-mentioned technical problems, the present invention provides a method and medium for accelerating memory safe runtime detection by combining static pointer analysis, which achieves fast memory safe runtime detection without modifying existing code, without introducing false negatives or missed positives, and while ensuring the timing of error reporting and memory errors.

[0011] Technical solution: The memory safety runtime detection acceleration method combining static pointer analysis described in this invention includes the following steps:

[0012] S1: The compiler preprocesses the source code of the program to be tested, including macro substitution and importing header files; then the compiler front-end parses the source code of the program to be tested, including lexical analysis, syntax analysis, semantic analysis, and generation of abstract syntax tree.

[0013] S2: Collect array variable metadata: Traverse all variable definition nodes in the abstract syntax tree. If the variable definition node is a fixed-length or variable-length array variable definition, obtain the sign value of the array length and the type of the array elements, and add them to the metadata table M. meta : Where VD is the variable definition node on the abstract syntax tree, Len is the number of array elements, and Type is the type of array elements;

[0014] S3: In-process alias analysis: Analyze the set of possible aliases for each variable definition in the metadata table, including all possible pointers to each variable, to obtain the alias table M. alias : and pointers to table M ref :

[0015] S4: Inter-procedural alias analysis: Based on the results of intra-procedural alias analysis, traverse the actual arguments in the function call nodes on the abstract syntax tree. If the actual argument references M... alias If a variable is defined as a key in the function definition table M, then the function definition node and parameter number will be added to the function parameter number table M. idx : The function definition that was called references M. aliasThe index of the subset of parameters defined as keys in the variable definition; then, in the called function definition, alias analysis is performed within the procedure, and M is updated. ref , where FD is the function definition node on the abstract syntax tree, and N is the set of natural numbers;

[0016] S5: Insert code snippets for passing metadata across procedures: Use the shadow stack mechanism at runtime to pass metadata of pointer arguments across procedures, so that the metadata can enter the shadow stack before the function call and be obtained from the shadow stack during the function call;

[0017] S6: Rewrite the array subscript expression in the code and insert a code snippet for out-of-bounds detection;

[0018] S7: Insert runtime detection code;

[0019] S8: Compiles and links the instrumented source code using a compiler and linker to generate an executable file; runs the executable file and outputs the results of memory-safe runtime detection.

[0020] Furthermore, step S3 is implemented as follows:

[0021] S31: Collect assignment operators, that is, collect the abstract syntax tree nodes related to assignment into the assignment operator set S. AO In the abstract syntax tree, the nodes related to assignment include variable definitions with initial values ​​and binary assignment operators:

[0022] Traverse the variable definition nodes in the abstract syntax tree. If a variable definition node has an initial value, add the variable definition node to the assignment operator set.

[0023] Traverse the binary operator nodes in the abstract syntax tree. If the binary operator is an assignment operator, add it to the assignment operator set.

[0024] S32: Calculate the core pointer KPE(E), obtain the core pointer in the pointer expression E in the left and right operands of the assignment operator, and record the parent-child node relationship mapping table M in the member expression. me The syntax of the pointer expression E is as follows:

[0025] E:=ptr|uop E|E bop E|EE

[0026] ptr:=arr|"str"|id

[0027] uop:=(type)|*|&|++|--|...

[0028] bop:=+|-|=|[]|...

[0029] Where arr is the array name, "str" ​​is the string, id is the pointer variable identifier, uop is the unary operator, bop is the binary operator, and EE is the member expression;

[0030] S33: Traverse S AO For each assignment operator in the table, update the alias table M. alias : and pointers to table M ref : Among them, alias table M alias The metadata table records the set of possible aliases for each variable definition node; a pointer points to table M. ref Record the possible set of pointers that each pointer variable definition node can point to.

[0031] Furthermore, the rules for calculating the core pointer KPE(E) are as follows:

[0032] If E is an array name arr, the string "str", or the pointer variable identifier id, return E;

[0033] If E is of type (type)E1, and if E1 is a pointer type, return KPE(E1); otherwise, return (type)E1.

[0034] If E is of the form &E1, and if E1 is of the form *E2 or E2[i], return KPE(E2), otherwise return &E1;

[0035] If E is of the form of any other uop E1, and uop is a type-preserving operator, return KPE(E1); otherwise, return null.

[0036] If E is of the form E1 bop E2, and bop is a type preservation operator, calculate KPE(E1) and KPE(E2) respectively, and return the non-empty result; otherwise, return empty.

[0037] If E is of the form E1.E2, return KPE(E2) and record M. me [E2] = E1;

[0038] If none of the above apply, return null.

[0039] Furthermore, the update alias table M alias : and pointers to table M ref : The implementation process is as follows:

[0040] Initialize M alias If empty, set the metadata table M to empty. meta Each key in vd iAdd it to your own set of aliases;

[0041] Start the loop iteration and set the loop end flag to true;

[0042] Traversing the set of assignment operators S AO For each assignment operator, the pointer variable definition node (vd) corresponding to the core pointers of the left and right operands is calculated. l and VD r Then perform the following operations:

[0043] Traversing the alias list M alias Each mapping in Where vd is a variable definition node, Alias vd It is the set of possible aliases for vd: if the pointer variable corresponding to the core pointer of the right operand is defined in node vd. r In Alias vd In the middle, the pointer variable corresponding to the core pointer of the left operator is defined in node vd. l Join Alias vd The loop termination marker is set to false; if the pointer variable corresponding to the core pointer of the right operand is defined in node vd... r Not in Alias vd In the middle, no operation is performed; the lookup pointer points to table M. ref Mapping in vd l It is the pointer variable definition node corresponding to the core pointer of the left operand, Ref l It's a VD. l Possible sets of pointers, add vd to Ref l middle;

[0044] If the loop termination flag is false, jump to the loop iteration; if the loop termination flag is true, terminate.

[0045] Furthermore, step S4 is implemented as follows:

[0046] S41: Initialization: Set the function parameter index table M idx Set the work queue (workQ) to empty, and the visited function definition node and parameter index set (visited) to empty; iterate through each function call expression. If the expression contains the name of the called function and is not a library function, iterate through all the arguments in the expression. If the argument references a variable definition node (M), then... alias Or M ref If the key is specified, the function definition node and parameter number will be added to the function parameter number table M. idx and work queue workQ;

[0047] S42: Iterative Loop:

[0048] S421: If the work queue workQ is not empty, dequeue the function definition node fd and parameter index idx from the head of the work queue and add them to the visited set visited, and add the parameter index idx to M. idx The parameter index set of the corresponding function definition node fd;

[0049] S422: Perform intra-procedural alias analysis on the function definition node fd, obtain the intra-procedural alias analysis results for the parameters with the corresponding ordinal numbers, and add them to M. ref ;

[0050] S423: Iterate through all the arguments of the calling expression in the function definition node fd. If the argument references a variable definition node M... idx The formal parameters within the call expression are added to the work queue along with the definition node of the function called by the call expression.

[0051] S424: If the work queue is not empty, jump to S421 to continue the analysis.

[0052] Furthermore, the implementation process of step S5 is as follows:

[0053] S51: Analyze the order in which arguments are pushed onto the stack. Based on the calling order from the inside out and the calculation order of arguments from right to left in nested function call expressions, the steps include:

[0054] Recursively access the actual arguments of each calling expression to obtain the nesting relationship of the calling expressions, and store it in a tree data structure. The root node of the data structure is the outermost calling expression, and the leaf nodes are the innermost calling expressions.

[0055] For each tree data structure, starting from the root node, traverse the actual parameters from right to left. If the core pointer variable of the actual parameter is in M... alias M ref Or M idx If the core pointer variable is not specified, it is stored in the argument push order list; otherwise, NULL is stored in the argument push order list, and then the calling expression in the argument is recursively traversed.

[0056] Based on the traversal order, a list of actual parameters pushed onto the stack is obtained, which stores the order of all actual parameters under the outermost calling expression and the order of actual parameters pushed onto the stack of nested calling expressions;

[0057] S52: Querying the metadata of the actual arguments: For each pointer p in the list of order in which the actual arguments are pushed onto the stack, query the length of the memory space pointed to by p, which is divided into four cases:

[0058] ① If p is in the metadata table M at compile time meta Inside, directly from M meta Get the length of the memory space pointed to by p;

[0059] ② If p is in the pointer pointing to table M ref Within M, and it has a unique pointer, and the variable it points to does not come from a function parameter, then from M ref Get the variable that it uniquely points to from M, and then get it from M. meta Get the length of the memory space pointed to by the variable;

[0060] ③ If p is in the pointer pointing to table M ref Within, and it has a unique pointer, and the variable it points to comes from the function parameter (M). idx If the function name and parameter number are given, then an address variable name is constructed based on the function name and parameter number as the address of the memory space pointed to by p, and a length variable name is constructed as the length of the memory space pointed to by p. The values ​​of the address variable and the length variable will be obtained by popping from the shadow stack.

[0061] ④ If the above three conditions are not met, the address of the memory space pointed to by p is NULL, and the length of the memory space pointed to by p is 0;

[0062] S53: Generate shadow stack push code snippet: Based on the list of actual parameters pushed onto the stack, replace each pointer p in the list with the code snippet: "PRFvar_addr_push(p),PRFvar_len_push(n)", where PRFvar_addr_push(p) is used to push the address of the memory space pointed to by p onto the shadow stack, n is the length of the memory space pointed to by pointer p, and PRFvar_len_push is used to push n onto the shadow stack;

[0063] S54: Inserting Shadow Stack Push Code Snippets: Based on the upper-level language structure of the calling expression, different instrumentation strategies are executed to insert generated shadow stack push code snippets:

[0064] For language constructs without branching structures, insert a semicolon separator before the calling expression;

[0065] For language constructs that may have branching structures, insert a comma-separated line before the calling expression;

[0066] For language constructs with lower precedence than the comma operator, insert the code before the calling expression, separate it with a comma, and enclose the inserted code and the calling expression in a pair of parentheses.

[0067] Furthermore, the implementation process of step S6 is as follows: For each array subscript expression p[s], where p is the array name and s is the subscript, the following operations are performed:

[0068] S61: Construct a Boolean expression for out-of-bounds detection:

[0069] If the array name p is in the compile-time metadata table M meta , determine whether the type of p[s] is consistent with the type of the memory space of p recorded in M meta : If they are consistent, the Boolean expression is "0 <= s && s < len", where len is the length of the memory space of p recorded in M meta ; if they are inconsistent, the Boolean expression is "0 <= s && s < len * sizeof(T1) / sizeof(T2)", where T1 is the type of the memory space of p recorded in M meta and T2 is the type of p[s];

[0070] If the array name p is in the pointer pointing table M ref , obtain the pointer variable name arr pointed to by p from the table, and the Boolean expression is "p == arr && 0 <= s && s < len", where len is the length of the memory space of arr recorded in M meta ;

[0071] S62: Use the constructed Boolean expression as the condition of the conditional expression, construct a conditional expression, and replace the subscript in the array subscript expression:

[0072] If the array name p is a pointer constant name, replace the subscript expression "[s]" with "[0 <= s && s < len? s : __RV_check_dpc_ss( / * omitted actual parameters * / )]";

[0073] If the array name p is a pointer variable name, replace the subscript expression "[s]" with "[p == arr && 0 <= s && s < len? s : __RV_check_dpv_ss( / * omitted actual parameters * / )]", where arr is the variable pointed to by p;

[0074] S63: If the array subscript expression has side effects, insert a temporary variable to store the value after the subscript expression is calculated, and then use this temporary variable as the subscript to construct a conditional expression, so as to ensure that the subscript expression is only calculated once;

[0075] S64: For multi-dimensional arrays, the Boolean expressions generated at the lower dimensions of the array will be recursively inherited and conjoined with the Boolean expressions generated at the higher dimensions to ensure that no memory errors in any dimension are missed.

[0076] Furthermore, step S7 is implemented as follows:

[0077] S71: Insert runtime pointer metadata definition, including the following fields: base address of memory region, upper limit of memory region, and status of memory region, where the status of memory region is used to identify whether the memory region is valid;

[0078] S72: Insert runtime pointer metadata lookup table, which uses a two-level prefix tree structure, where the first-level prefix tree uses the high 23 bits of the pointer variable address as an index to point to the second-level prefix tree; the second-level prefix tree uses the low 22 bits of the pointer variable address as an index to point to the pointer metadata item;

[0079] S73: Definitions of the memory access out-of-bounds checking functions __RV_check_dpc_ss and __RV_check_dpv_ss. These functions determine whether a memory access is out of bounds and whether it is freed based on runtime pointer metadata, the name of the accessed array, and its index. If the accessed memory region is within the memory region recorded in the metadata and is not freed, it is a safe access; otherwise, it is a dangerous access and a runtime error is reported.

[0080] The present invention discloses a storage medium storing a computer program, which, when executed by at least one processor, implements the steps of the memory-safe runtime detection acceleration method combining static pointer analysis as described above.

[0081] An electronic device according to the present invention includes a memory and a processor, wherein:

[0082] Memory is used to store computer programs that can run on a processor;

[0083] A processor, configured to, while running the computer program, perform the steps of the memory-safe runtime detection acceleration method incorporating static pointer analysis as described above.

[0084] Beneficial effects: Compared with the prior art, the beneficial effects of the present invention are: The present invention uses lightweight Boolean expression calculation to replace heavyweight runtime detection, which can effectively reduce the time overhead of runtime detection tools without modifying existing code, without introducing false negatives or missed positives, and while ensuring the timing of error reporting and memory errors, and has practical application value. Attached Figure Description

[0085] Figure 1 This is a flowchart of a memory-safe runtime detection acceleration method that combines static pointer analysis. Detailed Implementation

[0086] The present invention will now be described in further detail with reference to the accompanying drawings.

[0087] like Figure 1 As shown, this invention provides a memory safety runtime detection acceleration method combining static pointer analysis, including the following steps:

[0088] S1: The compiler preprocesses the source code of the program to be tested, including macro substitution and importing header files. Then, the compiler front-end parses the source code of the program to be tested, including lexical analysis, syntax analysis, semantic analysis, and generation of abstract syntax tree.

[0089] In this embodiment, the source code of the program to be detected is as follows:

[0090]

[0091] The generated abstract syntax tree is as follows:

[0092]

[0093]

[0094] S2: Collect metadata for array variables. Traverse all variable definition nodes in the abstract syntax tree. If the variable definition node is a fixed-length or variable-length array variable definition, obtain the sign value of the array length and the type of the array elements, and add them to the metadata table M. meta : Where VD is the variable definition node on the abstract syntax tree, Len is the number of array elements, and Type is the type of array elements.

[0095] In this embodiment, the metadata structure is defined as follows:

[0096]

[0097] In this embodiment, the source code of the program to be detected is as follows:

[0098]

[0099]

[0100] The collected metadata tables are as follows:

[0101]

[0102] This means that the arr1 variable in the main function contains 10 int type elements, and the arr2 variable in the main function contains N int type elements.

[0103] S3: In-process alias analysis, which involves analyzing the set of possible aliases for each variable definition in the metadata table, including all possible pointers to each variable. This is specifically divided into the following steps:

[0104] S31: Collect assignment operators, that is, collect the abstract syntax tree nodes related to assignment into the assignment operator set S. AO In the abstract syntax tree, the nodes related to assignment include variable definitions with initial values ​​and binary assignment operators, specifically:

[0105] Traverse the variable definition nodes in the abstract syntax tree. If a variable definition node has an initial value, add the variable definition node to the assignment operator set.

[0106] Traverse the binary operator nodes in the abstract syntax tree. If the binary operator is an assignment operator, add it to the assignment operator set.

[0107] In this embodiment, the set of assignment operators in the source code of the program to be tested is as follows:

[0108] S AO ={alias1=arr1,alias2=arr2,alias1=arr2}

[0109] S32: Calculate the core pointer KPE(E), obtain the core pointer in the pointer expression E in the left and right operands of the assignment operator, and record the parent-child node relationship mapping table M in the member expression. me The syntax of the pointer expression E is as follows:

[0110] E:=ptr|uop E|E bop E|EE

[0111] ptr:=arr|"str"|id

[0112] uop:=(type)|*|&|++|--|...

[0113] bop:=+|-|=|[]|...

[0114] Where arr is the array name, "str" ​​is the string, id is the pointer variable identifier, uop is the unary operator, bop is the binary operator, and EE is the member expression.

[0115] A core pointer is a pointer in a complex pointer expression that determines the memory it points to, namely an array name, a string, and a pointer variable identifier. The rules for calculating KPE(E) for different forms of E are as follows:

[0116] If E is an array name arr, the string "str", or the pointer variable identifier id, return E.

[0117] If E is of type (type)E1, and if E1 is a pointer type, return KPE(E1); otherwise, return (type)E1.

[0118] If E is of the form &E1, and if E1 is of the form *E2 or E2[i], return KPE(E2); otherwise, return &E1.

[0119] If E is of the form other than uop E1, and uop is a type preservation operator, return KPE(E1); otherwise, return null.

[0120] If E is of the form E1 bop E2, and bop is a type preservation operator, calculate KPE(E1) and KPE(E2) respectively, and return the non-empty result; otherwise, return empty.

[0121] If E is of the form E1.E2, return KPE(E2) and record M. me [E2] = E1;

[0122] If none of the above apply, return null.

[0123] In this embodiment, the complex expression to be analyzed is as follows:

[0124] &a.bcarr[0]+1*0

[0125] The process of calculating the core pointer is as follows:

[0126] Handling the binary operator "+": Since "+" is a type-preserving operator, calculate KPE(&a.bcarr[0]) and KPE(1*0) respectively. Since the result of KPE(1*0) is empty, return KPE(&a.bcarr[0]).

[0127] Process KPE(&a.bcarr[0]) and return KPE(abcarr);

[0128] Process KPE(abcarr), return arr, and for record M of ab... me [b] = a, for record M of bc me [c] = b, for record M in c.arr me [arr] = c;

[0129] Finally, the parent-child node relationship mapping table M is obtained. me={<arr,c> ,<c,b> ,<b,a> In the subsequent alias analysis phase, the parent structures of the core pointer arr are queried in sequence as c, b, a, and finally restored to the complete expression abcarr.

[0130] S33: In-process alias analysis. Traversing S AO For each assignment operator in the table, update the alias table M. alias : and pointers to table M ref : Among them, alias table M alias The metadata table records the set of possible aliases for each variable definition node, which is essentially the set of pointer variable definition nodes that could point to the memory region of that variable. These pointers point to table M. ref Record the possible sets of pointer variable definition nodes that each pointer variable definition node can point to; that is, the set of variable definition nodes contained in the metadata table that the pointer variable may point to. This includes the following steps:

[0131] Initialize M alias If empty, set the metadata table M to empty. meta Each key in vd i Add it to your own set of aliases.

[0132] Start the loop iteration and set the loop end flag to true.

[0133] Traversing the set of assignment operators S AO For each assignment operator, the pointer variable definition node (vd) corresponding to the core pointers of the left and right operands is calculated. l and VD r Then perform the following operations:

[0134] Traversing the alias list M alias Each mapping in Where vd is a variable definition node, Alias vd It is the set of possible aliases for vd: if the pointer variable corresponding to the core pointer of the right operand is defined in node vd. r In Alias vd In the middle, the pointer variable corresponding to the core pointer of the left operator is defined in node vd. l Join Alias vd The loop termination marker is set to false; if the pointer variable corresponding to the core pointer of the right operand is defined in node vd... r Not in Alias vd In the middle, no operation is performed; the lookup pointer points to table M. ref Mapping in vdl It is the pointer variable definition node corresponding to the core pointer of the left operand, Ref l It's a VD. l Possible sets of pointers, add vd to Ref l middle.

[0135] If the loop termination flag is false, the loop restarts; if the loop termination flag is true, the loop terminates.

[0136] In this embodiment, the source code of the program to be detected is as follows:

[0137]

[0138] The results of the alias analysis within the procedure are the following alias table and pointer pointer table:

[0139]

[0140]

[0141] S4: Inter-procedural aliasing analysis. Based on the results of the intra-procedural aliasing analysis in the previous step, traverse the actual arguments in the function call nodes on the abstract syntax tree. If the actual argument references M... alias If a variable is defined as a key in the function definition table M, then the function definition node and parameter number will be added to the function parameter number table M. idx : The function definition that was called references M. alias The parameter subset index, defined as the key in the variable definition, is then used for alias analysis within the called function definition and updated in M. ref , where FD is the function definition node on the abstract syntax tree, and N is the set of natural numbers. The steps include:

[0142] S41: Initialization. Set the function parameter index table M... idx Set the work queue (workQ) to empty, and the visited function definition node and parameter index set (visited) to empty; iterate through each function call expression. If the expression contains the name of the called function and is not a library function, iterate through all the arguments in the expression. If the argument references a variable definition node (M), then... alias Or M ref If the key is specified, the function definition node and parameter number will be added to the function parameter number table M. idx and the work queue workQ.

[0143] In this embodiment, the source code of the program to be detected is as follows:

[0144]

[0145] The initialization result is:

[0146]

[0147] S42: Iterative loop. Includes the following steps:

[0148] 1) If the work queue `workQ` is not empty, dequeue the function definition node `fd` and parameter index `idx` from the head of the work queue and add them to the visited set `visited`. Add the parameter index `idx` to `M`. idx The parameter sequence number set of the corresponding function definition node fd.

[0149] 2) Perform intra-procedural alias analysis on the function definition node fd, i.e., step S3, to obtain the intra-procedural alias analysis results for the parameters with the corresponding ordinal numbers, and add them to M. ref .

[0150] 3) Iterate through all the arguments of the calling expression in the function definition node fd. If the argument references a variable definition node M... idx The formal parameters within the call expression are added to the work queue along with the definition node of the function called by the call expression.

[0151] 4) If the work queue is not empty, jump to (1) to continue the analysis.

[0152] In this embodiment, the function parameter index table after loop iteration

[0153] S5: Insert a code snippet for passing metadata across procedures. Since the same function may be called in different contexts, and the metadata of pointer arguments (including the address and length of the memory region they point to) may differ in different contexts, a shadow stack mechanism is needed at runtime to pass the metadata of pointer arguments across procedures. This allows the metadata to enter the shadow stack before the function call, and thus be retrieved from the shadow stack during the function call. Specifically, this includes four steps:

[0154] S51: Analyze the order of arguments pushed onto the stack. Based on the calling order from the inside out and the evaluation order of arguments from right to left in nested function call expressions, specifically:

[0155] Recursively access the actual arguments of each calling expression to obtain the nesting relationship of the calling expressions, and store it in a tree data structure. The root node of this data structure is the outermost calling expression, and the leaf nodes are the innermost calling expressions.

[0156] For each tree data structure, starting from the root node, traverse the actual parameters from right to left. If the core pointer variable of the actual parameter is in M... alias M ref Or Midx If the core pointer variable is not specified, it is stored in the argument push order list; otherwise, NULL is stored in the argument push order list, and then the calling expression in the argument is recursively traversed.

[0157] Based on the traversal order, a list of actual arguments pushed onto the stack is obtained, which stores the order in which all actual arguments under the outermost calling expression and the actual arguments of nested calling expressions are pushed onto the stack.

[0158] In this embodiment, the code snippet to be analyzed is as follows, where the core pointer variables of all actual parameters of the foo function are in M. alias M ref Or M idx middle:

[0159] foo(arg1,foo(arg21,arg22,arg23),arg3);

[0160] The analysis process is as follows: First, iterate through the outermost rightmost parameter arg3; since the inner function's return value is used as an outer function parameter, the outer function's second parameter will be replaced with NULL; next, iterate through the outermost leftmost parameter arg1; finally, for the nested call at the second parameter, iterate through the inner function parameters arg23, arg22, and arg21 from right to left. Ultimately, the resulting list of actual arguments pushed onto the stack is as follows:

[0161] arg3,NULL,arg1,arg23,arg22,arg21

[0162] S52: Query the metadata of the actual parameter.

[0163] For each pointer p in the list of actual arguments pushed onto the stack, the length of the memory space pointed to by p is checked, and there are four cases:

[0164] (S52.1) If p is in the metadata table M at compile time meta Inside, directly from M meta Get the length of the memory space pointed to by p.

[0165] (S52.2) If p is in the pointer pointing to table M ref Within M, and it has a unique pointer, and the variable it points to does not come from a function parameter, then from M ref Get the variable that it uniquely points to from M, and then get it from M. meta The length of the memory space pointed to by the variable is obtained from the data.

[0166] (S52.3) If p is in the pointer pointing to table M ref Within, and it has a unique pointer, and the variable it points to comes from the function parameter (M). idxGiven the function name and parameter number corresponding to the function parameters, an address variable name is constructed based on the function name and parameter number as the address of the memory space pointed to by p, and a length variable name is constructed as the length of the memory space pointed to by p. The values ​​of the address variable and the length variable will be obtained by popping from the shadow stack.

[0167] In this embodiment, the address variable corresponding to the pointer arr in foo(int*arr) is named PRFvar_addr_foo_0, and the length variable is named PRFvar_len_foo_0.

[0168] (S52.4) If the above three conditions are not met, the address of the memory space pointed to by p is NULL, and the length of the memory space pointed to by p is 0.

[0169] S53: Generate a code snippet for pushing the shadow stack. Based on the list of actual arguments pushed onto the stack, replace each pointer p in the list with the code snippet: "PRFvar_addr_push(p),PRFvar_len_push(n)", where PRFvar_addr_push(p) is used to push the address of the memory space pointed to by p onto the shadow stack, n is the length of the memory space pointed to by pointer p, and PRFvar_len_push is used to push n onto the shadow stack.

[0170] S54: Inserting Shadow Stack Push Code Snippets: Based on the upper-level language structure of the calling expression, different instrumentation strategies are executed to insert generated shadow stack push code snippets, including the following cases:

[0171] (S54.1) For language structures without branching structures, such as compound statements and Case statements, insert a semicolon before the calling expression;

[0172] (S54.2) For language structures that may have branching structures, such as conditional expressions, For statements, and While statements, insert a comma-separated line before calling the expression;

[0173] (S54.3) For language constructs with lower precedence than the comma operator, such as the Return statement, conditional operator, and logical operator, insert the code before the calling expression, separated by a comma, and enclose the inserted code and the calling expression in a pair of parentheses.

[0174] In this embodiment, the source code of the program to be detected is as follows:

[0175]

[0176] The source code after instrumentation is as follows:

[0177]

[0178]

[0179] In this embodiment, the source code of the program to be detected is as follows:

[0180] The source code after stub insertion is as follows:

[0181]

[0182] S6: Rewrite the array subscript expressions in the code. For each array subscript expression p[s], where p is the array name and s is the subscript, the following steps are included:

[0183] S61: Construct a boolean expression for out-of-bounds detection:

[0184] (1) If the array name p is in the compile-time metadata table M meta judge whether the type of p[s] is consistent with the type of the memory space of p recorded in M meta : If they are consistent, the boolean expression is "0 <= s && s < len", where len is the length of the memory space of p recorded in M meta ; if they are inconsistent, the boolean expression is "0 <= s && s < len * sizeof(T1) / sizeof(T2)", where T1 is the type of the memory space of p recorded in M meta and T2 is the type of p[s].

[0185] (2) If the array name p is in the pointer pointing table M ref , obtain the pointer variable name arr pointed to by p from the table, and the boolean expression is "p == arr && 0 <= s && s < len", where len is the length of the memory space of arr recorded in M meta .

[0186] S62: Use the constructed boolean expression as the condition of the conditional expression, construct a conditional expression, and replace the subscript in the array subscript expression:

[0187] If the array name p is a pointer constant name, replace the subscript expression "[s]" with "[0 <= s && s < len? s : __RV_check_dpc_ss( / * omitted actual parameters * / )]";

[0188] [6]]If the array name p is a pointer variable name, replace the subscript expression "[s]" with "[p == arr && 0 <= s && s < len? s : __RV_check_dpv_ss( / * omitted actual parameters * / )]", where arr is the variable pointed to by p.

[0189] In this embodiment, the source code of the program to be detected is as follows:

[0190]

[0191] The source code after instrumentation is as follows:

[0192]

[0193]

[0194] S63: If the array subscript expression has side effects, insert a temporary variable to store the value of the subscript expression after evaluation, and then use this temporary variable as the subscript to construct the conditional expression, thereby ensuring that the subscript expression is evaluated only once.

[0195] In this embodiment, the source code of the program to be detected is as follows:

[0196]

[0197] The source code after instrumentation is as follows:

[0198]

[0199] S64: Multidimensional array instrumentation. For multidimensional arrays, Boolean expressions generated at lower dimensions are recursively inherited and conjunct Boolean expressions generated at higher dimensions to ensure that no memory errors in any dimension are missed.

[0200] In this embodiment, the source code of the program to be detected is as follows:

[0201]

[0202] The source code after instrumentation is as follows:

[0203]

[0204] S7: Insert runtime detection code, including the following steps:

[0205] S71: Insert runtime pointer metadata definition, including the following fields: base address of memory region, upper limit of memory region, and status of memory region, where the status of memory region is used to identify whether memory region is valid.

[0206] In this embodiment, the runtime pointer metadata is defined as follows, where __RV_ptr_addr is the pointer variable address type:

[0207]

[0208] S72: Insert a runtime pointer metadata lookup table. This table uses a two-level prefix tree structure, where the first-level prefix tree uses the high 23 bits of the pointer variable's address as an index to point to the second-level prefix tree; the second-level prefix tree uses the low 22 bits of the pointer variable's address as an index to point to the pointer metadata item.

[0209] In this embodiment, the second-level prefix tree is defined as follows, where __RV_pmd is a pointer metadata type, and PRFtrie_primary_entries and PRFtrie_secondary_entries are the capacities of the first-level table and the second-level table, respectively:

[0210] / *primary table* /

[0211] __RV_pmd**primary_table[PRFtrie_primary_entries];

[0212] / *secondary table* /

[0213] __RV_pmd*second_table[PRFtrie_secondary_entries];

[0214] S73: Definitions of the memory access out-of-bounds checking functions __RV_check_dpc_ss and __RV_check_dpv_ss. These functions determine whether a memory access is out of bounds and whether it is freed based on runtime pointer metadata, the name of the accessed array, and its index. If the accessed memory region is within the memory region recorded in the metadata and is not freed, it is a safe access; otherwise, it is a dangerous access and a runtime error is reported.

[0215] S8: Compiles and links the instrumented source code using a compiler and linker to generate an executable file. Running the executable file outputs the results of a memory-safe runtime test.

[0216] The present invention also provides a storage medium storing a computer program, which, when executed by at least one processor, implements the steps of the memory-safe runtime detection acceleration method combined with static pointer analysis as described above.

[0217] The present invention also provides an electronic device, including a memory and a processor, wherein: the memory is used to store a computer program that can run on the processor; and the processor is used to execute, when running the computer program, the steps of the memory-safe runtime detection acceleration method combined with static pointer analysis as described above.

[0218] The above-described embodiments are merely illustrative of several implementations of the present invention, and while the descriptions are relatively specific and detailed, they should not be construed as limiting the scope of the invention. It should be noted that those skilled in the art can make various modifications and improvements without departing from the concept of the present invention, and these modifications and improvements all fall within the scope of protection of the present invention.

Claims

1. A method for accelerating memory-safe runtime detection by combining static pointer analysis, characterized in that, Includes the following steps: S1: Use the compiler to preprocess the source code of the program to be tested, including macro substitution and importing header files; Then, the compiler front-end is used to parse the source code of the program to be tested, including lexical analysis, syntax analysis, semantic analysis, and generation of abstract syntax tree; S2: Collect array variable metadata: Traverse all variable definition nodes in the abstract syntax tree. If the variable definition node is a fixed-length or variable-length array variable definition, obtain the sign value of the array length and the type of the array elements, and add them to the metadata table. Where VD is the variable definition node on the abstract syntax tree, Len is the number of array elements, and Type is the type of array elements; S3: In-process alias analysis: Analyze the set of possible aliases for each variable definition in the metadata table, including all possible pointers to each variable, to obtain the alias table. and pointers to tables S4: Inter-procedural alias analysis: Based on the results of intra-procedural alias analysis, traverse the actual arguments in the function call nodes on the abstract syntax tree. If the actual argument references M... alias If a variable is defined as a key in the function definition table, then the function definition node and parameter number will be added to the function parameter number table. The function definition that was called references M. alias The index of the subset of parameters defined as keys in the variable definition; then, in the called function definition, alias analysis is performed within the procedure, and M is updated. ref , where FD is the function definition node on the abstract syntax tree, and N is the set of natural numbers; S5: Insert code snippets for passing metadata across procedures: Use the shadow stack mechanism at runtime to pass metadata of pointer arguments across procedures, so that the metadata can enter the shadow stack before the function call and be obtained from the shadow stack during the function call; S6: Rewrite the array subscript expression in the code and insert a code snippet for out-of-bounds detection; S7: Insert runtime detection code; S8: Compiles and links the instrumented source code using a compiler and linker to generate an executable file; runs the executable file and outputs the results of memory-safe runtime detection.

2. The memory safety runtime detection acceleration method combining static pointer analysis according to claim 1, characterized in that, The implementation process of step S3 is as follows: S31: Collect assignment operators, that is, collect the abstract syntax tree nodes related to assignment into the assignment operator set S. AO In the abstract syntax tree, the nodes related to assignment include variable definitions with initial values ​​and binary assignment operators: Traverse the variable definition nodes in the abstract syntax tree. If a variable definition node has an initial value, add the variable definition node to the assignment operator set. Traverse the binary operator nodes in the abstract syntax tree. If the binary operator is an assignment operator, add it to the assignment operator set. S32: Calculate the core pointer KPE(E), obtain the core pointer in the pointer expression E in the left and right operands of the assignment operator, and record the parent-child node relationship mapping table M in the member expression. me The syntax of the pointer expression E is as follows: E:=ptr|uop E|E bop E|EE ptr:=arr|"str"|id uop:=(type)|*|&|++|--|... bop:=+|-|=|[]|... Where arr is the array name, "str" ​​is the string, id is the pointer variable identifier, uop is the unary operator, bop is the binary operator, and EE is the member expression; S33: Traverse S AO For each assignment operator, update the alias table. and pointers to tables Among them, alias table M alias The metadata table records the set of possible aliases for each variable definition node; a pointer points to table M. ref Record the possible set of pointers that each pointer variable definition node can point to.

3. The memory safety runtime detection acceleration method combining static pointer analysis according to claim 2, characterized in that, The rules for calculating the core pointer KPE(E) are as follows: If E is an array name arr, the string "str", or the pointer variable identifier id, return E; If E is (type)E1, and if E1 is a pointer type, return KPE(E1); otherwise, return (type)E1. If E is &E1, and if E1 is *E2 or E2[i], return KPE(E2), otherwise return &E1; If E is any other uop E1, and uop is a type-preserving operator, return KPE(E1); otherwise, return null. If E is E1 bop E2, and bop is the type preservation operator, calculate KPE(E1) and KPE(E2) respectively, and return the non-empty result; otherwise, return empty. If E is E1.E2, return KPE(E2) and record M. me [E2] = E1; If none of the above apply, return null.

4. The memory safety runtime detection acceleration method combining static pointer analysis according to claim 2, characterized in that, The updated alias table and pointers to tables The implementation process is as follows: Initialize M alias If empty, set the metadata table M to empty. meta Each key in vd i Add it to your own set of aliases; Start the loop iteration and set the loop end flag to true; Traversing the set of assignment operators S AO For each assignment operator, the pointer variable definition node (vd) corresponding to the core pointers of the left and right operands is calculated. l and VD r Then perform the following operations: Traversing the alias list M alias Each mapping in Where vd is a variable definition node, Alias vd It is the set of possible aliases for vd: if the pointer variable corresponding to the core pointer of the right operand is defined in node vd. r In Alias vd In the middle, the pointer variable corresponding to the core pointer of the left operator is defined in node vd. l Join Alias vd The loop termination marker is set to false; if the pointer variable corresponding to the core pointer of the right operand is defined in node vd... r Not in Alias vd In the middle, no operation is performed; the lookup pointer points to table M. ref Mapping in vd l It is the pointer variable definition node corresponding to the core pointer of the left operand, Ref l It's a VD. l Possible sets of pointers, add vd to Ref l middle; If the loop termination flag is false, jump to the loop iteration; if the loop termination flag is true, terminate.

5. The memory safety runtime detection acceleration method combining static pointer analysis according to claim 1, characterized in that, The implementation process of step S4 is as follows: S41: Initialization: Set the function parameter index table M idx Set the work queue (workQ) to empty, and the visited function definition node and parameter index set (visited) to empty; iterate through each function call expression. If the expression contains the name of the called function and is not a library function, iterate through all the arguments in the expression. If the argument references a variable definition node (M), then... alias Or M ref If the key is specified, the function definition node and parameter number will be added to the function parameter number table M. idx and work queue workQ; S42: Iterative Loop: S421: If the work queue workQ is not empty, dequeue the function definition node fd and parameter index idx from the head of the work queue and add them to the visited set visited, and add the parameter index idx to M. idx The parameter index set of the corresponding function definition node fd; S422: Perform intra-procedural alias analysis on the function definition node fd, obtain the intra-procedural alias analysis results for the parameters with the corresponding ordinal numbers, and add them to M. ref ; S423: Iterate through all the arguments of the calling expression in the function definition node fd. If the argument references a variable definition node M... idx The formal parameters within the call expression are added to the work queue along with the definition node of the function called by the call expression. S424: If the work queue is not empty, jump to S421 to continue the analysis.

6. The memory safety runtime detection acceleration method combining static pointer analysis according to claim 1, characterized in that, The implementation process of step S5 is as follows: S51: Analyze the order in which arguments are pushed onto the stack. Based on the calling order from the inside out and the calculation order of arguments from right to left in nested function call expressions, the steps include: Recursively access the arguments of each call expression to obtain the nesting relationship of the call expressions, and store it in a tree data structure. The root node of this data structure is the outermost call expression, and the leaf nodes are the innermost call expressions; For each tree data structure, starting from the root node, traverse the actual parameters from right to left. If the core pointer variable of the actual parameter is in M... alias M ref Or M idx If the core pointer variable is not specified, it is stored in the argument push order list; otherwise, NULL is stored in the argument push order list, and then the calling expression in the argument is recursively traversed. Obtain a list of argument stack-in orders according to the traversal order, which stores the stack-in orders of all arguments and the arguments of nested call expressions under the outermost call expression; S52: Query the metadata of the arguments: For each pointer p in the list of argument stack-in orders, query the length of the memory space pointed to by p, which is divided into four cases: ① If p is in the metadata table M at compile time meta Inside, directly from M meta Get the length of the memory space pointed to by p; ② If p is in the pointer pointing to table M ref Within M, and it has a unique pointer, and the variable it points to does not come from a function parameter, then from M ref Get the variable that it uniquely points to from M, and then get it from M. meta Get the length of the memory space pointed to by the variable; ③ If p is in the pointer pointing to table M ref Within, and it has a unique pointer, and the variable it points to comes from the function parameter (M). idx If the function name and parameter number are given, then an address variable name is constructed based on the function name and parameter number as the address of the memory space pointed to by p, and a length variable name is constructed as the length of the memory space pointed to by p. The values ​​of the address variable and the length variable will be obtained by popping from the shadow stack. ④ If none of the above three cases are satisfied, the address of the memory space pointed to by p is NULL, and the length of the memory space pointed to by p is 0; S53: Generate the shadow stack stack-in code segment: According to the list of argument stack-in orders, replace each pointer p in the list with the code segment: "PRFvar_addr_push(p),PRFvar_len_push(n)", where PRFvar_addr_push(p) is used to push the address of the memory space pointed to by p onto the shadow stack, n is the length of the memory space pointed to by the pointer p, and PRFvar_len_push is used to push n onto the shadow stack; S54: Insert the shadow stack stack-in code segment: According to the upper-layer language structure of the call expression, execute different stubbing strategies to insert the generated shadow stack stack-in code segment: For language structures without branch structures, insert before the call expression, separated by semicolons; For language structures that may have branch structures, insert before the call expression, separated by commas; For language structures with an operation precedence lower than the comma operator, insert before the call expression, separated by commas, and use a pair of parentheses to enclose the inserted code and the call expression.

7. The memory safety runtime detection acceleration method combining static pointer analysis according to claim 1, characterized in that, The implementation process of step S6 is as follows: For each array subscript expression p[s], where p is the array name and s is the subscript, perform the following operations: S61: Construct a boolean expression for out-of-bounds detection: If the array name p is in the compile-time metadata table M meta , determine whether the type of p[s] is consistent with the type of the memory space of p recorded in M meta : If they are consistent, the boolean expression is "0 <= s && s < len", where len is the length of the memory space of p recorded in M meta ; if they are inconsistent, the boolean expression is "0 <= s && s < len * sizeof(T1) / sizeof(T2)", where T1 is the type of the memory space of p recorded in M meta and T2 is the type of p[s]; If the array name p is in the pointer pointing to table M ref In it, obtain the pointer variable name arr pointed to by p from the table. The Boolean expression is "p == arr && 0 <= s && s < len", where len is the length of the memory space of arr recorded in M meta In which; S62: Use the constructed boolean expression as the condition of the conditional expression, construct a conditional expression, and replace the subscript in the array subscript expression: If the array name p is a pointer constant name, replace the subscript expression "[s]" with "[0 <= s && s < len? s : __RV_check_dpc_ss( / * omitted arguments * / )]"; If the array name p is a pointer variable name, replace the subscript expression "[s]" with "[p == arr && 0 <= s && s < len? s : __RV_check_dpv_ss( / * omitted arguments * / )]", where arr is the variable pointed to by p; S63: If the array subscript expression has side effects, insert a temporary variable to store the value after the subscript expression is calculated, and then use this temporary variable as the subscript to construct a conditional expression, so as to ensure that the subscript expression is only calculated once; S64: For multidimensional arrays, the Boolean expression generated at the lower dimension of the array is recursively inherited and combined with the Boolean expression generated at the higher dimension to ensure that no memory errors in any dimension are missed.

8. The memory safety runtime detection acceleration method combining static pointer analysis according to claim 1, characterized in that, The implementation process of step S7 is as follows: S71: Insert runtime pointer metadata definition, including the following fields: base address of memory region, upper limit of memory region, and status of memory region, where the status of memory region is used to identify whether the memory region is valid; S72: Insert runtime pointer metadata lookup table, which uses a two-level prefix tree structure, where the first-level prefix tree uses the high 23 bits of the pointer variable address as an index to point to the second-level prefix tree; the second-level prefix tree uses the low 22 bits of the pointer variable address as an index to point to the pointer metadata item; S73: Definitions of the memory access out-of-bounds checking functions __RV_check_dpc_ss and __RV_check_dpv_ss. These functions determine whether a memory access is out of bounds and whether it is freed based on runtime pointer metadata, the name of the accessed array, and its index. If the accessed memory region is within the memory region recorded in the metadata and is not freed, it is a safe access; otherwise, it is a dangerous access and a runtime error is reported.

9. A storage medium, characterized in that, The storage medium stores a computer program that, when executed by at least one processor, implements the steps of the memory-safe runtime detection acceleration method incorporating static pointer analysis as described in any one of claims 1 to 8.

10. An electronic device, characterized in that, Includes memory and processor, wherein: Memory is used to store computer programs that can run on a processor; A processor, configured to, while running the computer program, perform the steps of the memory-safe runtime detection acceleration method incorporating static pointer analysis as described in any one of claims 1 to 8.

Citation Information

Patent Citations

  • Automatic detection and positioning method for memory error in source code

    CN106940654A

  • Determining based on static compiler analysis that execution of compiler code would result in unacceptable program behavior

    US20190310834A1