Unique Pastel Reindeer - VM Call_result_value Function Doesn't Charge Gas Which Can Result In Chain DoS
Unique Pastel Reindeer - VM call_result_value function doesn't charge gas which can result in chain DoS
Introduction
In the world of blockchain and smart contract development, security is of utmost importance. A recent discovery in the Seda Protocol has highlighted a critical vulnerability that could potentially bring the entire network to a grinding halt. The issue lies in the call_result_value
function, which is an imported function inside the WASM VM. This function performs memory operations without applying any gas cost, making it susceptible to exploitation by malicious programs. In this article, we will delve into the root cause of this vulnerability, its potential impact, and the necessary mitigation steps to prevent a chain DoS.
Summary
The call_result_value
function is responsible for performing memory operations, specifically a byte-by-byte memory write up to 2^32 times. However, unlike other imports such as http_fetch
, this function neglects to apply a gas price. This oversight could be exploited by a malicious program to cause a whole network DoS.
Root Cause
The call_result_value
function is located in the call_result.rs
file, which is part of the Seda Protocol's WASM VM runtime. The function is defined as follows:
fn call_result_value(
env: FunctionEnvMut<'_, VmContext>,
result_data_ptr: WasmPtr<u8>,
result_data_length: u32,
) -> Result<()> {
let ctx = env.data();
let memory = ctx.memory_view(&env);
let target = result_data_ptr.slice(&memory, result_data_length)?;
let call_value = ctx.call_result_value.read();
for index in 0..result_data_length {
target.index(index as u64).write(call_value[index as usize])?;
}
Ok(())
}
As can be seen, the function performs a memory write operation without applying any gas cost. This is in contrast to other imports such as http_fetch
, which properly calculate a gas cost proportional to the result_length:
fn http_fetch(mut env: FunctionEnvMut<'_, VmContext>, _result_ptr: WasmPtr<u8>, result_length: i32) -> Result<u32> {
apply_gas_cost(
crate::metering::ExternalCallType::HttpFetchRequest(result_length as u64),
&mut env,
)?;
Internal Pre-conditions
For the attack to be successful, the following internal pre-conditions must be met:
- A malicious oracle program must be submitted.
- A data request must be made which uses the malicious oracle program.
External Pre-conditions
There are no external pre-conditions required for the attack to be successful.
Attack Path
The attack path is as follows:
- Attacker crafts a malicious Oracle Program that repeatedly invokes the
call_result_value
WASM import with the maximum possibleresult_data_length
(approaching 2^32). - Attacker submits a request that invoked this oracle program.
- Tally endblock logic executes the malicious program on all validator nodes simultaneously.
- The chain is DoS'd an indeterminate amount of time as all nodes try to complete computation of the malicious program.
- When the nodes eventually finish computation, the user who submitted the data request is charged a small amount of gas un-proportional to the amount of computation.
Impact
This is a critical DoS vulnerability that could completely halt the network for an indeterminate amount of time. Since this is part of the cosmos EndBlocker loop, all validators would be stuck processing in an essentially infinite loop. The program will never break out of this because the gas subtracted from the gas_meter for the computation is miniscule in comparison to the computation.
Mitigation
To prevent this vulnerability, the call_result_value
function must be modified to apply an appropriate gas metering proportional to the size of the data write. This can be achieved by adding a gas cost calculation similar to the one used in the http_fetch
function.
fn call_result_value(
env: FunctionEnvMut<'_, VmContext>,
result_data_ptr: WasmPtr<u8>,
result_data_length: u32,
) -> Result<()> {
let ctx = env.data();
let memory = ctx.memory_view(&env);
let target = result_data_ptr.slice(&memory, result_data_length)?;
let call_value = ctx.call_result_value.read();
apply_gas_cost(
crate::metering::ExternalCallType::MemoryWrite(result_data_length as u64),
&mut env,
)?;
for index in 0..result_data_length {
target.index(index as u64).write(call_value[index as usize])?;
}
Ok(())
}
By applying a gas cost proportional to the size of the data write, we can prevent a malicious program from exploiting this vulnerability and causing a chain DoS.
Unique Pastel Reindeer - VM call_result_value function doesn't charge gas which can result in chain DoS
Q&A
We've received several questions from the community regarding the recent discovery of the call_result_value
function vulnerability. Below are some of the most frequently asked questions and their answers.
Q: What is the call_result_value
function and why is it vulnerable?
A: The call_result_value
function is an imported function inside the WASM VM that performs memory operations. It is vulnerable because it neglects to apply a gas price, making it susceptible to exploitation by malicious programs.
Q: What is the impact of this vulnerability?
A: This is a critical DoS vulnerability that could completely halt the network for an indeterminate amount of time. Since this is part of the cosmos EndBlocker loop, all validators would be stuck processing in an essentially infinite loop.
Q: How can a malicious program exploit this vulnerability?
A: A malicious program can exploit this vulnerability by repeatedly invoking the call_result_value
WASM import with the maximum possible result_data_length
(approaching 2^32). This would cause the chain to be DoS'd an indeterminate amount of time as all nodes try to complete computation of the malicious program.
Q: What is the mitigation for this vulnerability?
A: To prevent this vulnerability, the call_result_value
function must be modified to apply an appropriate gas metering proportional to the size of the data write. This can be achieved by adding a gas cost calculation similar to the one used in the http_fetch
function.
Q: Why is this vulnerability not a simple gas cost issue?
A: This vulnerability is not a simple gas cost issue because the gas subtracted from the gas_meter for the computation is miniscule in comparison to the computation. This means that the program will never break out of the infinite loop because the gas cost is not sufficient to terminate the computation.
Q: What is the timeline for fixing this vulnerability?
A: We are working diligently to fix this vulnerability as soon as possible. We will provide updates on the status of the fix and the expected timeline for deployment.
Q: How can I stay informed about the status of this vulnerability?
A: We will provide regular updates on the status of this vulnerability through our official channels, including our blog, social media, and email newsletters. You can also follow our GitHub repository for the latest information on the fix.
Q: What can I do to help prevent this vulnerability from being exploited?
A: We recommend that all users take the following precautions to prevent this vulnerability from being exploited:
- Do not use the
call_result_value
function without proper gas metering. - Use a reputable and up-to-date version of the Seda Protocol software.
- Keep your node software and dependencies up to date.
- Monitor your node's performance and report any suspicious activity to the Seda Protocol team.
By taking these precautions, you can help prevent this vulnerability from being exploited and ensure the security and integrity of the Seda Protocol network.