msc-smart-contract-auditing/vulnerable-functions-base huggingface dataset:
Vulnerability: keep-core - operator contracts disabled via panic button can be re-enabled by RegistryKeeper
Severity: high

Description:
The Registry contract defines three administrative accounts: `Governance`, `registryKeeper`, and `panicButton`. All permissions are initially assigned to the deployer when the contract is created. The account acting like a super-admin, being allowed to re-assign administrative accounts - is `Governance`. `registryKeeper` is a lower privileged account maintaining the registry and `panicButton` is an emergency account that can disable operator contracts.\\nThe keep specification states the following:\\nPanic Button The Panic Button can disable malicious or malfunctioning contracts that have been previously approved by the Registry Keeper. When a contract is disabled by the Panic Button, its status on the registry changes to reflect this, and it becomes ineligible to penalize operators. Contracts disabled by the Panic Button can not be reactivated. The Panic Button can be rekeyed by Governance.\\nIt is assumed that the permissions are `Governance` > `panicButton` > `registryKeeper`, meaning that `panicButton` should be able to overrule `registryKeeper`, while `registryKeeper` cannot overrule `panicButton`.\\nWith the current implementation of the Registry the `registryKeeper` account can re-enable an operator contract that has previously been disabled by the `panicButton` account.\\nWe would also like to note the following:\\nThe contract should use enums instead of integer literals when working with contract states.\\nChanges to the contract take effect immediately, allowing an administrative account to selectively front-run calls to the Registry ACL and interfere with user activity.\\nThe operator contract state can be set to the current value without raising an error.\\nThe panic button can be called for operator contracts that are not yet active.\\n```\\nfunction approveOperatorContract(address operatorContract) public onlyRegistryKeeper {\\n    operatorContracts[operatorContract] = 1;\\n}\\n\\nfunction disableOperatorContract(address operatorContract) public onlyPanicButton {\\n    operatorContracts[operatorContract] = 2;\\n}\\n```\\n

Impact:

Recommended mitigation:
The keep specification states:\\nThe Panic Button can be used to set the status of an APPROVED contract to DISABLED. Operator Contracts disabled with the Panic Button cannot be re-enabled, and disabled contracts may not punish operators nor be selected by service contracts to perform work.\\nAll three accounts are typically trusted. We recommend requiring the `Governance` or `paniceButton` accounts to reset the contract operator state before `registryKeeper` can change the state or disallow re-enabling of disabled operator contracts as stated in the specification.
Vulnerability: tbtc - Funder loses payment to keep if signing group is not established in time  Pending
Severity: high

Description:
To create a new deposit, the funder has to pay for the creation of a keep. If establishing the keep does not succeed in time, fails or the signing group decides not to return a public key when `retrieveSignerPubkey` is called to transition from `awaiting_signer_setup` to `awaiting_btc_funding_proof` the signer setup fails. After a timeout of 3 hrs, anyone can force the deposit to transition from `awaiting_signer_setup` to `failed_setup` by calling `notifySignerSetupFailure`.\\nThe funder had to provide payment for the keep but the signing group failed to establish. Payment for the keep is not returned even though one could assume that the signing group tried to play unfairly. The signing group might intentionally try to cause this scenario to interfere with the system.\\n`retrieveSignerPubkey` fails if keep provided pubkey is empty or of an unexpected length\\n```\\n/// @notice we poll the Keep contract to retrieve our pubkey\\n/// @dev We store the pubkey as 2 bytestrings, X and Y.\\n/// @param \\_d deposit storage pointer\\n/// @return True if successful, otherwise revert\\nfunction retrieveSignerPubkey(DepositUtils.Deposit storage \\_d) public {\\n    require(\\_d.inAwaitingSignerSetup(), "Not currently awaiting signer setup");\\n\\n    bytes memory \\_publicKey = IBondedECDSAKeep(\\_d.keepAddress).getPublicKey();\\n    require(\\_publicKey.length == 64, "public key not set or not 64-bytes long");\\n\\n    \\_d.signingGroupPubkeyX = \\_publicKey.slice(0, 32).toBytes32();\\n    \\_d.signingGroupPubkeyY = \\_publicKey.slice(32, 32).toBytes32();\\n    require(\\_d.signingGroupPubkeyY != bytes32(0) && \\_d.signingGroupPubkeyX != bytes32(0), "Keep returned bad pubkey");\\n    \\_d.fundingProofTimerStart = block.timestamp;\\n\\n    \\_d.setAwaitingBTCFundingProof();\\n    \\_d.logRegisteredPubkey(\\n        \\_d.signingGroupPubkeyX,\\n        \\_d.signingGroupPubkeyY);\\n}\\n```\\n\\n`notifySignerSetupFailure` can be called by anyone after a timeout of 3hrs\\n```\\n/// @notice Anyone may notify the contract that signing group setup has timed out\\n/// @dev We rely on the keep system punishes the signers in this case\\n/// @param \\_d deposit storage pointer\\nfunction notifySignerSetupFailure(DepositUtils.Deposit storage \\_d) public {\\n    require(\\_d.inAwaitingSignerSetup(), "Not awaiting setup");\\n    require(\\n        block.timestamp > \\_d.signingGroupRequestedAt + TBTCConstants.getSigningGroupFormationTimeout(),\\n        "Signing group formation timeout not yet elapsed"\\n    );\\n    \\_d.setFailedSetup();\\n    \\_d.logSetupFailed();\\n\\n    fundingTeardown(\\_d);\\n}\\n```\\n

Impact:

Recommended mitigation:
It should be ensured that a keep group always establishes or otherwise the funder is refunded the fee for the keep.
Vulnerability: tbtc - Unreachable state LIQUIDATION_IN_PROGRESS
Severity: high

Description:
According to the specification (overview, states, version 2020-02-06), a deposit can be in one of two liquidation_in_progress states.\\nLIQUIDATION_IN_PROGRESS\\nLIQUIDATION_IN_PROGRESS Liquidation due to undercollateralization or an abort has started Automatic (on-chain) liquidation was unsuccessful\\nFRAUD_LIQUIDATION_IN_PROGRESS\\nFRAUD_LIQUIDATION_IN_PROGRESS Liquidation due to fraud has started Automatic (on-chain) liquidation was unsuccessful\\nHowever, `LIQUIDATION_IN_PROGRESS` is unreachable and instead, `FRAUD_LIQUIDATION_IN_PROGRESS` is always called. This means that all non-fraud state transitions end up in the fraud liquidation path and will perform actions as if fraud was detected even though it might be caused by an undercollateralized notification or courtesy timeout.\\n`startSignerAbortLiquidation` transitions to `FRAUD_LIQUIDATION_IN_PROGRESS` on non-fraud events `notifyUndercollateralizedLiquidation` and `notifyCourtesyTimeout`\\n```\\n/// @notice Starts signer liquidation due to abort or undercollateralization\\n/// @dev We first attempt to liquidate on chain, then by auction\\n/// @param \\_d deposit storage pointer\\nfunction startSignerAbortLiquidation(DepositUtils.Deposit storage \\_d) internal {\\n    \\_d.logStartedLiquidation(false);\\n    // Reclaim used state for gas savings\\n    \\_d.redemptionTeardown();\\n    \\_d.seizeSignerBonds();\\n\\n    \\_d.liquidationInitiated = block.timestamp;  // Store the timestamp for auction\\n    \\_d.liquidationInitiator = msg.sender;\\n    \\_d.setFraudLiquidationInProgress();\\n}\\n```\\n

Impact:

