Skip to content
This repository was archived by the owner on Apr 12, 2021. It is now read-only.

Commit fbb2240

Browse files
committed
Disallow relayMessage to L2 to L1 Message Passer
This change prevents call spoofing on L2. Adds a test based on logs and successfulMessages mapping.
1 parent c39fcc4 commit fbb2240

File tree

4 files changed

+48
-14
lines changed

4 files changed

+48
-14
lines changed

contracts/optimistic-ethereum/OVM/bridge/messaging/OVM_L1CrossDomainMessenger.sol

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,9 @@ import { Abs_BaseCrossDomainMessenger } from "./Abs_BaseCrossDomainMessenger.sol
1919

2020
/**
2121
* @title OVM_L1CrossDomainMessenger
22-
* @dev The L1 Cross Domain Messenger contract sends messages from L1 to L2, and relays messages from L2 onto L1.
23-
* In the event that a message sent from L1 to L2 is rejected for exceeding the L2 epoch gas limit, it can be resubmitted
24-
* via this contract's replay function.
22+
* @dev The L1 Cross Domain Messenger contract sends messages from L1 to L2, and relays messages from L2 onto L1.
23+
* In the event that a message sent from L1 to L2 is rejected for exceeding the L2 epoch gas limit, it can be resubmitted
24+
* via this contract's replay function.
2525
*
2626
* Compiler used: solc
2727
* Runtime target: EVM

contracts/optimistic-ethereum/OVM/bridge/messaging/OVM_L2CrossDomainMessenger.sol

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ import { Abs_BaseCrossDomainMessenger } from "./Abs_BaseCrossDomainMessenger.sol
1818
* @title OVM_L2CrossDomainMessenger
1919
* @dev The L2 Cross Domain Messenger contract sends messages from L2 to L1, and is the entry point
2020
* for L2 messages sent via the L1 Cross Domain Messenger.
21-
*
21+
*
2222
* Compiler used: optimistic-solc
2323
* Runtime target: OVM
2424
*/
@@ -75,6 +75,16 @@ contract OVM_L2CrossDomainMessenger is iOVM_L2CrossDomainMessenger, Abs_BaseCros
7575
"Provided message has already been received."
7676
);
7777

78+
// Prevent calls to OVM_L2ToL1MessagePasser, which would enable
79+
// an attacker to maliciously craft the _message to spoof
80+
// a call from any L2 account.
81+
if(_target == resolve("OVM_L2ToL1MessagePasser")){
82+
// Write to the successfulMessages mapping and return immediately.
83+
successfulMessages[xDomainCalldataHash] = true;
84+
return;
85+
}
86+
87+
7888
xDomainMsgSender = _sender;
7989
(bool success, ) = _target.call(_message);
8090
xDomainMsgSender = DEFAULT_XDOMAIN_SENDER;

contracts/optimistic-ethereum/OVM/execution/OVM_ExecutionManager.sol

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -159,7 +159,7 @@ contract OVM_ExecutionManager is iOVM_ExecutionManager, Lib_AddressResolver {
159159
override
160160
public
161161
{
162-
require(transactionContext.ovmNUMBER == 0, "Only be callable at the start of a transaction");
162+
require(transactionContext.ovmNUMBER == 0, "Only callable at the start of a transaction");
163163
// Store our OVM_StateManager instance (significantly easier than attempting to pass the
164164
// address around in calldata).
165165
ovmStateManager = iOVM_StateManager(_ovmStateManager);
@@ -640,7 +640,7 @@ contract OVM_ExecutionManager is iOVM_ExecutionManager, Lib_AddressResolver {
640640
{
641641
// DELEGATECALL does not change anything about the message context.
642642
MessageContext memory nextMessageContext = messageContext;
643-
643+
644644
return _callContract(
645645
nextMessageContext,
646646
_gasLimit,
@@ -915,7 +915,7 @@ contract OVM_ExecutionManager is iOVM_ExecutionManager, Lib_AddressResolver {
915915
/**
916916
* Handles all interactions which involve the execution manager calling out to untrusted code (both calls and creates).
917917
* Ensures that OVM-related measures are enforced, including L2 gas refunds, nuisance gas, and flagged reversions.
918-
*
918+
*
919919
* @param _nextMessageContext Message context to be used for the external message.
920920
* @param _gasLimit Amount of gas to be passed into this message.
921921
* @param _contract OVM address being called or deployed to
@@ -1028,7 +1028,7 @@ contract OVM_ExecutionManager is iOVM_ExecutionManager, Lib_AddressResolver {
10281028
* Handles the creation-specific safety measures required for OVM contract deployment.
10291029
* This function sanitizes the return types for creation messages to match calls (bool, bytes).
10301030
* This allows for consistent handling of both types of messages in _handleExternalMessage().
1031-
*
1031+
*
10321032
* @param _gasLimit Amount of gas to be passed into this creation.
10331033
* @param _creationCode Code to pass into CREATE for deployment.
10341034
* @param _address OVM address being deployed to.
@@ -1075,7 +1075,7 @@ contract OVM_ExecutionManager is iOVM_ExecutionManager, Lib_AddressResolver {
10751075

10761076
// Actually execute the EVM create message,
10771077
address ethAddress = Lib_EthUtils.createContract(_creationCode);
1078-
1078+
10791079
if (ethAddress == address(0)) {
10801080
// If the creation fails, the EVM lets us grab its revert data. This may contain a revert flag
10811081
// to be used above in _handleExternalMessage.
@@ -1851,7 +1851,7 @@ contract OVM_ExecutionManager is iOVM_ExecutionManager, Lib_AddressResolver {
18511851
if (created == address(0)) {
18521852
return (false, revertData);
18531853
} else {
1854-
// The eth_call RPC endpoint for to = undefined will return the deployed bytecode
1854+
// The eth_call RPC endpoint for to = undefined will return the deployed bytecode
18551855
// in the success case, differing from standard create messages.
18561856
return (true, Lib_EthUtils.getCode(created));
18571857
}

test/contracts/OVM/bridge/base/OVM_L2CrossDomainMessenger.spec.ts

Lines changed: 28 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import {
1313
NON_ZERO_ADDRESS,
1414
getXDomainCalldata,
1515
} from '../../../../helpers'
16+
import { solidityKeccak256 } from 'ethers/lib/utils'
1617

1718
describe('OVM_L2CrossDomainMessenger', () => {
1819
let signer: Signer
@@ -146,16 +147,39 @@ describe('OVM_L2CrossDomainMessenger', () => {
146147
).to.be.revertedWith('xDomainMessageSender is not set')
147148
})
148149

149-
it('should revert if trying to send the same message twice', async () => {
150+
it.only('should not make a call if the target is the L2 MessagePasser', async () => {
150151
Mock__OVM_L1MessageSender.smocked.getL1MessageSender.will.return.with(
151152
Mock__OVM_L1CrossDomainMessenger.address
152153
)
154+
target = await AddressManager.getAddress('OVM_L2ToL1MessagePasser')
155+
message = Mock__OVM_L2ToL1MessagePasser.interface.encodeFunctionData('passMessageToL1(bytes)', [
156+
NON_NULL_BYTES32,
157+
])
153158

154-
await OVM_L2CrossDomainMessenger.relayMessage(target, sender, message, 0)
159+
const resProm = OVM_L2CrossDomainMessenger.relayMessage(target, sender, message, 0)
155160

161+
// The call to relayMessage() should succeed.
156162
await expect(
157-
OVM_L2CrossDomainMessenger.relayMessage(target, sender, message, 0)
158-
).to.be.revertedWith('Provided message has already been received.')
163+
resProm
164+
).to.not.be.reverted
165+
166+
// There should be no 'relayedMessage' event logged in the receipt.
167+
const logs = (
168+
await Mock__OVM_L2ToL1MessagePasser.provider.getTransactionReceipt(
169+
(await resProm).hash
170+
)
171+
).logs
172+
expect(logs).to.deep.equal([])
173+
174+
// The message should be registered as successful.
175+
expect(
176+
await OVM_L2CrossDomainMessenger.successfulMessages(
177+
solidityKeccak256(
178+
["bytes"],
179+
[getXDomainCalldata(await signer.getAddress(), target, message, 0)]
180+
)
181+
)
182+
).to.be.true
159183
})
160184
})
161185
})

0 commit comments

Comments
 (0)