Abstract 1 Introduction 2 Buffer Overflow Vulnerabilities 3 Challenges 4 PatchBin Approach and Design 5 PatchBin Implementation 6 Experimental Evaluation & Preliminary Results 7 Related work 8 Conclusions References

Discovering and Repairing Flaws in C Binaries Without Requiring Codebase and Instrumentation

Diogo Ferreira ORCID LASIGE, Department of Informatics, Faculty of Sciences of University of Lisboa, Portugal    Ibéria Medeiros111Corresponding author ORCID LASIGE, Department of Informatics, Faculty of Sciences of University of Lisboa, Portugal
Abstract

Industrial and embedded software systems frequently integrate various third-party components sourced from diverse providers into their codebases. These systems are commonly developed in C, a language known for its lack of variable bounds checking, making it vulnerable to Buffer Overflows (BOs), which, when exploited, can cause severe damage. Consequently, the binary code resulting from vulnerable C programs is also vulnerable and remains so in the final products. Fixing these software systems is challenging because only binary code is available. This paper presents PatchBin, a binary patching tool to automatically fix BO vulnerabilities and validate the effectiveness of fixes while ensuring no new flaws are introduced. The approach involves a combination of fuzzing, reverse static analysis and static rewriting techniques to, respectively, (i) identify possible malicious inputs that can trigger BOs, (ii) find their root cause by employing reverse data flow analysis, and (iii) remove them by rewriting the binary code with effective validation, thus generating a new binary without the original flaws and new ones. Experimental evaluations with synthetic and real-world applications demonstrated that PatchBin detects and fixes BO in binary programs without introducing new vulnerabilities. The results showed that PatchBin is an important aid for industrial partners, enabling them to test and fix their products, including third-party components, without access to source code, but only to binary code.

Keywords and phrases:
Buffer Overflow Vulnerabilities, Binary Patching, Reverse Engineering, Static Analysis, Software Security
Copyright and License:
[Uncaptioned image] © Diogo Ferreira and Ibéria Medeiros; licensed under Creative Commons License CC-BY 4.0
2012 ACM Subject Classification:
Security and privacy Penetration testing
; Security and privacy Software reverse engineering ; Security and privacy Software security engineering
Funding:
This work was partially supported by P2030 through project I2DT, ref. COMPETE2030-FEDER-00389100, an ITEA4 European project (ref. 22025), and by FCT through the LASIGE Research Unit, ref. UID/00408/2025.
Editors:
Antonio Filieri and Peter Backeman

1 Introduction

In recent years, software development has grown significantly, but security has often been overlooked due to the industry’s fast-paced nature. The rise of digital transformation and technological evolution, such as Industry 4.0 [23], along with the increasing number of devices in our daily lives, has created a constant demand for new features and faster development. As a result, software has become more complex and has integrated various third-party components from different provenances. Software for industrial and embedded systems has been built in this manner and deployed in binary form in many products (e.g., vehicles, aircraft, and water plants). Nevertheless, software integration and complexity have also favoured the emergence of bugs and vulnerabilities [47, 49], thus raising awareness of the need to take security seriously in software running on products.

C is the core language for developing this type of software. Yet, it is well known for its lack of variable-bound checking, making it vulnerable to Buffer Overflows (BOs). The resulting binary code from vulnerable programs is also vulnerable and deployed as such in final products. BOs are the most impactful vulnerabilities [9] and recorded in recent years [33, 9]. When exploited, these vulnerabilities can cause severe damage with critical consequences for products that run vulnerable software (e.g., car crashing [49]), as they can allow an attacker to compromise integrity, availability, and confidentiality [34].

Based on this fast-paced software development and security scenario, there is a growing need for tools that quickly identify potential application vulnerabilities. However, simply detecting a vulnerability is only half the job, and there is still a need for tools that can automatically correct the code to eliminate the vulnerability and validate the effectiveness of the correction. Hence, correctly identifying and fixing BOs is of utmost importance. This is a difficult task, especially in binary code, since applications’ source code is not always accessible, and in particular from third-party software for which only binary code is provided. Furthermore, fixing binary code involves creating extra space in it to allocate the instructions that will remove the vulnerabilities, which implies moving the existing instructions and recovering the control flow information (CFI) after the correction is applied. Otherwise, the program functionality would be compromised. Yet, correction implies detection first, but does not explicitly include validation.

Although there are tools that can help with the detection task [17], the scenario is not the same for the repair task [24]. Furthermore, tools that integrate all three actions (detection, repairing, and validation) are not usually available; when they exist, they are for source code [24]. BO detection tools are mainly based on static [46, 19] or dynamic analysis [25]. The former analyses the code without running it, tracking possible entry points (i.e., inputs) associated with vulnerabilities, but it can be low-precision, as it may generate many false positives. In contrast, the latter inspects the code while it is in execution, identifying the points in the program where a crash occurs but fails to identify the problem’s root cause – the sensitive sink point. Moreover, they usually require code instrumentation during the compilation process [50], thus requiring access to the source code. To repair vulnerabilities, most existing tools also require access to the source code [24, 19], which is not the case, especially for closed-production products and third-party components. Nevertheless, the tools for binary patching have limitations, such as high system requirements and resources [4], incompatibility with the limited environments typically found in embedded systems [4], and the need for the source code for the instrumentation to exercise and analyse binary code [50].

This paper presents PatchBin (Patch C Binary Automatically), a binary patching tool designed to automatically fix BOs in the binary code of C programs and validate its effectiveness, thereby ensuring that no new flaws have been introduced. The tool performs Detection, Repairing and Validation, without needing the source code and code instrumentation. To accomplish this, PatchBin comprises a four-step approach that involves: (i) fuzzing: confirm the presence of BOs in the binary program under test (bPUT); (ii) reverse engineering: identify the location of BOs in the bPUT, including the involved binary code on them, by performing reverse data flow analysis; (iii) automatic program repair (APR): remove the identified BOs by repairing the bPUT using trampolines, which statically rewrite the code in a way that allocates space to insert the appropriate binary fix and keeps unaltered the bPUT’s internal logic, i.e., the CFI; (iv) Regression Testing & Fuzzing: validate the effectiveness of the fix, considering the information from (i) and the newly generated bPUT.

PatchBin starts by fuzzing the bPUT with malicious inputs to exploit any existing BO, while simultaneously recording the fuzzing session, thereby storing binary traces of bPUT (i.e., the execution of assembly data flows) and their memory states. For every BO found, the corresponding binary trace is analysed in reverse together with its memory state to identify the root cause: the sensitive sink (i.e., a function sensitive to malicious data, such as strcpy) and the buffer associated with the BO. It then patches the bPUT with small pieces of binary code – fixes. Fixes are automatically generated using templates filled with the information obtained from the reverse analysis. Next, the new bPUT is tested with the exploits that triggered the vulnerabilities and new inputs to confirm that the code was correctly fixed and that no new flaws were introduced. The tool was validated and evaluated on binary C programs – synthetic from SARD [36] and real-world applications –, where it correctly identified and removed the BOs it found.