Recommended mitigation:
Verify state transitions and either remove `LIQUIDATION_IN_PROGRESS` if it is redundant or fix the state transitions for non-fraud liquidations.\\nNote that Deposit states can be simplified by removing redundant states by setting a flag (e.g. fraudLiquidation) in the deposit instead of adding a state to track the fraud liquidation path.\\nAccording to the specification, we assume the following state transitions are desired:\\n`LIQUIDATION_IN_PROGRESS`\\nIn case of liquidation due to undercollateralization or abort, the remaining bond value is split 50-50 between the account which triggered the liquidation and the signers.\\n`FRAUD_LIQUIDATION_IN_PROGRESS`\\nIn case of liquidation due to fraud, the remaining bond value in full goes to the account which triggered the liquidation by proving fraud.
Vulnerability: tbtc - Anyone can emit log events due to missing access control
Severity: high

Description:
Access control for `DepositLog` is not implemented. `DepositLog` is inherited by `TBTCSystem` and its functionality is usually consumed by `Deposit` contracts to emit log events on `TBTCSystem`. Due to the missing access control, anyone can emit log events on `TBTCSystem`. Users, client-software or other components that rely on these events might be tricked into performing actions that were not authorized by the system.\\n```\\nfunction approvedToLog(address \\_caller) public pure returns (bool) {\\n    /\\* TODO: auth via system \\*/\\n    \\_caller;\\n    return true;\\n}\\n```\\n

Impact:

Recommended mitigation:
Log events are typically initiated by the Deposit contract. Make sure only Deposit contracts deployed by an approved factory can emit logs on TBTCSystem.
Vulnerability: DKGResultVerification.verify unsafe packing in signed data
Severity: medium

Description:
`DKGResultVerification.verify` allows the sender to arbitrarily move bytes between `groupPubKey` and misbehaved:\\n```\\nbytes32 resultHash = keccak256(abi.encodePacked(groupPubKey, misbehaved));\\n```\\n

Impact:

Recommended mitigation:
Validate the expected length of both and add a salt between the two.
Vulnerability: tbtc - Disallow signatures with high-s values in DepositRedemption.provideRedemptionSignature
Severity: medium

Description:
`DepositRedemption.provideRedemptionSignature` is used by signers to publish a signature that can be used to redeem a deposit on Bitcoin. The function accepts a signature s value in the upper half of the secp256k1 curve:\\n```\\nfunction provideRedemptionSignature(\\n    DepositUtils.Deposit storage \\_d,\\n    uint8 \\_v,\\n    bytes32 \\_r,\\n    bytes32 \\_s\\n) public {\\n    require(\\_d.inAwaitingWithdrawalSignature(), "Not currently awaiting a signature");\\n\\n    // If we're outside of the signature window, we COULD punish signers here\\n    // Instead, we consider this a no-harm-no-foul situation.\\n    // The signers have not stolen funds. Most likely they've just inconvenienced someone\\n\\n    // The signature must be valid on the pubkey\\n    require(\\n        \\_d.signerPubkey().checkSig(\\n            \\_d.lastRequestedDigest,\\n            \\_v, \\_r, \\_s\\n        ),\\n        "Invalid signature"\\n    );\\n```\\n\\nAlthough `ecrecover` accepts signatures with these s values, they are no longer used in Bitcoin. As such, the signature will appear to be valid to the Ethereum smart contract, but will likely not be accepted on Bitcoin. If no users watching malleate the signature, the redemption process will likely enter a fee increase loop, incurring a cost on the deposit owner.

Impact:

Recommended mitigation:
Ensure the passed-in s value is restricted to the lower half of the secp256k1 curve, as done in BondedECDSAKeep:\\n```\\n// Validate `s` value for a malleability concern described in EIP-2.\\n// Only signatures with `s` value in the lower half of the secp256k1\\n// curve's order are considered valid.\\nrequire(\\n    uint256(\\_s) <=\\n        0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0,\\n    "Malleable signature - s should be in the low half of secp256k1 curve's order"\\n);\\n```\\n
Vulnerability: Consistent use of SafeERC20 for external tokens
Severity: medium

