xanoutas/solidity-security-findings huggingface dataset:
refPerTok() for one asset might revert and break the protocol
Audit: 2023-01-reserve-findings
Severity: HIGH

# Lines of code

https://github.com/reserve-protocol/protocol/blob/df7ecadc2bae74244ace5e8b39e94bc992903158/contracts/p1/BasketHandler.sol#L441
https://github.com/reserve-protocol/protocol/blob/962271bfcfe3202c4c440965e92afc9b9627776a/contracts/p1/BasketHandler.sol#L300


# Vulnerability details

## Impact
It turns out that function `refPerTok()` might revert for some assets. For example for `CTokenFiatCollateral` [an external call might revert](https://github.com/reserve-protocol/protocol/blob/df7ecadc2bae74244ace5e8b39e94bc992903158/contracts/plugins/assets/CTokenFiatCollateral.sol#L45). As a result `refPerTok()` also reverts.

Function `refPerTok()` is called in a loop for all assets inside `BasketHandlerP1.backetsHeldBy()` [function](https://github.com/reserve-protocol/protocol/blob/df7ecadc2bae74244ace5e8b39e94bc992903158/contracts/p1/BasketHandler.sol#L441). Because reverting isn't handled here, `backetsHeldBy()` might revert as well. Only because one asset misbehaves.

`BackingManager` uses `backetsHeldBy()` [here](https://github.com/reserve-protocol/protocol/blob/df7ecadc2bae74244ace5e8b39e94bc992903158/contracts/p1/BackingManager.sol#L191). And `handoutExcessAssets()` might revert as well. As a result `_manageTokens()` of `BackingManager` might revert.

Protocol stops function because of one misbehaving asset.

## Proof of Concept
* https://github.com/reserve-protocol/protocol/blob/df7ecadc2bae74244ace5e8b39e94bc992903158/contracts/p1/BasketHandler.sol#L441
* https://github.com/reserve-protocol/protocol/blob/962271bfcfe3202c4c440965e92afc9b9627776a/contracts/p1/BasketHandler.sol#L300

## Recommended Mitigation Steps
Process situation when `refPerTok()` is evaluated in a loop for all assets and might revert for one of assets.
Stakers lose their stakes when eras change
Audit: 2023-01-reserve-findings
Severity: HIGH

# Lines of code

https://github.com/reserve-protocol/protocol/blob/df7ecadc2bae74244ace5e8b39e94bc992903158/contracts/p1/StRSR.sol#L212
https://github.com/reserve-protocol/protocol/blob/df7ecadc2bae74244ace5e8b39e94bc992903158/contracts/p1/StRSR.sol#L257


# Vulnerability details

## Impact
_mint saves the stake into a given mapping for a given era. The staker cannot choose the era. 

unstake checks if stakeAmount is less than value saved in the stakes mapping. 

If a staker stakes in era 1 and attempts to unstake in era 2 the transaction will fail because `stakes[2][account]` will equal 0 while `stakes[1][account]` will equal the stakers staked amount. 

The staker effectively loses their stake.

## Proof of Concept
Staker stakes 100 tokens in era 1
Staker attempts to unstake in era 3 but the transaction reverts because the staker didn't stake in era 3


## Recommended Mitigation Steps
Allow stakers to unstake from previous eras
Missing reentrancy to melt() function
Audit: 2023-01-reserve-findings
Severity: HIGH

# Lines of code

https://github.com/reserve-protocol/protocol/blob/b30ab2068dddf111744b8feed0dd94925e10d947/contracts/p1/Furnace.sol#L70-L84


# Vulnerability details

## Impact
Funds can be melted more than it should be.

## Proof of Concept
melt() function of Furnace.sol used to melt RTokens.

```javascript
function melt() external notPausedOrFrozen {
        if (uint48(block.timestamp) < uint64(lastPayout) + period) return;

        // # of whole periods that have passed since lastPayout
        uint48 numPeriods = uint48((block.timestamp) - lastPayout) / period;

        // Paying out the ratio r, N times, equals paying out the ratio (1 - (1-r)^N) 1 time.
        uint192 payoutRatio = FIX_ONE.minus(FIX_ONE.minus(ratio).powu(numPeriods));

        uint256 amount = payoutRatio.mulu_toUint(lastPayoutBal);

        lastPayout += numPeriods * period;
        lastPayoutBal = rToken.balanceOf(address(this)) - amount;
        if (amount > 0) rToken.melt(amount);
    }
```

Attacker can deploy a malicious contract with `attack()` function which will be used to call `melt()` function multiple times, so for the same transaction block.timestamp will be the same for every call.

here suppose, block.timestamp = 2000, lastPayout = 1000, period = 10 then calculations for the first time call will be

1). call `melt()` for the first time

numPeriod = (2000 - 1000) / 10 = 100

lastPayout = 100 * 10 = 1000

payoutRatio = 1e18

ratio = 1 (as it's max value 1e18)

so for this data, amount will be melted.

2). call `melt()` for the second time

`if (uint48(block.timestamp) < uint64(lastPayout) + period) return;` will be bypass because 2000 > 1000 + 10

now,

numPeriod = (2000 - 1000) / 10 = 100

it will be same for all the transaction with same block.timestamp. It will again melt same amount.

3). repeat


## Tools Used
Manual Analysis

## Recommended Mitigation Steps
add reentrancy guard
DOS attack to sweepRewards()
Audit: 2023-01-reserve-findings
Severity: HIGH

# Lines of code

https://github.com/reserve-protocol/protocol/blob/df7ecadc2bae74244ace5e8b39e94bc992903158/contracts/p1/mixins/RewardableLib.sol#L67-L69


# Vulnerability details

## Impact
Detailed description of the impact of this finding.
``sweepRewards()`` has one vulnerability: if one of the ERC20 has a smaller balance than the corresponding liability, then the whole function will revert, regardless how excessive the rest of the ERC20s are in  their balances. A malicious user can exploit this and launch an DOS attack to ``sweepRewards``.   

## Proof of Concept
Provide direct links to all referenced code in GitHub. Add screenshots, logs, or any other relevant proof that illustrates the concept.
https://github.com/reserve-protocol/protocol/blob/df7ecadc2bae74244ace5e8b39e94bc992903158/contracts/p1/mixins/RewardableLib.sol#L67-L69
``sweepRewards()`` has one vulnerability: if one of the ERC20 has a smaller balance than the corresponding liability, then the whole function will revert due to the following code, which will revert when there is an underflow.
```
for (uint256 i = 0; i < erc20sLen; ++i) {
            deltas[i] = erc20s[i].balanceOf(address(this)) - liabilities[erc20s[i]]; // {qTok}
}
```
As a result, a malicious user can front-run this transaction and run another transaction to either decrease the balance of one ERC20 token from the contract or increase the liabilities of that ERC20, so that an underflow will occur. As a result, the whole ``sweekRewards()`` will revert. The malicious user can do this each time ``sweepRewards()`` is called, an effective DOS attack. 



## Tools Used
Remix

## Recommended Mitigation Steps
To mitigate this, simply allowe ``sweepRewards()`` to succeed, ignoring those ERC20 that might cause underflow.
```
// Calculate deltas
        for (uint256 i = 0; i < erc20sLen; ++i) {
             if(erc20s[i].balanceOf(address(this))  >   liabilities[erc20s[i]])          // skipping those who might underflow
                        deltas[i] = erc20s[i].balanceOf(address(this)) - liabilities[erc20s[i]]; // {qTok}; 
        }

```
Possible DoS because of *disabled* state control.
Audit: 2023-01-reserve-findings
Severity: HIGH

# Lines of code

https://github.com/reserve-protocol/protocol/blob/df7ecadc2bae74244ace5e8b39e94bc992903158/contracts/p1/Broker.sol#L124


# Vulnerability details

## Impact
A registered trader can call ```reportViolation()``` function to disable the ```openTrade``` functionality.

## Proof of Concept

```setDisabled``` emits event and when this event is caught by the malicious trader he will instantly can call ```reportViolation``` again to disable the system and thus the ```openTrade``` can be DoS forever.

## Tools Used

Manual.

## Recommended Mitigation Steps

Adding violation period for every trader can prevent from unlimited calls.
Potential Reentrancy Attack in StRSR.sol Contract in `transferFrom()` Function
Audit: 2023-01-reserve-findings
Severity: HIGH

# Lines of code

https://github.com/reserve-protocol/protocol/blob/df7ecadc2bae74244ace5e8b39e94bc992903158/contracts/p1/StRSR.sol#L641-L649


# Vulnerability details

## Impact
In the `transferFrom` function in the StRSR.sol smart contract, it appears that there is a potential issue with the way the **allowance** is being updated. Currently, the **allowance** is being decremented by the transferred amount after the transfer has been executed. This could potentially lead to an issue where an attacker could repeatedly call `transferFrom` and exhaust the **allowance** even if the underlying balance is not sufficient to complete the transfer.

If an attacker is able to successfully exploit the vulnerability in the `transferFrom` function, they could potentially perform a **reentrancy attack**. This would allow the attacker to repeatedly call the `transferFrom` function and drain the targeted account's balance, potentially leading to a loss of funds for the affected user and potentially a significant loss of funds for the overall protocol. Depending on the scale of the attack, it could also cause significant harm to a majority of users and potentially lead to a loss of trust in the protocol.

## Proof of Concept
https://gist.github.com/BadGenius22/c377daab2210395db02f6f726edce6d5


## Recommended Mitigation Steps
To fix this issue, the allowance should be decremented **before** the transfer is executed. This ensures that the allowance accurately reflects the remaining allowance for the user.

```Solidity
    // decrement allowance before transfer
    allowance[sender][msg.sender] -= amount;
```

```Solidity
function transferFrom(address sender, address recipient, uint256 amount) public notPausedOrFrozen {
    require(amount > 0, "amount is zero");
    require(amount <= allowance[sender][msg.sender], "allowance insufficient");
    require(amount <= balanceOf[sender], "balance insufficient");

    // decrement allowance before transfer
    allowance[sender][msg.sender] -= amount;

    balanceOf[sender] -= amount;
    balanceOf[recipient] += amount;
    emit Transfer(sender, recipient, amount);
}
```

The code provided above is able to solve the reentrancy vulnerability by first checking that the allowance for the transfer is sufficient before making the transfer, and then decrementing the allowance before the balance is updated. This way, even if an attacker were to call the `transferFrom` function again in their malicious contract, the allowance would already have been decreased and the malicious contract would not be able to transfer more than the intended amount. Additionally, the `require` statement that checks that the balance is sufficient before making the transfer also helps to prevent reentrancy attacks by ensuring that the account sending the transaction has enough balance to complete the transfer.
queue.right might not be correctly calculated in function issue()
Audit: 2023-01-reserve-findings
Severity: HIGH

# Lines of code

https://github.com/reserve-protocol/protocol/blob/df7ecadc2bae74244ace5e8b39e94bc992903158/contracts/p1/RToken.sol#L368


# Vulnerability details

## Impact
Detailed description of the impact of this finding.
``queue.right`` might not be correctly calculated in function ``issue()``. 

## Proof of Concept
Provide direct links to all referenced code in GitHub. Add screenshots, logs, or any other relevant proof that illustrates the concept.

The following code uses ``curr`` to point to the item located at index ``queue.right``. 
```
IssueItem storage curr = (queue.right < queue.items.length)
            ? queue.items[queue.right]
            : queue.items.push();
        curr.when = vestingEnd;
```
It uses ``(queue.right < queue.items.length)`` to check whether the queue is non-empty.  If it  is not empty, then it will retrieve ``queue.items[queue.right]``, otherwise, it will use ``queue.items.push()`` to push a new item into the queue. 

However, this condition is not correct when ``refundSpan(account, queue.left, queue.right)`` is called, which will reset both ``queue.left`` and ``queue.right`` to zero.  Unfortunately, ``queue.items.length`` will not be reset as ``refundSpan()`` does not actually  delete any items from the array. As a result, ``(queue.right < queue.items.length)` is still true, and the program will follow the logic that the queue is NOT empty, and push() will not happen. 


## Tools Used
Remix

## Recommended Mitigation Steps
We revise the condition to ensure the correct
Unclear updates and checks for longFreezes mapping values
Audit: 2023-01-reserve-findings
Severity: HIGH

# Lines of code

https://github.com/reserve-protocol/protocol/blob/df7ecadc2bae74244ace5e8b39e94bc992903158/contracts/mixins/Auth.sol#L34


# Vulnerability details

## Impact
```
mapping(address => uint256) public longFreezes;

/* ==== Invariants ====
   0 <= longFreeze[a] <= LONG_FREEZE_CHARGES for all addrs a
   set{a has LONG_FREEZER} == set{a : longFreeze[a] == 0}
*/
```
This bug could allow an attacker to manipulate the values stored in the `longFreezes` mapping, by updating them in a way that does not respect the stated invariant of `0 <= longFreeze[a] <= LONG_FREEZE_CHARGES` for all addresses a. This could lead to unexpected or unintended behavior in the contract. For example, if the `longFreeze` value for an address is set to a value higher than LONG_FREEZE_CHARGES, the attacker may be able to exploit this to cause the contract to malfunction or behave in unexpected ways. Additionally, if there is no check in place to enforce the stated invariant, the bug could allow the attacker to bypass any logic or restrictions that rely on the `longFreezes` mapping. This could lead to unauthorized access or other security vulnerabilities.


## Proof of Concept

For an attacker to manipulate the values stored in the `longFreezes` mapping by calling functions that update the values without proper checks or validation. The attacker could then use this manipulation to gain unauthorized access to the contract's functions or resources.

```
contract Attacker {
    address targetContract;

    constructor(address _targetContract) public {
        targetContract = _targetContract;
    }

    function exploit() public {
        // Call the function that updates the longFreezes mapping and pass in an address and a value greater than LONG_FREEZE_CHARGES
        targetContract.call(abi.encodeWithSignature("updateLongFreezes(address,uint256)", address(this), LONG_FREEZE_CHARGES+1));
    }
}
```
The malicious contract `Attacker` is able to call the `updateLongFreezes` function of the target contract and set the value of the mapping for the address of the attacker, to a value greater than LONG_FREEZE_CHARGES. This would enable the attacker to potentially gain unauthorized access to the contract's functions or resources.


## Tools Used

Manual audit, Vs Code

## Recommended Mitigation Steps

add functions for updating the `longFreezes` mapping that "include" checks to ensure that the values being set are within the correct range. For example, a function to increment the value stored at a given address in the mapping could include a check that the current value plus the increment does not exceed `LONG_FREEZE_CHARGES`. Another option would be to add a modifier that can be applied to functions that update the mapping to ensure that the checks are performed.

```
function incrementLongFreeze(address _address) public {
    require(_address != address(0), "Invalid address");
    require(longFreezes[_address] + 1 <= LONG_FREEZE_CHARGES, "Exceeded maximum number of charges
The issue() function fails to synchronize queue.basketNonce and basketHandler.nonce() when they are out of sync
Audit: 2023-01-reserve-findings
Severity: HIGH

# Lines of code

https://github.com/reserve-protocol/protocol/blob/df7ecadc2bae74244ace5e8b39e94bc992903158/contracts/p1/RToken.sol#L202-L213


# Vulnerability details

## Impact
Detailed description of the impact of this finding
The ``issue()`` function fails to synchronize ``queue.basketNonce`` and ``basketHandler.nonce()`` when they are out of sync. As a result, they will continue to be out of sync and future ``issue()`` will continue to refund all issues.

## Proof of Concept
Provide direct links to all referenced code in GitHub. Add screenshots, logs, or any other relevant proof that illustrates the concept.
The ``issue()`` function fails to synchronize ``queue.basketNonce`` and ``basketHandler.nonce()`` when they are out of sync. 
It does refund all the issues in the queue, but it needs also assign ``queue.basketNonce`` with ``basketHandler.nonce()`` so that they become synced again. 

## Tools Used
Remix

## Recommended Mitigation Steps
We resynced them in the if-block by an assignment.
```
if (queue.basketNonce > 0 && queue.basketNonce != basketNonce) {
            // == Interaction ==
            // This violates simple CEI, so we have to renew any potential transient state!
            refundSpan(recipient, queue.left, queue.right);

            // Refresh collateral after interaction
            assetRegistry.refresh();

            // Refresh local values after potential reentrant changes to contract state.
            queue.basketNonce = basketHandler.nonce(); // @audit sync them
        }

```
Unclear Dependency on IMain Contract in IFurnace Contract's init Function
Audit: 2023-01-reserve-findings
Severity: HIGH

# Lines of code

https://github.com/reserve-protocol/protocol/blob/df7ecadc2bae74244ace5e8b39e94bc992903158/contracts/interfaces/IFurnace.sol#L13-L17


# Vulnerability details

## Impact
```
function init(
        IMain main_,
        uint48 period_,
        uint192 ratio_
    ) external;
```
The signature of the `init` function in the IFurnace contract. It takes in a `IMain` contract address as its first parameter, but it is not clear what the purpose or functionality of this contract is and whether it is properly initialized or checked for before being used in the `IFurnace` contract.

If the `IMain` contract is not properly initialized or is not in the expected state, it could cause the `IFurnace` contract to malfunction or behave unexpectedly.

If the `IMain` contract has any security vulnerabilities, it could be exploited by malicious actors to compromise the security of the `IFurnace` contract.

If the `IMain` contract is not designed to be used in conjunction with the `IFurnace` contract, it could cause the `IFurnace` contract to malfunction or behave unexpectedly.

If the `IMain` contract is not updated or maintained, it could cause the `IFurnace` contract to malfunction or behave unexpectedly.


## Proof of Concept

creating an implementation of the `IFurnace` contract and calling its `init` function with a malicious `IMain` contract address.

```
pragma solidity 0.8.9;

import "./IFurnace.sol";

contract MyFurnace is IFurnace {
    IMain main;
    uint48 period;
    uint192 ratio;

    function init(IMain main_, uint48 period_, uint192 ratio_) external {
        main = main_;
        period = period_;
        ratio = ratio_;
    }

    function period() external view returns (uint48) {
        return period;
    }

    function setPeriod(uint48 newPeriod) external {
        period = newPeriod;
        emit PeriodSet(period, newPeriod);
    }

    function ratio() external view returns (uint192) {
        return ratio;
    }

    function setRatio(uint192 newRatio) external {
        ratio = newRatio;
        emit RatioSet(ratio, newRatio);
    }

    function melt() external {
        // Code to perform melting of RTokens goes here
    }
}
```

`IFurnace` contract that stores the `IMain` contract address, period, and ratio as state variables and implements the functions as specified in the interface.

Then create a malicious `IMain` contract that has a function that allows an attacker to steal Ether from the `MyFurnace` contract.

```
contract MaliciousMain {
    function stealEther() external {
        msg.sender.transfer(address(this).balance);
    }
}
```

Then by creating an instance of `MaliciousMain` contract and passing its address to the `init` function of the `MyFurnace` contract, an attacker can steal Ether from the `MyFurnace` contract, since the `MaliciousMain` contract can be called from `MyFurnace` contract without any check.

```
MyFurnace myFurnace = new MyFurnace();
MaliciousMain maliciousMain = new MaliciousMain();

my
BackingManager._manageTokens will fail in case when `stRSR.seizeRSR` is called with amount that is bigger than it's RSR balance
Audit: 2023-01-reserve-findings
Severity: HIGH

# Lines of code

https://github.com/reserve-protocol/protocol/blob/df7ecadc2bae74244ace5e8b39e94bc992903158/contracts/p1/BackingManager.sol#L141


# Vulnerability details

## Impact
BackingManager._manageTokens will fail in case when `stRSR.seizeRSR` is called with amount that is bigger than it's RSR balance

## Proof of Concept
In case when BackingManager is not fully collateralized he can trade RSR tokens in order to receive needed capital.
In this case if it holds enough RSR balance it will use it to make trade, otherwise it will [use StSRS contract](https://github.com/reserve-protocol/protocol/blob/df7ecadc2bae74244ace5e8b39e94bc992903158/contracts/p1/BackingManager.sol#L141) to use stakers RSR funds.

The amount that is needed to seize is calculated as `req.sellAmount - bal`. However in case if this amount is bigger than `SRS.balnceOf(StSRS)`, then `seizeRSR` function [will revert](https://github.com/reserve-protocol/protocol/blob/df7ecadc2bae74244ace5e8b39e94bc992903158/contracts/p1/StRSR.sol#L380). And as result BackingManager will not be able to make recollateralization, which can be crucial for the RToken system. 

## Tools Used
VsCode
## Recommended Mitigation Steps
In case if `SRS.balnceOf(StSRS) < req.sellAmount - bal` then provide `SRS.balnceOf(StSRS)` as `seizeRSR` function input.