msc-smart-contract-auditing/vulnerable-functions-base huggingface dataset:
Vulnerability: Tap - reserve can be updated in Tap but not in MarketMaker or Controller
Severity: medium

Description:
The address of the pool/reserve contract can be updated in `Tap` if someone owns the `UPDATE_RESERVE_ROLE` permission. The permission is currently not assigned in the template.\\nThe reserve is being referenced by multiple Contracts. `Tap` interacts with it to transfer funds to the beneficiary, `Controller` adds new protected tokens, and `MarketMaker` transfers funds when someone sells their Shareholder token.\\nUpdating reserve only in `Tap` is inconsistent with the system as the other contracts are still referencing the old reserve unless they are updated via the Aragon Application update mechanisms.\\n```\\n/\\*\\*\\n \\* @notice Update reserve to `\\_reserve`\\n \\* @param \\_reserve The address of the new reserve [pool] contract\\n\\*/\\nfunction updateReserve(Vault \\_reserve) external auth(UPDATE\\_RESERVE\\_ROLE) {\\n    require(isContract(\\_reserve), ERROR\\_CONTRACT\\_IS\\_EOA);\\n\\n    \\_updateReserve(\\_reserve);\\n}\\n```\\n

Impact:

Recommended mitigation:
Remove the possibility to update reserve in `Tap` to keep the system consistent. Provide information about update mechanisms in case the reserve needs to be updated for all components.
Vulnerability: Presale can be opened earlier than initially assigned date
Severity: medium

Description:
There are 2 ways how presale opening date can be assigned. Either it's defined on initialization or the presale will start when `open()` function is executed.\\n```\\nif (\\_openDate != 0) {\\n    \\_setOpenDate(\\_openDate);\\n}\\n```\\n\\nThe problem is that even if `openDate` is assigned to some non-zero date, it can still be opened earlier by calling `open()` function.\\n```\\nfunction open() external auth(OPEN\\_ROLE) {\\n    require(state() == State.Pending, ERROR\\_INVALID\\_STATE);\\n\\n    \\_open();\\n}\\n```\\n

Impact:

Recommended mitigation:
Require that `openDate` is not set (0) when someone manually calls the `open()` function.
Vulnerability: Presale - should not allow zero value contributions
Severity: low

Description:
The Presale accepts zero value contributions emitting a contribution event if none of the Aragon components (TokenManager, MinimeToken) raises an exception.\\n```\\nfunction contribute(address \\_contributor, uint256 \\_value) external payable nonReentrant auth(CONTRIBUTE\\_ROLE) {\\n    require(state() == State.Funding, ERROR\\_INVALID\\_STATE);\\n\\n    if (contributionToken == ETH) {\\n        require(msg.value == \\_value, ERROR\\_INVALID\\_CONTRIBUTE\\_VALUE);\\n    } else {\\n        require(msg.value == 0,      ERROR\\_INVALID\\_CONTRIBUTE\\_VALUE);\\n    }\\n\\n    \\_contribute(\\_contributor, \\_value);\\n}\\n```\\n

Impact:

Recommended mitigation:
Reject zero value `ETH` or `ERC20` contributions.
Vulnerability: FundraisingMultisigTemplate - should use BaseTemplate._createPermissionForTemplate() to assign permissions to itself
Severity: low

Description:
The template temporarily assigns permissions to itself to be able to configure parts of the system. This can either be done by calling `acl.createPermission(address(this), app, role, manager)` or by using a distinct method provided with the DAO-Templates BaseTemplate `_createPermissionForTemplate`.\\nWe suggest that in order to make it clear that permissions are assigned to the template and make it easier to audit that permissions are either revoked or transferred before the DAO is transferred to the new user, the method provided and used with the default Aragon DAO-Templates should be used.\\nuse `createPermission` if permissions are assigned to an entity other than the template contract.\\nuse `_createPermissionForTemplate` when creating permissions for the template contract.\\n```\\n// create and grant ADD\\_PROTECTED\\_TOKEN\\_ROLE to this template\\nacl.createPermission(this, controller, controller.ADD\\_COLLATERAL\\_TOKEN\\_ROLE(), this);\\n```\\n\\nSidenote: pass `address(this)` instead of the contract instance to `createPermission`.

Impact:

