Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

improve: Make changes after running Slither on SpokePool contracts #229

Merged
merged 30 commits into from
Feb 16, 2023

Conversation

nicholaspai
Copy link
Member

Slither is a Solidity static analysis framework written in Python 3. It runs a
suite of vulnerability detectors, prints visual information about contract details, and provides an API to easily write
custom analyses. Slither enables developers to find vulnerabilities, enhance their code comprehension, and quickly
prototype custom analyses.

Spire-Contracts has been analyzed using Slither@0.9.2 and no major bugs was found. To rerun the analytics, run:

slither contracts/SpokePool.sol
\ --solc-remaps @=node_modules/@
\ --solc-args "--optimize --optimize-runs 1000000"
\ --filter-paths "node_modules"
\ --exclude naming-convention

You can replace SpokePool.sol with the specific contract you want to analyze. For this PR I ran this on the following contracts:

  • Ovm_SpokePool.sol
  • Polygon_SpokePool.sol
  • Optimism_SpokePool.sol
  • Boba_SpokePool.sol
  • Ethereum_SpokePool.sol
  • SpokePool.sol
  • PolygonTokenBridger

This covered any contracts imported by those contracts. Notably I did not make any changes recommended to HubPool.sol, even though there were some, because we don't have plans to redeploy that soon.

Signed-off-by: nicholaspai npai.nyc@gmail.com

nicholaspai and others added 25 commits January 4, 2023 17:45
This PR makes SpokePools compatible with OpenZeppelin's upgradeable proxy pattern
UUPS proxies allow the implementation contract to define the conditions under which an upgrade can be called, compared to a Transparent proxy in which only the owner of the proxy can upgrade.

This allows the SpokePool upgrades to be triggered by the cross domain admin via the HubPool
 into nick/acx-520-add-features-to-spokepool-to-make-future
 into nick/acx-520-add-features-to-spokepool-to-make-future
 into nick/acx-520-add-features-to-spokepool-to-make-future
Signed-off-by: nicholaspai <npai.nyc@gmail.com>
Co-authored-by: Paul <108695806+pxrl@users.noreply.github.com>
Signed-off-by: nicholaspai <npai.nyc@gmail.com>
Signed-off-by: nicholaspai <npai.nyc@gmail.com>
Signed-off-by: nicholaspai <npai.nyc@gmail.com>
 into nick/acx-520-add-features-to-spokepool-to-make-future