The main contributions of this paper are the following: (1) an approach to search for BO vulnerabilities in binary code, fix them and validate the corrections made, without requiring access to the source code and code instrumentation. The approach involves and combines fuzzing, reverse static analysis, static rewriting as an automatic program repair technique, and regression testing to perform all the actions: detection, repairing, and validation; (2) a code repair approach able to modify the binary code without changing the application’s internal logic, i.e., CFI, employing trampoline and static rewriting techniques. Trampolines are used for the first time in vulnerability removal, instead of code instrumentation; (3) PatchBin, an implementation of the approach and its evaluation to detect and correct BOs.

2 Buffer Overflow Vulnerabilities

A Buffer overflow (BO) occurs when a program fails to bound-check a writing/reading operation to a buffer, allowing it to write/read beyond its limits. BO can occur on the heap, a memory region used to store dynamic data, or on the stack, a special memory region used to store local variables and parameters of functions and their return addresses [16]. Depending on the region where the BO occurs, it can be referred to as Heap Overflow or Stack Overflow [10]. The latter is more impactful than the former, so we focus on the latter.

The code presented on the left of Listing 1 illustrates a simple example of a stack BO. The function test creates a buffer of 10 bytes and uses the strcpy function (lines 5 and 6) to copy a user input into the buffer without any boundary checks. An attacker can exploit this code to overwrite a memory register to execute other code (e.g., his malicious code) that is not in the program’s scope. The right side of Listing 1 shows the assembly code of the function test, where we can see how the function is built and its memory representation (i.e., its stack frame). This code targets the x86-64 architecture, so each register and memory cell is 8 bytes. First, the rbp value (the base pointer register value) of the main function’s stack frame is saved in the stack (line 2). The rip (instruction pointer) is located immediately above this saved rbp, which contains the address of the next instruction to be executed after the test function call (i.e., line 11 in function main). The current value of the rsp (stack pointer register) is assigned to rbp (line 3), making rbp the base of the stack frame for the test function. After that, 32 bytes are allocated for this function (line 4). Line 5 receives through the rdi register the argument of the function test and copies it to the rdx (line 6). Then, the variable buf (a buffer) is allocated in the memory, 10 bytes right below the rbp (line 7), in which its address is placed in rax. Lines 8 and 9 prepare the registers to call the built-in strcpy function, following the System V ABI [31]. This vulnerability is exploited when, for example, an attacker inserts the input AAAAAAAAAABBBBBBBBCCCCCC, which contains more than 10 bytes (10 As, 8 Bs, and 6 Cs).

Listing 1 Stack overflow example (left) and the assembly code of the test function (right).
1#include <stdio.h>
2#include <string.h>
3
4void test(char *s){
5 char buf[10];
6 strcpy(buf, s);
7}
8
9main(int argc, char *argv){
10 test(argv[1]);
11 return 0;
12}
1test:
2 pushq %rbp
3 movq %rsp, %rbp
4 subq $32, %rsp
5 movq %rdi, -24(%rbp)
6 movq -24(%rbp), %rdx
7 leaq -10(%rbp), %rax
8 movq %rdx, %rsi
9 movq %rax, %rdi
10 xor %rax, %rax
11 callq strcpy@PLT
12 leave
13 ret

The value of the CPU registers when the application crashes is shown in Figure 1, where buf is filled with As, rbp with Bs and rip with an address composed of Cs (0x43 in hexadecimal). buf is not shown in the figure because it is not a register, but since it is allocated right below rpb and this was filled with the 8 Bs, we can immediately deduce the contents of buf. In the end, we can see that the rbp and rip contents were overwritten, altering the memory addresses they store, and consequently the application crashes when it tries to use the new contents.

 
Refer to caption 
Figure 1: Value of CPU registers when the application of Listing 1 crashes.

3 Challenges

We aim to create an automated binary patching tool for BO vulnerabilities that finds and repairs them, and validates the new binary code, ensuring that the BOs have been removed, no new flaws have been introduced, and CFI remains unaltered. Next, we present the challenges it should address and the ideas for solving them.

1) How can fuzzing be used to find vulnerabilities without instrumenting the binary code?

Traditional fuzzers (e.g., AFL [50]) to effectively perform their intended operations, such as triggering new internal states of bPUT, increasing functional coverage, and discovering vulnerabilities, the bPUT code must be instrumented. However, the compilation phase does not enable this feature by default, meaning that binaries of real-world applications are made available without their code being instrumented. This poses a challenge, particularly with closed-source and third-party software, where access to the source code needed for instrumentation is unavailable.

We aim to find vulnerabilities in binary applications using fuzzing without accessing and instrumenting their source code. Our idea is to use a binary-only fuzzing approach that involves fuzzing a non-instrumented bPUT, storing the exploits that crash the application, the binary traces and memory states associated with them, and later inspecting these traces using reverse data flow analysis to discover where BO vulnerabilities exist in binary code.

2) How to find the vulnerable sensitive sink without accessing the application source code?

The common way to discover vulnerabilities is to look for sensitive sinks (e.g., strcpy) in the code. While static analysis is the preferred technique for this, it can be challenging to analyse the code and may result in undesirable false positives (FPs). Analysing the entire assembly (human-readable machine code) of the bPUT is more arduous and complex, and can further increase the number of FPs.

Our idea is to perform a reverse data-flow analysis on the traces, starting from the point where the application crashed and tracing back to the sensitive sink that caused the crash.

3) How to remove vulnerabilities by directly patching the binary code?

Binary rewriting is a common practice for binary-only applications, primarily for instrumentation purposes [2, 14]. The current techniques can be dynamic or static. Dynamic techniques require an external component like an emulator (e.g., QEMU [4]) to analyse all interactions with the application at the cost of performance and system resources. Static techniques are not viable for large binaries, since the rewriting process needs to move instructions, thus making it dependent on recovering the CFI, which in itself is a difficult problem. Dynamic rewriting is ruled out when we think of embedded systems due to their limited resources, leaving static rewriting as the only viable option. But how do we overcome its complexity? Our idea to address this issue is to look into instruction punning and trampolines that allow static binary rewriting without the need for CFI recovery. More specifically, we propose replacing the sensitive sink associated with the BO with a binary trampoline. This trampoline will divert the program’s control flow to the patch code for execution, and then return to the same point to continue with the program’s normal execution.

Yet, code repair brings another challenge: which code is necessary to effectively fix the vulnerability and where to insert it? During the reverse data-flow analysis of traces, we will gather all the data of the sink involved in the target vulnerability (e.g., its arguments) and use it in fix templates associated with the sink. Some fixes, for example, will replace the sink with its safer version (e.g., strncpy for strcpy) and adjust its arguments. For sinks without safe versions (e.g., scanf), the fix will adjust its arguments to the correct data types.

4) How to validate the effectiveness of the patch?

We want to validate that a fix has successfully addressed a vulnerability and ensure that it neither breaks application logic nor introduces new vulnerabilities. We propose an automated validation process by fuzzing the patched bPUT with two types of test cases: those that crashed bPUT and new test cases generated for the patched bPUT.