Recommended mitigation:
Resolution\\nFixed with AragonBlack/[email protected]dd153e0.\\nUse `BaseTemplate._createPermissionForTemplate` to assign permissions to the template.
Vulnerability: FundraisingMultisigTemplate - misleading comments
Severity: low

Description:
The comment mentionsADD_PROTECTED_TOKEN_ROLE but permissions for `ADD_COLLATERAL_TOKEN_ROLE` are created.\\n```\\n// create and grant ADD\\_PROTECTED\\_TOKEN\\_ROLE to this template\\nacl.createPermission(this, controller, controller.ADD\\_COLLATERAL\\_TOKEN\\_ROLE(), this);\\n```\\n\\n```\\n// transfer ADD\\_PROTECTED\\_TOKEN\\_ROLE\\n\\_transferPermissionFromTemplate(acl, controller, shareVoting, controller.ADD\\_COLLATERAL\\_TOKEN\\_ROLE(), shareVoting);\\n```\\n

Impact:

Recommended mitigation:
`ADD_PROTECTED_TOKEN_ROLE` in the comment should be `ADD_COLLATERAL_TOKEN_ROLE`.
Vulnerability: FundraisingMultisigTemplate - unnecessary cast to address
Severity: low

Description:
The addresses of DAI (argument `address` _dai) and AND (argument `address` _ant) are unnecessarily cast to `address`.\\n```\\nconstructor(\\n    DAOFactory              \\_daoFactory,\\n    ENS                     \\_ens,\\n    MiniMeTokenFactory      \\_miniMeFactory,\\n    IFIFSResolvingRegistrar \\_aragonID,\\n    address                 \\_dai,\\n    address                 \\_ant\\n)\\n    BaseTemplate(\\_daoFactory, \\_ens, \\_miniMeFactory, \\_aragonID)\\n    public\\n{\\n    \\_ensureAragonIdIsValid(\\_aragonID);\\n    \\_ensureMiniMeFactoryIsValid(\\_miniMeFactory);\\n    \\_ensureTokenIsContractOrETH(\\_dai);\\n    \\_ensureTokenIsContractOrETH(\\_ant);\\n\\n    collaterals.push(address(\\_dai));\\n    collaterals.push(address(\\_ant));\\n}\\n```\\n

Impact:

Recommended mitigation:
Both arguments are already of type `address`, therefore remove the explicit cast to `address()` when pushing to the `collaterals` array.
Vulnerability: FundraisingMultisigTemplate - DAI/ANT token address cannot be zero
Severity: low

Description:
The fundraising template is configured with the `DAI` and `ANT` token address upon deployment and checks if the provided addresses are valid. The check performed is `_ensureTokenIsContractOrETH()` which allows the `address(0)` (constant for ETH) for the token contracts. However, `address(0)` is not a valid option for either `DAI` or `ANT` and the contract expects a valid token address to be provided as the deployment of a new DAO will have unexpected results (collateral `ETH` is added instead of an ERC20 token) or fail (DAI == `ANT` == 0x0).\\n```\\n\\_ensureTokenIsContractOrETH(\\_dai);\\n\\_ensureTokenIsContractOrETH(\\_ant);\\n```\\n\\n```\\n function \\_ensureTokenIsContractOrETH(address \\_token) internal view returns (bool) {\\n    require(isContract(\\_token) || \\_token == ETH, ERROR\\_BAD\\_SETTINGS);\\n}\\n```\\n

Impact:

Recommended mitigation:
Resolution\\nFixed with AragonBlack/[email protected]da561ce.\\nUse `isContract()` instead of `_ensureTokenIsContractOrETH()` and optionally require that `collateral[0] != collateral[1]` as an additional check to prevent that the fundraising template is being deployed with an invalid configuration.
Vulnerability: Anyone can remove a maker's pending pool join status
Severity: high

