Methods for testing a computer program
Patent Information
- Application Number
- DE102023208601
- Authority / Receiving Office
- DE · DE
- Patent Type
- Applications
- Current Assignee / Owner
- Filing Date
- 2023-09-06
- Publication Date
- 2025-03-06
Smart Images

Figure 00000000_0000_ABST
Abstract
Description
[0001] The present disclosure relates to methods for testing computer programs.
[0002] Testing is an essential component of software application development. In particular, errors that cause application failure should be identified and corrected. An example of software testing is the dynamic software testing technique known as fuzzing.
[0003] A black-box fuzzer generates inputs for a target program (i.e., a computer program under test) without knowing its internal behavior or implementation; that is, the computer program is, from the fuzzer's perspective, a black-box computer program. In this case, the source code of the target program is not available (to the fuzzer), preventing the fuzzer from obtaining feedback regarding the coverage achieved during testing (e.g., any type of code coverage, such as path, line, or branch coverage) that would guide the generation of additional test cases. A black-box fuzzer thus essentially tests randomly and has no chance to improve over time. This is common in the testing of embedded devices and their software.
[0004] A gray-box fuzzer would be significantly more effective. This means a fuzzer that doesn't have the program code but does have coverage information and can select test inputs accordingly. However, obtaining this coverage information, for example, through the use of an emulator or instrumentation, is complex and difficult to implement, especially in embedded systems.
[0005] Low-effort approaches that enable gray-box fuzzing of black-box computer programs, especially computer programs for embedded systems, are therefore desirable.
[0006] According to various embodiments, a method for testing a computer program is provided, comprising training a machine learning model to predict, for test inputs supplied to it, a coverage of the computer program which is achieved when the computer program is executed with the test inputs as inputs, testing the computer program in a plurality of iterations, wherein in each iteration • a test input is generated (e.g. by mutating a previous test input or a seed from a corpus); • the trained machine learning model is used to predict a coverage of the computer program that is achieved when the computer program is executed with the generated test input as input (i.e. the generated test input is fed to the trained machine learning model and the output of the trained machine learning model in response to this input is taken as an estimate of the coverage); • determine whether the predicted coverage increases the overall coverage achieved so far by testing the computer program; and • in response to determining that the predicted coverage increases the overall coverage achieved by testing the computer program to date, executing the computer program with the generated test input.
[0007] The method described above enables gray-box fuzzing for black-box computer programs, especially in the case of fuzzing for software for embedded systems (or devices), since the possibilities of using other approaches to achieve gray-box fuzzing (such as static instrumentation or the use of an emulator) are severely limited.
[0008] The coverage is, for example, a code coverage such as a path, line or branch coverage or also the information which functions or other program parts (such as functions) are reached when the computer program is executed with the respective test input as input.
[0009] According to various embodiments, the machine learning model (e.g., neural network) is trained individually for the one computer program under test. Nevertheless, this can be done based on a machine learning model that has been pre-trained for one or more other computer programs.
[0010] For example, if it is determined that the predicted coverage does not increase the total coverage achieved by testing the computer program so far, the generated test input is discarded (i.e., no test run is performed on it).
[0011] Various examples of implementation are given below.
[0012] Embodiment 1 is a method for testing a computer program as described above.
[0013] Embodiment 2 is a method according to embodiment 1, wherein in each iteration the test input is generated from a different test input depending on whether the coverage predicted by the trained machine learning model for the different test input increases the total coverage achieved by testing before executing the computer program with the different test input as input.
[0014] The oracle's estimate can therefore be used not only to decide whether a test case is executed for a given test input, but also whether the test input is used as the basis for generating further test inputs (e.g., added to the corpus). In other words, the method can also include adding the generated test input to a set of test inputs from which test inputs for further iterations are generated (e.g., through mutation). This can increase the efficiency of fuzzing.
[0015] Embodiment 3 is a method according to embodiment 1 or 2, further comprising checking whether the overall coverage achieved during testing of the computer program has increased by executing the computer program with the generated test input, and in response to the overall coverage achieved during testing of the computer program having increased by executing the computer program with the generated test input, adding the generated test input to a set of test inputs from which test inputs for further iterations are generated.
[0016] The prediction of the machine learning model (oracle) can thus be verified, ensuring that only test inputs that actually lead to an increase in overall coverage are added to the corpus. The verified coverages can also be used as ground truth information for further training of the machine learning model, thus increasing the efficiency of fuzzing.
[0017] Embodiment 4 is a method according to any one of embodiments 1 to 3, comprising generating the test input on a test system, determining the coverage of the computer program on the test system, and in response to the test system determining that the predicted coverage increases the overall coverage achieved so far by testing the computer program, executing the computer program with the generated test input on a system embedded with the test system.
[0018] The method thus enables gray-box fuzzing of a black-box computer program for an embedded system.
[0019] Embodiment 5 is a software test system configured to perform a method according to one of embodiments 1 to 4.
[0020] Embodiment 6 is a computer program including instructions that, when executed by a processor, cause the processor to perform a method according to any one of embodiments 1 to 4.
[0021] Embodiment 7 is a computer-readable medium storing instructions that, when executed by a processor, cause the processor to perform a method according to any one of embodiments 1 to 4.
[0022] In the drawings, like reference characters generally refer to the same parts throughout the several views. The drawings are not necessarily to scale, emphasis instead generally being placed upon illustrating the principles of the invention. In the following description, various aspects are described with reference to the following drawings. Fig. 1 shows a computer for developing and / or testing software applications. Fig. Figure 2 illustrates data collection for training a machine learning model to predict coverage. Fig. 3 illustrates the training of a machine learning model using the Fig. 2 recorded training data. Fig. 4 illustrates a method for Fig. 3 trained machine learning model controlled fuzzing. Fig. 5 shows a flowchart illustrating a method for testing a computer program according to an embodiment.
[0023] The following detailed description refers to the accompanying drawings, which, by way of illustration, show specific details and aspects of this disclosure in which the invention may be practiced. Other aspects may be utilized, and structural, logical, and electrical changes may be made without departing from the scope of the invention. The various aspects of this disclosure are not necessarily mutually exclusive, as some aspects of this disclosure may be combined with one or more other aspects of this disclosure to form new aspects.
[0024] Various examples are described in more detail below.
[0025] Fig. 1 shows a computer 100 for developing and / or testing software applications.
[0026] The computer 100 has a CPU (Central Processing Unit) 101 and a random access memory (RAM) 102. The RAM 102 is used to load program code, e.g., from a hard disk 103, and the CPU 101 executes the program code.
[0027] In this example, it is assumed that a user intends to use computer 100 to develop and / or test a software application.
[0028] To do this, the user executes a software development environment 104 on the CPU 101.
[0029] The software development environment 104 enables the user to develop and test an application (i.e., software) 105 for various devices 106, i.e., target hardware, such as embedded systems for controlling robotic devices, including robot arms and autonomous vehicles, or even for mobile (communication) devices. For this purpose, the CPU 101, as part of the software development environment 104, can execute an emulator to simulate the behavior of the respective device 106 for which an application is or was developed. If it is used only for testing software from another source, the software development environment 104 can also be viewed or configured as a software test environment.
[0030] The user can distribute the finished application to corresponding devices 106 via a communications network 107. Instead of via a communications network 107, this can also be done in another way, for example, using a USB stick.
[0031] Before doing so, however, the user should test the application 105 to avoid distributing a malfunctioning application to the devices 106. This may also be the case if the user did not write the application 105 themselves on the computer 100. In particular, the case may arise where the user does not have the source code of the application, but only its executable code (i.e., the binary program), i.e., where the application (computer program) 105 is, from the tester's perspective, a black-box computer program.
[0032] One testing technique is called fuzzing. Fuzzing, or fuzz testing, is an automated software testing technique in which invalid, unexpected, or random data is fed as input to a computer program under test. The program is then monitored for exceptions such as crashes, missing failed built-in code assertions, or potential memory leaks.
[0033] Typically, fuzzers (i.e., test programs that use fuzzing) are used to test programs that process structured inputs. This structure is specified, for example, in a file format or protocol and distinguishes between valid and invalid inputs. An effective fuzzer generates semi-valid inputs that are "valid enough" to not be rejected outright by the input parser of the program under test, but "invalid enough" to reveal unexpected behaviors and edge cases that are not properly handled in the program under test.
[0034] The following describes terminology used in connection with fuzzing: • Fuzzing or fuzz testing is the automated testing process of sending randomly generated inputs to a target program (the program under test) and observing its response. • A fuzzer or fuzzing engine is a program that automatically generates input. It is therefore not linked to the software under test (e.g., through instrumentation). However, the fuzzer has the ability to instrument code, generate test cases, and execute the programs under test. Well-known examples are AFL and libfuzzer. • A fuzz target is a software program or function to be tested through fuzzing. A key characteristic of a fuzz target is that it accepts potentially untrusted inputs generated by the fuzzer during the fuzzing process. • A fuzz test is the combined version of a fuzzer and a fuzz target. A fuzz target can then be instrumented code, where a fuzzer is linked to (i.e., provides) its inputs. A fuzz test is executable. A fuzzer can also start, observe, and stop multiple fuzz tests—that is, perform multiple test runs (usually hundreds or thousands per second), each with a slightly different input generated by the fuzzer. • A test case is a specific test input and a specific test run from a fuzz test. (Accordingly, each test input has (at least) one test case, and each test case has one test input.) Typically, test runs of interest (finding new code paths or crashes) are saved for reproducibility. This allows a specific test case with the corresponding input to be executed on a fuzz target that is not connected to a fuzzer, e.g., the release version of a program. • Coverage-guided fuzzing uses code coverage information as feedback during fuzzing to detect whether an input caused the execution of new code paths or blocks. • Generation-based fuzzing uses prior knowledge about the target program (fuzz target) to generate test inputs. An example of such prior knowledge is a grammar that conforms to the fuzz target's input specification, i.e., the input grammar of the fuzz target (i.e., the program under test). • Mutation-based fuzzing generates new test inputs by making small changes to initial test inputs that keep the test input valid but trigger new behavior. • A seed is an initial test input used as a starting point for mutation-based fuzzing. Seeds are typically provided by the user. • The energy of a seed is the number of test cases generated from a seed through mutations. • The power schedule is the importance that a mutation-based fuzzer assigns to the seeds, which directly affects the order in which the seeds are queued for mutation to generate test inputs. • The corpus is a collection of test inputs generated during the fuzzing process. These test inputs are typically derived based on the seeds and / or other previously generated test inputs (typically through mutation). The corpus grows over the course of fuzzing as more test inputs are generated and added. • Static instrumentation is the insertion of statements into a program (under test) to obtain feedback about its execution. It is usually implemented by the compiler and can, for example, indicate the code blocks reached during execution. • Dynamic instrumentation is the control of the execution of a program (under test) during runtime to generate feedback from the execution. It is usually implemented through operating system functionalities or through the use of emulators. • A debugger is a device or program that can control a target device or program and can provide functions such as retrieving register or memory values and pausing and stepping through the target program.
[0035] In the following, a black box fuzzer setting is considered, specifically for testing software for an embedded system, i.e., the software development environment 104 has a set of seeds and implements a black box fuzzer for testing a computer program 105 for a target device 106, which is an embedded system.
[0036] Black-box fuzzing is a common testing method when no source code is present. However, the majority of fuzzing research focuses on improving fuzzing in the gray-box or white-box configuration, as the black-box configuration is the most difficult to improve. Coverage-driven fuzzers, such as AFL, typically use static source-code instrumentation to obtain feedback on the target's coverage while a test input is being processed. AFL enables coverage-driven fuzzing for ARM-based microcontrollers through the Embedded Trace Macrocell (ETM) hardware tracing interface. For closed-source target programs, dynamic instrumentation can be used instead; that is, the binary under test is instrumented at runtime without any instrumentation being compiled into the target.
[0037] In embedded systems, static instrumentation for fuzzing is more difficult to achieve than in non-embedded systems for the following reasons: • Typically, the fuzzer runs on a different machine than the embedded system. Therefore, the code coverage information must be transferred. • An entire system is tested, typically consisting of multiple components. The software may contain third-party libraries and software components from other vendors or customers. If these components are delivered as binaries, they can be considered closed-source components that cannot be modified. Closed-source components therefore cannot be instrumented by the compiler. • Static instrumentation increases code size, which can be critical on embedded systems with limited resources, i.e. there is not enough memory for instrumentation or additional functionalities that support fuzzing.
[0038] Dynamic instrumentation is only possible on certain operating systems, and even then, code coverage information must be transferred (which significantly slows down testing). Furthermore, the program code may be located in read-only memory on the executing system, making it impossible to modify.
[0039] An alternative approach to instrumentation is to run the embedded system's software in a system emulator such as QEMU. This exploits the emulator's transparency to gather feedback for fuzzing. Unfortunately, setting up an emulator for a specific purpose can be a significant effort. This is because the software for an embedded system typically depends on the availability of external components such as sensors and actuators. If these components are missing from the emulator, the software (executed by the emulator) will most likely take different paths and therefore cannot be compared to real-world processes. Therefore, not only the instruction set of the respective embedded system must be emulated, but also all expected hardware peripherals.
[0040] Hardware-in-the-Loop (HiL) approaches like Avatar2 address this difficulty by forwarding all IO requests from the emulator to the hardware and transmitting the results back (peripheral proxying). However, HiL represents a major bottleneck.
[0041] Given the above difficulties in black-box fuzzing, according to various embodiments, an oracle for estimating the coverage achievable using test inputs is trained using machine learning and used to guide the fuzzing (specifically, the selection of test inputs fed to the computer program under test).
[0042] According to various embodiments, software fuzzing for embedded devices is improved by using an oracle capable of estimating the code coverage of a test input (and thus a test case) on the executing system without actually measuring it. This makes it possible to perform gray-box fuzzing for embedded devices (i.e., black-box fuzzing but with information about the coverage) without requiring the overhead of an exact coverage measurement. For example, the software development environment 104 implements a machine learning model (which it may also train itself).
[0043] Fig. Figure 2 illustrates the (training) data collection for training a machine learning model to predict coverage (herein also referred to as “(coverage) oracle”).
[0044] Training data can be collected by executing test cases on the embedded device 202 and observing code coverage 203. For this purpose, existing test inputs from the corpus 201 (in particular, seeds) can be used, or a standard black- or gray-box fuzzer can be used to generate new test inputs from existing test inputs. For each of the executed test cases, the coverage is observed on the embedded system 202.
[0045] To retrieve coverage information from the embedded system 202 (and thus observe the coverage), the embedded system 202 can be used, for example, in the HiL setup mentioned above (ie, the computer 100 sends the test inputs to the respective embedded system 106 and receives coverage information from the embedded system 106).
[0046] For example, this is done using a debugger, i.e., the computer 100 (or "host") sends the test inputs and debugging commands to control the embedded system 106, executes the computer program under test with the test inputs (via a debugging interface), and receives coverage information from the embedded system 106 (e.g., via debugging responses via the debugging interface). In this case, the communication network 107 comprises the debugging interface and a channel for the test inputs (e.g., via WiFi, Bluetooth, or a serial connection). For example, a debugger is connected to the microcontroller of the embedded system 106, from which the execution can be controlled.A simple way to determine coverage in such a setting is to step through the code of the computer program under test and simultaneously log the program addresses reached. Alternatively, an emulator with the peripheral proxying technique can be used to collect coverage data, or instrumentation can be used. It should be noted that these coverage collection techniques drastically increase runtime overhead and are therefore unsuitable for coverage-driven fuzzing. However, in the method provided here, they only occur when collecting training data (or verifying oracle predictions) and not during the actual fuzzing.
[0047] Fig. 3 illustrates the training of the oracle using the Fig. 2 recorded training data.
[0048] The Fig. 2 and the coverages 302 observed for them (as ground truth) are used as training data for training the oracle 303, so that a trained oracle 304 is generated which, based on a test case (i.e. a test input), can predict a code coverage that will be achieved when the computer program to be tested is executed on the embedded target system.
[0049] In practice, the oracle 303, 304 can be implemented by a machine learning model (e.g., a neural network) that is trained with the test cases as input and the coverage information (per test case) as the target task (i.e., as ground truth).
[0050] Fig. 4 illustrates an oracle-controlled fuzzing using the Fig. 3 trained oracles.
[0051] In oracle-driven fuzzing, test inputs are generated by a test input generator 402 based on test inputs from the corpus 401.
[0052] Each of these test inputs is fed to the trained oracle 403, which predicts a coverage for the test input, i.e., to obtain a coverage estimate 404.
[0053] Depending on the coverage estimate, the test case (corresponding to the test input) is executed on the target system 405 (i.e., the test input is used as input of the (black box) target program and executed) to observe errors and crashes 406. This dependency is defined in Fig. 4 is illustrated by the dashed arrow 407.
[0054] The test case is executed on the target system 405 if the coverage estimate means an increase in the total coverage achieved so far in the course of fuzzing (e.g. the oracle 403 predicts that the test case will achieve functions that have not yet been achieved in the course of fuzzing).
[0055] If a test input increases the (total) coverage (at least according to the oracle), it can be added to Corpus 401 (as is common with fuzzing). It is also possible to check whether a test case for which the oracle predicts an increase in (total) coverage actually results in this increase when the test case is executed. This can be done using one of the methods described above (e.g., a HiL setup), which, as described above, is complex but is only performed for test inputs that increase the total test coverage according to the oracle.
[0056] A standard gray-box fuzzer can be used, which uses the coverage estimates produced by the oracle per test case to select the test cases to be executed, without having to resort to the embedded device's expensive coverage detection mechanism. The oracle thus achieves the goal of providing a fast coverage detection mechanism and transforming fuzzing from black-box to gray-box.
[0057] Optionally, the process of Fig. 2, Fig. 3 and Fig. 4 can also be repeated multiple times. This means that additional training data can be collected at a later time to update the oracle for more accurate coverage predictions.
[0058] In summary, according to various embodiments, a method is provided as described in Fig. 5 shown.
[0059] Fig. 5 shows a flowchart 500 illustrating a method for testing a computer program according to one embodiment.
[0060] In 501, a machine learning model is trained to predict, for each test input supplied to it, a coverage of the computer program that is achieved when the computer program is executed with the test inputs as inputs (ie, to predict a respective coverage for each test input supplied to it).
[0061] In 502, the computer program is tested in several iterations, with each iteration • in 503 a test input is generated (e.g. by mutating a previous test input or a seed from a corpus); • in 504, a coverage of the computer program is predicted by means of the trained machine learning model, which coverage is achieved when the computer program is executed with the generated test input as input (ie the generated test input is fed to the (trained) machine learning model and the output of the machine learning model in response to this input is taken as an estimate for the coverage); • in 505, it is determined whether the predicted coverage increases the overall coverage achieved by testing the computer program so far (ie, by testing with the previous test inputs); and • in 506, in response to the fact that it has been determined that the predicted coverage increases the overall coverage achieved so far by testing the computer program, the computer program is executed with the generated test input (and otherwise not, but a new test input is generated; however, further iterations can also be interspersed in the iterations in which the machine learning model is not used, ie the selection of the test input is not dependent on the prediction of the machine learning model).
[0062] The procedure of Fig. 5 may be performed by one or more computers having one or more data processing units. The term "data processing unit" may be understood as any type of entity that enables the processing of data or signals. The data or signals may, for example, be handled according to at least one (i.e., one or more than one) specific function performed by the data processing unit. A data processing unit may include or be formed from an analog circuit, a digital circuit, a logic circuit, a microprocessor, a microcontroller, a central processing unit (CPU), a graphics processing unit (GPU), a digital signal processor (DSP), a programmable gate array (FPGA) integrated circuit, or any combination thereof.Any other way of implementing the respective functions described in more detail herein may also be understood as a data processing unit or logic circuit arrangement. One or more of the method steps described in detail herein may be performed (e.g., implemented) by a data processing unit through one or more specific functions performed by the data processing unit.
[0063] The approach of Fig. 5 is used to test a program, for example, control software for a robotic device. The term "robotic device" can be understood as referring to any technical system, such as a computer-controlled machine, a vehicle, a household appliance, a power tool, a manufacturing machine, a personal assistant, or an access control system. The control software can also be used for data processing systems such as a navigation device.
[0064] The procedure of Fig. 5 is, for example, by a test arrangement (e.g. computer 100 and target device 106 of Fig. 1) was carried out.
[0065] Although specific embodiments have been shown and described herein, it will be recognized by those skilled in the art that the specific embodiments shown and described may be substituted for a variety of alternative and / or equivalent implementations without departing from the scope of the present invention. This application is intended to cover any adaptations or variations of the specific embodiments discussed herein. Therefore, it is intended that this invention be limited only by the claims and the equivalents thereof.
Claims
[1] A method for testing a computer program (105), comprising: Training a machine learning model (303) to predict, for each test input (301) supplied to it, a coverage of the computer program (105) that is achieved when the computer program (105) is executed with the test inputs as inputs; Testing the computer program (105) in several iterations, wherein in each iteration a test input is generated; by means of the trained machine learning model (304, 403) a coverage (404) of the computer program (105) is predicted which is achieved when the computer program (105) is executed with the generated test input as input; determining whether the predicted coverage (404) increases the overall coverage achieved so far by testing the computer program (105); and in response to determining that the predicted coverage (404) increases the overall coverage achieved so far by testing the computer program (105), executing the computer program (105) with the generated test input. [2] The method of claim 1, wherein in each iteration the test input is generated from a different test input depending on whether the coverage (404) predicted by the trained machine learning model (304, 403) for the different test input increases the total coverage achieved by testing prior to execution of the computer program (105) with the different test input as input. [3] The method of claim 1 or 2, further comprising checking whether executing the computer program (105) with the generated test input has increased the overall coverage achieved in testing the computer program (105), and in response to executing the computer program (105) with the generated test input having increased the overall coverage achieved in testing the computer program (105), adding the generated test input to a set of test inputs from which test inputs for further iterations are generated. [4] Method according to one of claims 1 to 3, comprising generating the test input on a test system, Determining the coverage (404) of the computer program (105) on the test system and in response to the test system determining that the predicted coverage (404) increases the total coverage achieved so far by testing the computer program (105), executing the computer program (105) with the generated test input on a system (106, 405) embedded with the test system [5] Software test system (100) configured to carry out a method according to any one of claims 1 to 4. [6] A computer program comprising instructions which, when executed by a processor, cause the processor to perform a method according to any one of claims 1 to 4. [7] A computer-readable medium storing instructions which, when executed by a processor, cause the processor to perform a method according to any one of claims 1 to 4.