4 PatchBin Approach and Design

4.1 Approach Overview

We propose PatchBin, an approach and tool for identifying and removing BO vulnerabilities and validating the effectiveness of applied patches. All three actions are deployed by accessing the binary, i.e., without codebase access and code instrumentation. To this end and to address the challenges of Section 3, the approach uses a combination of different techniques, namely (i) fuzzing: to detect BO vulnerabilities, validate the effectiveness of patches, and check whether they have not introduced new vulnerabilities, (ii) reverse engineering: through reverse data-flow static analysis to identify the bPUT’s binary code involved in the exploited BOs, (iii) automatic program repair: through instruction punning and binary trampolines to rewrite the binary code directly, thus removing the BOs. The trampoline concept is applied for the first time in software security to correct code and remove vulnerabilities. And, (iv) regression testing: which, combined with fuzzing, allows validation of patches’ effectiveness.

Figure 2 gives an overview of the approach architecture and its four main modules: Vulnerability Exploiter, Vulnerability Identifier, Binary Patcher, and Patch Validator. The first and fourth modules respect the fuzzing phases, with the fourth also including regression testing, the second the reverse engineering, and the third the APR part. In a nutshell, the approach fuzzes a non-instrumented bPUT to detect BOs and their corresponding exploits (test cases), while simultaneously storing the associated binary traces and memory states, including CPU register contents. Next, reverse engineering, using a bottom-up data-flow analysis approach over the binary traces and the memory states, determines the sensitive sink of the exploited BOs, as well as the code involved in them. All collected data is used to generate appropriate fixes, which are then applied directly to the bPUT to remove the vulnerabilities, using trampolines and static code rewriting. The validation process, through fuzzing and regression testing, tests the new bPUT using the exploits (provided from the previous fuzzing phase) to confirm that the patch process was successful, i.e., that the program no longer crashes or hangs. It will also generate new test cases to ensure that no other exploits can break the fixes or exploit new vulnerabilities introduced by them. In the next sections, we detail the modules of PatchBin.

Figure 2: Overview of the PatchBin approach architecture.

4.2 Vulnerability Exploiter

The first phase of our approach (marked as 1 in Figure 2) involves discovering vulnerabilities by exploiting them. To do so, the module receives the non-instrumented binary (bPUT) and a set of initial benign test cases that allow bPUT to run normally. Afterwards, it starts the fuzzing activity, mutating the initial test cases to generate new ones and executing them with bPUT. This aims to increase code coverage and identify test cases that cause crashes or hangs in bPUT, i.e., exploit vulnerabilities. These, which we call exploits, are saved for later use by the other modules. During the fuzzing session, the module monitors its activity to stop the ongoing session when a predefined target number of crashes is reached or if no new crashes are observed during a specific (and predefined) time period.

4.3 Vulnerability Identifier

The Vulnerability Identifier (2 in Fig. 2) is responsible for confirming the BOs that occurred and finding the code associated with them. During this phase, it collects as much information as possible about the sensitive sink call, including the arguments, their sizes and data types, and their location within the code. However, for that, the module needs to obtain the binary traces for the exploited BOs, capturing the information of what is being executed (e.g., CPU registers, concrete values of variables, memory states), which later will be used by the Vulnerability Debugger to find the locations within bPUT where the exploited BOs reside.

The module starts by disassembling bPUT and retains the PLT (Procedure Linkage Table) section, which contains the information (memory addresses and offsets) of the standard C library functions (e.g., strcpy, puts) called throughout the bPUT’s code. Also, the names of the user functions are extracted from the .text section (which contains the program’s actual code), and checkpoints are set at the beginning of these functions. This activates the possibility of reversing them and easily switching between functions during the debugging step (explained next). Afterwards, the module creates a vulnerability debugging session for each exploit encountered in the previous phase by executing the following four steps.

4.3.1 Binary traces extraction

For each exploit, a debugging session is started to run the exploit with bPUT. At this point, the module confirms the detected BO and obtains the binary trace, so that the data associated with the BO can be gathered.

4.3.2 Vulnerable user function discovery

Since a program is composed of at least one function (the main function), the existence of BOs is implicitly associated with some code that belongs to a function. For a function to be executed, first, the addresses contained in CPU registers rip and rbp, which refer to the caller function, are saved in the stack, thus releasing such registers for the called function. Later, these addresses are restored so that the program execution can return to the caller function and continue from those saved addresses. A BO occurs when an application fails to bounds-check a write operation to a buffer, resulting in the overwriting of addresses saved on the stack, in particular one of these two, or both. Since the debugging session takes place at runtime, the contents of the CPU registers are captured in the binary trace. Based on this, one way to check for BO occurrences is to verify whether the values of these registers point to a valid memory address within the current session. If not, a BO has occurred. This method also allows identifying which function contains the code that triggered the BO exploit.

To perform this confirmation and discovery, this step initiates the reverse data-flow analysis of the binary trace, starting from the last called function. Using the previous set of function checkpoints as anchors, it jumps to the beginning of the functions and checks the values of rbp and rip until no trace of BO is detected, i.e., until these registers point to valid memory addresses within the current session. When such occurs, the module suspects that the vulnerability resides in the current function, delivering it for further inspection.

4.3.3 BO Sensitive sink identification

The target function undergoes a deep analysis to identify the sensitive sink that triggered BO exploitation. To do so, it searches for calls to sensitive sinks (e.g., strcpy) in the function’s assembly, sets a breakpoint on these calls, and checks the CPU register values after the calls have been executed. If one of these values differs from those before any call, it indicates that the call that caused the BO was found, and thereby the sensitive sink point. The sinks it looks for are contained in the BO sinks database.

If no deviations in the register values are found, it means that the sensitive sink that caused the BO does not belong to the database. In this case, an alarm is generated indicating that the BO belongs to that target function, but is not associated with any sensitive sink of the database. The user can manually inspect the function’s assembly to identify the sensitive sink, update the database with this information, and rerun PatchBin from the Vulnerability Identifier module.

4.3.4 Vulnerability information extraction

Once the sensitive sink point is discovered, the module checks the BO sinks database to determine what information should be retrieved from the sink and how to obtain it. Starting from the sensitive sink call, a reverse step-by-step analysis is performed on each assembly instruction to obtain the required information. Listing 2 (Left) presents the template definition (top) of a sink stored in the database, with the information that should be gathered:

  • type. Indicates what information should be retrieved from sink arguments and how it will be retrieved. E.g., if type is string, the module retrieves its value; if a buffer, its address.

  • reverse_steps. The number of reverse steps required from the sink call to access the argument.

  • call_pos. The position of the argument in the sink. This feature is essential because, during the patching phase, this position indicates which CPU register the module needs to modify. E.g., if it is the first position, the module needs to modify the rdi register.

  • taintable. If the argument can be (or not) the source of the vulnerability.

