msc-smart-contract-auditing/vulnerable-functions-base huggingface dataset:
Vulnerability: By manipulating the gas limit, relayers can affect the outcome of ZeroExTransactions  Won't Fix
Severity: low

Description:
ZeroExTransactions are meta transactions supported by the `Exchange`. They do not require that they are executed with a specific amount of gas, so the transaction relayer can choose how much gas to provide. By choosing a low gas limit, a relayer can affect the outcome of the transaction.\\nA `ZeroExTransaction` specifies a signer, an expiration, and call data for the transaction:\\n```\\nstruct ZeroExTransaction {\\n    uint256 salt;                   // Arbitrary number to ensure uniqueness of transaction hash.\\n    uint256 expirationTimeSeconds;  // Timestamp in seconds at which transaction expires.\\n    uint256 gasPrice;               // gasPrice that transaction is required to be executed with.\\n    address signerAddress;          // Address of transaction signer.\\n    bytes data;                     // AbiV2 encoded calldata.\\n}\\n```\\n\\nIn `MixinTransactions._executeTransaction()`, all available gas is forwarded in the delegate call, and the transaction is marked as executed:\\n```\\ntransactionsExecuted[transactionHash] = true;\\n(bool didSucceed, bytes memory returnData) = address(this).delegatecall(transaction.data);\\n```\\n\\nA likely attack vector for this is front running a `ZeroExTransaction` that ultimately invokes `_fillNoThrow()`. In this scenario, an attacker sees the call to `executeTransaction()` and makes their own call with a lower gas limit, causing the order being filled to run out of gas but allowing the transaction as a whole to succeed.\\nIf such an attack is successful, the `ZeroExTransaction` cannot be replayed, so the signer must produce a new signature and try again, ad infinitum.

Impact:

Recommended mitigation:
Resolution\\nFrom the development team:\\nWhile this is an annoyance when used in combination with `marketBuyOrdersNoThrow` and `marketSellOrdersNoThrow`, it does not seem worth it to add a `gasLimit` to 0x transactions for this reason alone. Instead, this quirk should be documented along with a recommendation to use the `fillOrKill` variants of each market fill function when used in combination with 0x transactions.\\nAdd a `gasLimit` field to `ZeroExTransaction` and forward exactly that much gas via `delegatecall`. (Note that you must explicitly check that sufficient gas is available because the EVM allows you to supply a gas parameter that exceeds the actual remaining gas.)
Vulnerability: Modifier ordering plays a significant role in modifier efficacy
Severity: low

Description:
The `nonReentrant` and `refundFinalBalance` modifiers always appear together across the 0x monorepo. When used, they invariably appear with `nonReentrant` listed first, followed by `refundFinalBalance`. This specific order appears inconsequential at first glance but is actually important. The order of execution is as follows:\\nThe `nonReentrant` modifier runs (_lockMutexOrThrowIfAlreadyLocked).\\nIf `refundFinalBalance` had a prefix, it would run now.\\nThe function itself runs.\\nThe `refundFinalBalance` modifier runs (_refundNonZeroBalanceIfEnabled).\\nThe `nonReentrant` modifier runs (_unlockMutex).\\nThe fact that the `refundFinalBalance` modifier runs before the mutex is unlocked is of particular importance because it potentially invokes an external call, which may reenter. If the order of the two modifiers were flipped, the mutex would unlock before the external call, defeating the purpose of the reentrancy guard.\\n```\\nnonReentrant\\nrefundFinalBalance\\n```\\n

Impact:

Recommended mitigation:
Resolution\\nThis is fixed in 0xProject/0x-monorepo#2228 by introducing a new modifier that combines the two: `refundFinalBalance`.\\nAlthough the order of the modifiers is correct as-is, this pattern introduces cognitive overhead when making or reviewing changes to the 0x codebase. Because the two modifiers always appear together, it may make sense to combine the two into a single modifier where the order of operations is explicit.
Vulnerability: Several overflows in LibBytes
Severity: low

Description:
Several functions in `LibBytes` have integer overflows.\\n`LibBytes.readBytesWithLength` returns a pointer to a `bytes` array within an existing `bytes` array at some given `index`. The length of the nested array is added to the given `index` and checked against the parent array to ensure the data in the nested array is within the bounds of the parent. However, because the addition can overflow, the bounds check can be bypassed to return an array that points to data out of bounds of the parent array.\\n```\\nif (b.length < index + nestedBytesLength) {\\n    LibRichErrors.rrevert(LibBytesRichErrors.InvalidByteOperationError(\\n        LibBytesRichErrors\\n            .InvalidByteOperationErrorCodes.LengthGreaterThanOrEqualsNestedBytesLengthRequired,\\n        b.length,\\n        index + nestedBytesLength\\n    ));\\n}\\n```\\n\\nThe following functions have similar issues:\\n`readAddress`\\n`writeAddress`\\n`readBytes32`\\n`writeBytes32`\\n`readBytes4`

