Skip to content

Commit 628a6e2

Browse files
Amxxfrangio
andauthored
Fix issues caused by abi.decode reverting (#3552)
Co-authored-by: Francisco Giordano <[email protected]>
1 parent d50e608 commit 628a6e2

File tree

7 files changed

+73
-2
lines changed

7 files changed

+73
-2
lines changed

CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,11 @@
1515

1616
ERC-721 integrators that interpret contract state from events should make sure that they implement the clearing of approval that is implicit in every transfer according to the EIP. Previous versions of OpenZeppellin Contracts emitted an explicit `Approval` event even though it was not required by the specification, and this is no longer the case.
1717

18+
## 4.7.1
19+
20+
* `SignatureChecker`: Fix an issue that causes `isValidSignatureNow` to revert when the target contract returns ill-encoded data. ([#3552](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/3552))
21+
* `ERC165Checker`: Fix an issue that causes `supportsInterface` to revert when the target contract returns ill-encoded data. ([#3552](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/3552))
22+
1823
## 4.7.0 (2022-06-29)
1924

2025
* `TimelockController`: Migrate `_call` to `_execute` and allow inheritance and overriding similar to `Governor`. ([#3317](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/3317))

contracts/mocks/ERC1271WalletMock.sol

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,3 +15,12 @@ contract ERC1271WalletMock is Ownable, IERC1271 {
1515
return ECDSA.recover(hash, signature) == owner() ? this.isValidSignature.selector : bytes4(0);
1616
}
1717
}
18+
19+
contract ERC1271MaliciousMock is IERC1271 {
20+
function isValidSignature(bytes32, bytes memory) public pure override returns (bytes4) {
21+
assembly {
22+
mstore(0, 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff)
23+
return(0, 32)
24+
}
25+
}
26+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
// SPDX-License-Identifier: MIT
2+
3+
pragma solidity ^0.8.0;
4+
5+
contract ERC165MaliciousData {
6+
function supportsInterface(bytes4) public view returns (bool) {
7+
assembly {
8+
mstore(0, 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff)
9+
return(0, 32)
10+
}
11+
}
12+
}

contracts/utils/cryptography/SignatureChecker.sol

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,8 @@ library SignatureChecker {
3535
(bool success, bytes memory result) = signer.staticcall(
3636
abi.encodeWithSelector(IERC1271.isValidSignature.selector, hash, signature)
3737
);
38-
return (success && result.length == 32 && abi.decode(result, (bytes4)) == IERC1271.isValidSignature.selector);
38+
return (success &&
39+
result.length == 32 &&
40+
abi.decode(result, (bytes32)) == bytes32(IERC1271.isValidSignature.selector));
3941
}
4042
}

contracts/utils/introspection/ERC165Checker.sol

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,6 @@ library ERC165Checker {
108108
bytes memory encodedParams = abi.encodeWithSelector(IERC165.supportsInterface.selector, interfaceId);
109109
(bool success, bytes memory result) = account.staticcall{gas: 30000}(encodedParams);
110110
if (result.length < 32) return false;
111-
return success && abi.decode(result, (bool));
111+
return success && abi.decode(result, (uint256)) > 0;
112112
}
113113
}

test/utils/cryptography/SignatureChecker.test.js

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ const { expect } = require('chai');
44

55
const SignatureCheckerMock = artifacts.require('SignatureCheckerMock');
66
const ERC1271WalletMock = artifacts.require('ERC1271WalletMock');
7+
const ERC1271MaliciousMock = artifacts.require('ERC1271MaliciousMock');
78

89
const TEST_MESSAGE = web3.utils.sha3('OpenZeppelin');
910
const WRONG_MESSAGE = web3.utils.sha3('Nope');
@@ -14,6 +15,7 @@ contract('SignatureChecker (ERC1271)', function (accounts) {
1415
before('deploying', async function () {
1516
this.signaturechecker = await SignatureCheckerMock.new();
1617
this.wallet = await ERC1271WalletMock.new(signer);
18+
this.malicious = await ERC1271MaliciousMock.new();
1719
this.signature = await web3.eth.sign(TEST_MESSAGE, signer);
1820
});
1921

@@ -67,5 +69,13 @@ contract('SignatureChecker (ERC1271)', function (accounts) {
6769
this.signature,
6870
)).to.equal(false);
6971
});
72+
73+
it('with malicious wallet', async function () {
74+
expect(await this.signaturechecker.isValidSignatureNow(
75+
this.malicious.address,
76+
toEthSignedMessageHash(TEST_MESSAGE),
77+
this.signature,
78+
)).to.equal(false);
79+
});
7080
});
7181
});

test/utils/introspection/ERC165Checker.test.js

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ const { expect } = require('chai');
44

55
const ERC165CheckerMock = artifacts.require('ERC165CheckerMock');
66
const ERC165MissingData = artifacts.require('ERC165MissingData');
7+
const ERC165MaliciousData = artifacts.require('ERC165MaliciousData');
78
const ERC165NotSupported = artifacts.require('ERC165NotSupported');
89
const ERC165InterfacesSupported = artifacts.require('ERC165InterfacesSupported');
910

@@ -51,6 +52,38 @@ contract('ERC165Checker', function (accounts) {
5152
});
5253
});
5354

55+
context('ERC165 malicious return data', function () {
56+
beforeEach(async function () {
57+
this.target = await ERC165MaliciousData.new();
58+
});
59+
60+
it('does not support ERC165', async function () {
61+
const supported = await this.mock.supportsERC165(this.target.address);
62+
expect(supported).to.equal(false);
63+
});
64+
65+
it('does not support mock interface via supportsInterface', async function () {
66+
const supported = await this.mock.supportsInterface(this.target.address, DUMMY_ID);
67+
expect(supported).to.equal(false);
68+
});
69+
70+
it('does not support mock interface via supportsAllInterfaces', async function () {
71+
const supported = await this.mock.supportsAllInterfaces(this.target.address, [DUMMY_ID]);
72+
expect(supported).to.equal(false);
73+
});
74+
75+
it('does not support mock interface via getSupportedInterfaces', async function () {
76+
const supported = await this.mock.getSupportedInterfaces(this.target.address, [DUMMY_ID]);
77+
expect(supported.length).to.equal(1);
78+
expect(supported[0]).to.equal(false);
79+
});
80+
81+
it('does not support mock interface via supportsERC165InterfaceUnchecked', async function () {
82+
const supported = await this.mock.supportsERC165InterfaceUnchecked(this.target.address, DUMMY_ID);
83+
expect(supported).to.equal(true);
84+
});
85+
});
86+
5487
context('ERC165 not supported', function () {
5588
beforeEach(async function () {
5689
this.target = await ERC165NotSupported.new();

0 commit comments

Comments
 (0)