The bottom part of the listing shows the information output about the sensitive sink, based on its definition. Specifically, its output contains: 1) for each argument, the call_pos value, the name of the variable that is passed in it, the size of its content, and if it is taintable or not; 2) the assembly line number corresponding to the sensitive sink call; and 3) the memory address of the call within the function and its corresponding PLT identification. Listing 2 (Right) presents an example of the definition for the strcpy sink and the output considering the code of Listing 1. However, as different exploits can trigger the same vulnerability and thus have the same sensitive sink information, after a debug session was successfully executed and returned, the module verifies if that sensitive sink with those details already exists in its findings. If no match is found, a new vulnerability is detected, and so the sensitive sink information will be associated with the current exploit in analysis, thus updating the Exploits database.

Listing 2 The left figure provides the schemas, the right has an example of a sink model for strcpy, and an instance where such a model was detected, instantiated to the values from the binary.
==== Definition ====
sens_sink{
arg_1: <type, reverse_steps, call_pos, taintable>
(...)
arg_n: <type, reverse_steps, call_pos, taintable>
}
==== Output ====
sens_sink:{
arg_1: <call_pos, variable_name, size, taintable>
(...)
arg_n: <call_pos, variable_name, size, taintable>
}
Line: Assembly’s line number
Address: => instruction: call PLT’
==== Definition ====
strcpy{
dst: <buffer, 3, 1, true>
src: <string, 4, 2, false>
}
==== Output ====
strcpy:{
dst: <1, buf, 24, taintable>
src: <2, s, 10, not taintable>
}
Line: 11
Address: 0x5555555551c5 <test+30>:
callq 0x555555555070 <strcpy@PLT>

4.4 Binary Patcher

This module (marked as 3 in Fig. 2) is responsible for creating and applying the fixes to remove the BOs found, using a static binary rewriting technique. The result is a new patched bPUT. Based on the sensitive sink information gathered by the previous module, for each BO, the Binary Patcher chooses the appropriate fix template from the Fixes Templates database, fills the fix with the data it received, generates the fix’s binary, and then applies the fix. These actions are performed by the following tasks: Fix generation, and Fix application.

4.4.1 Fix generation

In our binary patching approach, two types of fixes are proposed: i) replace insecure sensitive sink calls with calls of their secure versions (e.g., strcpy by strncpy); ii) run a bound checker before the insecure sensitive sink call, limiting the input maximum size to match the destination buffer length, thus avoiding an overflow. The ii) fix addresses sensitive sinks without a secure version (e.g., memcpy). The fix will contain the insecure sensitive sink call, delimited by the bound checker, and replace the original sensitive sink call.

Each supported sensitive sink has at least one fix template, depending on how the vulnerability is expressed in the code and the sink features. A fix template is a small pre-compiled C application that receives the CPU register values as arguments and other relevant information indicating the presence of stack canaries (i.e., a random integer placed in the stack just below the rip and rbp values to verify if it changes during the program execution). An example of a patch template for the strcpy sensitive sink is shown in Listing 3. The template receives information about the existence or absence of a canary (has_canary) and the three register values (rbp containing the base address of the stack frame where the BO resides, and rdi and rsi containing the arguments of the strcpy call). First, the fix determines whether a canary exists (lines 6 to 8). Then, it calculates the maximum allowed destination buffer size given the offset value of rdi to the rbp, and considers whether a canary exists (line 9). For example, considering the binary code of Listing 1, rdi receives the address contained in rax, which refers to the content placed at 10 bytes below rbp. So, the maximum capacity of the destination buffer is 10 bytes (i.e., size = 10). After filling the template, it is compiled and its binary is generated – the fix.

Listing 3 Patch template of strcpy sensitive sink
1 #include "stdlib.c"
2 #include <stdio.h>
3
4 void apply_patch(char *has_canary, void *rbp, void *rdi, char *rsi){
5 long offset = 0;
6 if(strcmp("True", has_canary) == 0){
7 offset = 8;
8 }
9 long size = (long)rbp - (long)rdi - offset;
10 strncpy(rdi, rsi, size-1);
11 rdi[size-1] = "\0";
12 }

A particular case we have to consider is the functions scanf and gets, which have their sensitive sinks in an external library (glibc). As they are almost always vulnerable and should not be used, this module also implements an auto-patch functionality that searches for them in the PLT section of the bPUT, gathers their offset address, generates the fixes and applies them. For gets, the fix replaces it with the fgets function. For scanf, the fix contains that function with its parameters readjusted.

4.4.2 Fix application

The fix is added to the vulnerable bPUT by using a static binary rewriting technique. Specifically, it uses an instruction punning [6] approach, where an e9jump opcode is encoded as a relative jmp opcode that redirects the program’s control flow to a trampoline. Then it returns the control flow to the program, keeping the program control flow information (CFI) unaltered. The trampoline is a memory area used to execute code additional to a program’s binary code, usually for code instrumentation or verification.

We leverage this technique to execute the fix from the trampoline. So, the e9jump is put at the address of the sensitive sink call, which redirects to the fix, and then the control flow returns to the instruction next to the sink call. The Fix Applier is responsible for making this application in bPUT, creating the e9jump and linking it to the fix. For e9jump to be correctly configured, it is crucial to obtain the correct information about the sensitive sink.

4.5 Patch Validator

The Patch Validator (4 in Fig. 2) verifies if the fixes successfully removed the BOs. It considers a vulnerability fixed only if none of the exploits associated with it (stored in the Exploits database) break the fix and crash the new bPUT, nor new test cases derived from the exploits.

Initially, using the exploits could be enough to consider the fix successful. However, to ensure its validation, this module uses the exploits as the initial set of test cases. It uses the Vulnerability Checker once again in order to initialise a new fuzzing session that will mutate these exploits, trying to find new vulnerable paths and new exploits, i.e., attempting to break the fixes or find a new vulnerability that the fixes might have introduced. If a fix is broken, an alarm is generated, which contains the information about the vulnerability collected during the process (e.g., target function, sensitive sink) for further manual inspection.

5 PatchBin Implementation

The PatchBin prototype was implemented in Python v3.11 and has four main modules, corresponding to the modules depicted in Figure 2. Since the approach does not depend on the source code and is independent of code compilation and instrumentation, all modules can be used individually, allowing the execution flow to start from any of the main modules. The tool uses AFL++ [17] to fuzz bPUT, GNU Debugger (GDB) [20] to capture traces, and E9Patch [13] to introduce trampolines into bPUT. As a fifth module, the tool contains an Orchestrator that monitors all the other modules, liaises between them and processes and prepares the data flow that goes through all the modules. Besides these, it integrates a set of scripts developed from scratch to perform tasks of reverse data analysis, parsing, sensitive sinks finding and their data retrieving, and fix construction, compilation, and application.

6 Experimental Evaluation & Preliminary Results

