msc-smart-contract-auditing/vulnerable-functions-base huggingface dataset:
Vulnerability: It is possible to frontrun liquidations with self liquidation with high strain value to clear warning and keep unhealthy positions from liquidation
Severity: high

Description:
`Borrower.warn` sets the time when the liquidation (involving swap) can happen:\\n```\\nslot0 = slot0_ | ((block.timestamp + LIQUIDATION_GRACE_PERIOD) << 208);\\n```\\n\\nBut `Borrower.liquidation` clears the warning regardless of whether account is healthy or not after the repayment:\\n```\\n_repay(repayable0, repayable1);\\nslot0 = (slot0_ & SLOT0_MASK_POSITIONS) | SLOT0_DIRT;\\n```\\n\\nThe scenario above is demonstrated in the test, add this to Liquidator.t.sol:\\n```\\nfunction test_liquidationFrontrun() public {\\n    uint256 margin0 = 1595e18;\\n    uint256 margin1 = 0;\\n    uint256 borrows0 = 0;\\n    uint256 borrows1 = 1e18 * 100;\\n\\n    // Extra due to rounding up in liabilities\\n    margin0 += 1;\\n\\n    deal(address(asset0), address(account), margin0);\\n    deal(address(asset1), address(account), margin1);\\n\\n    bytes memory data = abi.encode(Action.BORROW, borrows0, borrows1);\\n    account.modify(this, data, (1 << 32));\\n\\n    assertEq(lender0.borrowBalance(address(account)), borrows0);\\n    assertEq(lender1.borrowBalance(address(account)), borrows1);\\n    assertEq(asset0.balanceOf(address(account)), borrows0 + margin0);\\n    assertEq(asset1.balanceOf(address(account)), borrows1 + margin1);\\n\\n    _setInterest(lender0, 10100);\\n    _setInterest(lender1, 10100);\\n\\n    account.warn((1 << 32));\\n\\n    uint40 unleashLiquidationTime = uint40((account.slot0()  208) % (1 << 40));\\n    assertEq(unleashLiquidationTime, block.timestamp + LIQUIDATION_GRACE_PERIOD);\\n\\n    skip(LIQUIDATION_GRACE_PERIOD + 1);\\n\\n    // listen for liquidation, or be the 1st in the block when warning is cleared\\n    // liquidate with very high strain, basically keeping the position, but clearing the warning\\n    account.liquidate(this, bytes(""), 1e10, (1 << 32));\\n\\n    unleashLiquidationTime = uint40((account.slot0()  208) % (1 << 40));\\n    assertEq(unleashLiquidationTime, 0);\\n\\n    // the liquidation command we've frontrun will now revert (due to warning not set: "Aloe: grace")\\n    vm.expectRevert();\\n    account.liquidate(this, bytes(""), 1, (1 << 32));\\n}\\n```\\n

Impact:
Very important protocol function (liquidation) can be DOS'ed and make the unhealthy accounts avoid liquidations for a very long time. Malicious users can thus open huge risky positions which will then go into bad debt causing loss of funds for all protocol users as they will not be able to withdraw their funds and can cause a bank run - first users will be able to withdraw, but later users won't be able to withdraw as protocol won't have enough funds for this.

Recommended mitigation:
Consider clearing "warn" status only if account is healthy after liquidation.
Vulnerability: IV Can be Decreased for Free
Severity: high

Description:
The liquidity at a single `tickSpacing` is used to calcualte the `IV`. The more liquidity is in this tick spacing, the lower the `IV`, as demonstarated by the `tickTvl` dividing the return value of the `estimate` function:\\n```\\n            return SoladyMath.sqrt((4e24 * volumeGamma0Gamma1 * scale) / (b.timestamp - a.timestamp) / tickTvl);\\n```\\n\\nSince this is using data only from the block that the function is called, the liuquidyt can easily be increased by:\\ndepositing a large amount liquidity into the `tickSpacing`\\ncalling update\\nremoving the liquidity\\nNote that only a small portion of the total liquidity is in the entire pool is in the active liquidity tick. Therefore, the capital cost required to massively increase the liquidity is low. Additionally, the manipulation has zero cost (aside from gas fees), as no trading is done through the pool. Contract this with a pool price manipulation, which costs a significant amount of trading fees to trade through a large amount of the liquidity of the pool.\\nSince this manipulation costs nothing except gas, the `IV_CHANGE_PER_UPDATE` which limits of the amount that IV can be manipulated per update does not sufficiently disincentivise manipulation, it just extends the time period required to manipulate.\\nDecreasing the IV increases the LTV, and due to the free cost, its reasonable to increase the LTV to the max LTV of 90% even for very volatile assets. Aloe uses the IV to estimate the probability of insolvency of loans. With the delay inherent in TWAP oracle and the liquidation delay by the warn-then-liquidate process, this manipulation can turn price change based insolvency from a 5 sigma event (as designed by the protocol) to a likely event.