Description:
Using behavior described in https://github.com/ConsenSys/0x-v3-staking-audit-2019-10/issues/11, it is possible to delete the pending join status of any maker in any pool by passing in `NIL_POOL_ID` to `removeMakerFromStakingPool`. Note that the attacker in the following example must not be a confirmed member of any pool:\\nThe attacker calls `addMakerToStakingPool(NIL_POOL_ID, makerAddress)`. In this case, `makerAddress` can be almost any address, as long as it has not called `joinStakingPoolAsMaker` (an easy example is address(0)). The key goal of this call is to increment the number of makers in pool 0:\\n```\\n\\_poolById[poolId].numberOfMakers = uint256(pool.numberOfMakers).safeAdd(1).downcastToUint32();\\n```\\n\\nThe attacker calls `removeMakerFromStakingPool(NIL_POOL_ID, targetAddress)`. This function queries `getStakingPoolIdOfMaker(targetAddress)` and compares it to the passed-in pool id. Because the target is an unconfirmed maker, their staking pool id is NIL_POOL_ID:\\n```\\nbytes32 makerPoolId = getStakingPoolIdOfMaker(makerAddress);\\nif (makerPoolId != poolId) {\\n    LibRichErrors.rrevert(LibStakingRichErrors.MakerPoolAssignmentError(\\n        LibStakingRichErrors.MakerPoolAssignmentErrorCodes.MakerAddressNotRegistered,\\n        makerAddress,\\n        makerPoolId\\n    ));\\n}\\n```\\n\\nThe check passes, and the target's `_poolJoinedByMakerAddress` struct is deleted. Additionally, the number of makers in pool 0 is decreased:\\n```\\ndelete \\_poolJoinedByMakerAddress[makerAddress];\\n\\_poolById[poolId].numberOfMakers = uint256(\\_poolById[poolId].numberOfMakers).safeSub(1).downcastToUint32();\\n```\\n\\nThis can be used to prevent any makers from being confirmed into a pool.

Impact:

Recommended mitigation:
See `issue 5.6`.
Vulnerability: MixinParams.setParams bypasses safety checks made by standard StakingProxy upgrade path.
Severity: medium

Description:
The staking contracts use a set of configurable parameters to determine the behavior of various parts of the system. The parameters dictate the duration of epochs, the ratio of delegated stake weight vs operator stake, the minimum pool stake, and the Cobb-Douglas numerator and denominator. These parameters can be configured in two ways:\\n```\\n// Call `init()` on the staking contract to initialize storage.\\n(bool didInitSucceed, bytes memory initReturnData) = stakingContract.delegatecall(\\n    abi.encodeWithSelector(IStorageInit(0).init.selector)\\n);\\nif (!didInitSucceed) {\\n    assembly {\\n        revert(add(initReturnData, 0x20), mload(initReturnData))\\n    }\\n}\\n  \\n// Assert initialized storage values are valid\\n\\_assertValidStorageParams();\\n```\\n\\nAn authorized address can call `MixinParams.setParams` at any time and set the contract's parameters to arbitrary values.\\nThe latter method introduces the possibility of setting unsafe or nonsensical values for the contract parameters: `epochDurationInSeconds` can be set to 0, `cobbDouglassAlphaNumerator` can be larger than `cobbDouglassAlphaDenominator`, `rewardDelegatedStakeWeight` can be set to a value over 100% of the staking reward, and more.\\nNote, too, that by using `MixinParams.setParams` to set all parameters to 0, the `Staking` contract can be re-initialized by way of `Staking.init`. Additionally, it can be re-attached by way of `StakingProxy.attachStakingContract`, as the delegatecall to `Staking.init` will succeed.

Impact:

Recommended mitigation:
Resolution\\nThis is fixed in 0xProject/0x-monorepo#2279. Now the parameter validity is asserted in `setParams()`.\\nEnsure that calls to `setParams` check that the provided values are within the same range currently enforced by the proxy.
Vulnerability: Authorized addresses can indefinitely stall ZrxVaultBackstop catastrophic failure mode
Severity: medium

Description:
The `ZrxVaultBackstop` contract was added to allow anyone to activate the staking system's “catastrophic failure” mode if the `StakingProxy` is in “read-only” mode for at least 40 days. To enable this behavior, the `StakingProxy` contract was modified to track the last timestamp at which “read-only” mode was activated. This is done by way of StakingProxy.setReadOnlyMode:\\n```\\n/// @dev Set read-only mode (state cannot be changed).\\nfunction setReadOnlyMode(bool shouldSetReadOnlyMode)\\n    external\\n    onlyAuthorized\\n{\\n    // solhint-disable-next-line not-rely-on-time\\n    uint96 timestamp = block.timestamp.downcastToUint96();\\n    if (shouldSetReadOnlyMode) {\\n        stakingContract = readOnlyProxy;\\n        readOnlyState = IStructs.ReadOnlyState({\\n            isReadOnlyModeSet: true,\\n            lastSetTimestamp: timestamp\\n        });\\n```\\n\\nBecause the timestamp is updated even if “read-only” mode is already active, any authorized address can prevent `ZrxVaultBackstop` from activating catastrophic failure mode by repeatedly calling `setReadOnlyMode`.