Signed-off-by: nicholaspai <npai.nyc@gmail.com>
@@ -59,6 +59,7 @@ contract Ovm_SpokePool is SpokePool {
l1Gas = 5_000_000;
__SpokePool_init(_initialDepositId, _crossDomainAdmin, _hubPool, _wrappedNativeToken, _timerAddress);
messenger = Lib_PredeployAddresses.L2_CROSS_DOMAIN_MESSENGER;
require(_l2Eth != address(0), "Invalid L2 ETH address");
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Question: Slither recommends we add these zero address checks. It does add a fixed gas cost to construction but adds some safety. Thoughts?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure about the value of these checks. 0 is only one possible invalid address, but there are ~infinitely many other invalid addresses as well. The only way we can really be confident that the address is set correctly is via post-deployment verification.

I'm not firmly against it or anything, it just seems a bit redundant and might give a false sense of security.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i think I agree with your comment and will remove these zero address checks

@@ -38,7 +38,7 @@ interface MaticToken {
* This ultimately allows create2 to generate deterministic addresses that don't depend on the transaction count of the
* sender.
*/
contract PolygonTokenBridger is Lockable {
contract PolygonTokenBridger is ReentrancyGuard {
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Slither had some complaints about Lockable but i figured this was a good time to use OZ's ReentrancyGuard instead

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think this contract will be in scope for the audit. Are we okay making this change?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

good point, removed

@@ -290,6 +290,11 @@ abstract contract SpokePool is
* to ensure that a small input range doesn't limit which indices this method is able to reach.
*/
function emergencyDeleteRootBundle(uint256 rootBundleId) public override onlyAdmin nonReentrant {
// Deleting a struct containing a mapping does not delete the mapping in Solidity, therefore the bitmap's
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This warning was the most interesting in this PR IMO

Copy link
Contributor

@pxrl pxrl Feb 6, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agree - interesting find. It seems OK to suppress the warning:

  • In the event that a bad bundle makes it to the SpokePool and is subsequently deleted, rootBundle.relayerRefundRoot and rootBundle.slowRelayRoot will be zeroed.
  • Proof verification is one of the first things to occur in _executeRelayerRefundLeaf() and _executeSlowRelayLeaf().
  • Any immediate attempt to execute its leaves via executeRelayerRefundRoot() or executeSlowRelayLeaf() should revert on failed proof verification.
  • This array entry will, at some later time, be overwritten with different data, but the bitmap will indefinitely record that the old deleted leaves remain unclaimed. Any attempt to execute those would rely on collisions and should therefore still fail on proof verification. Wrong & irrelevant, as identified by Matt.

Additionally:

  • emergencyDeleteRootBundle() is an admin function, so this can't be initiated by any arbitrary caller. To end up here, a specific sequence of unlikely of events needs to occur. It seems highly improbable that a malicious third party could orchestrate that.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree with @pxrl. Just to add one point: we will never reuse this array slot (because deleting an element of an array does not actually reduce the array.length I believe, meaning the slot is still reserved), so the mapping will never be read or written to again, by definition, no? So we shouldn't need to worry about this at all right?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just to add one point: we will never reuse this array slot (because deleting an element of an array does not actually reduce the array.length I believe, meaning the slot is still reserved), so the mapping will never be read or written to again, by definition, no? So we shouldn't need to worry about this at all right?

You're right - I'd understood that the array element was actually removed, but I can see that it's only zeroed. So my 4th point was garbage!

require(
getCurrentTime() >= quoteTimestamp - depositQuoteTimeBuffer &&
getCurrentTime() <= quoteTimestamp + depositQuoteTimeBuffer,
"invalid quote time"
);

// Increment count of deposits so that deposit ID for this spoke pool is unique.
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Move the storage variable modification to numberOfDeposits above the external call safeTransferFrom to align with CEI pattern

@@ -8,5 +8,5 @@ interface WETH9Interface {

function balanceOf(address guy) external view returns (uint256 wad);

function transfer(address guy, uint256 wad) external;
function transfer(address guy, uint256 wad) external returns (bool);
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This interface was incorrect and inconsistent with how we implement WETH9

@nicholaspai nicholaspai marked this pull request as ready for review February 4, 2023 18:16
@nicholaspai nicholaspai requested review from mrice32 and pxrl February 4, 2023 18:16
Copy link
Contributor

@pxrl pxrl left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good initiative!

@@ -290,6 +290,11 @@ abstract contract SpokePool is
* to ensure that a small input range doesn't limit which indices this method is able to reach.
*/
function emergencyDeleteRootBundle(uint256 rootBundleId) public override onlyAdmin nonReentrant {
// Deleting a struct containing a mapping does not delete the mapping in Solidity, therefore the bitmap's
Copy link
Contributor

@pxrl pxrl Feb 6, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agree - interesting find. It seems OK to suppress the warning:

  • In the event that a bad bundle makes it to the SpokePool and is subsequently deleted, rootBundle.relayerRefundRoot and rootBundle.slowRelayRoot will be zeroed.
  • Proof verification is one of the first things to occur in _executeRelayerRefundLeaf() and _executeSlowRelayLeaf().
  • Any immediate attempt to execute its leaves via executeRelayerRefundRoot() or executeSlowRelayLeaf() should revert on failed proof verification.
  • This array entry will, at some later time, be overwritten with different data, but the bitmap will indefinitely record that the old deleted leaves remain unclaimed. Any attempt to execute those would rely on collisions and should therefore still fail on proof verification. Wrong & irrelevant, as identified by Matt.

Additionally:

  • emergencyDeleteRootBundle() is an admin function, so this can't be initiated by any arbitrary caller. To end up here, a specific sequence of unlikely of events needs to occur. It seems highly improbable that a malicious third party could orchestrate that.

@@ -59,6 +59,7 @@ contract Ovm_SpokePool is SpokePool {
l1Gas = 5_000_000;
__SpokePool_init(_initialDepositId, _crossDomainAdmin, _hubPool, _wrappedNativeToken, _timerAddress);
messenger = Lib_PredeployAddresses.L2_CROSS_DOMAIN_MESSENGER;
require(_l2Eth != address(0), "Invalid L2 ETH address");
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure about the value of these checks. 0 is only one possible invalid address, but there are ~infinitely many other invalid addresses as well. The only way we can really be confident that the address is set correctly is via post-deployment verification.

I'm not firmly against it or anything, it just seems a bit redundant and might give a false sense of security.

Comment on lines +54 to +58
slither contracts/SpokePool.sol
\ --solc-remaps @=node_modules/@
\ --solc-args "--optimize --optimize-runs 1000000"
\ --filter-paths "node_modules"
\ --exclude naming-convention
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we make a shortcut for this via package.json?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hmm i'm having trouble getting this to work from an npm run will look into it further

Base automatically changed from nick/acx-520-add-features-to-spokepool-to-make-future to master February 10, 2023 20:42
Copy link
Contributor

@mrice32 mrice32 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM!

@@ -38,7 +38,7 @@ interface MaticToken {
* This ultimately allows create2 to generate deterministic addresses that don't depend on the transaction count of the
* sender.
*/
contract PolygonTokenBridger is Lockable {
contract PolygonTokenBridger is ReentrancyGuard {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think this contract will be in scope for the audit. Are we okay making this change?

@nicholaspai nicholaspai merged commit 3ff88e2 into master Feb 16, 2023
@nicholaspai nicholaspai deleted the npai/slither branch February 16, 2023 16:02
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants