From fd540c91b46cf2544024d025f6a803e8bb4e5ebc Mon Sep 17 00:00:00 2001 From: Csongor Kiss Date: Tue, 23 Aug 2022 20:41:03 +0200 Subject: [PATCH] ethereum: Check that bytes32 fits into 20 bytes before truncating (#1457) --- ethereum/contracts/bridge/Bridge.sol | 15 +++++++++++++-- ethereum/forge-test/Bridge.t.sol | 16 ++++++++++++++++ 2 files changed, 29 insertions(+), 2 deletions(-) create mode 100644 ethereum/forge-test/Bridge.t.sol diff --git a/ethereum/contracts/bridge/Bridge.sol b/ethereum/contracts/bridge/Bridge.sol index a65250a48..e78dcc064 100644 --- a/ethereum/contracts/bridge/Bridge.sol +++ b/ethereum/contracts/bridge/Bridge.sol @@ -472,6 +472,17 @@ contract Bridge is BridgeGovernance, ReentrancyGuard { _completeTransfer(encodedVm, true); } + /* + * @dev Truncate a 32 byte array to a 20 byte address. + * Reverts if the array contains non-0 bytes in the first 12 bytes. + * + * @param bytes32 bytes The 32 byte array to be converted. + */ + function _truncateAddress(bytes32 b) internal pure returns (address) { + require(bytes12(b) == 0, "invalid EVM address"); + return address(uint160(uint256(b))); + } + // Execute a Transfer message function _completeTransfer(bytes memory encodedVm, bool unwrapWETH) internal returns (bytes memory) { (IWormhole.VM memory vm, bool valid, string memory reason) = wormhole().parseAndVerifyVM(encodedVm); @@ -482,7 +493,7 @@ contract Bridge is BridgeGovernance, ReentrancyGuard { BridgeStructs.Transfer memory transfer = _parseTransferCommon(vm.payload); // payload 3 must be redeemed by the designated proxy contract - address transferRecipient = address(uint160(uint256(transfer.to))); + address transferRecipient = _truncateAddress(transfer.to); if (transfer.payloadID == 3) { require(msg.sender == transferRecipient, "invalid sender"); } @@ -494,7 +505,7 @@ contract Bridge is BridgeGovernance, ReentrancyGuard { IERC20 transferToken; if (transfer.tokenChain == chainId()) { - transferToken = IERC20(address(uint160(uint256(transfer.tokenAddress)))); + transferToken = IERC20(_truncateAddress(transfer.tokenAddress)); // track outstanding token amounts bridgedIn(address(transferToken), transfer.amount); diff --git a/ethereum/forge-test/Bridge.t.sol b/ethereum/forge-test/Bridge.t.sol new file mode 100644 index 000000000..482281984 --- /dev/null +++ b/ethereum/forge-test/Bridge.t.sol @@ -0,0 +1,16 @@ +// SPDX-License-Identifier: Apache 2 + +pragma solidity ^0.8.0; + +import "../contracts/bridge/Bridge.sol"; +import "forge-std/Test.sol"; + +contract TestBridge is Bridge, Test { + function testTruncate(bytes32 b) public { + if (bytes12(b) != 0) { + vm.expectRevert( "invalid EVM address"); + } + bytes32 converted = bytes32(uint256(uint160(bytes20(_truncateAddress(b))))); + require(converted == b, "truncate does not roundrip"); + } +}