Impact:

Recommended mitigation:
If “read-only” mode is already active, `setReadOnlyMode(true)` should result in a no-op.
Vulnerability: Pool 0 can be used to temporarily prevent makers from joining another pool
Severity: medium

Description:
`removeMakerFromStakingPool` reverts if the number of makers currently in the pool is 0, due to `safeSub` catching an underflow:\\n```\\n\\_poolById[poolId].numberOfMakers = uint256(\\_poolById[poolId].numberOfMakers).safeSub(1).downcastToUint32();\\n```\\n\\nBecause of this, edge behavior described in issue 5.6 can allow an attacker to temporarily prevent makers from joining a pool:\\nThe attacker calls `addMakerToStakingPool(NIL_POOL_ID, victimAddress)`. This sets the victim's `MakerPoolJoinStatus.confirmed` field to `true` and increases the number of makers in pool 0 to 1:\\n```\\npoolJoinStatus = IStructs.MakerPoolJoinStatus({\\n    poolId: poolId,\\n    confirmed: true\\n});\\n\\_poolJoinedByMakerAddress[makerAddress] = poolJoinStatus;\\n\\_poolById[poolId].numberOfMakers = uint256(pool.numberOfMakers).safeAdd(1).downcastToUint32();\\n```\\n\\nThe attacker calls `removeMakerFromStakingPool(NIL_POOL_ID, randomAddress)`. The net effect of this call simply decreases the number of makers in pool 0 by 1, back to 0:\\n```\\ndelete \\_poolJoinedByMakerAddress[makerAddress];\\n\\_poolById[poolId].numberOfMakers = uint256(\\_poolById[poolId].numberOfMakers).safeSub(1).downcastToUint32();\\n```\\n\\nTypically, the victim should be able to remove themselves from pool 0 by calling `removeMakerFromStakingPool(NIL_POOL_ID, victimAddress)`, but because the attacker can set the pool's number of makers to 0, the aforementioned underflow causes this call to fail. The victim must first understand what is happening in `MixinStakingPool` before they are able to remedy the situation:\\nThe victim must call `addMakerToStakingPool(NIL_POOL_ID, randomAddress2)` to increase pool 0's number of makers back to 1.\\nThe victim can now call `removeMakerFromStakingPool(NIL_POOL_ID, victimAddress)`, and remove their confirmed status.\\nAdditionally, if the victim in question currently has a pending join, the attacker can use issue 5.1 to first remove their pending status before locking them in pool 0.

Impact:

Recommended mitigation:
See issue 5.1.
Vulnerability: Recommendation: Fix weak assertions in MixinStakingPool stemming from use of NIL_POOL_ID
Severity: medium

Description:
The modifier `onlyStakingPoolOperatorOrMaker(poolId)` is used to authorize actions taken on a given pool. The sender must be either the operator or a confirmed maker of the pool in question. However, the modifier queries `getStakingPoolIdOfMaker(maker)`, which returns `NIL_POOL_ID` if the maker's `MakerPoolJoinStatus` struct is not confirmed. This implicitly makes anyone a maker of the nonexistent “pool 0”:\\n```\\nfunction getStakingPoolIdOfMaker(address makerAddress)\\n    public\\n    view\\n    returns (bytes32)\\n{\\n    IStructs.MakerPoolJoinStatus memory poolJoinStatus = \\_poolJoinedByMakerAddress[makerAddress];\\n    if (poolJoinStatus.confirmed) {\\n        return poolJoinStatus.poolId;\\n    } else {\\n        return NIL\\_POOL\\_ID;\\n    }\\n}\\n```\\n\\n`joinStakingPoolAsMaker(poolId)` makes no existence checks on the provided pool id, and allows makers to become pending makers in nonexistent pools.\\n`addMakerToStakingPool(poolId, maker)` makes no existence checks on the provided pool id, allowing makers to be added to nonexistent pools (as long as the sender is an operator or maker in the pool).

Impact:

Recommended mitigation:
Avoid use of `0x00...00` for `NIL_POOL_ID`. Instead, use `2**256 - 1`.\\nImplement stronger checks for pool existence. Each time a pool id is supplied, it should be checked that the pool id is between 0 and `nextPoolId`.\\n`onlyStakingPoolOperatorOrMaker` should revert if `poolId` == `NIL_POOL_ID` or if `poolId` is not in the valid range: (0, nextPoolId).
Vulnerability: LibFixedMath functions fail to catch a number of overflows
Severity: medium

