NFTBridge
60,000 USDC
View results
Submission Details
Severity: high
Valid

Unbounded loop in the function _white_list_collection

Summary

Due to unbounded loop, admin can not change the whitelist status of a collection from true to false.

Vulnerability Details

The internal function _white_list_collection is invoked when a new L2 collection is deployed during withdrawal or the admin is changing the whitelisting status of a collection.
https://github.com/Cyfrin/2024-07-ark-project/blob/main/apps/blockchain/starknet/src/bridge.cairo#L473
https://github.com/Cyfrin/2024-07-ark-project/blob/main/apps/blockchain/starknet/src/bridge.cairo#L319

In this function, if the whitelisting status of a collection is going to be changed from true to false, the else-clause will be executed.

else {
// change head
if prev == collection {
let (_, next) = self.white_listed_list.read(prev);
self.white_listed_list.write(collection, (false, no_value));
self.white_listed_head.write(next);
return;
}
// removed element from linked list
loop {
let (active, next) = self.white_listed_list.read(prev);
if next.is_zero() {
// end of list
break;
}
if !active {
break;
}
if next == collection {
let (_, target) = self.white_listed_list.read(collection);
self.white_listed_list.write(prev, (active, target));
break;
}
};
self.white_listed_list.write(collection, (false, no_value));
}

https://github.com/Cyfrin/2024-07-ark-project/blob/main/apps/blockchain/starknet/src/bridge.cairo#L515

The issue is as follows:

  • Suppose there are three collections collection1, collection2, and collection3, and all of them are active (their whitelist status is true).

  • So, the head of the linked list is collection1.

  • Suppose admin is going to change the status of collection3 from true to false.

  • In the else-clause, the loop-body will be executed.

// removed element from linked list
loop {
let (active, next) = self.white_listed_list.read(prev);
if next.is_zero() {
// end of list
break;
}
if !active {
break;
}
if next == collection {
let (_, target) = self.white_listed_list.read(collection);
self.white_listed_list.write(prev, (active, target));
break;
}
};

https://github.com/Cyfrin/2024-07-ark-project/blob/main/apps/blockchain/starknet/src/bridge.cairo#L524

  • In the first iteration, prev = collection1, next = collection2, and collection = collection3.

  • At the end of the first iteration, since prev is not updated, the loop repeats the steps again indefinitely. In other words, at the end of the loop the statement prev = next; is missing.

As a result, admin can not change the whitelist status of a collection from true to false if the collection is at least the third element in the linked list.

PoC

In the following test, three collection with status true are added. Then the status of the last collection is changed from true to false. Because of the infinite loop, the transaction reverts. To run the code use snforge test starklane::tests::bridge_t::tests::whitelist_collection_infinite_loop_when_collection_is_removed --exact.

#[test]
fn whitelist_collection_infinite_loop_when_collection_is_removed() {
let erc721b_contract_class = declare("erc721_bridgeable");
let BRIDGE_ADMIN = starknet::contract_address_const::<'starklane'>();
let BRIDGE_L1 = EthAddress { address: 'starklane_l1' };
let bridge_address = deploy_starklane(BRIDGE_ADMIN, BRIDGE_L1, erc721b_contract_class.class_hash);
let bridge = IStarklaneDispatcher { contract_address: bridge_address };
start_prank(CheatTarget::One(bridge_address), BRIDGE_ADMIN);
bridge.enable_white_list(true);
stop_prank(CheatTarget::One(bridge_address));
let collection1 = starknet::contract_address_const::<'collection1'>();
let collection2 = starknet::contract_address_const::<'collection2'>();
let collection3 = starknet::contract_address_const::<'collection3'>();
start_prank(CheatTarget::One(bridge_address), BRIDGE_ADMIN);
bridge.white_list_collection(collection1, true);
bridge.white_list_collection(collection2, true);
bridge.white_list_collection(collection3, true);
stop_prank(CheatTarget::One(bridge_address));
let white_listed = bridge.get_white_listed_collections();
assert_eq!(white_listed.len(), 3, "White list shall contain 3 elements");
assert_eq!(*white_listed.at(0), collection1, "Wrong collection address in white list");
assert_eq!(*white_listed.at(1), collection2, "Wrong collection address in white list");
assert_eq!(*white_listed.at(2), collection3, "Wrong collection address in white list");
assert!(bridge.is_white_listed(collection1), "Collection1 should be whitelisted");
assert!(bridge.is_white_listed(collection2), "Collection1 should be whitelisted");
assert!(bridge.is_white_listed(collection3), "Collection1 should be whitelisted");
start_prank(CheatTarget::One(bridge_address), BRIDGE_ADMIN);
bridge.white_list_collection(collection3, false); // last collection is changed to false
stop_prank(CheatTarget::One(bridge_address));
let white_listed = bridge.get_white_listed_collections();
assert_eq!(white_listed.len(), 2, "White list shall contain 2 elements");
assert_eq!(*white_listed.at(0), collection1, "Wrong collection address in white list");
assert_eq!(*white_listed.at(1), collection2, "Wrong collection address in white list");
assert!(bridge.is_white_listed(collection1), "Collection1 should be whitelisted");
assert!(bridge.is_white_listed(collection2), "Collection1 should not be whitelisted");
assert!(!bridge.is_white_listed(collection3), "Collection1 should be whitelisted");
}

The output is:

[FAIL] starklane::tests::bridge_t::tests::whitelist_collection_infinite_loop_when_collection_is_removed
Failure data:
Got an exception while executing a hint: Hint Error: Error in the called contract (0x03b24bdfb3983f3361a7f81e871041cc45f3e1c21bfe3f1cbcaf7bec224627d5):
Error at pc=0:17590:
Could not reach the end of the program. RunResources has no remaining steps.
Cairo traceback (most recent call last):
Unknown location (pc=0:17592)
Unknown location (pc=0:17592)
Unknown location (pc=0:17592)
Unknown location (pc=0:17592)
Unknown location (pc=0:17592)
Unknown location (pc=0:17592)
Unknown location (pc=0:17592)
Unknown location (pc=0:17592)
Unknown location (pc=0:17592)
Unknown location (pc=0:17592)
Unknown location (pc=0:17592)
Unknown location (pc=0:17592)
Unknown location (pc=0:17592)
Unknown location (pc=0:17592)
Unknown location (pc=0:17592)
Unknown location (pc=0:17592)
Unknown location (pc=0:17592)
Unknown location (pc=0:17592)
Unknown location (pc=0:17592)
Unknown location (pc=0:17592)

Impact

  • Admin can not change the whitelist status of a collection from true to false.

Tools Used

Recommendations

Following is recommended:

loop {
let (active, next) = self.white_listed_list.read(prev);
if next.is_zero() {
// end of list
break;
}
if !active {
break;
}
if next == collection {
let (_, target) = self.white_listed_list.read(collection);
self.white_listed_list.write(prev, (active, target));
break;
}
+ prev = next;
};

https://github.com/Cyfrin/2024-07-ark-project/blob/main/apps/blockchain/starknet/src/bridge.cairo#L524

Updates

Lead Judging Commences

n0kto Lead Judge 10 months ago
Submission Judgement Published
Validated
Assigned finding tags:

finding-L2-unwhitelist-from-third collection-impossible

Likelyhood: High, owner can only unwhitelist the 2 first collections. Impact: Medium/High, owner has to empty the list to remove any collection, and replace all the new ones.

Support

FAQs

Can't find an answer? Chat with us on Discord, Twitter or Linkedin.