Static pointer analysis-combined detection acceleration method during safe operation of memory and medium
By combining static pointer analysis and shadow stack mechanism memory safe runtime detection method, the problem of high time overhead of memory security detection tools in the existing technology is solved, and efficient memory security detection is achieved.
Patent Information
- Application Number
- CN202411910962.0
- Authority / Receiving Office
- CN · China
- Patent Type
- Applications(China)
- Current Assignee / Owner
- Filing Date
- 2024-12-24
- Publication Date
- 2025-04-29
- Estimated Expiration
- 2044-12-24
AI Technical Summary
The existing memory security runtime detection tools have high time overhead due to frequent security checks, which seriously weakens the efficiency and practicality of the tool to check memory security issues.
Combined with the memory-safe runtime detection acceleration method of static pointer analysis, the source code is preprocessed and abstract syntax tree analysis through the compiler, metadata of array variables is collected, and metadata is passed across processes using the shadow stack mechanism at runtime, and code snippets are inserted for out-of-bounds detection to reduce unnecessary detection.
Without changing existing code, not introducing missed and false alarms, and ensuring the timing of errors and memory errors, the time overhead of running-time detection tools is effectively reduced and the efficiency of memory security detection is improved.
Smart Images

Figure CN119903505A_ABST
Abstract
Description
Technical Field
[0001] The present invention belongs to the field of electronic digital data processing, and in particular relates to a memory security runtime detection acceleration method and medium combined with static pointer analysis. Background Art
[0002] C language is widely used in the development of various embedded systems and performance-sensitive software due to its direct and efficient control of underlying memory. However, its control of underlying memory easily introduces memory safety issues. Among them, buffer overflow caused by array out-of-bounds is one of the most frequent memory safety errors, and often leads to serious consequences.
[0003] Memory safety runtime detection technology is a method to solve the memory safety problem of C language. This technology inserts additional code into the source code of the program to be detected to record the metadata of each memory area at runtime and perform security checks based on the metadata before accessing the memory. However, intensive security checks lead to time overhead, so various current memory safety runtime detection tools will slow down the program running speed by up to dozens of times, which seriously weakens the efficiency and practicality of such tools in checking memory safety issues.
[0004] Nowadays, more and more technologies are focused on reducing the time overhead of memory safety runtime detection tools, which can be divided into the following four categories:
[0005] 1) Static analysis: Perform program analysis without actually running the program. Classic methods include model checking, data flow analysis, abstract interpretation, etc. Empirical studies have shown that simple static analysis with incomplete contextual semantic modeling often leads to poor detection of complex defects, high false positive and false negative rates, and poor practicality.
[0006] 2) A method combining manual annotation and runtime detection: extracting the semantic information of annotations during compilation to prove the memory safety of some codes, thereby reducing the memory safety detection overhead at runtime. This method requires manual modification of existing codes, cannot achieve automatic annotation, is inefficient, and is prone to incorrect annotation.
[0007] 3) A method combining static analysis and runtime detection: By analyzing the time cost of each runtime detection operation, the more time-consuming detection operations are discarded, so as to perform as many checks as possible within a given time limit. However, this method will miss memory errors.
[0008] 4) Parallel runtime detection: parallelize memory safety checks and program execution, and separate the check operation from the main thread of the program. However, this method 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 memory errors by calling the memory safety check function inserted in the source code by the tool. The check function will first query the metadata of the memory area and then perform the detection. This process occurs frequently and has high overhead. Safe memory access accounts for the vast majority of program memory access behaviors, and dangerous memory access accounts for only a very small number. In particular, most accesses to arrays allocated on the stack are safe, so most of the detection is redundant, resulting in large unnecessary runtime overhead. Summary of the invention
[0010] Purpose of the invention: In order to solve the above-mentioned technical problems, the present invention provides a memory security runtime detection acceleration method and medium combined with static pointer analysis, which can realize fast memory security runtime detection without changing the existing code, introducing missed alarms and false alarms, and ensuring the timing of error reports and memory errors.
[0011] Technical solution: The memory security runtime detection acceleration method combined with static pointer analysis described in the present invention includes the following steps:
[0012] S1: Use the compiler to preprocess the source code of the program to be tested, including macro replacement and importing header files; then use the compiler front end to parse the source code of the program to be tested, including lexical analysis, syntax analysis, semantic analysis, and generation of an abstract syntax tree;
[0013] S2: Collect metadata of array variables: traverse all variable definition nodes of the abstract syntax tree. If the variable definition node is a fixed-length or variable-length array variable definition, obtain the symbolic value of the array length and the type of the array element, 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 possible alias sets defined for each variable in the metadata table, including all possible pointers to each variable, and obtain the alias table M alias : and pointer to table M ref :
[0015] S4: Inter-procedural alias analysis: Based on the results of the intra-procedural alias analysis, traverse the actual parameters in the function call node on the abstract syntax tree. If the actual parameters refer to M alias The variable definition as the key in the function definition node and the parameter number of the called function are added to the function parameter number table M. idx : Record the M referenced in the called function definition aliasThe sequence number of the parameter subset defined as the key variable in the called function definition is then invoked in the called function definition and M is updated. ref , where FD is the function definition node on the abstract syntax tree, and N is a set of natural numbers;
[0016] S5: Insert code snippets for transferring metadata across procedures: Use the shadow stack mechanism to transfer metadata of pointer arguments across procedures at runtime, so that the metadata can enter the shadow stack before the function is called, and obtain the metadata from the shadow stack during the function call;
[0017] S6: Rewrite the array subscript expression in the code and insert the code snippet for out-of-bounds detection;
[0018] S7: Insert runtime detection code;
[0019] S8: Use the compiler and linker to compile and link the source code after the stub to generate an executable file; run the executable file and output the result of the memory safety runtime detection.
[0020] Furthermore, the implementation process of step S3 is as follows:
[0021] S31: Collect assignment operators, that is, collect assignment-related abstract syntax tree nodes into the assignment operator set S AO , where the assignment-related abstract syntax tree nodes include variable definitions with initial values and binary assignment operators:
[0022] Traverse the variable definition nodes of the abstract syntax tree. If the variable definition node has an initial value, add the variable definition node to the assignment operator set.
[0023] Traverse the binary operator nodes of the abstract syntax tree, and if the binary operator is an assignment operator, add the binary operator 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 , where 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] Among them, arr is the array name, "str" is a string, id is a pointer variable identifier, uop is a unary operator, bop is a binary operator, and EE is a member expression;
[0030] S33: Traverse S AO Each assignment operator in updates the alias table M alias : and pointer to table M ref : Among them, the alias table M alias Record the possible alias set of each variable definition node contained in the metadata table; the pointer points to table M ref Record each pointer variable to define the possible pointing set of the node.
[0031] Furthermore, the rule for calculating the core pointer KPE(E) is as follows:
[0032] If E is the array name arr, the string "str" or the pointer variable identifier id, return E;
[0033] If E is in the form of (type) E1, and if E1 is a pointer type, return KPE(E1), otherwise return (type) E1;
[0034] If E is in the form of &E1, and if E1 is in the form of *E2 or E2[i], return KPE(E2), otherwise return &E1;
[0035] If E is of the same shape as other uop E1, and the 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-preserving operator, calculate KPE(E1) and KPE(E2) respectively, and return the non-null result of either one, otherwise return null;
[0037] If E is in the form of E1.E2, return KPE(E2) and record M me [E2] = E1;
[0038] If none of the above, returns null.
[0039] Furthermore, the update alias table M alias : and pointer to table M ref : The implementation process is as follows:
[0040] Initialize M alias If it is empty, the metadata table M meta Each key vd iAdd to your own alias collection;
[0041] Start loop iteration and set loop end flag to true;
[0042] Traverse the assignment operator set S AO Each assignment operator in the left operand and the right operand core pointers are calculated to define the pointer variables corresponding to the nodes vd l and vd r , and then do the following:
[0043] Traverse the alias table M alias Each mapping in Where vd is a variable definition node, Alias vd is a possible alias set of vd: if the pointer variable corresponding to the core pointer of the right operand defines the node vd r In Alias vd In the example, the pointer variable corresponding to the core pointer of the left operator is defined as node vd l Join Alias vd , the loop end flag is marked as false, if the pointer variable corresponding to the core pointer of the right operand defines the node vd r Not in Alias vd No operation is performed; the search pointer points to table M ref Mapping in where vd l It is the pointer variable definition node corresponding to the core pointer of the left operand, Ref l It's vd l Possible pointing set, add vd to Ref l middle;
[0044] If the loop end indicator is false, jump to the loop iteration; if the loop end indicator is true, end.
[0045] Furthermore, the implementation process of step S4 is as follows:
[0046] S41: Initialization: Set the function parameter serial number table M idx Set to empty, set the work queue workQ to empty, set the visited function definition node and parameter sequence number set visited to empty; traverse each function call expression, if the expression contains the called function name and is not a library function, then traverse all the actual parameters in the expression, if the variable definition node referenced by the actual parameter is M alias or M ref The key of the called function definition node and parameter sequence number are added to the function parameter sequence number table M idx and work queue workQ;
[0047] S42: Loop iteration:
[0048] S421: If the work queue workQ is not empty, remove the function definition node fd and parameter number idx at the head of the work queue and add them to the visited set visited, and add the parameter number idx to M idx In the parameter sequence number set of the corresponding function definition node fd;
[0049] S422: Perform an intra-process alias analysis on the function definition node fd to obtain the intra-process alias analysis result of the parameter with the corresponding serial number, and add M ref ;
[0050] S423: Traverse all the actual parameters of the call expression in the function definition node fd. If the variable definition node referenced by the actual parameter is M idx The formal parameters in the call expression are added to the work queue by adding the definition node and parameter sequence number of the function called by the call expression;
[0051] S424: If the work queue is not empty, jump to S421 to continue analysis.
[0052] Furthermore, the implementation process of step S5 is as follows:
[0053] S51: Analyze the order in which actual parameters are pushed into the stack, and according to the calling order from inside to outside of the nested function call expression and the actual parameter calculation order from right to left, the following steps are included:
[0054] Recursively access the actual parameters of each call expression, obtain the nested relationship of the call expressions, and store them in a tree data structure, where the root node of the data structure is the outermost call expression and the leaf node is the innermost call expression;
[0055] For each tree data structure, start from the root node and 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 in the actual parameter stack order list, otherwise NULL is stored in the actual parameter stack order list, and then the call expression in the actual parameter is recursively traversed;
[0056] According to the traversal order, a list of actual parameters stacking order is obtained, which stores the stacking order of all actual parameters under the outermost call expression and the actual parameters of the nested call expressions;
[0057] S52: Query the metadata of the actual parameter: For each pointer p in the actual parameter stacking sequence list, 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 and it has a unique pointer, and the variable it points to does not come from the function parameter, then from M ref Get the only variable it points to, 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 and it has a unique pointer, and the variable it points to comes from the function parameter (M idx If there is a function name and parameter number corresponding to the function parameter, then the address variable name is constructed according to the function name and parameter number as the address of the memory space pointed to by p, and the 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 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 a shadow stack push code snippet: According to the actual parameter push order list, 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 into 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 into the shadow stack;
[0063] S54: Insert shadow stack push code snippet: According to the upper-level language structure of the call expression, different insertion strategies are executed to insert the generated shadow stack push code snippet:
[0064] For language structures without branching structures, insert before the call expression, separated by a semicolon;
[0065] For language constructs that may have branching structures, insert them before the call expression, separated by commas;
[0066] For language constructs with a lower precedence than the comma operator, they are inserted before the call expression, separated by a comma, and a pair of parentheses are used to surround the inserted code and the call expression.
[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 compilation-time metadata table M meta check 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, the implementation process of step S7 is as follows:
[0077] S71: inserting runtime pointer metadata definition, including the following fields: a base address of a memory area, an upper limit of a memory area, and a state of the memory area, wherein the state of the memory area is used to identify whether the memory area is valid;
[0078] S72: inserting a runtime pointer metadata lookup table, the table uses a two-level prefix tree structure, wherein 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: Insert the definition of memory access out-of-bounds check functions __RV_check_dpc_ss and __RV_check_dpv_ss. These functions determine whether the memory access is out-of-bounds and whether there is a use after free based on the runtime pointer metadata, the accessed array name and subscript: if the accessed memory area is within the memory area recorded in the metadata and is unreleased memory, it is a safe access; otherwise it is a dangerous access and a runtime error is reported.
[0080] A storage medium described in the present invention stores a computer program on the storage medium, and when the computer program is executed by at least one processor, the steps of the memory security runtime detection acceleration method combined with static pointer analysis as described above are implemented.
[0081] An electronic device according to the present invention comprises a memory and a processor, wherein:
[0082] A memory for storing computer programs that can be run on the processor;
[0083] A processor is used to execute the steps of the memory safety runtime detection acceleration method combined with static pointer analysis as described above when running the computer program.
[0084] Beneficial effects: Compared with the prior art, the beneficial effects of the present invention are as follows: the present invention uses lightweight Boolean expression calculations instead of heavyweight runtime detection, which can effectively reduce the time overhead of runtime detection tools without changing existing codes, introducing missed alerts and false alerts, and ensuring the timing of error reports and memory errors, and has practical application value. BRIEF DESCRIPTION OF THE DRAWINGS
[0085] Figure 1 It is a flowchart of the memory safety runtime detection acceleration method combined with static pointer analysis. DETAILED DESCRIPTION
[0086] The present invention is further described in detail below with reference to the accompanying drawings.
[0087] like Figure 1 As shown, the present invention provides a memory security runtime detection acceleration method combined with static pointer analysis, comprising the following steps:
[0088] S1: Use the compiler to preprocess the source code of the program to be tested, including macro replacement and importing header files, and then use the compiler front end to parse the source code of the program to be tested, including lexical analysis, syntax analysis, semantic analysis, and generating an 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 of array variables. Traverse all variable definition nodes of the abstract syntax tree. If the variable definition node is a fixed-length or variable-length array variable definition, obtain the symbolic value of the array length and the type of the array element, 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:
[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 includes 10 int type elements, and the arr2 variable in the main function includes N int type elements.
[0103] S3: Intra-procedural alias analysis, that is, analyzing the possible alias sets defined for each variable in the metadata table, including all possible pointers to each variable. The specific steps are as follows:
[0104] S31: Collect assignment operators, that is, collect assignment-related abstract syntax tree nodes into the assignment operator set S AO In the example, the assignment-related abstract syntax tree nodes include variable definitions with initial values and binary assignment operators, specifically:
[0105] Traverse the variable definition nodes of the abstract syntax tree. If the variable definition node has an initial value, add the variable definition node to the assignment operator set.
[0106] Traverse the binary operator nodes of the abstract syntax tree, and if the binary operator is an assignment operator, add the binary operator to the assignment operator set.
[0107] In this embodiment, the set of assignment operators in the source code of the program to be detected is:
[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 , where 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] Among them, arr is the array name, "str" is a string, id is a pointer variable identifier, uop is a unary operator, bop is a binary operator, and EE is a member expression.
[0115] A core pointer is a pointer that determines the memory pointer in a complex pointer expression, that is, 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 the array name arr, the string "str" or the pointer variable identifier id, return E.
[0117] If E is of the form (type)E1, and if E1 is a pointer type, returns KPE(E1), otherwise returns (type)E1.
[0118] If E is in the form of &E1, and if E1 is in the form of *E2 or E2[i], returns KPE(E2), otherwise returns &E1.
[0119] If E is of the same form as any other uop E1, and the uop is a type-preserving operator, return KPE(E1), otherwise return NULL.
[0120] If E is of the form E1 bop E2, and bop is a type-preserving operator, compute KPE(E1) and KPE(E2) separately and return the non-null result of either one, otherwise return null.
[0121] If E is in the form of E1.E2, return KPE(E2) and record M me [E2]=E1;.
[0122] If none of the above, returns 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] Processing the binary operator "+": Since "+" is a type-preserving operator, KPE(&a.bcarr[0]) and KPE(1*0) are calculated respectively. Since the result of KPE(1*0) is empty, KPE(&a.bcarr[0]) is returned.
[0127] Process KPE(&a.bcarr[0]), return KPE(abcarr);
[0128] Process KPE(abcarr), return arr, and record M for ab me [b] = a, for bc record M me [c] = b, for c.arr record M me [arr] = c;
[0129] Finally, we get the parent-child node relationship mapping table M me={<arr,c> ,<c,b> ,<b,a>}, in the next alias analysis phase, the parent structures of the core pointer arr are queried, which are c, b, and a respectively, and finally restored to the complete expression abcarr.
[0130] S33: Alias analysis within the procedure. Traverse S AO Each assignment operator in updates the alias table M alias : and pointer to table M ref : Among them, the alias table M alias Record the possible alias set of each variable definition node contained in the metadata table, that is, the set of pointer variable definition nodes that may point to the variable memory area, and the pointer points to table M ref Record the possible pointing set of each pointer variable definition node, that is, the set of variable definition nodes contained in the metadata table that the pointer variable may point to. It includes the following steps:
[0131] Initialize M alias If it is empty, the metadata table M meta Each key vd i Add to your own alias collection.
[0132] Starts a loop iteration and sets the loop end flag to true.
[0133] Traverse the assignment operator set S AO Each assignment operator in the left operand and the right operand core pointers are calculated to define the pointer variables corresponding to the nodes vd l and vd r , and then do the following:
[0134] Traverse the alias table M alias Each mapping in Where vd is a variable definition node, Alias vd is a possible alias set of vd: if the pointer variable corresponding to the core pointer of the right operand defines the node vd r In Alias vd In the example, the pointer variable corresponding to the core pointer of the left operator is defined as node vd l Join Alias vd , the loop end flag is marked as false, if the pointer variable corresponding to the core pointer of the right operand defines the node vd r Not in Alias vd No operation is performed; the search pointer points to table M ref Mapping in where vdl It is the pointer variable definition node corresponding to the core pointer of the left operand, Ref l It's vd l Possible pointing set, add vd to Ref l middle.
[0135] If the loop end flag is false, restart the loop; if the loop end flag is true, end it.
[0136] In this embodiment, the source code of the program to be detected is as follows:
[0137]
[0138] The result of the alias analysis within the procedure is the following alias table and pointer table:
[0139]
[0140]
[0141] S4: Inter-procedural alias analysis. Based on the result of the intra-procedural alias analysis in the previous step, traverse the actual parameters in the function call node on the abstract syntax tree. If the actual parameters refer to M alias The variable definition as the key in the function definition node and the parameter number of the called function are added to the function parameter number table M. idx : Record the M referenced in the called function definition alias Then, in the called function definition, it invokes intra-procedural alias analysis and updates M ref , where FD is the function definition node on the abstract syntax tree, and N is a set of natural numbers. It includes the following steps:
[0142] S41: Initialization. Set the function parameter serial number table M idx Set to empty, set the work queue workQ to empty, set the visited function definition node and parameter sequence number set visited to empty; traverse each function call expression, if the expression contains the called function name and is not a library function, then traverse all the actual parameters in the expression, if the variable definition node referenced by the actual parameter is M alias or M ref The key of the called function definition node and parameter sequence number are added to the function parameter sequence 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: loop iteration. It includes the following steps:
[0148] 1) If the work queue workQ is not empty, remove the function definition node fd and parameter number idx at the head of the work queue and add them to the visited set visited, and add the parameter number idx to M idx In the parameter sequence number set of the corresponding function definition node fd.
[0149] 2) Perform in-process alias analysis on the function definition node fd, i.e., step S3, to obtain the in-process alias analysis result of the parameter with the corresponding serial number, and add M ref .
[0150] 3) Traverse all the actual parameters of the call expression in the function definition node fd. If the variable definition node referenced by the actual parameter is M idx The formal parameters in the call expression add the definition node and parameter sequence number of the function called by the call expression to the work queue.
[0151] 4) If the work queue is not empty, jump to (1) to continue analysis.
[0152] In this embodiment, the function parameter sequence number table after loop iteration is
[0153] S5: Insert code snippets for transferring metadata across processes. Since the same function may be called in different contexts, and the metadata of pointer arguments (including the address and length of the memory area pointed to) may be different in different contexts, it is necessary to use the shadow stack mechanism to transfer the metadata of pointer arguments across processes at runtime, so that the metadata can enter the shadow stack before the function is called, and thus the metadata can be obtained from the shadow stack during the function call. Specifically, there are four steps:
[0154] S51: Analyze the order in which actual parameters are pushed into the stack. According to the calling order from inside to outside of the nested function call expression and the order in which actual parameters are calculated from right to left, it is specifically as follows:
[0155] Recursively access the actual parameters of each call expression to obtain the nested relationship of the call expressions and store them in a tree data structure, where the root node of the data structure is the outermost call expression and the leaf node is the innermost call expression.
[0156] For each tree data structure, start from the root node and 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 in the actual parameter stack order list, otherwise NULL is stored in the actual parameter stack order list, and then the call expression in the actual parameter is recursively traversed.
[0157] According to the traversal order, a list of the order in which actual parameters are pushed into the stack is obtained, which stores the order in which the actual parameters under the outermost call expression and the actual parameters of the nested call expressions are pushed into the stack.
[0158] In this embodiment, the code fragment to be analyzed is as follows, in which 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 traverse the outermost rightmost parameter arg3; since the return value of the inner function is used as the outer function parameter, the second parameter of the outer function will be replaced with NULL; then traverse the outermost leftmost parameter arg1; finally, for the nested call at the second parameter, traverse the inner function parameters arg23, arg22, and arg21 from right to left. Finally, the order list of actual parameters pushed into the stack is:
[0161] arg3,NULL,arg1,arg23,arg22,arg21
[0162] S52: Query metadata of actual parameters.
[0163] For each pointer p in the list of actual parameters pushed into the stack, query the length of the memory space pointed to by p, which is divided into four cases:
[0164] (S52.1) If p is in the compile-time metadata table M 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 and it has a unique pointer, and the variable it points to does not come from the function parameter, then from M ref Get the only variable it points to, and then get it from M meta Get the length of the memory space pointed to by the variable.
[0166] (S52.3) If p is in the pointer pointing to table M ref and it has a unique pointer, and the variable it points to comes from the function parameter (M idxIf there is a function name and parameter number corresponding to the function parameter, then the address variable name is constructed according to the function name and parameter number as the address of the memory space pointed to by p, and the 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 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 shadow stack push code snippet. According to the actual parameter push order list, replace each pointer p in the list with a 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 into 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 into the shadow stack.
[0170] S54: insert shadow stack push code snippet: according to the upper language structure of the call expression, execute different instrumentation strategies to insert the generated shadow stack push code snippet, including the following cases:
[0171] (S54.1) For language structures without branching structures, such as compound statements and case statements, they are inserted before the call expression, separated by a semicolon;
[0172] (S54.2) For language constructs that may have branching structures, such as conditional expressions, For statements, and While statements, insert them before the call expression, separated by commas;
[0173] (S54.3) For language constructs with a lower operation priority than the comma operator, such as the Return statement, conditional operator, and logical operator, they are inserted before the call expression, separated by a comma, and a pair of parentheses are used to wrap the inserted code and the call expression.
[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 instrumentation 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 compilation-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] 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 after the subscript expression is calculated, and then use this temporary variable as the subscript to construct the conditional expression, thereby ensuring that the subscript expression is only calculated 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, the Boolean expressions generated at the lower dimensions of the array are recursively inherited and conjuncted with the Boolean expressions generated at the higher dimensions to ensure that memory errors in any dimension are not 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 the memory area, upper limit of the memory area, and status of the memory area, wherein the status of the memory area is used to identify whether the memory area is valid.
[0206] In this embodiment, the runtime pointer metadata is defined as follows, where __RV_ptr_addr is a pointer variable address type:
[0207]
[0208] S72: Insert a 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.
[0209] In this embodiment, the definition of the secondary prefix tree is as follows, where __RV_pmd is a pointer metadata type, and PRFtrie_primary_entries and PRFtrie_secondary_entries are the capacities of the primary table and the secondary 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: Insert the definition of memory access out-of-bounds check functions __RV_check_dpc_ss and __RV_check_dpv_ss. These functions determine whether the memory access is out-of-bounds and whether there is a use after free based on the runtime pointer metadata, the accessed array name and subscript: if the accessed memory area is within the memory area recorded in the metadata and is unreleased memory, it is a safe access; otherwise it is a dangerous access and a runtime error is reported.
[0215] S8: Use the compiler and linker to compile and link the source code after the stub to generate an executable file. Run the executable file and output the results of the memory safety runtime detection.
[0216] The present invention also provides a storage medium, on which a computer program is stored. When the computer program is executed by at least one processor, the steps of the memory security runtime detection acceleration method combined with static pointer analysis as described above are implemented.
[0217] The present invention also provides an electronic device, comprising a memory and a processor, wherein: the memory is used to store a computer program that can be run on the processor; the processor is used to execute the steps of the memory security runtime detection acceleration method combined with static pointer analysis as described above when running the computer program.
[0218] The above-mentioned embodiments only express several implementation methods of the present invention, and the description is relatively specific and detailed, but it cannot be understood as limiting the scope of the invention patent. It should be pointed out that for ordinary technicians in this field, several modifications and improvements can be made without departing from the concept of the present invention, which all belong to the protection scope of the present invention.
Claims
1. A memory safety runtime detection acceleration method combined with static pointer analysis, characterized in that: The following steps are involved: S1: Use the compiler to preprocess the source code of the program to be tested, including macro replacement and importing header files; Then use the compiler front end to parse the source code of the program to be tested, including lexical analysis, syntax analysis, semantic analysis, and generate an abstract syntax tree; S2: Collect metadata of array variables: traverse all variable definition nodes of the abstract syntax tree. If the variable definition node is a fixed-length or variable-length array variable definition, obtain the symbolic value of the array length and the type of the array element, 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; S3: In-process alias analysis: Analyze the possible alias sets defined for each variable in the metadata table, including all possible pointers to each variable, and obtain the alias table M alias : and pointer to table M ref : S4: Inter-procedural alias analysis: Based on the results of the intra-procedural alias analysis, traverse the actual parameters in the function call node on the abstract syntax tree. If the actual parameters refer to M alias The variable definition as the key in the function definition node and the parameter number of the called function are added to the function parameter number table M. idx : Record the M referenced in the called function definition alias The sequence number of the parameter subset defined as the key variable in the called function definition is then invoked in the called function definition and M is updated. ref , where FD is the function definition node on the abstract syntax tree, and N is a set of natural numbers; S5: Insert code snippets for transferring metadata across procedures: Use the shadow stack mechanism to transfer metadata of pointer arguments across procedures at runtime, so that the metadata can enter the shadow stack before the function is called, and obtain the metadata from the shadow stack during the function call; S6: Rewrite the array subscript expression in the code and insert the code snippet for out-of-bounds detection; S7: Insert runtime detection code; S8: Use the compiler and linker to compile and link the source code after the stub to generate an executable file; run the executable file and output the result of the memory safety runtime detection.
2. The memory security runtime detection acceleration method combined with static pointer analysis according to claim 1 is characterized in that: The implementation process of step S3 is as follows: S31: Collect assignment operators, that is, collect assignment-related abstract syntax tree nodes into the assignment operator set S AO , where the assignment-related abstract syntax tree nodes include variable definitions with initial values and binary assignment operators: Traverse the variable definition nodes of the abstract syntax tree. If the variable definition node has an initial value, add the variable definition node to the assignment operator set. Traverse the binary operator nodes of the abstract syntax tree, and if the binary operator is an assignment operator, add the binary operator 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 , where 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:=+|-|=|[]|... Among them, arr is the array name, "str" is a string, id is a pointer variable identifier, uop is a unary operator, bop is a binary operator, and EE is a member expression; S33: Traverse S AO Each assignment operator in updates the alias table M alias : and pointer to table M ref : Among them, the alias table M alias Record the possible alias set of each variable definition node contained in the metadata table; the pointer points to table M ref Record each pointer variable to define the possible pointing set of the node.
3. The memory security runtime detection acceleration method combined with static pointer analysis according to claim 2 is characterized in that: The rule for calculating the core pointer KPE(E) is as follows: If E is the array name arr, the string "str" or the pointer variable identifier id, return E; If E is in the form of (type) E1, and if E1 is a pointer type, return KPE(E1), otherwise return (type) E1; If E is in the form of &E1, and if E1 is in the form of *E2 or E2[i], return KPE(E2), otherwise return &E1; If E is of the same shape as other uop E1, and the uop is a type-preserving operator, return KPE(E1), otherwise return NULL; If E is of the form E1 bop E2, and bop is a type-preserving operator, calculate KPE(E1) and KPE(E2) respectively, and return the non-null result of either one, otherwise return null; If E is in the form of E1.E2, return KPE(E2) and record M me [E2] = E1; If none of the above, returns null.
4. The memory security runtime detection acceleration method combined with static pointer analysis according to claim 2 is characterized in that: The update alias table M alias : and pointer to table M ref : The implementation process is as follows: Initialize M alias If it is empty, the metadata table M meta Each key vd i Add to your own alias collection; Start loop iteration and set loop end flag to true; Traverse the assignment operator set S AO Each assignment operator in the left operand and the right operand core pointers are calculated to define the pointer variables corresponding to the nodes vd l and vd r , and then do the following: Traverse the alias table M alias Each mapping in Where vd is a variable definition node, Alias vd is a possible alias set of vd: if the pointer variable corresponding to the core pointer of the right operand defines the node vd r In Alias vd In the example, the pointer variable corresponding to the core pointer of the left operator is defined as node vd l Join Alias vd , the loop end flag is marked as false, if the pointer variable corresponding to the core pointer of the right operand defines the node vd r Not in Alias vd No operation is performed; the search pointer points to table M ref Mapping in Ref l , where vd l It is the pointer variable definition node corresponding to the core pointer of the left operand, Ref l It's vd l Possible pointing set, add vd to Ref l middle; If the loop end indicator is false, jump to the loop iteration; if the loop end indicator is true, end.
5. The memory security runtime detection acceleration method combined with static pointer analysis according to claim 1 is characterized in that: The implementation process of step S4 is as follows: S41: Initialization: Set the function parameter serial number table M idx Set to empty, set the work queue workQ to empty, set the visited function definition node and parameter sequence number set visited to empty; traverse each function call expression, if the expression contains the called function name and is not a library function, then traverse all the actual parameters in the expression, if the variable definition node referenced by the actual parameter is M alias or M ref The key of the called function definition node and parameter sequence number are added to the function parameter sequence number table M idx and work queue workQ; S42: Loop iteration: S421: If the work queue workQ is not empty, remove the function definition node fd and parameter number idx at the head of the work queue and add them to the visited set visited, and add the parameter number idx to M idx In the parameter sequence number set of the corresponding function definition node fd; S422: Perform an intra-process alias analysis on the function definition node fd to obtain the intra-process alias analysis result of the parameter with the corresponding serial number, and add M ref ; S423: Traverse all the actual parameters of the call expression in the function definition node fd. If the variable definition node referenced by the actual parameter is M idx The formal parameters in the call expression are added to the work queue by adding the definition node and parameter sequence number of the function called by the call expression; S424: If the work queue is not empty, jump to S421 to continue analysis.
6. The memory security runtime detection acceleration method combined with static pointer analysis according to claim 1 is characterized in that: The implementation process of step S5 is as follows: S51: Analyze the order in which actual parameters are pushed into the stack, and according to the calling order from inside to outside of the nested function call expression and the actual parameter calculation order from right to left, the following steps are included: Recursively visit the arguments of each call expression to obtain the nesting relationship of the call expressions, and store it in a tree-like 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, start from the root node and 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 in the actual parameter stack order list, otherwise NULL is stored in the actual parameter stack order list, and then the call expression in the actual parameter 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 and it has a unique pointer, and the variable it points to does not come from the function parameter, then from M ref Get the only variable it points to, 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 and it has a unique pointer, and the variable it points to comes from the function parameter (M idx If there is a function name and parameter number corresponding to the function parameter, then the address variable name is constructed according to the function name and parameter number as the address of the memory space pointed to by p, and the 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 the shadow stack; ④ If the above three cases are not 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 a shadow stack push code snippet: According to the list of argument stack-in orders, 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 the pointer p, and PRFvar_len_push is used to push n onto the shadow stack; S54: Insert the shadow stack push code snippet: According to the upper-level language structure of the call expression, execute different instrumentation strategies to insert the generated shadow stack push code snippet: 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 security runtime detection acceleration method combined with static pointer analysis according to claim 1 is 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 compilation-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 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 the middle; S62: Use the constructed boolean expression as the condition of the conditional expression, construct the 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( / * omit 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( / * omit 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 the conditional expression, so as to ensure that the subscript expression is only calculated once; S64: For multidimensional arrays, Boolean expressions generated at lower dimensions of the array are recursively inherited and conjuncted with Boolean expressions generated at higher dimensions to ensure that memory errors in any dimension are not missed.
8. The memory security runtime detection acceleration method combined with static pointer analysis according to claim 1 is characterized in that: The implementation process of step S7 is as follows: S71: inserting runtime pointer metadata definition, including the following fields: a base address of a memory area, an upper limit of a memory area, and a state of the memory area, wherein the state of the memory area is used to identify whether the memory area is valid; S72: inserting a runtime pointer metadata lookup table, the table uses a two-level prefix tree structure, wherein 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: Insert the definition of memory access out-of-bounds check functions __RV_check_dpc_ss and __RV_check_dpv_ss. These functions determine whether the memory access is out-of-bounds and whether there is a use after free based on the runtime pointer metadata, the accessed array name and subscript: if the accessed memory area is within the memory area recorded in the metadata and is unreleased memory, 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, which, when executed by at least one processor, implements the steps of the memory security runtime detection acceleration method combined with static pointer analysis as described in any one of claims 1 to 8.
10. An electronic device, characterized in that: comprising a memory and a processor, wherein: A memory for storing computer programs that can be run on the processor; A processor is used to execute the steps of the memory safety runtime detection acceleration method combined with static pointer analysis as described in any one of claims 1 to 8 when running the computer program.
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