Description:
The `__add()`, `__mul()`, and `__div()` functions perform arithmetic on 256-bit signed integers, and they all miss some specific overflows.\\nAddition Overflows\\n```\\n/// @dev Adds two numbers, reverting on overflow.\\nfunction \\_add(int256 a, int256 b) private pure returns (int256 c) {\\n    c = a + b;\\n    if (c > 0 && a < 0 && b < 0) {\\n        LibRichErrors.rrevert(LibFixedMathRichErrors.BinOpError(\\n            LibFixedMathRichErrors.BinOpErrorCodes.SUBTRACTION\\_OVERFLOW,\\n            a,\\n            b\\n        ));\\n    }\\n    if (c < 0 && a > 0 && b > 0) {\\n        LibRichErrors.rrevert(LibFixedMathRichErrors.BinOpError(\\n            LibFixedMathRichErrors.BinOpErrorCodes.ADDITION\\_OVERFLOW,\\n            a,\\n            b\\n        ));\\n    }\\n}\\n```\\n\\nThe two overflow conditions it tests for are:\\nAdding two positive numbers shouldn't result in a negative number.\\nAdding two negative numbers shouldn't result in a positive number.\\n`__add(-2**255, -2**255)` returns `0` without reverting because the overflow didn't match either of the above conditions.\\nMultiplication Overflows\\n```\\n/// @dev Returns the multiplication two numbers, reverting on overflow.\\nfunction \\_mul(int256 a, int256 b) private pure returns (int256 c) {\\n    if (a == 0) {\\n        return 0;\\n    }\\n    c = a \\* b;\\n    if (c / a != b) {\\n        LibRichErrors.rrevert(LibFixedMathRichErrors.BinOpError(\\n            LibFixedMathRichErrors.BinOpErrorCodes.MULTIPLICATION\\_OVERFLOW,\\n            a,\\n            b\\n        ));\\n    }\\n}\\n```\\n\\nThe function checks via division for most types of overflows, but it fails to catch one particular case. `__mul(-2**255, -1)` returns `-2**255` without error.\\nDivision Overflows\\n```\\n/// @dev Returns the division of two numbers, reverting on division by zero.\\nfunction \\_div(int256 a, int256 b) private pure returns (int256 c) {\\n    if (b == 0) {\\n        LibRichErrors.rrevert(LibFixedMathRichErrors.BinOpError(\\n            LibFixedMathRichErrors.BinOpErrorCodes.DIVISION\\_BY\\_ZERO,\\n            a,\\n            b\\n        ));\\n    }\\n    c = a / b;\\n}\\n```\\n\\nIt does not check for overflow. Due to this, `__div(-2**255, -1)` erroneously returns `-2**255`.

Impact:

Recommended mitigation:
For addition, the specific case of `__add(-2**255, -2**255)` can be detected by using a `>= 0` check instead of `> 0`, but the below seems like a clearer check for all cases:\\n```\\n// if b is negative, then the result should be less than a\\nif (b < 0 && c >= a) { /\\* subtraction overflow \\*/ }\\n\\n// if b is positive, then the result should be greater than a\\nif (b > 0 && c <= a) { /\\* addition overflow \\*/ }\\n```\\n\\nFor multiplication and division, the specific values of `-2**255` and `-1` are the only missing cases, so that can be explicitly checked in the `__mul()` and `__div()` functions.
Vulnerability: Misleading MoveStake event when moving stake from UNDELEGATED to UNDELEGATED
Severity: low