Impact:
Decreasing IV can be done at zero cost aside from gas fees.\\nThis can be used to borrow assets at far more leverage than the proper LTV\\nBorrowers can use this to avoid liquidation\\nThis also breaks the insolvency estimation based on IV for riskiness of price-change caused insolvency.

Recommended mitigation:
Use the time weighted average liquidity of in-range ticks of the recent past, so that single block + single tickSpacing liquidity deposits cannot manipulate IV significantly.
Vulnerability: governor can permanently prevent withdrawals in spite of being restricted
Severity: medium

Description:
Quoting from the Contest README:\\n```\\nIs the admin/owner of the protocol/contracts TRUSTED or RESTRICTED?\\n\\nRestricted. The governor address should not be able to steal funds or prevent users from withdrawing. It does have access to the govern methods in Factory, and it could trigger liquidations by increasing nSigma. We consider this an acceptable risk, and the governor itself will have a timelock.\\n```\\n\\nThe mechanism by which users are ensured that they can withdraw their funds is the interest rate which increases with utilization.\\nMarket forces will keep the utilization in balance such that when users want to withdraw their funds from the `Lender` contracts, the interest rate increases and Borrowers pay back their loans (or get liquidated).\\nWhat the `governor` is allowed to do is to set a interest rate model via the `Factory.governMarketConfig` function.\\nThis clearly shows that the `governor` should be very much restricted in setting the `RateModel` such as to not damage users of the protocol which is in line with how the `governor` role is described in the README.\\nHowever the interest rate can be set to zero even if the utilization is very high. If Borrowers can borrow funds for a zero interest rate, they will never pay back their loans. This means that users in the Lenders will never be able to withdraw their funds.\\nIt is also noteworthy that the timelock that the governor uses won't be able to prevent this scenario since even if users withdraw their funds as quickly as possible, there will probably not be enough time / availability of funds for everyone to withdraw in time (assuming a realistic timelock length).

Impact:
The `governor` is able to permanently prevent withdrawals from the Lenders which it should not be able to do according to the contest README.

Recommended mitigation:
The `SafeRateLib` should ensure that as the utilization approaches `1e18` (100%), the interest rate cannot be below a certain minimum value.\\nThis ensures that even if the `governor` behaves maliciously or uses a broken `RateModel`, Borrowers will never borrow all funds without paying them back.
Vulnerability: Oracle.sol: observe function has overflow risk and should cast to uint256 like Uniswap V3 does
Severity: medium

