Vulnerability Type
Infinite Loop Bugs
Abstract
For this contract, we actually reveal an infinite loop situation based on Vntchain smart contract, which indicates the call ring of functions. On one hand, with the mechanism of fallback function, the execution of a smart contract will fall into the fallback function once the input is empty or error. Another situation is that there exists a mutual invocation between functions in a single contract.
1. Infinite_loop_fallback_ring.c
Details
At first, the initial attack will call the function withdraw of contract Vulnerable.
// initial attack
void attack () {
CallParams prams = {Address("0xaaaa"), U256(10000), 100000}; // "0xaaaa" represents Vulnerable contract.
withdraw(prams, 100);
}
Then, the upcoming operation is amount-check (i.e., checkAmount) that set in the function withdraw.
// withdraw asset
void withdraw(uint256 amount) {
checkAmount(); // input error or input empty
address addr = GetSender();
uint256 balance = accounts.value.balance;
if (balance >= amount) {
TransferFromContract(addr, amount);
}
}
// check if the amount is enough
void checkAmount(uint256 amount) {
Require(U256_Cmp(amount, U256(0) == 1), "amount must > 0");
address from = GetSender();
accounts.key = from;
uint256 balance = accounts.value.balance;
PrintAddress("get sender:", from);
PrintUint256T("get balance:", balance);
Require(U256_Cmp(U256SafeSub(balance, amount), 0) != -1,
"No enough money to bet");
}
However, due to the input to the function checkAmount is empty or error, it will automatically activate the fallback function. Afterwards, the fallback function repeatedly calls the function withdraw, which triggers the checkAmount to be called again. Finally, this will form the call ring due to the operation hidden in the fallback function. It does not only consume the gas but also causes the Vnt (the digital currency of Vntchian) losses.
// fallback function
$_(){
count++;
if (count < 1000){
withdraw(amount);
}
}
Notably, we argue that it may cause the call ring due to the fallback function. Owing to the fallback mechanism of Vntchain smart contracts, the execution of fallback function can automatically occur once the input data of the caller is empty or error, apart from the example introduced in this issue, the other kinds of instances of the infinite loop might be caused by the hidden malicious call.
We show that a vivid picture is described as follows: fallback_call_ring.pdf
Specific Attack Procedures
First step: attack (Attacker) -> withdraw (Vulnerable);
Second step: withdraw (Vulnerable) -> checkAmount (Vulnerable)
Third step: checkAmount (Vulnerable) -> fallback (Vulnerable) (automatically triggered as the input is empty or error)
Fourth step: fallback (Vulnerable) -> withdraw (Vulnerable) -> checkAmount (Vulnerable) -> fallback (Vulnerable)
......
2. Infinite_loop_function_ring.c
Details
we excavate a simple function call ring in a single contract. First of all, the function checkAmount is called to tell if the amount is enough.
void checkAmount(uint256 amount) { // check if the amount is enough
Require(U256_Cmp(amount, U256(0) == 1), "amount must > 0");
address from = GetSender();
accounts.key = from;
uint256 balance = accounts.value.balance;
PrintAddress("get sender:", from);
PrintUint256T("get balance:", balance);
Require(U256_Cmp(U256SafeSub(balance, amount), 0) != -1,
"No enough money to bet");
GetAmount(amount);
}
Noticed, after performing the checkAmount operation, there is a GetAmount operation called by checkAmount.
UNMUTABLE
uint256 GetAmount(uint256 amount) { // get Vnt
checkAmount(amount);
return GetAmountFromAddress(GetSender());
}
However, in the function GetAmount, there is also a check operation checkAmount**, which causes the function GetAmount to call back the function checkAmount, thus forming a function call loop.
Conclusion
All in all, from the perspective of fallback function, the infinite loop can be probably triggered by the specific operations hidden in the fallback function. If the input data is empty or error, it will probably make the control flow enter the fallback function. From the most ordinary perspective, the mutual calls between functions will also trigger an infinite loop, causing gas and Vnt (the currency of Vntchain) to be consumed.
Reference
https://github.com/vntchain
ethereum/solidity#104