A Program Vulnerability Analysis Method Based on the Executable Formal Semantics of the Go Language
By converting the syntax format of Go to the format supported by the K framework, defining and verifying its executable semantics, the problem of incomplete formal analysis of Go is solved, and effective analysis and discovery of program vulnerabilities is achieved.
Patent Information
- Application Number
- CN202111141171.2
- Authority / Receiving Office
- CN · China
- Patent Type
- Patents(China)
- Current Assignee / Owner
- Filing Date
- 2021-09-28
- Publication Date
- 2025-07-11
- Estimated Expiration
- 2041-09-28
AI Technical Summary
The formal analysis of Go language in the prior art has not been carried out directly, the semantics are incomplete, and the tools for analyzing program vulnerabilities cannot be formed based on the K framework, and there is little attention to the executability of semantics, which limits the application value of semantics.
Convert the Bacos paradigm syntax format of Go to the BNF syntax format that the K framework can support, define the executable semantics of Go based on rewriting logic, including syntax, configuration and rules, analyze the semantic correctness through test cases, and apply the K framework for symbolic execution analysis.
It realizes the complete formal semantics of Go language, generates tools for analyzing program vulnerabilities, supports complete formal proof and symbolic execution, and discovers hidden problems in the program.
Smart Images

Figure CN113868136B_ABST
Abstract
Description
Technical Field
[0001] The present invention relates to the field of computer technologies, and in particular to a program vulnerability analysis method based on the executable formal semantics of the Go language. Background Art
[0002] The Go language is an open-source compiled programming language launched by Google in 2009, also known as GOLang. It evolved from the C language and is designed specifically for concurrency. It not only has a series of advantages such as the simplicity and efficiency of C, cross-platform of Java, and convenience of Python, but also can achieve efficient concurrency of programs based on coroutines (goroutines) and channels. Among them, coroutines are responsible for executing code, and channels are responsible for passing events between coroutines. The allocation and scheduling of coroutine tasks are completed by the runtime itself. In addition, the Go language also has a garbage collector, which can automate memory management. Its strong static typing feature also enables most type mismatch operations to be detected during program compilation. At the same time, it also supports many common system programming methods such as inheritance, overloading, and objects. Compared with other high-level programming languages, the Go language has the advantages of simplicity, concurrency, security, and rapid compilation, and its powerful functions have been widely applied in various fields.
[0003] Application of the Go language in blockchain. With the rapid development and large-scale deployment and application of blockchain, smart contracts, as the core part of blockchain, have also attracted much attention. At present, blockchain has developed three chain models: public chain, private chain, and consortium chain. Due to the advantages of strong data confidentiality, easy consensus reaching, and fast transaction speed of the consortium chain, it is more suitable for real-world application scenarios and has great development potential in the future. Led by the consortium chain Hyperledger Fabric, since its Docker containers are developed in the Go language, the Go language has become the smart contract development language with the best compatibility and the strongest stability in the Fabric blockchain, and has also become the core and key development language for the code on the smart contract chain. Due to its support for concurrency, the Go language can better handle the concurrency problems of smart contracts, and at the same time has powerful functions and high security, making it one of the main development languages for future smart contracts.
[0004] Application of the Go language in distributed systems. With the rapid development of concurrent services, developing distributed systems or platforms with high requirements for concurrent services based on the Go language has become one of the first choices. The Go language can implement the scheduling of concurrent coroutines based on the golang runtime, reduce concurrent blocking in distributed systems, and improve concurrent efficiency.
[0005] Analyzing programming languages based on formal semantics methods is of great significance in aspects such as program analysis and verification. Formal semantics is based on mathematical logic and uses symbols and symbol-related formulas to formalize the process of a programming language processing data and its results, achieving an exact definition and interpretation of the semantics of a certain language. Currently, operational semantics, denotational semantics, algebraic semantics, and axiomatic semantics have been developed. All programming languages have their formal semantics. As a widely used concurrent language, Go has been formally analyzed by many scholars.
[0006] The current status of the formal semantics research on the Go language. Currently, for the formal semantics research of the Go language, the analysis mainly focuses on aspects such as the concurrency mechanism and memory model of the Go language. It mainly uses operational semantics to represent and can verify the relevant properties of the program. For the formal research on the concurrency mechanism, in 2016, Prasertsang et al. proposed a semi-automated verification method for the concurrency mechanism of the GO language using the CSP formal description language. The content formalized based on the CSP model includes assignment statements, declaration statements, conditional statements, loop statements, functions, GOroutines, and channels in the GO language, etc., to exclude program failures caused by concurrency and ensure the correctness of the program. In the same year, Nicholas Ng et al. proposed a static analyzer that formalizes the static single assignment (SSA) corresponding to the Go source code as a communicating finite state machine (CFSM) to detect communication deadlocks during concurrency. SSA simplifies the syntax of the Go program into a finite instruction set and simplifies the control flow of the program into jumps between instruction blocks for analysis. In the same year, Bodden et al. used an abstract syntax to formally represent a subset of the Go language, including important basic functions such as types, structures, and channel operations. At the same time, they analyzed closures. By analyzing the data during program execution, they achieved static analysis of the information flow of the Go language program to prevent program execution violations caused by malicious data and verified the execution results of the semantics when dealing with Go program concurrency. Julien Lange et al. also analyzed the SSA of the Go source code, applied control flow analysis to obtain the behavior type of the program, and used an operational semantics model to propose a static analysis tool to explore security vulnerabilities during Go program concurrency, including deadlock freedom and communication security, etc.For the formal analysis of the memory model, Steffen focused on runtime behavior and operational semantics, and proposed a Small-Step Semantics for a small concurrent language composed of constructs related to the Go programming language keywords defer, panic, and recover, aiming to capture the concurrency and non-standard control flow of Go using defer functions; in the same year, Valle formally analyzed various parts of the Go language memory model using a structured operational semantics, including reading, writing, and channel communication, and could model happen-before events through sets and illustrate how to gradually complete programs using semantic rules through some examples; Stadtmüller et al. proposed a new trace-based static deadlock detection method, formally analyzed the concurrent locks of the Go language using an operational semantics, complemented Valle's research content, and developed a prototype tool for analyzing Go program deadlocks, realizing the verification work of the semantics.
[0007] Research Status of Defining the Executable Semantics of Programming Languages Based on the K Framework. K is a framework for defining executable semantics implemented based on rewriting logic. It mainly formalizes the execution process of programming languages in the form of symbols and formulas, and the resulting executable semantics is an operational semantics. The K framework includes various tools, such as symbolic execution frameworks, model checkers, compilers, interpreters, test case generators, etc. In addition to defining executable formal semantics, the K framework can also define type systems. Although some functions are not yet perfect, it still has strong application prospects. Currently, many scholars have implemented executable formal research on programming languages based on the K framework. Charles et al. implemented an executable formal semantic analysis of the C language based on the K framework. The semantics includes basic semantics, static semantics, and concurrent semantics, and an analysis tool KCC was formed. It can not only be used as an interpreter for programs, but also debug, capture undefined behaviors of programs, perform state space search and model checking, and verify the correctness and coverage of the semantics. Jiao et al. implemented a formal analysis of the executable semantics of the Ethereum smart contract development language Solidity based on the K framework. They specifically analyzed the basic judgment statements, type declarations, and function calls in the contract, covering most of the semantics of the Solidity language, and verified the important application of the semantics in the analysis of smart contract vulnerabilities. Fava et al.
[16] referred to the channel communication mechanism of the Go language and implemented an executable formal analysis of a weak memory model represented by π-calculus that follows the happens-before principle based on the K framework, including operations such as reading, writing, and sending in the memory model, and verified that the model follows sequential consistency when there is no data race. Bogdanas et al. defined the executable formal semantics of Java in [ ], which is divided into static semantics and dynamic semantics. They conducted extensive tests on the official test suite based on the interpreter generated from the semantics, and demonstrated examples of symbolic execution and model checking of Java program code using the built-in functions of K and this semantics. In addition, more languages have also implemented the definition of executable formal semantics, such as Python, PHP, JavaScript, Rust, KEVM, etc., and the correctness of the semantics has been verified based on the interpreter of the K framework and the compiler related to the programming language.
[0008] In 2015, Andrei Arusoaie et al. proposed a language-independent symbolic execution framework based on the rewrite logic theory, which extended the functions of version K 3.5 and added symbolic execution analysis functions. Since K itself is an executable semantic framework based on rewrite logic, which provides a representation method for the program state space and semantic rules and can be conveniently used to define the executable semantics of a language, the core of this work is an executable semantic definition framework based on rewrite logic and matching logic, which can define the semantics of any programming language, such as domain-specific languages, imperative languages, etc., and then perform symbolic execution and concrete execution on the program code, and output path constraints and state information, etc. Based on this language-independent symbolic execution framework, the --symbolic option can be used to enable the symbolic analysis mode when compiling the K definition. When the symbolic mode is enabled, the program can be given any type of symbolic input natively supported by the K tool.
[0009] In the process of implementing the present invention, the inventors of the present application found that there are at least the following technical problems in the prior art:
[0010] (1) For the formal analysis and research of the Go language, first, there is a situation where the intermediate language is analyzed instead of directly analyzing the Go language; second, less attention is paid to the executability of the semantics, and corresponding tools cannot be generated based on the semantics, which limits the application value of the semantics; finally, the semantic content is mostly incomplete, and the coverage of the characteristics of the Go language is relatively small;
[0011] (2) Through the application analysis of the formal semantics of the language defined based on the K framework, it is found that there is currently no executable formal semantics of the Go language with distributed concurrent objects defined based on the K framework, and tools for analyzing program vulnerabilities cannot be formed based on the K framework. Summary of the Invention
[0012] The present invention proposes a method for analyzing program vulnerabilities based on the executable formal semantics of the Go language, which is used to solve or at least partially solve the technical limitations of the insufficient semantic integrity of the Go language and the analysis of program vulnerabilities existing in the prior art methods.
[0013] To solve the above technical problems, the present invention provides a method for analyzing program vulnerabilities based on the executable formal semantics of the Go language, including:
[0014] S1: Convert the official extended Backus-Naur form grammar format GO[EBNF] of the Go language into the BNF grammar format GO[BNF] supported by the K framework;
[0015] S2: According to the characteristics of GO[BNF] and the Go language, apply the K framework, and based on rewriting logic, describe the executable semantics GO[KS] of the Go language, including syntax, configuration, and rules. GO[KS] includes basic semantics and concurrent semantics. The basic semantics cover the basic type definitions, basic expressions, and basic statements of the Go language, and the concurrent semantics include data reading and writing based on channels;
[0016] S3: Adopt a test case-based method to analyze the correctness of GO[KS]. Execute the test cases individually in the K framework and the IntelliJ IDEA tool, and analyze the correctness of GO[KS] by comparing the consistency of the results of each test case;
[0017] S4: Apply the executable semantics GO[KS] of the Go language, and based on the K framework, perform symbolic execution analysis on Go programs to find program vulnerabilities.
[0018] In one implementation, the syntax of the executable semantics GO[KS] of the Go language in step S2 is defined based on the K framework.
[0019] The configuration of GO[KS] uses lattice cells based on the K framework to describe the state of Go programs. Different lattice cells are used to describe different state information of Go programs. The lattice cells include global configuration lattice cells, coroutine configuration lattice cells, and channel configuration lattice cells. The global configuration lattice cells include lattice cells for representing program calculations and lattice cells for storing global variables; the coroutine configuration lattice cells include lattice cells for representing coroutine calculations and lattice cells for representing unique coroutine identifiers; the channel configuration lattice cells include lattice cells for representing unique channel identifiers, lattice cells for representing the number of channel operations, lattice cells for representing channel types, lattice cells for representing channel states, and lattice cells for representing channel queues.
[0020] In one implementation, the rules of the executable semantics GO[KS] of the Go language in step S2 are obtained by rewriting the state transition relationship of Go programs based on the K framework using rewriting logic. The rewritten content includes the information before and after the information conversion within the lattice cells.
[0021] In one implementation, the basic semantics of step S2 include basic type definitions, basic expressions, and basic statements. Among them,
[0022] Variable declarations include untyped declarations, typed declarations, untyped declarations with assignments, typed declarations with assignments, batch declarations, and batch declarations with assignments;
[0023] Array declarations are defined for typed one-dimensional array declarations;
[0024] Function declarations and definitions, for function declarations with parameters, including typed returns and untyped returns;
[0025] Struct definitions, where the struct contains parameters within the struct and parameter types, and the parameter types include integer, character, and boolean;
[0026] Basic expressions, including simple operations, complex operations, logical operations, and variable increment and decrement;
[0027] Basic statements, some of which are basic statements obtained based on basic type definitions, including variable assignment and array assignment, and some are general statements, including function calls, return statements, if statements, for statements, println statements, switch statements, select statements, function definitions, package, and import.
[0028] In one embodiment, in the concurrent semantics of step S2, data reading and writing are implemented based on channels, including go statements, make statements, channel sends, channel receives, and channel closes. Among them, the go statement is used to describe the static semantics of coroutine creation, the make statement is used to describe the static semantics of channel creation, and channel sends, channel receives, and channel closes are used to describe the state changes of channels and the data changes within channels.
[0029] In one embodiment, step S3 includes:
[0030] Compile GO[KS] based on the K framework;
[0031] Select appropriate test cases from the official test suite for each semantic rule of GO[KS], batch run the test cases based on the K framework, check whether GO[KS] is fully covered. If not, develop new test cases to achieve full coverage of GO[KS] by the test cases, forming a test set with a semantic coverage rate of 100%;
[0032] Analyze the correctness of the semantics by comparing the consistency of the results of each test case. Specifically: based on the test cases in the test set with a semantic coverage rate of 100%, run the test cases in the test set individually in the K framework and the tool IntelliJ IDEA, compare and analyze the results of the execution in the K framework and the results of the program running in the tool IntelliJ IDEA to verify the correctness of GO[KS]. If the running results of each test case are consistent, it means GO[KS] is correct; otherwise, it is incorrect.
[0033] In one embodiment, after compiling GO[KS] in the K framework, enable the symbolic analysis model in the K framework to perform symbolic execution analysis on the Go program and search for and analyze program vulnerabilities.
[0034] One or more of the above technical solutions in the embodiments of the present application have at least one or more of the following technical effects:
[0035] A program vulnerability analysis method based on the executable formal semantics of the Go language provided by the present invention proposes an executable formal semantics based on rewrite logic, and the executable formal semantics is implemented in the K framework. First, based on the K framework, language rules can be specified modularly and can accurately represent a truly concurrent programming language, which is especially suitable for the analysis of the concurrent language Go. Secondly, the semantics in K has a strict meaning as a term rewriting system and supports complete formal proofs. In fact, after the semantics is defined, first use the kompile tool to compile the semantics, and the K framework will automatically generate a corresponding interpreter and automatically support model checking and symbolic execution analysis of the program. Then, based on this interpreter, use the krun tool to run the program code, and various hidden problems in the program can be discovered through technologies such as symbolic execution, solving the problems of insufficient semantic integrity of the Go language and technical limitations in analyzing program vulnerabilities existing in the prior art methods. BRIEF DESCRIPTION OF THE DRAWINGS
[0036] In order to more clearly illustrate the technical solutions in the embodiments of the present invention or the prior art, the following will briefly introduce the drawings required for use in the description of the embodiments or the prior art. Obviously, the following drawings are some embodiments of the present invention. For those of ordinary skill in the art, other drawings can be obtained based on these drawings without creative efforts.
[0037] Figure 1 Schematic diagram of the GO[KS] semantic framework in the embodiments of the present invention;
[0038] Figure 2 Schematic diagram of the conversion method from GO[EBNF] to GO[BNF] in the embodiments of the present invention;
[0039] Figure 3 Example diagram of the GO[KS] configuration in the embodiments of the present invention;
[0040] Figure 4 Framework diagram of all contents of GO[KS] in the embodiments of the present invention;
[0041] Figure 5 Flow chart of the rewrite rule definition process of the select statement in the embodiments of the present invention;
[0042] Figure 6 Overall verification framework of GO[KS] in the embodiments of the present invention;
[0043] Figure 7 Flow chart of the correctness verification method of GO[KS] in the embodiments of the present invention;
[0044] Figure 8 This is the schematic diagram of the GO[KS] symbol execution technology application in the embodiments of the present invention. Detailed implementation manners
[0045] To solve the technical limitation problems of the existing technology regarding the comprehensiveness and verification of the formal semantics of the Go language and the technology for program vulnerability analysis, the present invention proposes a program vulnerability analysis method based on the executable formal semantics of rewrite logic, and the executable formal semantics is implemented in the K framework. First, based on the K framework, language rules can be specified modularly and can accurately represent true concurrent programming languages, especially suitable for the analysis of the concurrent language Go. Second, the semantics in K has a strict meaning as a term rewriting system and supports complete formal proofs. In fact, after the semantics is defined, the kompile tool is first used to compile the semantics, and the K framework will automatically generate the corresponding interpreter and automatically support model checking and symbolic execution analysis of the program. Then, based on this interpreter, the krun tool is used to run the program code, and various hidden problems in the program can be helped to be discovered through technologies such as symbolic execution.
[0046] The main idea of the present invention is as follows:
[0047] The present invention discloses a program vulnerability analysis method based on the executable formal semantics of the Go language. First, a definition method of the executable semantics GO[KS] of the Go language is proposed, and then based on this semantics, more features of the program written in the Go language can be helped to be analyzed to help find Go program vulnerabilities. The content involved in the present invention includes: (1) studying the extended Backus-Naur form (abbreviated as GO[EBNF]) given by the official of the GO language, and combining with the BNF syntax format supported by the K framework, a method of converting GO[EBNF] into the general Backus-Naur form (abbreviated as GO[BNF]) is proposed, and GO[BNF] is obtained; (2) according to the characteristics of GO[BNF] and the Go language, applying the K framework and based on rewrite logic, the executable semantics GO[KS] of the Go language is defined, including syntax, configuration and rules; (3) a method based on test cases is adopted to analyze the correctness of GO[KS], and the test cases are executed individually in the K framework and the IntelliJ IDEA tool respectively, and the correctness of the semantics is analyzed by comparing the consistency of the results of each test case; (4) based on the K framework, applying GO[KS], the Go program vulnerabilities are found.
[0048] To make the objectives, technical solutions, and advantages of the embodiments of the present invention clearer, the technical solutions in the embodiments of the present invention will be clearly and completely described below with reference to the accompanying drawings in the embodiments of the present invention. Apparently, the described embodiments are some, but not all, of the embodiments of the present invention. All other embodiments obtained by those of ordinary skill in the art based on the embodiments of the present invention without creative efforts shall fall within the protection scope of the present invention.
[0049] An embodiment of the present invention provides a program vulnerability analysis method based on the executable formal semantics of the Go language, including:
[0050] S1: Convert the official grammar format GO[EBNF] of the Go language into the BNF grammar format GO[BNF] supported by the K framework;
[0051] S2: According to GO[BNF] and the characteristics of the Go language, apply the K framework and, based on rewrite logic, describe the executable semantics GO[KS] of the Go language, including syntax, configuration, and rules. GO[KS] includes basic semantics and concurrent semantics. The basic semantics cover the basic type definitions, basic expressions, and basic statements of the Go language, and the concurrent semantics include data reading and writing based on channels;
[0052] S3: Analyze the correctness of GO[KS] using a test case-based method. Execute the test cases individually in the K framework and the IntelliJ IDEA tool, and analyze the correctness of GO[KS] by comparing the consistency of the results of each test case;
[0053] S4: Apply the executable semantics GO[KS] of the Go language and, based on the K framework, perform symbolic execution analysis on the Go program to find program vulnerabilities.
[0054] Specifically, steps S1 to S3 propose the executable semantics GO[KS], and step S4 is a specific application based on the executable semantics GO[KS], specifically for finding and analyzing program vulnerabilities. Please refer to Figure 1 , which is a schematic diagram of the GO[KS] semantic framework in the embodiments of the present invention.
[0055] In step S1, the extended Backus-Naur form officially given for the Go language (abbreviated as GO[EBNF] in the present invention) is studied, and in combination with the BNF grammar format supported by the K framework, a method for converting GO[EBNF] into the general Backus-Naur form (abbreviated as GO[BNF] in the present invention) is proposed, and GO[BNF] is obtained. Figure 2 Summarizes the basic conversion method from GO[EBNF] to GO[BNF].
[0056] First, unify and simplify the four symbols "=", "|", "()", "[]", and "{}" involved in GO [EBNF] into a form that only contains "|". The main conversion methods are as follows:
[0057] (1) Convert the definition symbols of the grammar. Replace "=" in GO [EBNF] with "::=", and both symbols represent the meaning of equivalence. The reason for the conversion is that the grammar in the K framework only supports the notation of "::=".
[0058] (2) Replace the optional symbol "[]" in GO [EBNF] with a grammar represented by the symbol "|". Since [] means that the items inside can be repeated 0 times or 1 time, in GO [BNF], directly represent both the content with this item and the content without this item, and separate them with "|".
[0059] (3) Replace the repeatable symbol "{}" in GO [EBNF] with a grammar represented by the symbol "|". Since {} means that the item can be repeated multiple times, in GO [BNF], use two expressions and separate them with "|". For example, represent X = E{A} as X ::= E|XA;
[0060] (4) Replace the grouping symbol "()" in GO [EBNF] with a grammar represented by the symbol "|". For example, convert X = (E1|E2)A to X = E1A|E2A;
[0061] During the entire grammar conversion process, the naming of some grammar classes will be simplified if necessary, but it does not affect the conversion method of each grammar content. Take the conversion of the if statement as an example to illustrate the specific conversion process. In GO [EBNF], IfStmt = "if" [SimpleStmt ";"] Expression Block ["else" (IfStmt|Block)]. In order to obtain the form in GO [BNF], represent the item SimpleStmt in the above EBNF with AExp, and split the item Expression into AExp and BExp. AExp is an expression with an integer return type, and BExp is an expression with a boolean return type. Finally, it is converted into the following relatively complex form. In K, after the keyword syntax is followed by a K class. For example, IfStmt in GO [BNF] can be called a K class. To facilitate the description of the conversion process, each item of the K grammar content of IfStmt is identified by a serial number, and it is specifically represented in the following six forms:
[0062] 1) "if" AExp ";" BExp Block "else" Block
[0063] 2) "if" BExp Block "else" Block
[0064] 3) "if" AExp ";" BExp Block
[0065] 4) "if" BExp Block
[0066] 5) "if" AExp ";" BExp Body "else" IfStmt
[0067] 6) "if" BExp Body "else" IfStmt
[0068] Step 1: Remove the items with "[]" indicating zero or one repetition. Here, there are two items, SimpleStmt and the else statement. Split SimpleStmt into two items, and split the else into two items as well. This gives the first expression;
[0069] Step 2: Remove the grouping "()", resulting in the 5th and 6th expressions;
[0070] Step 3: Separate the above six expressions with the symbol "|", indicating an "or" relationship. That is, if an if statement in the program code satisfies any one of the above six, it means the syntax meets the requirements and the K framework can parse it successfully; otherwise, the parsing fails.
[0071] Then, add specific attributes to the syntax according to the characteristics of the K framework. The K framework has many attributes. In GO[BNF], the main attributes involved are strictness attributes, function attributes, token attributes, and custom label attributes.
[0072] Finally, obtain GO[BNF], which contains four basic types, four basic expressions, and twelve basic statements in the Go language. The four basic types are: variable declaration, array declaration, function declaration, and structure definition; the four basic expressions are: simple operations, complex operations, logical operations, and variable increment and decrement; the twelve basic statements are: variable assignment, array assignment, function call, return, if, for, println, switch, select, function definition, package, and import, and the main function main.
[0073] Table 1 Partial conversion results from GO[EBNF] to GO[BNF]
[0074]
[0075] Table 1 shows the conversion results of the EBNF and BNF parts of the GO language. Due to space limitations, this table simplifies the content of some syntax displays. For the omitted parts of the syntax, "…" is used to indicate.
[0076] In one implementation, the syntax of the executable semantics GO[KS] of the Go language in step S2 is defined based on the K framework.
[0077] The configuration of GO[KS] uses lattice cells to describe the state of a Go program based on the K framework. Different lattice cells are used to describe different state information of the Go program. The lattice cells include a global configuration lattice cell, a coroutine configuration lattice cell, and a channel configuration lattice cell. The global configuration lattice cell includes a lattice cell for representing program computation and a lattice cell for storing global variables; the coroutine configuration lattice cell includes a lattice cell for representing coroutine computation and a lattice cell for representing the unique identifier of the coroutine; the channel configuration lattice cell includes a lattice cell for representing the unique identifier of the channel, a lattice cell for representing the number of channel operations, a lattice cell for representing the channel type, a lattice cell for representing the channel state, and a lattice cell for representing the channel queue.
[0078] In one implementation, the rules of the executable semantics GO[KS] of the Go language in step S2 are obtained by rewriting the state transition relationship of the Go program based on the K framework using rewrite logic. The rewritten content includes the information before and after the information conversion within the lattice cell.
[0079] In one implementation, the basic semantics of step S2 includes basic type definitions, basic expressions, and basic statements. Among them,
[0080] Variable declarations include untyped declarations, typed declarations, untyped declarations with assignment, typed declarations with assignment, batch declarations, and batch declarations with assignment;
[0081] Array declarations are defined for typed one-dimensional array declarations;
[0082] Function declarations and definitions are for function declarations with parameters, including typed returns and untyped returns;
[0083] Structure definitions, where the structure includes parameters and parameter types within the structure, and the parameter types include integer type, character type, and boolean type;
[0084] Basic expressions include simple operations, complex operations, logical operations, and variable increment and decrement;
[0085] Basic statements, some are basic statements obtained based on basic type definitions, including variable assignment and array assignment, and some are general statements, including function calls, return statements, if statements, for statements, println statements, switch statements, select statements, function definitions, package, and import.
[0086] In one implementation, data reading and writing based on channels is implemented in the concurrent semantics of step S2, including the go statement, make statement, channel send, channel receive, and channel close. Among them, the go statement is used to describe the static semantics of coroutine creation, the make statement is used to describe the static semantics of channel creation, and channel send, channel receive, and channel close are used to describe the state changes of the channel and the data changes within the channel.
[0087] Specifically, in step S2, according to the GO[BNF] and the characteristics of the Go language, the K framework is applied to describe the executable semantics GO[KS] of the Go language based on rewriting logic.
[0088] In the specific implementation process, for the configuration definition in GO[KS]. The K configuration is represented by a nested representation of tagged lattice cells and is used to describe the state information when the program is running. Figure 3 The configuration of GO[KS] is introduced. Each unit describes the important information required for a complete execution snapshot, including global configuration, function configuration, coroutine, and channel configuration.
[0089] Global configuration. The lattice cell T encompasses the entire configuration and is a top-level cell that contains all lattice cells, enabling it to be operated as a single unit when necessary. Among them, lattice cell k is used to represent the computation of the program, lattice cell env represents the global environment for storing global variables, and lattice cell genv represents a full backup of env. Since both parameter passing and receiving use the stack, lattice cell control contains the function stack and information about the current object and class. Inside the control unit is the fstack cell, which stores the call stack of the called and returned functions, represented in the form of a list of data structures, mainly encoding the stack frames. Each element contains an address, some computations, and a return type. Lattice cell typeEnv is used to record the type at a given variable location, lattice cell store is used to store the values of all defined variables, nextLoc represents the next location of the current variable. When declaring a new variable, a new integer value location is allocated from the nextLoc lattice cell, and then the integer value in nextLoc is incremented by 1 as the start address of the next item. Lattice cell state indicates that the running program is always in a certain state, describing all expectations for the state of the executing program, such as the values of variables and the time points during program execution. The state of the program is an element of the state space, storing all possible states of the running program. Lattice cell out, the output buffer, is mainly used for print statements; lattice cell error is used to return error messages, mainly for displaying the information returned by the panic function; lattice cell scount, similar to nextLoc, is used to record the next location of the current case statement in a switch statement; lattice cell nextcase, similar to nextLoc, is used to record the location of the next case statement in a select statement; lattice cell package is used to record the name of the package, containing the unique package identifier pid and the package name.
[0090] Configuration of coroutines and channels. Coroutines are represented by the goroutine lattice cell, mainly containing two pieces of information. One is used to represent the computation k of the coroutine, and the other is the unique coroutine identifier gid, automatically generated by K. Channels are represented by the channel lattice cell. For the semantic rewriting of channels, it mainly involves five lattice cells, namely the channel unique identifier cid, the channel operation count cnun, which defaults to 0, the channel type ctype, the channel state cstate, and the channel queue cseq. The detailed introduction is as follows:
[0091] (1) Channel cid, which is uniformly generated by the K framework as a unique integer identifier.
[0092] (2) The number of channel operations cnum, which is an integer. It is incremented by 1 when sending (writing data to the channel) and decremented by 1 when receiving (reading data from the channel). When it is a positive number, it means that the sending operation is greater than the receiving operation, and the sender is blocked. When it is a negative number, it means that the receiving operation is greater than the sending operation, and the receiver is blocked. When num is 0, it indicates that the blockage is lifted.
[0093] (3) The channel type ctype, which is written when the channel is created and cannot be changed once written.
[0094] (4) The channel state cstate, where 0 indicates an exception, here it is the closed state, and 1 indicates normal.
[0095] (5) The channel queue cseq, which belongs to the buffer list of the channel and is used to store the content to be transmitted.
[0096] Figure 4 Displays all the content of GO[KS]. For the definition of the basic semantics in GO[KS], the select statement is one of the characteristic statements in the Go language. It mainly listens to the channel. Once a message is detected, read and write operations are performed on the channel to complete the processing of the message. Figure 5 Defines the rewriting rule process for the select statement in the present invention. Taking the select statement as an example, the method of defining semantics is described in detail below.
[0097] The execution process of the select statement follows the following steps:
[0098] 1) First, check whether the channels in each case statement can perform read or take operations. If only one of them is satisfied, execute that case statement;
[0099] 2) If multiple cases are satisfied, a random one will be selected from the multiple executable case statements to execute. That is, when multiple channels are ready, a random one is selected to execute for sending or receiving operations;
[0100] 3) If each case statement cannot be executed, if there is a default statement, execute the default statement;
[0101] 4) If each case statement cannot be executed and there is no default statement at the same time, the entire select statement will be blocked. Since the select statement is usually used in combination with the for loop statement, the for loop continues to check the operability of the channel until one of the case statements can be executed (that is, when one of the channels is operable).
[0102] Table 2 Semantics of the Select Statement
[0103]
[0104] Note that the case statements in select do not test in order, and select is blocked by default. However, it will loop to detect the case conditions. If a condition is met, the corresponding statement will be executed and the select statement will exit. Otherwise, it will keep looping and detecting until a send or receive can be performed on the monitored channel. Judging whether the communication of each case statement in select can be executed or will be blocked is the difficulty of using the select statement and one of the key statements for studying the concurrent characteristics of the Go language.
[0105] Detailed description of the semantics in Table 2:
[0106] Rule ①: The select statement consists of two parts. One is the judgment condition in the case statement, represented by C, where ci is the judgment condition in the i-th case statement. The other is the execution expression after the condition in the case statement, represented by S, where si is the main body of the expression in the i-th case statement. I is used to record the I-th case expression that is finally executed. Overall, first rewrite the select statement as a combination of an if statement and a select statement. Among them, bool(ci) represents the boolean value returned by the judgment condition in the i-th case statement. According to the channel state, it judges whether the write or read is successful. Success is true and failure is false. When the return is true, according to the variable assignment rule, the value V of I is stored in the store. When the return is false, continue to execute the select statement. And regardless of which situation is met, the grid cell nextcase needs to be incremented by 1, and then continue to judge the judgment condition ci+1 in the next case statement. When the if statement returns false, continue to execute the select statement. If none of them are satisfied, execute the default statement. If there is no default statement, execute rule (2);
[0107] Rule ②: It is mainly used to randomly select a case statement for execution. First, randomly obtain a random number I from 1 to N, and then start to execute the I-th case statement.
[0108] For the definition of concurrent semantics in GO[KS], the sending and receiving operations of channels and closing channels are taken as examples for detailed description here. Based on the sending and receiving of data through channels, the symbol "<-" is used. For example, "ch<-v" means sending data v to channel ch (this process is also called writing data), and "v := <-ch" means receiving data from channel ch and assigning the value to v (this process is also called reading data). A channel can only receive one data element at a time. <-ch means receiving any data, and when this statement is executed, it will block until data is received, but the received data will be ignored. The write and read semantics of channels are shown in Table 3:
[0109] Table 3 Write and Read Semantics Based on Channels
[0110]
[0111] (2) Closing channels. After data reception or transmission is completed, the channel can be closed through the close statement (as shown in Table 4). After the channel is closed, data cannot be sent to this channel, and there will be no block when receiving data from the closed channel.
[0112] Table 4 Semantics of the close Statement
[0113]
[0114] In one implementation, step S3 includes:
[0115] Compiling GO[KS] based on the K framework;
[0116] Select appropriate test cases from the official test suite for each semantic rule of GO[KS], batch run the test cases based on the K framework, check whether GO[KS] is fully covered. If not, develop new test cases to achieve full coverage of GO[KS] by test cases, and form a test set with a semantic coverage rate of 100%;
[0117] Analyze the correctness of semantics by comparing the consistency of the results of each test case. Specifically: Based on the test cases in the test set with a semantic coverage rate of 100%, run the test cases in the test set individually in the K framework and the tool IntelliJ IDEA, compare and analyze the results executed in the K framework and the program results run in the tool IntelliJ to verify the correctness of GO[KS]. If the running results of each test case are consistent, it means GO[KS] is correct; otherwise, it is incorrect.
[0118] Specifically, for step S3 to verify the correctness of GO[KS], the overall verification framework Figure 6As shown below. First, compile GO[KS] based on the K framework; second, select appropriate test cases from the official test suite for each semantic rule of GO[KS], run the test cases in batches, and check whether GO[KS] is fully covered. If not, develop new test cases to achieve full coverage of GO[KS] by the test cases, forming a test set with a semantic coverage rate of 100%; finally, run the test cases in the test set individually in the K framework and the tool IntelliJ IDEA, and compare and analyze the results executed in the K framework and the results run in the tool IntelliJ to verify the correctness of GO[KS].
[0119] In the specific implementation process, for the semantic correctness verification of GO[]KS], the verification objectives include two aspects. One is to verify the semantic coverage rate of the test cases to ensure that the test cases fully cover the defined semantic rules. The other is to verify the correctness of the semantic results. The overall verification method is as Figure 7 shown below.
[0120] First, compile the GO[KS] semantics. Kompile is the compiler of K, which accepts programs or specifications written in K and stored in.k files, and generates the corresponding parser and interpreter. When compiling the k file, use the command "kompile file name --coverage", where --coverage is the backend of the compiler for outputting the summary of the rules covered during the program execution.
[0121] Then, run the test cases in the test set in batches and analyze the semantic coverage rate of the test cases. The semantic coverage rate refers to the coverage range of the defined semantics by the test cases. In actual semantic writing, the start of the semantic definition is indicated by the keyword "rule", which is also called a rule. The higher the semantic coverage rate, the higher the utilization rate of the defined rules, the more language semantic content covered by the relevant test cases, that is, the more comprehensive the semantic coverage. On the contrary, it means that the utilization rate of the defined rules is relatively low, and more test cases are needed to verify the uncovered rules. Verifying the semantics of the present invention based on a more complete test case set is the basis for verifying the correctness of GO[KS]. The calculation formula for the semantic coverage rate is as follows:
[0122]
[0123] In the above formula, covered represents the covered rules, rules represents all the rules, len() counts the number of corresponding rules, and coverage_frac represents the finally obtained semantic coverage rate. To make the statistics more accurate, the result of coverage_frac is reserved to two decimal places. When the obtained coverage rate reaches more than 100%, it means that the selection range of test cases meets the standard, and the test cases cover all the semantics defined in the present invention.
[0124] When using —coverage during compilation, a coverage file will be generated in the file directory. At the same time, when running the program code using krun, a document starting with a random number and ending with coverage will be added. This document records all the rules used by the program, including the rules inherent in the K framework and the custom rules. If the rules inherent in the K framework are included in the statistics of the semantic coverage rate, the obtained semantic coverage rate will be greatly reduced and cannot truly reflect the coverage of the test cases for GO[KS]. To achieve only counting the coverage of the test cases for the custom rules of the present invention, the present invention modifies the official given python file for counting the rule coverage to form a new file test_coverage.py for re-counting the rule coverage. After running all the test cases, execute "python3 test_coverage.py" in the command window to obtain the rule coverage rate from the generated file coverage.txt document. When the coverage rate is less than 100%, it means that the test cases cannot cover all the semantics, and test cases need to be added to cover the untested rules. When the coverage rate is 100%, it indicates that the test case set for verifying the semantics is formed.
[0125] Finally, run the test cases individually to verify the correctness of GO[KS] through the result consistency. In the K framework, use the command "krun test case file name", and the krun tool will call the parser and interpreter to run the program code. Each time a test case is run, a file directory will be output, and each directory contains all the content required to execute the program or perform the proof using this definition. At the same time, the running result information will be output in the command window. Similarly, test each test case in the test case set in the IntelLij IDEA tool, obtain the execution result corresponding to each test case, and analyze the correctness of the semantics by comparing the results of the K framework and the Intellij IDEA tool.
[0126] Verify the semantic correctness based on the execution results of test cases. There are two cases. One is for test cases that display the output results with print statements. After running in K, the content of the buffer, that is, the content of the cell out, can be directly viewed and compared with the running results in the IntelliJ IDEA tool. The contents of the two are compared. The other is for test cases that indicate success or failure with the exitcode return code in Intellij. It is necessary to manually check whether the running results in K are consistent with the contents of the variables in the judgment conditions before the panic function in the program.
[0127] If a test case fails to execute, the panic function will be used to return some information, either the value of a variable or any string. No matter what value is output, it indicates that an exception has occurred during the program execution and the execution results are inconsistent. The K framework will return the error information to the cell error, and the reason needs to be found.
[0128] In one implementation, after compiling GO[KS] in the K framework, enable the symbolic analysis model through the K framework to perform symbolic execution analysis on the Go program and search for and analyze program vulnerabilities.
[0129] Specifically, compile the GO[KS] semantics in the K framework proposed by Andrei. This framework adds symbolic execution technology, and the partial connection with the Z3 SMT solver is completed in K itself. It not only supports all the semantics and rules mentioned in GO[KS], but also, based on this tool, through the use of the backend --backend symbolic, perform symbolic analysis on Go language programs, obtain the symbolic execution paths of the programs and all the states of the programs, and can obtain the evaluation order of the programs by detecting the program execution process, output all the process data, and achieve the inspection and analysis of program vulnerabilities.
[0130] Please refer to Figure 8 , which is the schematic diagram of the application of the GO[KS] symbolic execution technology in the embodiments of the present invention.
[0131] In the specific implementation process, compile the GO[KS] semantics in the K framework proposed by Andrei. This framework adds symbolic execution technology, and then execute the program code with control flow statements such as if, while, and for. When executing the code, specify the conditional judgment variables in the control flow statements as symbolic variables. If there are multiple conditional judgment variables, one of them can be defined as a symbolic variable according to actual needs;
[0132] Then, during the process of symbolic execution technology analysis, control flow statements are used as path branch points to obtain symbolic execution paths. Among them, symbolic execution paths are represented by the AND, OR, and NOT operations of one or more comparison expressions. The comparison expressions mainly include ">", ">=", "<", "<=", "!=", "==", etc., and can be automatically generated according to the expressions in the control flow statements;
[0133] Next, the K framework connects to the constraint solver Z3 to perform constraint solving on symbolic variables for the currently obtained execution path while searching for execution paths. If there is a solution, it means that each comparison expression is satisfied (and K will automatically obtain the specific symbolic values and store them in the environment built into K), then continue to search for paths along this branch. If there is no solution, stop searching this path and switch to the next branch to continue searching for paths; when there are no paths to search, it means that the path search for the program code is completed, and all symbolic execution paths, as well as the solution values of the symbolic variables corresponding to each symbolic execution path (i.e., specific symbolic values), are output and displayed.
[0134] Finally, input the automatically obtained specific symbolic values into the instructions for executing the Go program based on the K framework, run the same program code again, and view all the status information of the program running results, including the final values of each variable in the program. If a variable has no specific value, it indicates that the result of the variable is an expression represented by a symbolic variable and is displayed in the result corresponding to the variable. If the program has a vulnerability, the execution fails, and the K framework throws an exception. Otherwise, the execution is successful, and the K framework displays the specific program status information.
[0135] To apply the GO[KS] semantics to find program vulnerabilities, the following Go program code is used as an example to illustrate the process in detail. The core code of the program is shown in Table 5.
[0136] Table 5 Go program code
[0137]
[0138] After compiling GO[KS] in the K framework, execute the command krun if.go –cIN="ListItem(#symInt(n))" –cPC="true" --search, then start the symbolic execution analysis of the program code. Here, # is used to specify symbolic variables, and #symInt(n) means defining the variable n in the program code as a symbolic variable. Finally, there are three obtained symbolic execution paths:
[0139] Path 1: #symInt(n)>Int 5 == Bool false, after path simplification:!(n>5);
[0140] Path 2: #symInt(n)>Int 5 == Bool true and Bool #symInt(n)<=Int 20 == Bool false, after path simplification: n>5 ∩ ¬(n<=20);
[0141] Path 3: "#symInt(n)>Int 5 == Bool true and Bool #symInt(n)<=Int 20 == Bool true", after path simplification: n>5 ∩ n<=20.
[0142] For the above symbolic paths, the specific values of the symbolic variables obtained through constraint solving are n = 4, n = 21, and n = 6 respectively. Taking the symbolic specific value n = 4 as the specific input and executing the command krun if.go –cIN="ListItem(4)" –cPC="true" will trigger a program vulnerability, and the error result of the lattice cell of K shows "assert false".
[0143] Compared with the prior art, the advantages and beneficial effects of the present invention are:
[0144] 1. An executable formal semantics based on rewriting logic is proposed, and the executable formal semantics is implemented in the K framework. Based on the K framework, language rules can be specified modularly and a truly concurrent programming language can be accurately represented, which is especially suitable for the analysis of the concurrent language Go, and the semantics in K has a strict meaning as a term rewriting system and supports complete formal proofs.
[0145] 2. The kompile tool is used to compile the semantics, and the K framework will automatically generate a corresponding interpreter, and automatically support model checking and symbolic execution analysis of the program. Then, based on this interpreter, the krun tool is used to run the program code, and various techniques such as symbolic execution can be used for program vulnerability analysis, thereby helping to discover various hidden problems in the program.
[0146] The above embodiments are only used to illustrate the technical solutions of the present invention, rather than to limit it; although the present invention has been described in detail with reference to the foregoing embodiments, those of ordinary skill in the art should understand that: they can still modify the technical solutions recorded in the foregoing embodiments, or perform equivalent replacements for some of the technical features; and these modifications or replacements do not make the essence of the corresponding technical solutions deviate from the spirit and scope of the technical solutions of the various embodiments of the present invention.
Claims
1. A program vulnerability analysis method based on the executable formal semantics of the Go language, characterized in that, Including: S1: Convert the official extended Backus-Naur Form grammar format GO[EBNF] of the Go language into the BNF grammar format GO[BNF] supported by the K framework; S2: According to the characteristics of GO[BNF] and the Go language, apply the K framework and, based on rewrite logic, describe the executable semantics GO[KS] of the Go language, including syntax, configuration, and rules. GO[KS] includes basic semantics and concurrent semantics. The basic semantics cover the basic type definitions, basic expressions, and basic statements of the Go language, and the concurrent semantics include data reading and writing based on channels; S3: Analyze the correctness of GO[KS] using a test case-based method. Individually execute the test cases in the K framework and the IntelliJ IDEA tool, and analyze the correctness of GO[KS] by comparing the consistency of the results of each test case; S4: Apply the executable semantics GO[KS] of the Go language and, based on the K framework, perform symbolic execution analysis on Go programs to find program vulnerabilities; Step S3 includes: Compile GO[KS] based on the K framework; Select appropriate test cases from the official test suite for each semantic rule of GO[KS], batch run the test cases based on the K framework, check whether GO[KS] is fully covered. If not, develop new test cases to achieve full coverage of GO[KS] by the test cases, forming a test set with a semantic coverage rate of 100%; Analyze the correctness of the semantics by comparing the consistency of the results of each test case. Specifically: Based on the test cases in the test set with a semantic coverage rate of 100%, individually run the test cases in the test set in the K framework and the IntelliJ IDEA tool, compare and analyze the results of the execution in the K framework and the results of the program run in the IntelliJ tool to verify the correctness of GO[KS]. If the running results of each test case are consistent, it means GO[KS] is correct; otherwise, it is incorrect.
2. The program vulnerability analysis method based on the executable formal semantics of the Go language according to claim 1, characterized in that, In step S2, the syntax of the executable semantics GO[KS] of the Go language is defined based on the K framework. The configuration of GO[KS] is represented using lattice cells based on the K framework and is used to describe the state of Go programs. Different lattice cells are used to describe different state information of Go programs. The lattice cells include a global configuration lattice cell, a coroutine configuration lattice cell, and a channel configuration lattice cell. The global configuration lattice cell includes a lattice cell for representing program calculations and a lattice cell for storing global variables; the coroutine configuration lattice cell includes a lattice cell for representing coroutine calculations and a lattice cell for representing the unique identifier of the coroutine; the channel configuration lattice cell includes a lattice cell for representing the unique identifier of the channel, a lattice cell for representing the number of channel operations, a lattice cell for representing the channel type, a lattice cell for representing the channel state, and a lattice cell for representing the channel queue.
3. The program vulnerability analysis method based on the executable formal semantics of the Go language according to claim 1, wherein In step S2, the rules of the executable semantics GO[KS] of the Go language are obtained by rewriting the state transition relationship of Go programs using rewrite logic based on the K framework. The rewritten content includes the information before and after the information conversion within the lattice cells.
4. The program vulnerability analysis method based on the executable formal semantics of the Go language according to claim 1, characterized in that, The basic semantics of step S2 include basic type definitions, basic expressions, and basic statements. Among them, Variable declarations, including untyped declarations, typed declarations, untyped declarations with assignments, typed declarations with assignments, batch declarations, and batch declarations with assignments; Array declarations, which are defined for typed one-dimensional array declarations; Function declarations and definitions, for function declarations with parameters, including typed returns and untyped returns; Struct definitions, where a struct contains parameters and parameter types within the struct, and the parameter types include integer, character, and boolean; Basic expressions, including simple operations, complex operations, logical operations, and variable increment and decrement; Basic statements, some of which are basic statements obtained based on basic type definitions, including variable assignments and array assignments, and some are general statements, including function calls, return statements, if statements, for statements, println statements, switch statements, select statements, function definitions, package, and import.
5. The program vulnerability analysis method based on the executable formal semantics of the Go language according to claim 1, characterized in that, In the concurrent semantics of step S2, data reading and writing are implemented based on channels, including go statements, make statements, channel sends, channel receives, and channel closes. Among them, the go statement is used to describe the static semantics of coroutine creation, the make statement is used to describe the static semantics of channel creation, and channel sends, channel receives, and channel closes are used to describe the state changes of channels and the data changes within channels.
6. The program vulnerability analysis method based on the executable formal semantics of the Go language according to claim 1, characterized in that After compiling GO[KS] in the K framework, the symbolic analysis model is enabled through the K framework to perform symbolic execution analysis on Go programs and search for and analyze program vulnerabilities.
Citation Information
Patent Citations
Method and system for compiling and executing TTCN-3 language
CN101408849A
Method for program transformation and apparatus for COBOL to Java program transformation
US20060031820A1