Description:
Use `SafeERC20` features to interact with potentially broken tokens used in the system. E.g. `TokenGrant.receiveApproval()` is using `safeTransferFrom` while other contracts aren't.\\n`TokenGrant.receiveApproval` using `safeTransferFrom`\\n```\\ntoken.safeTransferFrom(\\_from, address(this), \\_amount);\\n```\\n\\n`TokenStaking.receiveApproval` not using `safeTransferFrom` while `safeTransfer` is being used.\\n```\\ntoken.transferFrom(\\_from, address(this), \\_value);\\n```\\n\\n```\\ntoken.safeTransfer(owner, amount);\\n```\\n\\n```\\ntoken.transfer(tattletale, tattletaleReward);\\n```\\n\\n`distributeERC20ToMembers` not using `safeTransferFrom`\\n```\\ntoken.transferFrom(\\n    msg.sender,\\n    tokenStaking.magpieOf(members[i]),\\n    dividend\\n);\\n```\\n

Impact:

Recommended mitigation:
Consistently use `SafeERC20` to support potentially broken tokens external to the system.
Vulnerability: Initialize implementations for proxy contracts and protect initialization methods
Severity: medium

Description:
It should be avoided that the implementation for proxy contracts can be initialized by third parties. This can be the case if the `initialize` function is unprotected. Since the implementation contract is not meant to be used directly without a proxy delegate-calling it is recommended to protect the initialization method of the implementation by initializing on deployment.\\nChanging the proxies implementation (upgradeTo()) to a version that does not protect the initialization method may allow someone to front-run and initialize the contract if it is not done within the same transaction.\\n`KeepVendor` delegates to `KeepVendorImplV1`. The implementations initialization method is unprotected.\\n```\\n/// @notice Initializes Keep Vendor contract implementation.\\n/// @param registryAddress Keep registry contract linked to this contract.\\nfunction initialize(\\n    address registryAddress\\n)\\n    public\\n{\\n    require(!initialized(), "Contract is already initialized.");\\n    \\_initialized["BondedECDSAKeepVendorImplV1"] = true;\\n    registry = Registry(registryAddress);\\n}\\n```\\n\\n`KeepRandomBeaconServiceImplV1` and `KeepRandomBeaconServiceUpgradeExample`\\n```\\nfunction initialize(\\n    uint256 priceFeedEstimate,\\n    uint256 fluctuationMargin,\\n    uint256 dkgContributionMargin,\\n    uint256 withdrawalDelay,\\n    address registry\\n)\\n    public\\n{\\n    require(!initialized(), "Contract is already initialized.");\\n    \\_initialized["KeepRandomBeaconServiceImplV1"] = true;\\n    \\_priceFeedEstimate = priceFeedEstimate;\\n    \\_fluctuationMargin = fluctuationMargin;\\n    \\_dkgContributionMargin = dkgContributionMargin;\\n    \\_withdrawalDelay = withdrawalDelay;\\n    \\_pendingWithdrawal = 0;\\n    \\_previousEntry = \\_beaconSeed;\\n    \\_registry = registry;\\n    \\_baseCallbackGas = 18845;\\n}\\n```\\n\\n`Deposit` is deployed via `cloneFactory` delegating to a `masterDepositAddress` in `DepositFactory`. The `masterDepositAddress` (Deposit) might be left uninitialized.\\n```\\ncontract DepositFactoryAuthority {\\n\\n    bool internal \\_initialized = false;\\n    address internal \\_depositFactory;\\n\\n    /// @notice Set the address of the System contract on contract initialization\\n    function initialize(address \\_factory) public {\\n        require(! \\_initialized, "Factory can only be initialized once.");\\n\\n        \\_depositFactory = \\_factory;\\n        \\_initialized = true;\\n    }\\n```\\n

Impact:

Recommended mitigation:
Initialize unprotected implementation contracts in the implementation's constructor. Protect initialization methods from being called by unauthorized parties or ensure that deployment of the proxy and initialization is performed in the same transaction.
Vulnerability: keep-tecdsa - If caller sends more than is contained in the signer subsidy pool, the value is burned
Severity: medium