This section describes the process we performed to evaluate the PatchBin tool and discusses its preliminary results. Considering the challenges identified in Section 3 and the solutions proposed to solve them (Section 4), a number of key elements were needed to evaluate the tool: the ability to find the sensitive sink, the ability to fix vulnerabilities in a binary and the effectiveness of such fixes. Based on these key elements, we define the following questions: Q1. Can the tool identify a sensitive sink given an exploit? Q2. Is PatchBin capable of fixing a vulnerability in binaries with and without security protections? Q3. Does the fix introduce new vulnerabilities? Q4. How does the fix affect the overall performance? Q5. How much does the fix increase the final size of the binary? Q6. Is PatchBin capable of identifying and fixing vulnerabilities in real-world applications? Q7. How does PatchBin compare to other repair tools?

6.1 Evaluation Process & Setup

To better evaluate PatchBin, we tested it on 2 datasets – synthetic and real –, with the first allowing us to validate the tool and the second assessing it in real-world applications. The synthetic dataset comprises vulnerable C programs, some designed specifically to test the tool with particular code and others sourced from the NIST SARD [36] repository. For the second dataset, we used real-world applications and modified some of them to introduce BO vulnerabilities. This allows us to test our tool in scenarios with applications containing multiple code paths and a larger number of lines of code. Our modifications were minor, involving either replacing secure sensitive sink calls with their vulnerable versions or adding a vulnerable call before the return of an output.

Also, to verify how PatchBin performs with security protection mechanisms, all programs were compiled twice – with and without canary [8] protection. Then, using the two versions, we evaluate the effectiveness of the patching module and the fix templates we built. Finally, to measure the performance overhead introduced by the fix, we run both the patched and the vulnerable bPUT 5 times with good inputs, which will not trigger an overflow (otherwise, the vulnerable binary would crash). For each binary, we recorded the execution time and computed the average across 5 runs.

6.2 Validation with SARD dataset

The synthetic dataset contains 695 C programs, where 685 were from SARD, a repository with vulnerable and non-vulnerable programs used for testing purposes, and 10 were created by us. In order to evaluate the effectiveness of the tool, we manually analysed the SARD programs and classified them as vulnerable and not vulnerable. The vulnerable cases contain at least one sensitive sink supported by the tool. In turn, in the other 10 applications (created by us), each application contains at least 3 sink calls (distinct or repeated), and we tried to cover the most common programming practices seen in real applications (e.g., nested if statements and loops).

Table 1: Characterisation of the synthetic dataset.
Functions #Apps Vul N-Vul
gets 33 20 13
scanf 120 60 60
strcpy 115 60 55
strcat 115 60 55
sprintf 56 30 26
multi-sinks 256 130 126
Total 695 360 335

Table 1 summarises the characterisation of this dataset. In sum, the dataset contains 360 vulnerable (Vul) and 335 not-vulnerable (N-Vul) cases, divided by the functions supported by PatchBin. We conducted three types of tests using the dataset: One-Vulnerability, Multi-Vulnerabilities, and No-Vulnerabilities. With the first two tests, we aim to measure the tool’s effectiveness and performance. With the last test, we intend to check how the tool behaves with safe programs.

6.2.1 One-Vulnerability

We run the tool with the 230 vulnerable programs, each containing a single sensitive sink, i.e., a single vulnerable call of a function from Table 1. However, to measure the PatchBin’s performance and assess its effectiveness, we ran the tool on two binaries for each program: one compiled without canaries and the other with canaries. All 460 binaries were correctly processed by the tool. Table 2 shows the average testing results.

Analysing the results of the table, we can see that only the programs with functions gets and scanf were not processed by the Vulnerability Identifier module, as expected, because the calls of these functions are associated with system calls and the vulnerability takes place inside the glibc library (where the user input is requested), and this module does not consider this type of system libraries for debugging. All other vulnerable calls were successfully inspected; their sensitive sinks were found, and, on average, those compiled with canaries took longer to debug than those compiled without canaries (as expected). This answers the question Q1 affirmatively, meaning the tool can detect the vulnerable calls associated with BOs.

Regarding the Binary Patcher module, all applications (including those of gets and scanf), with and without canaries, were patched successfully with a patch template that replaced the vulnerable call with the correct fix. Based on this information, we can answer question Q2 affirmatively. All generated fixes produced a binary of 47,576 bytes, regardless of whether the sensitive sink was used or the binary’s initial size. This is due to the way e9Patch allocates space for trampolines, which divides the virtual address space into fixed-length blocks. In fact, this is why the file size increase percentage in the table is less on the binaries with canaries than the ones without canaries, since the former has a greater initial size due to the canary information. However, this increased percentage is very high because the number of LoC in programs ranges from 20 to 100, resulting in binaries much smaller than a patched binary. These facts and observations allow us to answer question Q5.

We also analysed the patched binaries’ runtime performance by measuring the overhead when compared to the original binary. We found that, on average, each patched binary took around 32% and 42% more time to execute for binaries without and with canaries, respectively. As expected, the overhead was greater for binaries with canaries than for the others because, by nature, the former takes longer to execute than the latter due to the memory verification they perform. We also observe that the overhead was lower in the patched binaries of the gets and scanf functions, which could be explained by these functions being associated with system calls. These observations answer question Q4.

Table 2: Average testing results of PatchBin with single-vuln applications, without and with canaries.
Test/Measure gets scanf strcpy strcat sprintf
WITHOUT CANARIES
Debug successful Yes Yes Yes
Debug time (s) 141 185 128
Patch successful Yes Yes Yes Yes Yes
% File size increase 182.79% 182.65% 181.39% 180.47% 181.60%
% Execution time overhead 26.36% 16.01% 44.83% 38.59% 35.34%
Patch validated successfully Yes Yes Yes Yes Yes
WITH CANARIES
Debug successful Yes Yes Yes
Debug time (s) 146 192 147
Patch successful Yes Yes Yes Yes Yes
% File size increase 181.03% 181.71% 178.46% 178.29% 178.46%
% Execution time overhead 28.76% 30.86% 57.12% 42.64% 51.55%
Patch validated successfully Yes Yes Yes Yes Yes

Finally, we validated the patched binaries with benign and malicious inputs, the latter through fuzzing. With benign inputs, we confirmed that applications behaved as expected, meaning the fix did not corrupt their initial program’s control flow. With malicious inputs, we verified that the binaries did not crash or hang, indicating that the applied fixes effectively removed the existing BOs and did not introduce new vulnerabilities. This last verification allows us to answer Q3 negatively.

6.2.2 Multi-Vulnerabilities

In this second test, we evaluated PatchBin with the 130 vulnerable programs that contain more than one sink call. Specifically, some programs call the same sink more than once, while others call different sinks at least once. We ran the tool only with binaries compiled without canaries since the results of the previous test clearly showed the difference between the two types of binaries. The results align with our observations in the One-Vulnerability test: the fixes were effective, there was an increase in the size of the resulting binaries, and there was an overhead in the execution time. Regarding overhead, we once again verified a significant increase in execution for each fix applied, but when a binary has the same sink call fixed in multiple locations, this increase is around 13%. For example, in two programs that call the sensitive sink strcpy, one calls it once and the other calls it twice, the runtime overhead in the first is 32% and in the second is 45%. These results answer question Q4.