Description:
Although moving stake between the same status (UNDELEGATED <=> UNDELEGATED) should be a no-op, calls to `moveStake` succeed even for invalid `amount` and nonsensical `poolId`. The resulting `MoveStake` event can log garbage, potentially confusing those observing events.\\nWhen moving between `UNDELEGATED` and `UNDELEGATED`, each check and function call results in a no-op, save the final event:\\nNeither `from` nor `to` are `StakeStatus.DELEGATED`, so these checks are passed:\\n```\\nif (from.status == IStructs.StakeStatus.DELEGATED) {\\n    \\_undelegateStake(\\n        from.poolId,\\n        staker,\\n        amount\\n    );\\n}\\n  \\nif (to.status == IStructs.StakeStatus.DELEGATED) {\\n    \\_delegateStake(\\n        to.poolId,\\n        staker,\\n        amount\\n    );\\n}\\n```\\n\\nThe primary state changing function, `_moveStake`, immediately returns because the `from` and `to` balance pointers are equivalent:\\n```\\nif (\\_arePointersEqual(fromPtr, toPtr)) {\\n    return;\\n}\\n```\\n\\nFinally, the `MoveStake` event is invoked, which can log completely invalid values for `amount`, `from.poolId`, and to.poolId:\\n```\\nemit MoveStake(\\n    staker,\\n    amount,\\n    uint8(from.status),\\n    from.poolId,\\n    uint8(to.status),\\n    to.poolId\\n);\\n```\\n

Impact:

Recommended mitigation:
If `amount` is 0 or if moving between `UNDELEGATED` and `UNDELEGATED`, this function should no-op or revert. An explicit check for this case should be made near the start of the function.
Vulnerability: Remove unneeded fields from StoredBalance and Pool structs
Severity: low

Description:
Resolution\\nThis is fixed in 0xProject/0x-monorepo#2248. As part of a larger refactor, these fields were removed.\\nBoth structs have fields that are only written to, and never read:\\nStoredBalance.isInitialized:\\n```\\nbool isInitialized;\\n```\\n\\nPool.initialized:\\n```\\nbool initialized;\\n```\\n

Impact:

Recommended mitigation:
The unused fields should be removed.
Vulnerability: Pool IDs can just be incrementing integers
Severity: low

Description:
Pool IDs are currently `bytes32` values that increment by `2**128`. After discussion with the development team, it seems that this was in preparation for a feature that was ultimately not used. Pool IDs should instead just be incrementing integers.\\n```\\n// The upper 16 bytes represent the pool id, so this would be pool id 1. See MixinStakinPool for more information.\\nbytes32 constant internal INITIAL\\_POOL\\_ID = 0x0000000000000000000000000000000100000000000000000000000000000000;\\n\\n// The upper 16 bytes represent the pool id, so this would be an increment of 1. See MixinStakinPool for more information.\\nuint256 constant internal POOL\\_ID\\_INCREMENT\\_AMOUNT = 0x0000000000000000000000000000000100000000000000000000000000000000;\\n```\\n\\n```\\n/// @dev Computes the unique id that comes after the input pool id.\\n/// @param poolId Unique id of pool.\\n/// @return Next pool id after input pool.\\nfunction \\_computeNextStakingPoolId(bytes32 poolId)\\n    internal\\n    pure\\n    returns (bytes32)\\n{\\n    return bytes32(uint256(poolId).safeAdd(POOL\\_ID\\_INCREMENT\\_AMOUNT));\\n}\\n```\\n

Impact:

Recommended mitigation:
Resolution\\nThis is fixed in 0xProject/0x-monorepo#2250. Pool IDs now start at 1 and increment by 1 each time.\\nMake pool IDs `uint256` values and simply add 1 to generate the next ID.
Vulnerability: LibProxy.proxyCall() may overwrite important memory
Severity: low

Description:
`LibProxy.proxyCall()` copies from call data to memory, starting at address 0:\\n```\\nassembly {\\n    // store selector of destination function\\n    let freeMemPtr := 0\\n    if gt(customEgressSelector, 0) {\\n        mstore(0x0, customEgressSelector)\\n        freeMemPtr := add(freeMemPtr, 4)\\n    }\\n\\n    // adjust the calldata offset, if we should ignore the selector\\n    let calldataOffset := 0\\n    if gt(ignoreIngressSelector, 0) {\\n        calldataOffset := 4\\n    }\\n\\n    // copy calldata to memory\\n    calldatacopy(\\n        freeMemPtr,\\n        calldataOffset,\\n        calldatasize()\\n    )\\n```\\n\\nThe first 64 bytes of memory are treated as “scratch space” by the Solidity compiler. Writing beyond that point is dangerous, as it will overwrite the free memory pointer and the “zero slot” which is where length-0 arrays point.\\nAlthough the current callers of `proxyCall()` don't appear to use any memory after calling `proxyCall()`, future changes to the code may introduce very serious and subtle bugs due to this unsafe handling of memory.

Impact:

Recommended mitigation:
Use the actual free memory pointer to determine where it's safe to write to memory.