Impact:

Recommended mitigation:
An overflow check should be added to the function. Alternatively, because `readBytesWithLength` does not appear to be used anywhere in the 0x project, the function should be removed from `LibBytes`. Additionally, the following functions in `LibBytes` are also not used and should be considered for removal:\\n`popLast20Bytes`\\n`writeAddress`\\n`writeBytes32`\\n`writeUint256`\\n`writeBytesWithLength`\\n`deepCopyBytes`
Vulnerability: NSignatureTypes enum value bypasses Solidity safety checks  Won't Fix
Severity: low

Description:
The `ISignatureValidator` contract defines an enum `SignatureType` to represent the different types of signatures recognized within the exchange. The final enum value, `NSignatureTypes`, is not a valid signature type. Instead, it is used by `MixinSignatureValidator` to check that the value read from the signature is a valid enum value. However, Solidity now includes its own check for enum casting, and casting a value over the maximum enum size to an enum is no longer possible.\\nBecause of the added `NSignatureTypes` value, Solidity's check now recognizes `0x08` as a valid `SignatureType` value.\\nThe check is made here:\\n```\\n// Ensure signature is supported\\nif (uint8(signatureType) >= uint8(SignatureType.NSignatureTypes)) {\\n    LibRichErrors.rrevert(LibExchangeRichErrors.SignatureError(\\n        LibExchangeRichErrors.SignatureErrorCodes.UNSUPPORTED,\\n        hash,\\n        signerAddress,\\n        signature\\n    ));\\n}\\n```\\n

Impact:

Recommended mitigation:
The check should be removed, as should the `SignatureTypes.NSignatureTypes` value.
Vulnerability: Intentional secret reuse can block borrower and lender from accepting liquidation payment
Severity: high

Description:
For Dave (the liquidator) to claim the collateral he's purchasing, he must reveal secret D. Once that secret is revealed, Alice and Bob (the borrower and lender) can claim the payment.\\nSecrets must be provided via the `Sales.provideSecret()` function:\\n```\\n function provideSecret(bytes32 sale, bytes32 secret\\_) external {\\n  require(sales[sale].set);\\n  if      (sha256(abi.encodePacked(secret\\_)) == secretHashes[sale].secretHashA) { secretHashes[sale].secretA = secret\\_; }\\n        else if (sha256(abi.encodePacked(secret\\_)) == secretHashes[sale].secretHashB) { secretHashes[sale].secretB = secret\\_; }\\n        else if (sha256(abi.encodePacked(secret\\_)) == secretHashes[sale].secretHashC) { secretHashes[sale].secretC = secret\\_; }\\n        else if (sha256(abi.encodePacked(secret\\_)) == secretHashes[sale].secretHashD) { secretHashes[sale].secretD = secret\\_; }\\n        else                                                                          { revert(); }\\n }\\n```\\n\\nNote that if Dave chooses the same secret hash as either Alice, Bob, or Charlie (arbiter), there is no way to set `secretHashes[sale].secretD` because one of the earlier conditionals will execute.\\nFor Alice and Bob to later receive payment, they must be able to provide Dave's secret:\\n```\\n function accept(bytes32 sale) external {\\n        require(!accepted(sale));\\n        require(!off(sale));\\n  require(hasSecrets(sale));\\n  require(sha256(abi.encodePacked(secretHashes[sale].secretD)) == secretHashes[sale].secretHashD);\\n```\\n\\nDave can exploit this to obtain the collateral for free:\\nDave looks at Alice's secret hashes to see which will be used in the sale.\\nDave begins the liquidation process, using the same secret hash.\\nAlice and Bob reveal their secrets A and B through the process of moving the collateral.\\nDave now knows the preimage for the secret hash he provided. It was revealed by Alice already.\\nDave uses that secret to obtain the collateral.\\nAlice and Bob now want to receive payment, but they're unable to provide Dave's secret to the `Sales` smart contract due to the order of conditionals in `provideSecret()`.\\nAfter an expiration, Dave can claim a refund.\\nMitigating factors\\nAlice and Bob could notice that Dave chose a duplicate secret hash and refuse to proceed with the sale. This is not something they are likely to do.

Impact:

Recommended mitigation:
Either change the way `provideSecret()` works to allow for duplicate secret hashes or reject duplicate hashes in `create()`.
Vulnerability: There is no way to convert between custom and non-custom funds  Won't Fix
Severity: medium

Description:
Each fund is created using either `Funds.create()` or `Funds.createCustom()`. Both enforce a limitation that there can only be one fund per account:\\n```\\nfunction create(\\n    uint256  maxLoanDur\\_,\\n    uint256  maxFundDur\\_,\\n    address  arbiter\\_,\\n    bool     compoundEnabled\\_,\\n    uint256  amount\\_\\n) external returns (bytes32 fund) {\\n    require(fundOwner[msg.sender].lender != msg.sender || msg.sender == deployer); // Only allow one loan fund per address\\n```\\n\\n```\\nfunction createCustom(\\n    uint256  minLoanAmt\\_,\\n    uint256  maxLoanAmt\\_,\\n    uint256  minLoanDur\\_,\\n    uint256  maxLoanDur\\_,\\n    uint256  maxFundDur\\_,\\n    uint256  liquidationRatio\\_,\\n    uint256  interest\\_,\\n    uint256  penalty\\_,\\n    uint256  fee\\_,\\n    address  arbiter\\_,\\n    bool     compoundEnabled\\_,\\n    uint256  amount\\_\\n) external returns (bytes32 fund) {\\n    require(fundOwner[msg.sender].lender != msg.sender || msg.sender == deployer); // Only allow one loan fund per address\\n```\\n\\nThese functions are the only place where `bools[fund].custom` is set, and there's no way to delete a fund once it exists. This means there's no way for a given account to switch between a custom and non-custom fund.\\nThis could be a problem if, for example, the default parameters change in a way that a user finds unappealing. They may want to switch to using a custom fund but find themselves unable to do so without moving to a new Ethereum account.

Impact:

Recommended mitigation:
Either allow funds to be deleted or allow funds to be switched between custom and non-custom.
Vulnerability: Funds.maxFundDur has no effect if maxLoanDur is set
Severity: medium

Description:
`Funds.maxFundDur` specifies the maximum amount of time a fund should be active. It's checked in `request()` to ensure the duration of the loan won't exceed that time, but the check is skipped if `maxLoanDur` is set:\\n```\\nif (maxLoanDur(fund) > 0) {\\n    require(loanDur\\_       <= maxLoanDur(fund));\\n} else {\\n    require(now + loanDur\\_ <= maxFundDur(fund));\\n}\\n```\\n\\nIf a user sets `maxLoanDur` (the maximum loan duration) to 1 week and sets the `maxFundDur` (timestamp when all loans should be complete) to December 1st, then there can actually be a loan that ends on December 7th.

Impact:

Recommended mitigation:
Check against `maxFundDur` even when `maxLoanDur` is set.
Vulnerability: Funds.update() lets users update fields that may not have any effect
Severity: low

Description:
`Funds.update()` allows users to update the following fields which are only used if `bools[fund].custom` is set:\\n`minLoanamt`\\n`maxLoanAmt`\\n`minLoanDur`\\n`interest`\\n`penalty`\\n`fee`\\n`liquidationRatio`\\nIf `bools[fund].custom` is not set, then these changes have no effect. This may be misleading to users.\\n```\\nfunction update(\\n    bytes32  fund,\\n    uint256  minLoanAmt\\_,\\n    uint256  maxLoanAmt\\_,\\n    uint256  minLoanDur\\_,\\n    uint256  maxLoanDur\\_,\\n    uint256  maxFundDur\\_,\\n    uint256  interest\\_,\\n    uint256  penalty\\_,\\n    uint256  fee\\_,\\n    uint256  liquidationRatio\\_,\\n    address  arbiter\\_\\n) external {\\n    require(msg.sender == lender(fund));\\n    funds[fund].minLoanAmt       = minLoanAmt\\_;\\n    funds[fund].maxLoanAmt       = maxLoanAmt\\_;\\n    funds[fund].minLoanDur       = minLoanDur\\_;\\n    funds[fund].maxLoanDur       = maxLoanDur\\_;\\n    funds[fund].maxFundDur       = maxFundDur\\_;\\n    funds[fund].interest         = interest\\_;\\n    funds[fund].penalty          = penalty\\_;\\n    funds[fund].fee              = fee\\_;\\n    funds[fund].liquidationRatio = liquidationRatio\\_;\\n    funds[fund].arbiter          = arbiter\\_;\\n}\\n```\\n

Impact:

