Platform: Code4rena
Start Date: 01/12/2022
Pot Size: $26,900 USDC
Total HM: 3
Participants: 19
Period: 4 days
Judge: GalloDaSballo
Id: 188
League: ETH
Rank: 12/19
Findings: 2
Award: $665.02
🌟 Selected for report: 0
🚀 Solo Findings: 0
🌟 Selected for report: 0xSmartContract
611.5965 USDC - $611.60
setExecutor
functionSince the setExecutor
function can only be called once, which is to set the executor, meanwhile if the executor is not set, the executeCalls
will always revert because not passing the _isAuthorized()
function, so it's best just remove the setExecutor
and directly set the executor on the constructor (and make immutable).
As simple as, if executor is yet to be known, just wait until it's known, then deployment the contract.
139: function setExecutor(ICrossChainExecutor _executor) external { 140: require(address(executor) == address(0), "Relayer/executor-already-set"); 141: executor = _executor; 142: }
File: CallLib.sol 64: (bool _success, bytes memory _returnData) = _call.target.call( 65: abi.encodePacked(_call.data, _nonce, _sender) 66: );
abi.encodePacked is a non standard encoding that should preferably not be used due to its ambiguity with dynamic types, if you are not dealing with dynamic types and inputs which size is already multiple of 32 then they can both give the same output.
In general, the encoding is ambiguous as soon as there are two dynamically-sized elements, because of the missing length field.
_calls
is empty (length is 0)All of Executors (EthereumToArbitrumExecutor, EthereumToOptimismExecutor, EthereumToPolygonExecutor), when trying to call CallLib.executeCalls
the _calls
parameter input are not checked if it cointains call (length > 0) or not.
We can just revert if the _calls
length is 0
On setRelayer
function, it doesnt have any emit event, it's good practice to emit an event when storage variable is changed.
#0 - GalloDaSballo
2022-12-14T19:59:58Z
I think I'll ignore the finding as technically that's correct, but you need relayer and executor to be set, it's a chicken and egg problem
Disputed by Sponsor
Will flag to sponsor as packed vs unpacked will have a different result, but am not sure this is valid Specifically I believe call.data and nonce can actually clash and create undefined scenarios.
This I believe is the first time in C4 in which I believe encode should be preferred to avoid very low probably edge cases
R
NC
#1 - GalloDaSballo
2022-12-26T22:08:03Z
@PierrickGT I made a note to flag this one, specifically I have some reason to believe that abi.encode should be preferred vs abi.encodePacked
1L 1R 1NC
#2 - c4-judge
2022-12-26T23:14:08Z
GalloDaSballo marked the issue as grade-a
#3 - PierrickGT
2022-12-27T18:30:28Z
@GalloDaSballo we follow EIP-2771 to happen the nonce
and sender
to the call data which in most cases will be a function call using abi.encodeWithSignature()
:
https://github.com/pooltogether/ERC5164/blob/62d562b19e0d08e54e498e2322af456f6abd6550/script/bridge/BridgeToOptimismGoerli.s.sol#L23
If we use abi.encode
instead of abi.encodePacked
, the call will revert and fail to execute.
#4 - GalloDaSballo
2022-12-27T19:14:54Z
I believe, per the Sponsors comment, that while I had some concerns regarding calldata clashing, we must use abi.encodePacked per the EIP.
Will be updating the score to reflect that.
My thoughts around the clash risk are that I can imagine a situation using fallback, in which the last param is ignored and instead a injected param via abi.encodePacked is used.
#5 - GalloDaSballo
2023-01-05T08:17:24Z
After removing 1L
1R 1NC
The report is still grade A
53.4153 USDC - $53.42
relayer
immutableSince relayer can only be set once via setRelayer
, this concept is similar to immutable
, and this immutable will get directly embedded in bytecode, saving SLOAD.
In order for relayer
to be immutable, the relayer
need to be set inside constructor, and remove the setRelayer
.
If we think about it again, the setRelayer
can only be called once when the relayer is not set (address 0), and the executeCalls
will revert if the relayer is not set (because of _isAuthorized
). There are no other function inside Executor
contracts beside executeCalls
therefore we can safely remove the setRelayer
and set the relayer
inside constructor with no changes made to logic.
#0 - GalloDaSballo
2022-12-26T21:21:01Z
1050
#1 - c4-judge
2022-12-26T23:13:35Z
GalloDaSballo marked the issue as grade-b