Arbitrum BoLD - josephdara's results

A new dispute protocol that unlocks permissionless validation for Arbitrum chains.

General Information

Platform: Code4rena

Start Date: 10/05/2024

Pot Size: $300,500 USDC

Total HM: 4

Participants: 27

Period: 17 days

Judge: Picodes

Total Solo HM: 1

Id: 375

League: ETH

Arbitrum Foundation

Findings Distribution

Researcher Performance

Rank: 10/27

Findings: 2

Award: $5,993.97

🌟 Selected for report: 0

🚀 Solo Findings: 0

Findings Information

🌟 Selected for report: SpicyMeatball

Also found by: dontonka, josephdara

Labels

bug
2 (Med Risk)
satisfactory
sufficient quality report
:robot:_primary
:robot:_52_group
duplicate-5

Awards

5993.9737 USDC - $5,993.97

External Links

Lines of code

https://github.com/code-423n4/2024-05-arbitrum-foundation/blob/6f861c85b281a29f04daacfe17a2099d7dad5f8f/src/rollup/BOLDUpgradeAction.sol#L341-L363

Vulnerability details

Impact

cleanupOldRollup() gets the stakerCount from the oldRollup, then loops over that number if they are less than 50 or 50 addresses if they are over 50 stakers.

  • Rollup.stakerCount() gets the number of staker
  • Rollup.getStakerAddress() gets the staker address from the _stakerList array
  • Rollup.forceRefundStaker() makes the staker's stake withdrawable and then deletes the staker

However, by deleting a staker, the array is being reduced from the right while the by incrementing the loop, we are skipping addresses from the left.

Proof of Concept

        for (uint64 i = 0; i < stakerCount; i++) {
            address stakerAddr = ROLLUP_READER.getStakerAddress(i);

            OldStaker memory staker = ROLLUP_READER.getStaker(stakerAddr);
            if (staker.isStaked && staker.currentChallenge == 0) {
                address[] memory stakersToRefund = new address[](1);
                stakersToRefund[0] = stakerAddr;

                IOldRollupAdmin(address(OLD_ROLLUP)).forceRefundStaker(stakersToRefund);
            }

By increaasing the index i, and getting the next staker address, the loop will run out of bounds since the array is being reduced in the OLD_ROLLUP contract.

This is not an issue with the old rollup, but with the loop depicted above.

Tools Used

MANUAL REVIEW, Josephdara

Always get the first address in the loop, only change that if the currentChallenge for the first address is not zero.

uint64 j;
       for (uint64 i = 0; i < stakerCount; i++) {
            address stakerAddr = ROLLUP_READER.getStakerAddress(j);

            OldStaker memory staker = ROLLUP_READER.getStaker(stakerAddr);
            if (staker.isStaked && staker.currentChallenge == 0) {
                address[] memory stakersToRefund = new address[](1);
                stakersToRefund[0] = stakerAddr;

                IOldRollupAdmin(address(OLD_ROLLUP)).forceRefundStaker(stakersToRefund);
            }else{
                j++;
            }
        }

Assessed type

Error

#0 - c4-judge

2024-06-05T08:21:20Z

Picodes changed the severity to QA (Quality Assurance)

#1 - Josephdara

2024-06-05T21:33:57Z

Hi @Picodes Thanks for the judging. I believe this bug has been grouped wrongly. It is a duplicate of issues #5 Please review.

#2 - c4-judge

2024-06-08T16:43:12Z

This previously downgraded issue has been upgraded by Picodes

#3 - c4-judge

2024-06-08T16:43:15Z

Picodes marked the issue as not a duplicate

#4 - c4-judge

2024-06-08T16:43:36Z

Picodes marked the issue as satisfactory

#5 - c4-judge

2024-06-08T16:43:43Z

Picodes marked the issue as duplicate of #5

Awards

0 USDC - $0.00

Labels

bug
downgraded by judge
grade-b
QA (Quality Assurance)
sufficient quality report
:robot:_39_group
duplicate-42
Q-16

External Links

Lines of code

https://github.com/code-423n4/2024-05-arbitrum-foundation/blob/6f861c85b281a29f04daacfe17a2099d7dad5f8f/src/rollup/BOLDUpgradeAction.sol#L344-L359

Vulnerability details

Impact

Although it is stated that not more than 50 stakers are expected, this remains a projection. Therefore if there exists more than 50 stakers, these stakers would be stuck on the older rollup.

Their funds would also be permanently stuck on the old rollup contract.

Proof of Concept

      uint64 stakerCount = ROLLUP_READER.stakerCount();
        // since we for-loop these stakers we set an arbitrary limit - we dont
        // expect any instances to have close to this number of stakers
        if (stakerCount > 50) {
            stakerCount = 50;
        }

        for (uint64 i = 0; i < stakerCount; i++) {
            address stakerAddr = ROLLUP_READER.getStakerAddress(i);

            OldStaker memory staker = ROLLUP_READER.getStaker(stakerAddr);
            if (staker.isStaked && staker.currentChallenge == 0) {
                address[] memory stakersToRefund = new address[](1);
                stakersToRefund[0] = stakerAddr;

                IOldRollupAdmin(address(OLD_ROLLUP)).forceRefundStaker(stakersToRefund);
            }
        }

Tools Used

Manual Review, Josephdara

Implement a function to complete refunds, or transfer the ownership of the old rollup contract to a new address after upgrade to allow for manual forceRefund calls.

Assessed type

Other

#0 - c4-judge

2024-06-05T08:21:20Z

Picodes changed the severity to QA (Quality Assurance)

#1 - c4-judge

2024-06-08T16:43:10Z

This previously downgraded issue has been upgraded by Picodes

#2 - c4-judge

2024-06-08T16:43:23Z

Picodes changed the severity to QA (Quality Assurance)

#3 - c4-judge

2024-06-10T17:23:44Z

Picodes marked the issue as grade-b

AuditHub

A portfolio for auditors, a security profile for protocols, a hub for web3 security.

Built bymalatrax © 2024

Auditors

Browse

Contests

Browse

Get in touch

ContactTwitter