The Extended Berkeley Packet Filter (eBPF) was originally created to filter and monitor packets in the kernel.
Because of its efficiency and scalability, eBPF is being employed in a variety of fields, including application profiling.
Given these advantages, when Solana Smart Contracts are compiled, they are converted into eBPF Programs, and a separate virtual machine is created to execute them.
As such, the rBPF VM exists in Solana, much like EVM exists in Solidity-based blockchains. (https://crates.io/crates/solana_rbpf)
According to the Audit Report (https://solana.com/solana-security-audit-2019.pdf) issued by Kudelski Security in 2019, rBPF was not an audit scope, and no audits for rBPF were seen after that.
So, among the attack surfaces that exist in Solana, I thought it was the point where zero day vulnerabilities can be found with the highest probability, and I documented what I had learned before looking for bugs.
The eBPF (Extended Berkley packet filter) was developed to filter packets, but it provides a generic and flexible instruction sets that allows it to do more than just a filter.
It is also used as an element for executing Smart Contracts in Solana, and other outstanding use cases include networking, tracing, and security.
Solana uses rBPF (BPF Virtual Machine written in Rust) to obtain state by executing eBPF Bytecode at the application level.
eBPF is basically a generic purpose RISC instruction set, and in Solana, code written in Rust is converted into BPF bytecode form through LLVM during the compilation process.
The converted BPF Bytecode is stored in a separate account, and when a specific Smart Contract is executed, the account is referred to.
The overall process is shown in the figure below.
Before delving into rBPF vulnerability cases, let me discuss the BPF Instruction Set briefly.
In solana-rBPF, Virtual Machine have 11 64-bit registers, pc, and user-defined size stack, and the registers are named r0-r10.
Instruction is composed as shown in the image above, and Instruction does not necessarily use all fields.
In this case, unused fields are filled with zeros.
A detailed specification of Instruction Set can be found at https://docs.kernel.org/bpf/instruction-set.html.
eBPF has the advantage of being able to run the instructions safely, which means that the eBPF program effectively prevents crashes that can occur for various reasons, such as memory access to wrong addresses.
To ensure the safety of eBPF, rBPF execute separate verifier and it checks the eBPF Program provided as an input.
However, unlike the original kernel, it operates at the application level, so there is a difference during the verification process.
It is possible that this difference could lead to vulnerabilities that would not have existed in Kernel space.
Let's take an example of the difference.Kernel checks whether the BPF program is a directed acyclic graph (DAG), but the rBPF Virtual Machine adopted by solana does not check such a part.
Rather, rBPF Virtual Machine set the Maximum Instruction Count and check whether or not the Maximum Instruction Count is exceeded by counting each time the Instruction is executed.if the count exceeds the maximum number of instructions, Error is occurred.
As in the example above, there are parts that are verified using different method by the different verifier, and this part will also be verified whether is vulnerable in the future.
Since eBPF has been created for a very long time, many vulnerabilities have been discovered, and security has been greatly improved during long time.
However, there are many changed parts from the existing eBPF Virtual Machine to the rBPF created for application level and the solana-rBPF customized for use in Solana, so I think that these parts can lead to vulnerabilities.
Vulnerability can occur in solana-rBPF as follows.
Now, let's take a look at the 1-Day vulnerability that occurred in the attack surface mentioned above.
https://www.cvedetails.com/cve/CVE-2021-46102/
This vulnerability was found by the BlockSec Team.
if solana smart contract developer compile the code for the Solana contract in Rust or C, a file in the form of .so with the elf format is created.
When deploying a contract through the BPF Loader and so on, the .so file is uploaded and entered into the elf parser in rBPF first. (load function of “elf.rs”)
.so file can be enough user input to trigger a vulnerability during the elf parsing process.
Let’s see at the process where the vulnerability occurs.
This vulnerability occurred during the process of parsing the elf header.
Below is the code that the vulnerability occurred
The code that vulnerability occurred performs parsing to get the necessary information to arrange and relocate the symbol definition and reference of the program in the Symbol Table Entry.
Let's see the structure of the original Symbol Table Entry used parsing.
Both sym.st_value and refd_pa values are user input.Therefore, if these two values are added, it becomes larger than the maximum value of the given type and can cause integer overflow.
it is best practice to prevent overflow and underflow using saturating_add in Rust.but it seems to catch the part that did not use safe function that provide by Rust.
After a vulnerability was discovered, functions provided by rust were added to the code to perform safe arithmetic operations.("saturating_add", "saturating_sub", "saturating_mul", "checked_div", "checked_shr", "checked_rem", "checked_add")The patched code can prevent the same type of vulnerability.
https://github.com/solana-labs/rbpf/pull/236/files
https://www.cvedetails.com/cve/CVE-2022-23066/
This vulnerability is also a vulnerability discovered by the Blocksec Team.Unlike CVE-2021-46102, it is a vulnerability that can cause Network Fork by making the state between the JIT Mode Node and the Interpreter Mode Node different.
As mentioned above, rBPF Virtual Machine can select JIT mode option and Interpreter mode option.
The validator can select and execute incoming eBPF Bytecode among two modes.
If the result of executing eBPF Bytecode in JIT Mode and the result of executing eBPF Bytecode in Intepreter Mode are different, Network Fork can be caused because the state stored in each node is different.
I wonder why there is not only one Intepreter Mode in terms of minimizing the attack surface.
The vulnerable instruction that sdiv32 supports signed division on operands.
The problem with the sdiv32 instruction is that the calculation result for 32 bits is stored in a 64-bit register, but sign extension is not performed when the calculation result for 32 bits is stored in a 64-bit register.
In the code below, sign extension is applied only to the MUL opcode.
On the other hand, code extension is performed in Interpreter Mode.
Suppose we divide 12 by -4 using sdiv32.
In Interpreter Mode, 0xFFFFFFFFFFFFFFFD(-3) is stored in 64-bit register.On the other hand, in JIT Mode, 0x00000000FFFFFFFD is stored in a 64-bit register.
In JIT Mode, it is recognized as a positive number, not a negative number.
so as a result, the results of Interpreter Mode and JIT Mode are different, so Network Fork can occur.
Afterwards, the code extension applied only to the MUL opcode was applied to the SDIV opcode to prevent the vulnerability.
https://github.com/solana-labs/rbpf/commit/e61e045f8c244de978401d186dcfd50838817297
https://www.cvedetails.com/cve/CVE-2022-31264/
This is a vulnerability discovered by Ainevsia.
It looks similar to CVE-2021-46102, but the location of the vulnerable code is different.
It does not occur because of the rBPF Virtual Machine code, but it occurs in the library, "goblin" for Elf Parsing in the rBPF Virtual Machine.
It is presumed that the developer overlooked the fact that the sum of ELF Virtual Address and Memsize becomes larger than the maximum value of usize type.
This case showed that the library used in rBPF could also be used as an attack vector.
Afterwards, goblin library where the vulnerability occurred was updated to remove the vulnerability using the saturating_add function, and the vulnerability was completely removed by including that part in rbpf 0.2.29.
https://github.com/m4b/goblin/pull/306/files
https://github.com/solana-labs/rbpf/pull/320
In this post, we learned what rBPF is for running smart contracts in Solana, which parts are vulnerable, and the 1-Day vulnerabilities accordingly.
We believe that the possibility of vulnerabilities occurring in rBPF is very high as an attack surface that has not been formally audited.
We will continue to research vulnerabilities in Solana and contribute to the Solana Community 🙂