Recommended mitigation:
Resolution\\nThis is fixed in AtomicLoans/atomicloans-eth-contracts#67.\\nThis could be addressed by creating two update functions: one for custom funds and one for non-custom funds. Only the update for custom funds would allow setting these values.
Vulnerability: Ingress.setContractAddress() can cause duplicate entries in contractKeys
Severity: medium

Description:
`setContractAddress()` checks `ContractDetails` existence by inspecting `contractAddress`. A `contractAddress` of `0` means that the contract does not already exist, and its name must be added to contractKeys:\\n```\\nfunction setContractAddress(bytes32 name, address addr) public returns (bool) {\\n    require(name > 0x0000000000000000000000000000000000000000000000000000000000000000, "Contract name must not be empty.");\\n    require(isAuthorized(msg.sender), "Not authorized to update contract registry.");\\n\\n    ContractDetails memory info = registry[name];\\n    // create info if it doesn't exist in the registry\\n    if (info.contractAddress == address(0)) {\\n        info = ContractDetails({\\n            owner: msg.sender,\\n            contractAddress: addr\\n        });\\n\\n        // Update registry indexing\\n        contractKeys.push(name);\\n   } else {\\n        info.contractAddress = addr;\\n   }\\n    // update record in the registry\\n    registry[name] = info;\\n\\n    emit RegistryUpdated(addr,name);\\n\\n    return true;\\n}\\n```\\n\\nIf, however, a contract is actually added with the address `0`, which is currently allowed in the code, then the contract does already exists, and adding the name to `contractKeys` again will result in a duplicate.\\nMitigation\\nAn admin can call `removeContract` repeatedly with the same name to remove multiple duplicate entries.

Impact:

Recommended mitigation:
Resolution\\nThis is fixed in PegaSysEng/[email protected]faff726.\\nEither disallow a contract address of `0` or check for existence via the `owner` field instead (which can never be 0).
Vulnerability: Use specific contract types instead of address where possible
Severity: low

Description:
For clarity and to get more out of the Solidity type checker, it's generally preferred to use a specific contract type for variables rather than the generic `address`.\\n`AccountRules.ingressContractAddress` could instead be `AccountRules.ingressContract` and use the type IngressContract:\\n```\\naddress private ingressContractAddress;\\n```\\n\\n```\\nAccountIngress ingressContract = AccountIngress(ingressContractAddress);\\n```\\n\\n```\\nconstructor (address ingressAddress) public {\\n```\\n\\nThis same pattern is found in NodeRules:\\n```\\naddress private nodeIngressContractAddress;\\n```\\n

Impact:

Recommended mitigation:
Where possible, use a specific contract type rather than `address`.
Vulnerability: Ingress should use a set
Severity: low

Description:
The `AdminList`, `AccountRulesList`, and `NodeRulesList` contracts have been recently rewritten to use a set. `Ingress` has the semantics of a set but has not been written the same way.\\nThis leads to some inefficiencies. In particular, `Ingress.removeContract` is an O(n) operation:\\n```\\nfor (uint i = 0; i < contractKeys.length; i++) {\\n    // Delete the key from the array + mapping if it is present\\n    if (contractKeys[i] == name) {\\n        delete registry[contractKeys[i]];\\n        contractKeys[i] = contractKeys[contractKeys.length - 1];\\n        delete contractKeys[contractKeys.length - 1];\\n        contractKeys.length--;\\n```\\n

Impact:

Recommended mitigation:
Use the same set implementation for Ingress: an array of `ContractDetails` and a mapping of names to indexes in that array.
Vulnerability: ContractDetails.owner is never read
Severity: low

Description:
The `ContractDetails` struct used by `Ingress` contracts has an `owner` field that is written to, but it is never read.\\n```\\nstruct ContractDetails {\\n    address owner;\\n    address contractAddress;\\n}\\n\\nmapping(bytes32 => ContractDetails) registry;\\n```\\n

Impact:

Recommended mitigation:
Resolution\\nThis is fixed in PegaSysEng/[email protected]d3f505e.\\nIf `owner` is not (yet) needed, the `ContractDetails` struct should be removed altogether and the type of `Ingress.registry` should change to `mapping(bytes32 => address)`
Vulnerability: [M-2] Failure in Maintaining Gauge Points
Severity: medium