Description:
Looking at the `Oracle.observe` function, the `secondsPerLiquidityCumulativeX128` return value is calculated as follows:\\n```\\nliqCumL + uint160(((liqCumR - liqCumL) * delta) / denom)\\n```\\n\\nThe calculation is done in an `unchecked` block. `liqCumR` and `liqCumL` have type `uint160`.\\n`delta` and `denom` have type `uint56`.\\nLet's compare this to the Uniswap V3 code.\\n```\\nbeforeOrAt.secondsPerLiquidityCumulativeX128 +\\n    uint160(\\n        (uint256(\\n            atOrAfter.secondsPerLiquidityCumulativeX128 - beforeOrAt.secondsPerLiquidityCumulativeX128\\n        ) * targetDelta) / observationTimeDelta\\n    )\\n```\\n\\nThe result of `atOrAfter.secondsPerLiquidityCumulativeX128 - beforeOrAt.secondsPerLiquidityCumulativeX128` is cast to `uint256`.\\nThat's because multiplying the result by `targetDelta` can overflow the `uint160` type.\\nThe maximum value of `uint160` is roughly `1.5e48`.\\n`delta` is simply the time difference between `timeL` and `target` in seconds.\\n```\\nsecondsPerLiquidityCumulativeX128: last.secondsPerLiquidityCumulativeX128 +\\n    ((uint160(delta) << 128) / (liquidity > 0 ? liquidity : 1)),\\n```\\n\\nIf `liquidity` is very low and the time difference between observations is very big (hours to days), this can lead to the intermediate overflow in the `Oracle` library, such that the `secondsPerLiquidityCumulative` is much smaller than it should be.\\nThe lowest value for the above division is `1`. In that case the accumulator grows by `2^128` (~3.4e38) every second.\\nIf observations are apart 24 hours (86400 seconds), this can lead to an overflow: Assume for simplicity `target - timeL = timeR - timeL`\\n```\\n(liqCumR - liqCumL) * delta = 3.4e38 * 86400 * 86400 > 1.5e48`\\n```\\n

Impact:
The corrupted return value affects the `Volatility` library. Specifically, the IV calculation.\\nThis can lead to wrong IV updates and LTV ratios that do not reflect the true IV, making the application more prone to bad debt or reducing capital efficiency.

Recommended mitigation:
Perform the same cast to `uint256` that Uniswap V3 performs:\\n```\\nliqCumL + uint160((uint256(liqCumR - liqCumL) * delta) / denom)\\n```\\n
Vulnerability: The whole ante balance of a user with a very small loan, who is up for liquidation can be stolen without repaying the debt
Severity: medium

Description:
Users face liquidation risk when their Borrower contract's collateral falls short of covering their loan. The `strain` parameter in the liquidation process enables liquidators to partially repay an unhealthy loan. Using a `strain` smaller than 1 results in the liquidator receiving a fraction of the user's collateral based on `collateral / strain`.\\nThe problem arises from the fact that the `strain` value is not capped, allowing for a potentially harmful scenario. For instance, a user with an unhealthy loan worth $0.30 in a WBTC (8-decimal token) vault on Arbitrum (with very low gas costs) has $50 worth of ETH (with a price of $1500) as collateral in their Borrower contract. A malicious liquidator spots the unhealthy loan and submits a liquidation transaction with a `strain` value of 1e3 + 1. Since the `strain` exceeds the loan value, the liquidator's repayment amount gets rounded down to 0, effectively allowing them to claim the collateral with only the cost of gas.\\n```\\nassembly ("memory-safe") {\\n // // rest of code\\n liabilities0 := div(liabilities0, strain) // @audit rounds down to 0 <-\\n liabilities1 := div(liabilities1, strain) // @audit rounds down to 0 <-\\n // // rest of code\\n}\\n```\\n\\nFollowing this, the execution bypasses the `shouldSwap` if-case and proceeds directly to the following lines:\\n```\\n// @audit Won't be repaid in full since the loan is insolvent\\n_repay(repayable0, repayable1);\\nslot0 = (slot0_ & SLOT0_MASK_POSITIONS) | SLOT0_DIRT;\\n\\n// @audit will transfer the user 2e14 (0.5$)\\npayable(callee).transfer(address(this).balance / strain);\\nemit Liquidate(repayable0, repayable1, incentive1, priceX128);\\n```\\n\\nGiven the low gas price on Arbitrum, this transaction becomes profitable for the malicious liquidator, who can repeat it to drain the user's collateral without repaying the loan. This not only depletes the user's collateral but also leaves a small amount of bad debt on the market, potentially causing accounting issues for the vaults.

Impact:
Users with small loans face the theft of their collateral without the bad debt being covered, leading to financial losses for the user. Additionally, this results in a potential amount of bad debt that can disrupt the vault's accounting.

Recommended mitigation:
Consider implementing a check to determine whether the repayment impact is zero or not before transferring ETH to such liquidators.\\n```\\nrequire(repayable0 != 0 || repayable1 != 0, "Zero repayment impact.") // @audit <-\\n_repay(repayable0, repayable1);\\n\\nslot0 = (slot0_ & SLOT0_MASK_POSITIONS) | SLOT0_DIRT;\\n\\npayable(callee).transfer(address(this).balance / strain);\\nemit Liquidate(repayable0, repayable1, incentive1, priceX128);\\n```\\n\\nAdditionally, contemplate setting a cap for the `strain` and potentially denoting it in basis points (BPS) instead of a fraction. This allows for more flexibility when users intend to repay a percentage lower than 100% but higher than 50% (e.g., 60%, 75%, etc.).
Vulnerability: Creditor can maliciously burn UniV3 position to permanently lock funds
Severity: high

Description:
NonfungiblePositionManager\\n```\\nfunction ownerOf(uint256 tokenId) public view virtual override returns (address) {\\n    return _tokenOwners.get(tokenId, "ERC721: owner query for nonexistent token");\\n}\\n```\\n\\nWhen querying a nonexistent token, ownerOf will revert. Now assuming the NFT is burnt we can see how every method for repayment is now lost.\\nLiquidityManager.sol#L306-L308\\n```\\n        address creditor = underlyingPositionManager.ownerOf(loan.tokenId);\\n        // Increase liquidity and transfer liquidity owner reward\\n        _increaseLiquidity(cache.saleToken, cache.holdToken, loan, amount0, amount1);\\n```\\n\\nIf the user is being liquidated or repaying themselves the above lines are called for each loan. This causes all calls of this nature to revert.\\nLiquidityBorrowingManager.sol#L727-L732\\n```\\n    for (uint256 i; i < loans.length; ) {\\n        LoanInfo memory loan = loans[i];\\n        // Get the owner address of the loan's token ID using the underlyingPositionManager contract.\\n        address creditor = underlyingPositionManager.ownerOf(loan.tokenId);\\n        // Check if the owner of the loan's token ID is equal to the `msg.sender`.\\n        if (creditor == msg.sender) {\\n```\\n\\nThe only other option to recover funds would be for each of the other lenders to call for an emergency withdrawal. The problem is that this pathway will also always revert. It cycles through each loan causing it to query ownerOf() for each token. As we know this reverts. The final result is that once this happens, there is no way possible to close the position.

Impact:
Creditor can maliciously lock all funds

Recommended mitigation:
I would recommend storing each initial creditor when a loan is opened. Add try-catch blocks to each `ownerOf()` call. If the call reverts then use the initial creditor, otherwise use the current owner.
Vulnerability: Adversary can overwrite function selector in _patchAmountAndCall due to inline assembly lack of overflow protection
Severity: medium

Description:
`The use of YUL or inline assembly in a solidity smart contract also makes integer overflow/ underflow possible even if the compiler version of solidity is 0.8. In YUL programming language, integer underflow & overflow is possible in the same way as Solidity and it does not check automatically for it as YUL is a low-level language that is mostly used for making the code more optimized, which does this by omitting many opcodes. Because of its low-level nature, YUL does not perform many security checks therefore it is recommended to use as little of it as possible in your smart contracts.`\\nSource\\nInline assembly lacks overflow/underflow protections, which opens the possibility of this exploit.\\nExternalCall.sol#L27-L38\\n```\\n        if gt(swapAmountInDataValue, 0) {\\n            mstore(add(add(ptr, 0x24), mul(swapAmountInDataIndex, 0x20)), swapAmountInDataValue)\\n        }\\n        success := call(\\n            maxGas,\\n            target,\\n            0, //value\\n            ptr, //Inputs are stored at location ptr\\n            data.length,\\n            0,\\n            0\\n        )\\n```\\n\\nIn the code above we see that `swapAmountInDataValue` is stored at `ptr + 36 (0x24) + swapAmountInDataIndex * 32 (0x20)`. The addition of 36 (0x24) in this scenario should prevent the function selector from being overwritten because of the extra 4 bytes (using 36 instead of 32). This is not the case though because `mul(swapAmountInDataIndex, 0x20)` can overflow since it is a uint256. This allows the attacker to target any part of the memory they choose by selectively overflowing to make it write to the desired position.\\nAs shown above, overwriting the function selector is possible although most of the time this value would be a complete nonsense since swapAmountInDataValue is calculated elsewhere and isn't user supplied. This also has a work around. By creating their own token and adding it as LP to a UniV3 pool, swapAmountInDataValue can be carefully manipulated to any value. This allows the attacker to selectively overwrite the function selector with any value they chose. This bypasses function selectors restrictions and opens calls to dangerous functions.

Impact:
Attacker can bypass function restrictions and call dangerous/unintended functions

Recommended mitigation:
Limit `swapAmountInDataIndex` to a reasonable value such as uint128.max, preventing any overflow.
Vulnerability: Blacklisted creditor can block all repayment besides emergency closure
Severity: medium

Description:
```\\n        address creditor = underlyingPositionManager.ownerOf(loan.tokenId);\\n        // Increase liquidity and transfer liquidity owner reward\\n        _increaseLiquidity(cache.saleToken, cache.holdToken, loan, amount0, amount1);\\n        uint256 liquidityOwnerReward = FullMath.mulDiv(\\n            params.totalfeesOwed,\\n            cache.holdTokenDebt,\\n            params.totalBorrowedAmount\\n        ) / Constants.COLLATERAL_BALANCE_PRECISION;\\n\\n        Vault(VAULT_ADDRESS).transferToken(cache.holdToken, creditor, liquidityOwnerReward);\\n```\\n\\nThe following code is executed for each loan when attempting to repay. Here we see that each creditor is directly transferred their tokens from the vault. If the creditor is blacklisted for holdToken, then the transfer will revert. This will cause all repayments to revert, preventing the user from ever repaying their loan and forcing them to default.

Impact:
Borrowers with blacklisted creditors are forced to default

Recommended mitigation:
Create an escrow to hold funds in the event that the creditor cannot receive their funds. Implement a try-catch block around the transfer to the creditor. If it fails then send the funds instead to an escrow account, allowing the creditor to claim their tokens later and for the transaction to complete.
Vulnerability: Incorrect calculations of borrowingCollateral leads to DoS for positions in the current tick range due to underflow
Severity: medium

Description:
This calculation is most likely to underflow\\n```\\nuint256 borrowingCollateral = cache.borrowedAmount - cache.holdTokenBalance;\\n```\\n\\nThe `cache.borrowedAmount` is the calculated amount of holdTokens based on the liquidity of a position. `cache.holdTokenBalance` is the balance of holdTokens queried after liquidity extraction and tokens transferred to the `LiquidityBorrowingManager`. If any amounts of the saleToken are transferred as well, these are swapped to holdTokens and added to `cache.holdTokenBalance`.\\nSo in case when liquidity of a position is in the current tick range, both tokens would be transferred to the contract and saleToken would be swapped for holdToken and then added to `cache.holdTokenBalance`. This would make `cache.holdTokenBalance > cache.borrowedAmount` since `cache.holdTokenBalance == cache.borrowedAmount + amount of sale token swapped` and would make the tx revert due to underflow.

Impact:
Many positions would be unavailable to borrowers. For non-volatile positions like that which provide liquidity to stablecoin pools the DoS could last for very long period. For volatile positions that provide liquidity in a wide range this could also be for more than 1 year.

Recommended mitigation:
The borrowedAmount should be subtracted from holdTokenBalance\\n```\\nuint256 borrowingCollateral = cache.holdTokenBalance - cache.borrowedAmount;\\n```\\n
Vulnerability: Invalid oracle version can cause the vault to open too large and risky position and get liquidated due to using unadjusted global current position
Severity: medium

Description:
`StrategyLib._loadContext` for the market loads `currentPosition` as:\\n```\\ncontext.currentPosition = registration.market.pendingPosition(global.currentId);\\n```\\n\\nHowever, this is unadjusted position, so its value is incorrect if invalid oracle version happens while this position is pending.\\nLater on, when calculating minimum and maxmium positions enforced by the vault in the market, they're calculated in _positionLimit:\\n```\\nfunction _positionLimit(MarketContext memory context) private pure returns (UFixed6, UFixed6) {\\n    return (\\n        // minimum position size before crossing the net position\\n        context.currentAccountPosition.maker.sub(\\n            context.currentPosition.maker\\n                .sub(context.currentPosition.net().min(context.currentPosition.maker))\\n                .min(context.currentAccountPosition.maker)\\n                .min(context.closable)\\n        ),\\n        // maximum position size before crossing the maker limit\\n        context.currentAccountPosition.maker.add(\\n            context.riskParameter.makerLimit\\n                .sub(context.currentPosition.maker.min(context.riskParameter.makerLimit))\\n        )\\n    );\\n}\\n```\\n\\nAnd the target maker size for the market is set in allocate:\\n```\\n(targets[marketId].collateral, targets[marketId].position) = (\\n    Fixed6Lib.from(_locals.marketCollateral).sub(contexts[marketId].local.collateral),\\n    _locals.marketAssets\\n        .muldiv(registrations[marketId].leverage, contexts[marketId].latestPrice.abs())\\n        .min(_locals.maxPosition)\\n        .max(_locals.minPosition)\\n);\\n```\\n\\nSince `context.currentPosition` is incorrect, it can happen that both `_locals.minPosition` and `_locals.maxPosition` are too high, the vault will open too large and risky position, breaking its risk limit and possibly getting liquidated, especially if it happens during high volatility.

Impact:
If invalid oracle version happens, the vault might open too large and risky position in such market, potentially getting liquidated and vault users losing funds due to this liquidation.

Recommended mitigation:
Adjust global current position after loading it:\\n```\\n    context.currentPosition = registration.market.pendingPosition(global.currentId);\\n+   context.currentPosition.adjust(registration.market.position());\\n```\\n