6.2.3 No-Vulnerabilities

Lastly, we aim to verify how PatchBin interacts with secure programs, i.e., without vulnerabilities. To do so, we run the tool on the 335 non-vulnerable programs. For each program, we let the Vulnerability Exploiter module run for 6 hours without finding any vulnerabilities. For this particular test, we recorded the fuzzing sessions for the Vulnerability Identifier module for inspection, and no vulnerabilities were found. Hence, we can state that the tool does not generate FPs.

6.3 Evaluation with Real-World Application dataset

To test our tool in a more robust environment, we downloaded 6 applications from GitHub and SourceForge and ran two types of tests: one with vulnerable applications that we forced to be vulnerable, and another with applications whose security state we did not know.

For the first test, since we need specific vulnerable functions to be available in the application and to test the Vulnerability Identifier and Binary Patcher modules, we modified two applications from the dataset (TicTacToe [15] (App 1) and Simple Password Generator [7] (App 2)). So, we replaced the secure sensitive sink they contained with their insecure alternatives, thereby creating vulnerabilities that our tool could identify and fix. Listing 4 shows, for example, the modifications made to TicTacToe, where we replaced fgets by gets.

Listing 4 TicTacToe added vulnerability
1 printf("\n(turn #%i) To which square
2 would you (player %c) like to move? ", turn, player);
3
4 gets(input); // original was fgets(input, 3, stdin)
5 moveTo = atoi(input);
6
7 if(mv(board, moveTo, player))
8 turn++;
Table 3: Test results for real apps without canaries.
Test / Measure App 1 App 2
Lines of Code 126 160
Debug successful Yes
Debug time (s) 6191
Patch successful Yes Yes
% File size increase 176.48% 179.60%
% Execution time overhead 46.50% 62.36%
Patch validated successfully Yes Yes

Both applications were compiled with and without canaries, and all tests followed the same methodology as in Section 6.2. The results presented in Table 3 are very similar to the ones obtained with synthetic applications (Section 6.2). Both applications were successfully patched and validated.

For the second test, we used 4 applications, whose characterisation is shown in Table 4, columns Files and LoC. We compared the results of PatchBin (last 2 columns) with those of CorCA [24] tool (4th and 5th columns), a tool that finds potential BO vulnerabilities in the source code of C programs, and then repairs the code with fixes, after confirming that the potential vulnerabilities are effective. Both tools handled the 4 applications, meaning they processed 59K LoC across 109 files. PatchBin performed slightly better than CorCA, fixing 3 more BOs. It can be seen that the tool was able to fix all BOs it found, while CorCA had to decide which of the 44 potential vulnerabilities were real BOs. The 6 real vulnerabilities identified by CorCA were also found by our tool, and the other 3 BOs identified only by PatchBin were checked manually.

Table 4: Comparison of PatchBin with CorCA over real applications.
CorCA PatchBin
Application Files LoC Potential Fixes Vulns Patches
Vulns found
Zervit 0.4 17 1,014 6 4 4 4
Macgen 1.1 1 15 0 0 0 0
Tiny HTTPd 0.1 3 765 38 2 4 4
LIBPNG 1.6.37 88 57,075 0 0 1 1
Total 109 58,869 44 6 9 9

Based on the results of both tests, we can answer question Q6 and demonstrate our tool’s ability to discover and correct vulnerabilities even in more robust environments without needing codebase access or code instrumentation. Also, with the results of the second test, we can respond to Q7, i.e., PatchBin performs better than other code repair tool.

7 Related work

This section summarises the main related work in the areas of vulnerability detection and automatic program repair (APR).

7.1 Vulnerability Detection

Static and dynamic analysis are the two widely used methodologies to detect software vulnerabilities. Static analysis [38] is commonly used in software development and is performed in a non-runtime environment, testing and evaluating the application’s code (source or binary) for patterns that may indicate vulnerabilities. As such, it relies heavily on an updated set of patterns and rules to function properly; otherwise, its accuracy and precision will be significantly affected by the high levels of false positives and negatives it produces. Dynamic analysis [38], on the other hand, is performed at runtime, evaluating the application’s running state and manipulating it in order to discover new vulnerabilities. Although it can expose complex vulnerabilities that would not be easily found with static analysis, it can only analyse parts of the code that are actually executed.

Fuzzing is the most used dynamic analysis technique, which tries to achieve maximum code coverage by generating new test cases. It helps discover and explore new code paths while monitoring the application to detect any possible errors and crashes. AFL [50] is one of the best-known grey-box fuzzers (a mix of static and dynamic analysis), and it uses compile-time instrumentation along with a genetic algorithm to discover new test cases that could trigger new internal states, thus improving the functional coverage of the code. AFL++ [17][18], an improved version of AFL, supports faster instrumentation modules such as LLVM [21] and QEMU [4], as well as new and better mutators such as MOPT [30], a mutation scheduling to discover vulnerabilities more efficiently. SmartSeed [29] improves fuzzing efficiency by enhancing the genetic algorithm that mutates the seed files provided by the users to create several new inputs that will later be used to test the target application in order to try to trigger potential crashes. The fuzzer leverages a machine learning model to learn and generate high-value binary seeds. Ispoglou et al. presented FuzzGen [26], a library fuzzing tool that automatically synthesises fuzzers for complex libraries in a given environment. Rustamov et al. [39] designed DeepDiver, a hybrid fuzzing tool that combines AFL++ with concolic execution, whose objective is to discover vulnerabilities deep within the code while negating roadblock checks, a limitation found in other existing hybrid fuzzing frameworks, and thus allowing the fuzzer to explore new execution paths that can trigger vulnerabilities deep within the binary. Vadayath et al. [48] identified a set of vulnerability properties that improve the precision of static vulnerability detection and the scalability of dynamic vulnerability detection, and created a prototype, Arbitrer, that implements some of these properties using a hybrid analysis technique.

7.2 Software Repair

Automatic program repair (APR) consists of the automatic correction of software errors (e.g., faults, bugs) without human intervention by healing the software state and/or repairing its behaviour [37, 32], the latter being more effective since it modifies the software code. This is a challenging task, as it requires identifying the code to be repaired and the appropriate fix, and applying the fix without introducing new faults. Various approaches were proposed to repair program source code. Some works, in order to discover the errors to be corrected, require instrumenting the code during the compilation process [45, 28], others, instead of repairing, give suggestions on how to fix the errors found [19, 42], and still others fix the code but do not validate that the correction is effective and does not introduce new errors [44, 11, 35]. Just a few validate the correction, for example, verifying if the patch terminates correctly [1]. Yet, recently, repair AI-based approaches have emerged, showing promising results in this field [12, 5, 22].

Shahriar et al. [42] proposed a rule-based approach to identify and mitigate BO in C/C++ programs, addressing both simple and complex code forms, ranging from unsafe library function calls to pointer usage in control-flow structures. Klieber et al. [27] presented a technique to repair the source code of C programs by using an intermediate language representation to invalidate potential violations of spatial memory safety, in contrast to traditional program repair techniques that focus on preventing attackers from exploiting vulnerabilities. Inácio et al. [24] presented CorCA, a tool that extracts vulnerable program slices from the original program, fixes them, and then fixes the original program after validating the fixed program slices. Bader et al. [3] address the issue of automatically fixing common bugs by learning from past fixes. Schulte et al. [40] established a benchmark for different binary rewriting techniques. They concluded that rewriters that resort to intermediate representations of the code are unsuitable for generality and reliability. Interesting results from other rewriters were found, including a decision tree model trained on the features they identified to predict whether a specific rewriter suits a target binary. One rewriting technique is to rewrite the binary code directly. Schulte et al. [41] proposed an approach of this technique to repair binary programs directly in embedded and mobile systems using evolutionary computation algorithms. Another type of technique not included in such a benchmark is the use of existing fragments of binary code as a reference to repair functional and security bugs [43].

The approach we propose directly repairs the binary code without requiring its source code and code instrumentation, and allows for the identification of vulnerabilities and the locations where the code must be fixed. Additionally, our approach validates the fixed code, ensuring that no new flaws have been introduced during the repair process.

8 Conclusions

In this paper, we focused on buffer overflow (BO) vulnerabilities in binary code provided by C programs. We proposed an approach to mitigate them by directly repairing the binary without requiring the source code and code instrumentation. The approach detects, confirms and fixes BO directly in the binary code, validates the fix’s effectiveness, and checks if no new vulnerabilities were introduced by the fix. The solution uses fuzzing to discover BO vulnerabilities and, together with regression testing, validate the fixes applied to eliminate them. To locate vulnerabilities in the binary, it inspects the binary traces produced by the exploits through reverse data-flow analysis, starting from the crash point until finding the sensitive sink that triggered it. Lastly, the vulnerabilities are removed by a static rewriting approach that uses instruction punning and trampolines to patch the binary. The approach produced the PatchBin tool, and an experimental evaluation of the tool was performed with two datasets, one with SARD examples and the other with real applications. The results showed that the tool could successfully detect and confirm BO vulnerabilities, fix them, and operate on binaries compiled with or without security mechanisms, such as canaries. In addition, a comparison was made with CorCA, a source code repair tool, in which PatchBin performed slightly better. Although the tool is still in its early stages, the prototype shows promising results and proves to be a useful tool for companies that need to test software for which only the binary version is available, including third-party components.

References

  • [1] Omar I. Al-Bataineh. Reduce Before you Repair: Advantages of Combining Program Slicing with Automated Program Repair . In 2025 IEEE International Conference on Software Analysis, Evolution and Reengineering (SANER), pages 1–6, March 2025. doi:10.1109/SANER64311.2025.00094.
  • [2] Mj Muhammad Aslam. Binary instrumentation with QEMU. Master’s thesis, University of Technology of Eindhoven, July 2016.
  • [3] Johannes Bader, Andrew Scott, Michael Pradel, and Satish Chandra. Getafix: learning to fix bugs automatically. Proc. ACM Programming Languages, 3(OOPSLA), October 2019. doi:10.1145/3360585.
  • [4] Fabrice Bellard. QEMU: A generic and open source machine emulator and virtualizer, 2009. URL: https://www.qemu.org/.
  • [5] Xuemeng Cai and Lingxiao Jiang. Adapting Knowledge Prompt Tuning for Enhanced Automated Program Repair . In 2025 IEEE International Conference on Software Analysis, Evolution and Reengineering (SANER), pages 360–371, March 2025. doi:10.1109/SANER64311.2025.00041.
  • [6] Buddhika Chamith, Bo Joel Svensson, Luke Dalessandro, and Ryan R Newton. Instruction punning: Lightweight instrumentation for x86-64. SIGPLAN, 52:320–332, June 2017. doi:10.1145/3062341.3062344.
  • [7] Code-Live. Simple password generator. URL: https://github.com/codeliveru/pgen.
  • [8] C Cowan, F Wagle, Calton Pu, S Beattie, and J Walpole. Buffer overflows: attacks and defenses for the vulnerability of the decade. In In Proceedings of the DARPA Information Survivability Conference and Exposition, volume 2, pages 119–129, 2000.
  • [9] CWE. Top 25 most dangerous software weaknesses, 2026. URL: https://cwe.mitre.org/top25/index.html.
  • [10] Jason Deckard. Buffer Overflow Attacks: Detect, Exploit, Prevent. Syngress, 2005.
  • [11] Sun Ding, Hee Beng Kuan Tan, and Hongyu Zhang. Automatic removal of buffer overflow vulnerabilities in C/C++ programs. In Proceedings of the 16th International Conference on Enterprise Information Systems, 2:49–59, 2014. doi:10.5220/0004888000490059.
  • [12] Qingao Dong, Yuanzhang Lin, Hailong Sun, and Xiang Gao. Enhancing automated vulnerability repair through dependency embedding and pattern store. In 2025 IEEE International Conference on Software Analysis, Evolution and Reengineering (SANER), pages 193–204, March 2025. doi:10.1109/SANER64311.2025.00026.
  • [13] Gregory J. Duck. E9Patch: A powerful static binary rewriter, 2020. URL: https://github.com/GJDuck/e9patch.
  • [14] Gregory J Duck, Xiang Gao, and Abhik Roychoudhury. Binary rewriting without control flow recovery. In In Proceedings of the 41st ACM SIGPLAN Conference on Programming Language Design and Implementation, pages 151–163, 2020. doi:10.1145/3385412.3385972.
  • [15] emacdona. Tictactoe. URL: https://github.com/emacdona/tictactoe.
  • [16] Jon Erickson. Hacking: The Art of Exploitation, 2nd Edition. No Starch Press, 2008.
  • [17] Andrea Fioraldi, Dominik Maier, Heiko Eißfeldt, and Marc Heuse. The AFL++ fuzzing framework, 2017. URL: https://aflplus.plus/.
  • [18] Andrea Fioraldi, Dominik Maier, Heiko Eißfeldt, and Marc Heuse. AFL++: Combining incremental steps of fuzzing research. In In Proceedings of the 14th USENIX Workshop on Offensive Technologies. USENIX Association, agt 2020.
  • [19] Fengjuan Gao, Linzhang Wang, and Xuandong Li. BovInspector: Automatic inspection and repair of buffer overflow vulnerabilities. In Proceedings of the 31st IEEE/ACM International Conference on Automated Software Engineering, pages 786–791, 2016. doi:10.1145/2970276.2970282.
  • [20] GNU and Fee Software Foundation. GDB: The gnu project debugger, 1986. URL: https://sourceware.org/gdb/.
  • [21] LLVM Developer Group. The llvm compiler infrastructure project, 2003. URL: https://llvm.org/.
  • [22] Kanon Harada and Katsuhisa Maruyama. Towards efficient program repair with apr tools based on genetic algorithms. In 2024 IEEE International Conference on Software Analysis, Evolution and Reengineering (SANER), pages 896–901, March 2024. doi:10.1109/SANER60148.2024.00097.
  • [23] IBM. IBM - What is Industry 4.0, 2024. URL: https://www.ibm.com/topics/industry-4-0.
  • [24] João Inácio and Ibéria Medeiros. Corca: An automatic program repair tool for checking and removing effectively C flaws. In In Proceedings of the IEEE Conference on Software Testing, Verification and Validation, pages 71–82, April 2023. doi:10.1109/ICST57152.2023.00016.
  • [25] Intel. Intel® inspector, 2025. URL: https://www.intel.com/content/www/us/en/developer/tools/oneapi/inspector.html.
  • [26] Kyriakos Ispoglou, Daniel Austin, Vishwath Mohan, and Mathias Payer. Fuzzgen: Automatic fuzzer generation. In In Proceedings of the 29th USENIX Security Symposium, pages 2271–2287, agt 2020. URL: https://www.usenix.org/conference/usenixsecurity20/presentation/ispoglou.
  • [27] William Klieber, Ruben Martins, Ryan Steele, Matt Churilla, Mike McCall, and David Svoboda. Automated code repair to ensure spatial memory safety. In In Proceedings of IEEE/ACM International Workshop on Automated Program Repair, pages 23–30, June 2021. doi:10.1109/APR52552.2021.00013.
  • [28] Zhiqiang Lin, Xuxian Jiang, Dongyan Xu, Bing Mao, and Li Xie. AutoPaG: Towards Automated Software Patch Generation with Source Code Root Cause Identification and Repair. In Proceedings of the 2nd ACM Symposium on Information, Computer and Communications Security, pages 329–340, 2007.
  • [29] Chenyang Lyu, Shouling Ji, Yuwei Li, Junfeng Zhou, Jianhai Chen, and Jing Chen. Smartseed: Smart seed generation for efficient fuzzing. arXiv, 2018.
  • [30] Chenyang Lyu, Shouling Ji, Chao Zhang, Yuwei Li, Wei-Han Lee, Yu Song, and Raheem Beyah. Mopt: Optimized mutation scheduling for fuzzers. In In Proceedings of the 28th USENIX Security Symposium, pages 1949–1966, agt 2019. URL: https://www.usenix.org/conference/usenixsecurity19/presentation/lyu.
  • [31] Michael Matz, Jan Hubicka, Andreas Jaeger, and Mark Mitchell. System v application binary interface. AMD64 Architecture Processor Supplement, Draft v0, 99(2013):57, 2013.
  • [32] Sergey Mechtaev, Jooyong Yi, and Abhik Roychoudhury. Angelix: Scalable Multiline Program Patch Synthesis via Symbolic Analysis. In Proceedings of the 2016 IEEE/ACM 38th International Conference on Software Engineering, 2016.
  • [33] Mend.io. Most secure programming languages, 2024. URL: https://www.whitesourcesoftware.com/ resources/research-reports/what-are-the-most-secure-programming-
    languages/
    .
  • [34] Microsoft. Definition of a security vulnerability, 2014. URL: https://docs.microsoft.com/en-us/previous-versions/tn-archive/cc751383(v=technet.10).
  • [35] Ricardo Morgado, Ibéria Medeiros, and Nuno Neves. Towards web application security by automated code correction. In Proceedings of the 15th International Conference on Evaluation of Novel Approaches to Software Engineering, 2020.
  • [36] NIST. Nist software assurance reference dataset, 2026. URL: https://samate.nist.gov/SARD/.
  • [37] Eduard Pinconschi, Rui Abreu, and Pedro Adão. A comparative study of automatic program repair techniques for security vulnerabilities. In Proceedings of the IEEE International Symposium on Software Reliability Engineering (ISSRE), pages 196–207, 2021. doi:10.1109/ISSRE52982.2021.00031.
  • [38] James Ransome and Anmol Misra. Core Software Security: Security at the Source. CRC Press, 2018.
  • [39] Fayozbek Rustamov and Joobeom Yun. Deepdiver: Diving into abysmal depth of the binary for hunting deeply hidden software vulnerabilities. Future Internet, 12:74, January 2020. doi:10.3390/FI12040074.
  • [40] Eric Schulte, Michael D. Brown, and Vlad Folts. A broad comparative evaluation of x86-64 binary rewriters. In In Proceedings of the 15th Workshop on Cyber Security Experimentation and Test, pages 129–144, agt 2022. doi:10.1145/3546096.3546112.
  • [41] Eric Schulte, Jonathan DiLorenzo, Westley Weimer, and Stephanie Forrest. Automated repair of binary and assembly programs for cooperating embedded devices. Journal SIGARCH Computer Architecture, 41:317–328, March 2013. doi:10.1145/2451116.2451151.
  • [42] Hossain Shahriar, Hisham M. Haddad, and Ishan Vaidya. Buffer Overflow Patching for C and C++ Programs: Rule-Based Approach. ACM SIGAPP Applied Computing Review, 13(2):8–19, 2013.
  • [43] Vaibhav Sharma. Automatically repairing binary programs using adapter synthesis. In In Proceedings of the 34th IEEE/ACM International Conference on Automated Software Engineering, pages 1238–1241, November 2019. doi:10.1109/ASE.2019.00149.
  • [44] Stelios Sidiroglou-Douskos, Eric Lahtinen, Fan Long, and Martin Rinard. Automatic Error Elimination by Horizontal Code Transfer across Multiple Applications. ACM SIGPLAN Notices, 50(6):43?54, 2015.
  • [45] Alexey Smirnov and Tzi-cker Chiueh. Automatic Patch Generation for Buffer Overflow Attacks. In Proceedings of the 3rd International Symposium on Information Assurance and Security, pages 165–170, 2007. doi:10.1109/IAS.2007.87.
  • [46] Sonar. Sonarqube, 2025. URL: https://www.sonarqube.org/.
  • [47] Threatpost. St. jude medical patches vulnerable cardiac devices, 2024. URL: https://threatpost.com/st-jude-medical-patches-vulnerable-cardiac-devices/122955/.
  • [48] Jayakrishna Vadayath, Moritz Eckert, Kyle Zeng, Nicolaas Weideman, Gokulkrishna Praveen Menon, Yanick Fratantonio, Davide Balzarotti, Adam Doupé, Tiffany Bao, Ruoyu Wang, Christophe Hauser, and Yan Shoshitaishvili. Arbiter: Bridging the static and dynamic divide in vulnerability discovery on binary programs. In In Proceedings of the USENIX Security Symposium, pages 413–430, agt 2022. URL: https://www.usenix.org/conference/usenixsecurity22/presentation/vadayath.
  • [49] Wired. Jeep remote vulnerability, 2024. URL: https://www.wired.com/2015/07/hackers-remotely-kill-jeep-highway/.
  • [50] Michal Zalewski. AFL: American fuzzy loop, 2026. URL: https://lcamtuf.coredump.cx/afl/.