Description:
The signer subsidy pool in `BondedECDSAKeepFactory` tracks funds sent to the contract. Each time a keep is opened, the subsidy pool is intended to be distributed to the members of the new keep:\\n```\\n// If subsidy pool is non-empty, distribute the value to signers but\\n// never distribute more than the payment for opening a keep.\\nuint256 signerSubsidy = subsidyPool < msg.value\\n    ? subsidyPool\\n    : msg.value;\\nif (signerSubsidy > 0) {\\n    subsidyPool -= signerSubsidy;\\n    keep.distributeETHToMembers.value(signerSubsidy)();\\n}\\n```\\n\\nThe tracking around subsidy pool increases is inconsistent, and can lead to sent value being burned. In the case that `subsidyPool` contains less Ether than is sent in `msg.value`, `msg.value` is unused and remains in the contract. It may or may not be added to `subsidyPool`, depending on the return status of the random beacon:\\n```\\n(bool success, ) = address(randomBeacon).call.gas(400000).value(msg.value)(\\n    abi.encodeWithSignature(\\n        "requestRelayEntry(address,string,uint256)",\\n        address(this),\\n        "setGroupSelectionSeed(uint256)",\\n        callbackGas\\n    )\\n);\\nif (!success) {\\n    subsidyPool += msg.value; // beacon is busy\\n}\\n```\\n

Impact:

Recommended mitigation:
Rather than tracking the `subsidyPool` individually, simply distribute `this.balance` to each new keep's members.
Vulnerability: tbtc - Inconsistency between increaseRedemptionFee and provideRedemptionProof may create un-provable redemptions
Severity: medium

Description:
`DepositRedemption.increaseRedemptionFee` is used by signers to approve a signable bitcoin transaction with a higher fee, in case the network is congested and miners are not approving the lower-fee transaction.\\nFee increases can be performed every 4 hours:\\n```\\nrequire(block.timestamp >= \\_d.withdrawalRequestTime + TBTCConstants.getIncreaseFeeTimer(), "Fee increase not yet permitted");\\n```\\n\\nIn addition, each increase must increment the fee by exactly the initial proposed fee:\\n```\\n// Check that we're incrementing the fee by exactly the redeemer's initial fee\\nuint256 \\_previousOutputValue = DepositUtils.bytes8LEToUint(\\_previousOutputValueBytes);\\n\\_newOutputValue = DepositUtils.bytes8LEToUint(\\_newOutputValueBytes);\\nrequire(\\_previousOutputValue.sub(\\_newOutputValue) == \\_d.initialRedemptionFee, "Not an allowed fee step");\\n```\\n\\nOutside of these two restrictions, there is no limit to the number of times `increaseRedemptionFee` can be called. Over a 20-hour period, for example, `increaseRedemptionFee` could be called 5 times, increasing the fee to `initialRedemptionFee * 5`. Over a 24-hour period, `increaseRedemptionFee` could be called 6 times, increasing the fee to `initialRedemptionFee * 6`.\\nEventually, it is expected that a transaction will be submitted and mined. At this point, anyone can call `DepositRedemption.provideRedemptionProof`, finalizing the redemption process and rewarding the signers. However, `provideRedemptionProof` will fail if the transaction fee is too high:\\n```\\nrequire((\\_d.utxoSize().sub(\\_fundingOutputValue)) <= \\_d.initialRedemptionFee \\* 5, "Fee unexpectedly very high");\\n```\\n\\nIn the case that `increaseRedemptionFee` is called 6 times and the signers provide a signature for this transaction, the transaction can be submitted and mined but `provideRedemptionProof` for this will always fail. Eventually, a redemption proof timeout will trigger the deposit into liquidation and the signers will be punished.

Impact:

