From cf12daea2f3a907f6a111c12d1e67c2e8b0a8797 Mon Sep 17 00:00:00 2001 From: Amir Bandeali Date: Tue, 21 Aug 2018 17:53:40 -0700 Subject: Add ReentrantToken --- packages/contracts/compiler.json | 1 + packages/contracts/package.json | 2 +- .../ReentrantERC20Token/ReentrantERC20Token.sol | 173 +++++++++++++++++++++ 3 files changed, 175 insertions(+), 1 deletion(-) create mode 100644 packages/contracts/src/2.0.0/test/ReentrantERC20Token/ReentrantERC20Token.sol (limited to 'packages') diff --git a/packages/contracts/compiler.json b/packages/contracts/compiler.json index 27bb69a36..f66114e87 100644 --- a/packages/contracts/compiler.json +++ b/packages/contracts/compiler.json @@ -40,6 +40,7 @@ "MultiSigWallet", "MultiSigWalletWithTimeLock", "OrderValidator", + "ReentrantERC20Token", "TestAssetProxyOwner", "TestAssetProxyDispatcher", "TestConstants", diff --git a/packages/contracts/package.json b/packages/contracts/package.json index 0ead2f43f..8d75eb4ce 100644 --- a/packages/contracts/package.json +++ b/packages/contracts/package.json @@ -34,7 +34,7 @@ }, "config": { "abis": - "../migrations/artifacts/2.0.0/@(AssetProxyOwner|DummyERC20Token|DummyERC721Receiver|DummyERC721Token|DummyNoReturnERC20Token|ERC20Proxy|ERC721Proxy|Forwarder|Exchange|ExchangeWrapper|IAssetData|IAssetProxy|InvalidERC721Receiver|MixinAuthorizable|MultiSigWallet|MultiSigWalletWithTimeLock|OrderValidator|TestAssetProxyOwner|TestAssetProxyDispatcher|TestConstants|TestExchangeInternals|TestLibBytes|TestLibs|TestSignatureValidator|TestStaticCallReceiver|Validator|Wallet|TokenRegistry|Whitelist|WETH9|ZRXToken).json" + "../migrations/artifacts/2.0.0/@(AssetProxyOwner|DummyERC20Token|DummyERC721Receiver|DummyERC721Token|DummyNoReturnERC20Token|ERC20Proxy|ERC721Proxy|Forwarder|Exchange|ExchangeWrapper|IAssetData|IAssetProxy|InvalidERC721Receiver|MixinAuthorizable|MultiSigWallet|MultiSigWalletWithTimeLock|OrderValidator|ReentrantERC20Token|TestAssetProxyOwner|TestAssetProxyDispatcher|TestConstants|TestExchangeInternals|TestLibBytes|TestLibs|TestSignatureValidator|Validator|Wallet|TokenRegistry|Whitelist|WETH9|ZRXToken).json" }, "repository": { "type": "git", diff --git a/packages/contracts/src/2.0.0/test/ReentrantERC20Token/ReentrantERC20Token.sol b/packages/contracts/src/2.0.0/test/ReentrantERC20Token/ReentrantERC20Token.sol new file mode 100644 index 000000000..f5c98177f --- /dev/null +++ b/packages/contracts/src/2.0.0/test/ReentrantERC20Token/ReentrantERC20Token.sol @@ -0,0 +1,173 @@ +/* + + Copyright 2018 ZeroEx Intl. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +pragma solidity 0.4.24; +pragma experimental ABIEncoderV2; + +import "../../tokens/ERC20Token/ERC20Token.sol"; +import "../../protocol/Exchange/interfaces/IExchange.sol"; +import "../../protocol/Exchange/libs/LibOrder.sol"; + + +contract ReentrantERC20Token is + ERC20Token +{ + // solhint-disable-next-line var-name-mixedcase + IExchange internal EXCHANGE; + + // All of these functions are potentially vulnerable to reentrancy + enum ExchangeFunction { + FILL_ORDER, + FILL_OR_KILL_ORDER, + FILL_ORDER_NO_THROW, + BATCH_FILL_ORDERS, + BATCH_FILL_OR_KILL_ORDERS, + BATCH_FILL_ORDERS_NO_THROW, + MARKET_BUY_ORDERS, + MARKET_BUY_ORDERS_NO_THROW, + MARKET_SELL_ORDERS, + MARKET_SELL_ORDERS_NO_THROW, + MATCH_ORDERS + } + + uint8 internal currentFunctionId = 0; + + constructor (address _exchange) + public + { + EXCHANGE = IExchange(_exchange); + } + + /// @dev Set the current function that will be called when `transferFrom` is called. + /// @param _currentFunctionId Id that corresponds to function name. + function setCurrentFunction(uint8 _currentFunctionId) + external + { + currentFunctionId = _currentFunctionId; + } + + /// @dev A version of `transferFrom` that attempts to reenter the Exchange contract. + /// @param _from The address of the sender + /// @param _to The address of the recipient + /// @param _value The amount of token to be transferred + function transferFrom( + address _from, + address _to, + uint256 _value + ) + external + returns (bool) + { + // This order would normally be invalid, but it will be used strictly for testing reentrnacy. + // Any reentrancy checks will happen before any other checks that invalidate the order. + LibOrder.Order memory order = LibOrder.Order({ + makerAddress: address(0), + takerAddress: address(0), + feeRecipientAddress: address(0), + senderAddress: address(0), + makerAssetAmount: 0, + takerAssetAmount: 0, + makerFee: 0, + takerFee: 0, + expirationTimeSeconds: 0, + salt: 0, + makerAssetData: "", + takerAssetData: "" + }); + + // Initialize remaining null parameters + bytes memory signature = ""; + LibOrder.Order[] memory orders = new LibOrder.Order[](1); + orders[0] = order; + uint256[] memory takerAssetFillAmounts = new uint256[](1); + takerAssetFillAmounts[0] = 0; + bytes[] memory signatures = new bytes[](1); + signatures[0] = signature; + + // Attempt to reenter Exchange by calling function with currentFunctionId + if (currentFunctionId == uint8(ExchangeFunction.FILL_ORDER)) { + EXCHANGE.fillOrder( + order, + 0, + signature + ); + } else if (currentFunctionId == uint8(ExchangeFunction.FILL_OR_KILL_ORDER)) { + EXCHANGE.fillOrKillOrder( + order, + 0, + signature + ); + } else if (currentFunctionId == uint8(ExchangeFunction.FILL_ORDER_NO_THROW)) { + EXCHANGE.fillOrderNoThrow( + order, + 0, + signature + ); + } else if (currentFunctionId == uint8(ExchangeFunction.BATCH_FILL_ORDERS)) { + EXCHANGE.batchFillOrders( + orders, + takerAssetFillAmounts, + signatures + ); + } else if (currentFunctionId == uint8(ExchangeFunction.BATCH_FILL_OR_KILL_ORDERS)) { + EXCHANGE.batchFillOrKillOrders( + orders, + takerAssetFillAmounts, + signatures + ); + } else if (currentFunctionId == uint8(ExchangeFunction.BATCH_FILL_ORDERS_NO_THROW)) { + EXCHANGE.batchFillOrdersNoThrow( + orders, + takerAssetFillAmounts, + signatures + ); + } else if (currentFunctionId == uint8(ExchangeFunction.MARKET_BUY_ORDERS)) { + EXCHANGE.marketBuyOrders( + orders, + 0, + signatures + ); + } else if (currentFunctionId == uint8(ExchangeFunction.MARKET_BUY_ORDERS_NO_THROW)) { + EXCHANGE.marketBuyOrdersNoThrow( + orders, + 0, + signatures + ); + } else if (currentFunctionId == uint8(ExchangeFunction.MARKET_SELL_ORDERS)) { + EXCHANGE.marketSellOrders( + orders, + 0, + signatures + ); + } else if (currentFunctionId == uint8(ExchangeFunction.MARKET_SELL_ORDERS_NO_THROW)) { + EXCHANGE.marketSellOrdersNoThrow( + orders, + 0, + signatures + ); + } else if (currentFunctionId == uint8(ExchangeFunction.MATCH_ORDERS)) { + EXCHANGE.matchOrders( + order, + order, + signature, + signature + ); + } + return true; + } +} \ No newline at end of file -- cgit v1.2.3