diff --git a/contracts/asset-proxy/contracts/src/ERC1155Proxy.sol b/contracts/asset-proxy/contracts/src/ERC1155Proxy.sol index bc1b1cdc7a..4973deafd8 100644 --- a/contracts/asset-proxy/contracts/src/ERC1155Proxy.sol +++ b/contracts/asset-proxy/contracts/src/ERC1155Proxy.sol @@ -205,6 +205,14 @@ contract ERC1155Proxy is let idsOffset := add(paramsInAssetDataOffset, calldataload(add(assetDataOffset, 68))) let idsLength := calldataload(idsOffset) let idsLengthInBytes := mul(idsLength, 32) + if sub(div(idsLengthInBytes, 32), idsLength) { + // Revert with `Error("UINT256_OVERFLOW")` + mstore(0, 0x08c379a000000000000000000000000000000000000000000000000000000000) + mstore(32, 0x0000002000000000000000000000000000000000000000000000000000000000) + mstore(64, 0x0000001055494e543235365f4f564552464c4f57000000000000000000000000) + mstore(96, 0) + revert(0, 100) + } let idsBegin := add(idsOffset, 32) let idsEnd := add(idsBegin, idsLengthInBytes) if gt(idsEnd, assetDataEnd) { @@ -230,6 +238,14 @@ contract ERC1155Proxy is let valuesOffset := add(paramsInAssetDataOffset, calldataload(add(assetDataOffset, 100))) let valuesLength := calldataload(valuesOffset) let valuesLengthInBytes := mul(valuesLength, 32) + if sub(div(valuesLengthInBytes, 32), valuesLength) { + // Revert with `Error("UINT256_OVERFLOW")` + mstore(0, 0x08c379a000000000000000000000000000000000000000000000000000000000) + mstore(32, 0x0000002000000000000000000000000000000000000000000000000000000000) + mstore(64, 0x0000001055494e543235365f4f564552464c4f57000000000000000000000000) + mstore(96, 0) + revert(0, 100) + } let valuesBegin := add(valuesOffset, 32) let valuesEnd := add(valuesBegin, valuesLengthInBytes) if gt(valuesEnd, assetDataEnd) { diff --git a/contracts/asset-proxy/test/erc1155_proxy.ts b/contracts/asset-proxy/test/erc1155_proxy.ts index 33e6df7956..eb0a531b93 100644 --- a/contracts/asset-proxy/test/erc1155_proxy.ts +++ b/contracts/asset-proxy/test/erc1155_proxy.ts @@ -1152,6 +1152,163 @@ describe('ERC1155Proxy', () => { RevertReason.InvalidIdsOffset, ); }); + it('should revert token ids length overflows', async () => { + // setup test parameters + const tokensToTransfer = fungibleTokens.slice(0, 1); + const valuesToTransfer = [fungibleValueToTransferLarge]; + const valueMultiplier = valueMultiplierSmall; + const erc1155ContractAddress = erc1155Wrapper.getContract().address; + const assetData = assetDataUtils.encodeERC1155AssetData( + erc1155ContractAddress, + tokensToTransfer, + valuesToTransfer, + receiverCallbackData, + ); + // The asset data we just generated will look like this: + // a7cb5fb7 + // 0x 0000000000000000000000000b1ba0af832d7c05fd64161e0db78e85978e8082 + // 0x20 0000000000000000000000000000000000000000000000000000000000000080 // offset to token ids + // 0x40 00000000000000000000000000000000000000000000000000000000000000c0 + // 0x60 0000000000000000000000000000000000000000000000000000000000000100 + // 0x80 0000000000000000000000000000000000000000000000000000000000000001 + // 0xA0 0000000000000000000000000000000100000000000000000000000000000000 + // 0xC0 0000000000000000000000000000000000000000000000000000000000000001 + // 0xE0 0000000000000000000000000000000000000000000000878678326eac900000 + // 0x100 0000000000000000000000000000000000000000000000000000000000000004 + // 0x120 0102030400000000000000000000000000000000000000000000000000000000 + // + // We want to chan ge the offset to token ids to point to the end of calldata + const encodedOffsetToTokenIds = '0000000000000000000000000000000000000000000000000000000000000080'; + const badEncodedOffsetToTokenIds = '0000000000000000000000000000000000000000000000000000000000000140'; + const assetDataWithBadTokenIdsOffset = assetData.replace( + encodedOffsetToTokenIds, + badEncodedOffsetToTokenIds, + ); + // We want a length that will overflow when converted to bytes - ie, multiplied by 32. + const encodedIdsLengthOverflow = '0800000000000000000000000000000000000000000000000000000000000001'; + const buffer = '0'.repeat(64 * 10); + const assetDataWithOverflow = `${assetDataWithBadTokenIdsOffset}${encodedIdsLengthOverflow}${buffer}`; + // execute transfer + await expectTransactionFailedAsync( + erc1155ProxyWrapper.transferFromAsync( + spender, + receiverContract, + erc1155Contract.address, + tokensToTransfer, + valuesToTransfer, + valueMultiplier, + receiverCallbackData, + authorized, + assetDataWithOverflow, + ), + RevertReason.Uint256Overflow, + ); + }); + it('should revert token values length overflows', async () => { + // setup test parameters + const tokensToTransfer = fungibleTokens.slice(0, 1); + const valuesToTransfer = [fungibleValueToTransferLarge]; + const valueMultiplier = valueMultiplierSmall; + const erc1155ContractAddress = erc1155Wrapper.getContract().address; + const assetData = assetDataUtils.encodeERC1155AssetData( + erc1155ContractAddress, + tokensToTransfer, + valuesToTransfer, + receiverCallbackData, + ); + // The asset data we just generated will look like this: + // a7cb5fb7 + // 0x 0000000000000000000000000b1ba0af832d7c05fd64161e0db78e85978e8082 + // 0x20 0000000000000000000000000000000000000000000000000000000000000080 + // 0x40 00000000000000000000000000000000000000000000000000000000000000c0 // offset to token values + // 0x60 0000000000000000000000000000000000000000000000000000000000000100 + // 0x80 0000000000000000000000000000000000000000000000000000000000000001 + // 0xA0 0000000000000000000000000000000100000000000000000000000000000000 + // 0xC0 0000000000000000000000000000000000000000000000000000000000000001 + // 0xE0 0000000000000000000000000000000000000000000000878678326eac900000 + // 0x100 0000000000000000000000000000000000000000000000000000000000000004 + // 0x120 0102030400000000000000000000000000000000000000000000000000000000 + // + // We want to chan ge the offset to token values to point to the end of calldata + const encodedOffsetToTokenIds = '00000000000000000000000000000000000000000000000000000000000000c0'; + const badEncodedOffsetToTokenIds = '0000000000000000000000000000000000000000000000000000000000000140'; + const assetDataWithBadTokenIdsOffset = assetData.replace( + encodedOffsetToTokenIds, + badEncodedOffsetToTokenIds, + ); + // We want a length that will overflow when converted to bytes - ie, multiplied by 32. + const encodedIdsLengthOverflow = '0800000000000000000000000000000000000000000000000000000000000001'; + const buffer = '0'.repeat(64 * 10); + const assetDataWithOverflow = `${assetDataWithBadTokenIdsOffset}${encodedIdsLengthOverflow}${buffer}`; + // execute transfer + await expectTransactionFailedAsync( + erc1155ProxyWrapper.transferFromAsync( + spender, + receiverContract, + erc1155Contract.address, + tokensToTransfer, + valuesToTransfer, + valueMultiplier, + receiverCallbackData, + authorized, + assetDataWithOverflow, + ), + RevertReason.Uint256Overflow, + ); + }); + it('should revert token data length overflows', async () => { + // setup test parameters + const tokensToTransfer = fungibleTokens.slice(0, 1); + const valuesToTransfer = [fungibleValueToTransferLarge]; + const valueMultiplier = valueMultiplierSmall; + const erc1155ContractAddress = erc1155Wrapper.getContract().address; + const assetData = assetDataUtils.encodeERC1155AssetData( + erc1155ContractAddress, + tokensToTransfer, + valuesToTransfer, + receiverCallbackData, + ); + // The asset data we just generated will look like this: + // a7cb5fb7 + // 0x 0000000000000000000000000b1ba0af832d7c05fd64161e0db78e85978e8082 + // 0x20 0000000000000000000000000000000000000000000000000000000000000080 + // 0x40 00000000000000000000000000000000000000000000000000000000000000c0 + // 0x60 0000000000000000000000000000000000000000000000000000000000000100 // offset to token data + // 0x80 0000000000000000000000000000000000000000000000000000000000000001 + // 0xA0 0000000000000000000000000000000100000000000000000000000000000000 + // 0xC0 0000000000000000000000000000000000000000000000000000000000000001 + // 0xE0 0000000000000000000000000000000000000000000000878678326eac900000 + // 0x100 0000000000000000000000000000000000000000000000000000000000000004 + // 0x120 0102030400000000000000000000000000000000000000000000000000000000 + // + // We want to chan ge the offset to token ids to point to the end of calldata, + // which we'll extend with a bad length. + const encodedOffsetToTokenIds = '0000000000000000000000000000000000000000000000000000000000000100'; + const badEncodedOffsetToTokenIds = '0000000000000000000000000000000000000000000000000000000000000140'; + const assetDataWithBadTokenIdsOffset = assetData.replace( + encodedOffsetToTokenIds, + badEncodedOffsetToTokenIds, + ); + // We want a length that will overflow when converted to bytes - ie, multiplied by 32. + const encodedIdsLengthOverflow = '0800000000000000000000000000000000000000000000000000000000000001'; + const buffer = '0'.repeat(64 * 10); + const assetDataWithOverflow = `${assetDataWithBadTokenIdsOffset}${encodedIdsLengthOverflow}${buffer}`; + // execute transfer + await expectTransactionFailedAsync( + erc1155ProxyWrapper.transferFromAsync( + spender, + receiverContract, + erc1155Contract.address, + tokensToTransfer, + valuesToTransfer, + valueMultiplier, + receiverCallbackData, + authorized, + assetDataWithOverflow, + ), + RevertReason.InvalidDataOffset, + ); + }); it('should revert if token values resolves to outside the bounds of calldata', async () => { // setup test parameters const tokensToTransfer = fungibleTokens.slice(0, 1);