Recommended mitigation:
Because it is difficult to say with certainty that a 5x fee increase will always ensure a transaction's redeemability, the upper bound on fee bumps should be removed from `provideRedemptionProof`.\\nThis should be implemented in tandem with https://github.com/ConsenSys/thesis-tbtc-audit-2020-01/issues/38, so that signers cannot provide a proof that bypasses `increaseRedemptionFee` flow to spend the highest fee possible.
Vulnerability: keep-tecdsa - keep cannot be closed if a members bond was seized or fully reassigned
Severity: medium

Description:
A keep cannot be closed if the bonds have been completely reassigned or seized before, leaving at least one member with zero `lockedBonds`. In this case `closeKeep()` will throw in `freeMembersBonds()` because the requirement in `keepBonding.freeBond` is not satisfied anymore (lockedBonds[bondID] > 0). As a result of this, none of the potentially remaining bonds (reassign) are freed, the keep stays active even though it should be closed.\\n```\\n/// @notice Closes keep when owner decides that they no longer need it.\\n/// Releases bonds to the keep members. Keep can be closed only when\\n/// there is no signing in progress or requested signing process has timed out.\\n/// @dev The function can be called by the owner of the keep and only is the\\n/// keep has not been closed already.\\nfunction closeKeep() external onlyOwner onlyWhenActive {\\n    require(\\n        !isSigningInProgress() || hasSigningTimedOut(),\\n        "Requested signing has not timed out yet"\\n    );\\n\\n    isActive = false;\\n\\n    freeMembersBonds();\\n\\n    emit KeepClosed();\\n}\\n\\n/// @notice Returns bonds to the keep members.\\nfunction freeMembersBonds() internal {\\n    for (uint256 i = 0; i < members.length; i++) {\\n        keepBonding.freeBond(members[i], uint256(address(this)));\\n    }\\n}\\n```\\n\\n```\\n/// @notice Releases the bond and moves the bond value to the operator's\\n/// unbounded value pool.\\n/// @dev Function requires that caller is the holder of the bond which is\\n/// being released.\\n/// @param operator Address of the bonded operator.\\n/// @param referenceID Reference ID of the bond.\\nfunction freeBond(address operator, uint256 referenceID) public {\\n    address holder = msg.sender;\\n    bytes32 bondID = keccak256(\\n        abi.encodePacked(operator, holder, referenceID)\\n    );\\n\\n    require(lockedBonds[bondID] > 0, "Bond not found");\\n\\n    uint256 amount = lockedBonds[bondID];\\n    lockedBonds[bondID] = 0;\\n    unbondedValue[operator] = amount;\\n}\\n```\\n

Impact:

Recommended mitigation:
Make sure the keep can be set to an end-state (closed/inactive) indicating its end-of-life even if the bond has been seized before. Avoid throwing an exception when freeing member bonds to avoid blocking the unlocking of bonds.
Vulnerability: tbtc - provideFundingECDSAFraudProof attempts to burn non-existent funds
Severity: medium

Description:
The funding flow was recently changed from requiring the funder to provide a bond that stays in the Deposit contract to forwarding the funds to the keep, paying for the keep setup.\\nSo at a high level, the funding bond was designed to ensure that funders had some minimum skin in the game, so that DoSing signers/the system was expensive. The upside was that we could refund it in happy paths. Now that we've realized that opening the keep itself will cost enough to prevent DoS, the concept of refunding goes away entirely. We definitely missed cleaning up the funder handling in provideFundingECDSAFraudProof though.\\n```\\n// If the funding timeout has elapsed, punish the funder too!\\nif (block.timestamp > \\_d.fundingProofTimerStart + TBTCConstants.getFundingTimeout()) {\\n    address(0).transfer(address(this).balance);  // Burn it all down (fire emoji)\\n    \\_d.setFailedSetup();\\n```\\n

Impact:

Recommended mitigation:
Remove the line that attempts to punish the funder by burning the Deposit contract balance which is zero due to recent changes in how the payment provided with createNewDepositis handled.