Description:
The defaultGaugePointFunction in the smart contract does not explicitly handle the scenario where the percentage of the Base Deposited Value (BDV) equals the optimal percentage (optimalPercentDepositedBdv), resulting in an unintended reduction of gauge points to 0 instead of maintaining their current value.\\nThe testnew_GaugePointAdjustment() test demonstrated this flaw by providing inputs where currentGaugePoints = 1189, optimalPercentDepositedBdv = 64, and percentOfDepositedBdv = 64, expecting newGaugePoints to equal currentGaugePoints. However, the outcome was newGaugePoints = 0, indicating an unexpected reduction to zero.\\n```\\nfunction testnew_GaugePointAdjustment() public {\\n    uint256 currentGaugePoints = 1189; \\n    uint256 optimalPercentDepositedBdv = 64; \\n    uint256 percentOfDepositedBdv = 64; \\n\\n    uint256 newGaugePoints = gaugePointFacet.defaultGaugePointFunction(\\n        currentGaugePoints,\\n        optimalPercentDepositedBdv,\\n        percentOfDepositedBdv\\n    );\\n\\n    assertTrue(newGaugePoints <= MAX_GAUGE_POINTS, "New gauge points exceed the maximum allowed");\\n    assertEq(newGaugePoints, currentGaugePoints, "Gauge points adjustment does not match expected outcome");\\n}\\n```\\n

Impact:
This behavior can lead to an undesired decrease in incentives for contract participants, potentially affecting participation and reward accumulation within the contract's ecosystem. Users may lose gauge points and, consequently, rewards due to a technical flaw rather than their actions.

Recommended mitigation:
Implement Explicit Returns: Ensure the defaultGaugePointFunction has an explicit return for the case where gauge points should not be adjusted. This can be achieved by adding a final return statement that simply returns currentGaugePoints if neither condition for incrementing nor decrementing is met, as shown below:\\n```\\nelse {\\n    return currentGaugePoints; \\n}\\n```\\n
Vulnerability: Silo is not compatible with Fee-on-transfer or rebasing tokens
Severity: medium

Description:
According to the documentation there are certain conditions that need to be met for a token to be whitelisted:\\n```\\nAdditional tokens may be added to the Deposit Whitelist via Beanstalk governance. In order for a token to be added to the Deposit Whitelist, Beanstalk requires:\\n1. The token address;\\n2. A function to calculate the Bean Denominated Value (BDV) of the token (see Section 14.2 of the whitepaper for complete formulas); and\\n3. The number of Stalk and Seeds per BDV received upon Deposit.\\n```\\n\\nThus if the community proposes any kind of Fee-on-Transfer or rebasing tokens like (PAXG or stETH) and the Beanstalk governance approves it, then the protocol needs to integrate them into the system. But as it is now the system is definitely not compatible with such tokens.\\n`deposit`, `depositWithBDV`, `addDepositToAccount`, `removeDepositFromAccount` and any other `silo` accounting related functions perform operations using inputed/recorded amounts. They don't query the existing balance of tokens before or after receiving/sending in order to properly account for tokens that shift balance when received (FoT) or shift balance over time (rebasing).

Impact:
Likelyhood - low/medium - At the moment of writing lido has over 31% of the ETH staked which makes `stETH` a very popular token. There's a strong chance that stakeholder would want to have `stETH` inside the silo.\\nOverall severity is medium.

Recommended mitigation:
Clearly state in the docs that weird tokens won't be implemented via Governance Vote or adjust the code to check the `token.balanceOf()` before and after doing any operation related to the `silo`.
Vulnerability: `removeWhitelistStatus` function Ignores updating `milestoneSeason` variable
Severity: medium

Description:
The issue in the `LibWhitelistedTokens:removeWhitelistStatus` function is that it removes the Whitelist status of a token without considering the impact on other related variables, such as the `milestoneSeason` variable.\\n`milestoneSeason` Is used in many functions for checking whether a token is whitelisted or not i.e.\\n```\\n require(s.ss[token].milestoneSeason == 0, "Whitelist: Token already whitelisted");\\n```\\n\\nIf the milestoneSeason variable is not updated or cleared when removing the Whitelist status, it may lead to incorrect behavior in subsequent checks or operations that rely on this variable.

Impact:
`removeWhitelistStatus` function Ignores updating `milestoneSeason` variable\\nRemoving the Whitelist status of a token without updating related variables can lead to inconsistencies in the data stored in the contract. The `milestoneSeason` variable, used for checking whitelist status in many functions, may still hold outdated or incorrect information after removing the status, potentially leading to unexpected behavior or vulnerabilities.

Recommended mitigation:
To address this issue, ensure that related variables, such as `milestoneSeason`, are appropriately updated or cleared when removing the Whitelist status of a token. If the `milestoneSeason` variable is no longer relevant after removing the Whitelist status, it should be updated or cleared to maintain data integrity.