Skip to content

Commit d60289f

Browse files
authored
Merge pull request #283 from hyperledger-labs/mitigate-reentrancy
Introduce slither Signed-off-by: Jun Kimura <jun.kimura@datachain.jp>
2 parents c591d09 + 066f002 commit d60289f

File tree

11 files changed

+141
-90
lines changed

11 files changed

+141
-90
lines changed

.gas-snapshot

Lines changed: 30 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,20 @@
11
IBCMockAppTest:testHandshake() (gas: 4420488)
22
IBCMockAppTest:testHandshakeBetweenDifferentPorts() (gas: 3334373)
3-
IBCMockAppTest:testPacketRelay() (gas: 13931365)
3+
IBCMockAppTest:testPacketRelay() (gas: 13935239)
44
IBCMockAppTest:testPacketTimeout() (gas: 4279263)
55
IBCTest:testBenchmarkCreateMockClient() (gas: 233366)
66
IBCTest:testBenchmarkLCUpdateMockClient() (gas: 62005)
7-
IBCTest:testBenchmarkRecvPacket() (gas: 158899)
7+
IBCTest:testBenchmarkRecvPacket() (gas: 158870)
88
IBCTest:testBenchmarkSendPacket() (gas: 128432)
99
IBCTest:testBenchmarkUpdateMockClient() (gas: 160229)
1010
IBCTest:testToUint128((uint64,uint64)) (runs: 256, μ: 947, ~: 947)
11-
TestICS02:testCreateClient() (gas: 36551707)
12-
TestICS02:testInvalidCreateClient() (gas: 36448926)
13-
TestICS02:testInvalidUpdateClient() (gas: 36447834)
14-
TestICS02:testRegisterClient() (gas: 36103500)
15-
TestICS02:testRegisterClientDuplicatedClientType() (gas: 36088809)
16-
TestICS02:testRegisterClientInvalidClientType() (gas: 36118270)
17-
TestICS02:testUpdateClient() (gas: 36616034)
11+
TestICS02:testCreateClient() (gas: 36496843)
12+
TestICS02:testInvalidCreateClient() (gas: 36394062)
13+
TestICS02:testInvalidUpdateClient() (gas: 36392970)
14+
TestICS02:testRegisterClient() (gas: 36048636)
15+
TestICS02:testRegisterClientDuplicatedClientType() (gas: 36033945)
16+
TestICS02:testRegisterClientInvalidClientType() (gas: 36063406)
17+
TestICS02:testUpdateClient() (gas: 36561170)
1818
TestICS03Handshake:testConnOpenAck() (gas: 1858230)
1919
TestICS03Handshake:testConnOpenConfirm() (gas: 2054143)
2020
TestICS03Handshake:testConnOpenInit() (gas: 1429838)
@@ -37,32 +37,32 @@ TestICS04Handshake:testChanOpenInit() (gas: 2543524)
3737
TestICS04Handshake:testChanOpenTry() (gas: 3099898)
3838
TestICS04Handshake:testInvalidChanOpenAck() (gas: 2439749)
3939
TestICS04Handshake:testInvalidChanOpenConfirm() (gas: 2517338)
40-
TestICS04Handshake:testInvalidChanOpenInit() (gas: 1758704)
41-
TestICS04Handshake:testInvalidChanOpenTry() (gas: 1773674)
42-
TestICS04Packet:testAcknowledgementPacket() (gas: 3351152)
40+
TestICS04Handshake:testInvalidChanOpenInit() (gas: 1760558)
41+
TestICS04Handshake:testInvalidChanOpenTry() (gas: 1775528)
42+
TestICS04Packet:testAcknowledgementPacket() (gas: 3351116)
4343
TestICS04Packet:testInvalidSendPacket() (gas: 3551583)
44-
TestICS04Packet:testRecvPacket() (gas: 10995054)
44+
TestICS04Packet:testRecvPacket() (gas: 10996130)
4545
TestICS04Packet:testRecvPacketTimeoutHeight() (gas: 3259727)
4646
TestICS04Packet:testRecvPacketTimeoutTimestamp() (gas: 3278877)
4747
TestICS04Packet:testSendPacket() (gas: 6412442)
4848
TestICS04Packet:testTimeoutOnClose() (gas: 3553289)
49-
TestICS04Upgrade:testUpgradeAuthorityCancel() (gas: 46737910)
50-
TestICS04Upgrade:testUpgradeCannotCancelWithOldErrorReceipt() (gas: 3455585)
51-
TestICS04Upgrade:testUpgradeCannotRecvNextUpgradePacket() (gas: 5266374)
52-
TestICS04Upgrade:testUpgradeCounterpartyAdvanceNextSequenceBeforeOpen() (gas: 5235544)
53-
TestICS04Upgrade:testUpgradeCrossingHelloIncompatibleProposals() (gas: 4405811)
54-
TestICS04Upgrade:testUpgradeFull() (gas: 57775990)
55-
TestICS04Upgrade:testUpgradeInit() (gas: 3068711)
56-
TestICS04Upgrade:testUpgradeNoChanges() (gas: 2471930)
57-
TestICS04Upgrade:testUpgradeOutOfSync() (gas: 3902151)
58-
TestICS04Upgrade:testUpgradeRelaySuccessAtCounterpartyFlushComplete() (gas: 5238138)
59-
TestICS04Upgrade:testUpgradeRelaySuccessAtFlushing() (gas: 5612071)
60-
TestICS04Upgrade:testUpgradeSendPacketFailAtFlushingOrFlushComplete() (gas: 4070658)
61-
TestICS04Upgrade:testUpgradeTimeoutAbortAck() (gas: 17677097)
62-
TestICS04Upgrade:testUpgradeTimeoutAbortConfirm() (gas: 21313205)
63-
TestICS04Upgrade:testUpgradeTimeoutUpgrade() (gas: 44260340)
64-
TestICS04Upgrade:testUpgradeToOrdered() (gas: 56489888)
65-
TestICS04Upgrade:testUpgradeToUnordered() (gas: 45099675)
49+
TestICS04Upgrade:testUpgradeAuthorityCancel() (gas: 46742335)
50+
TestICS04Upgrade:testUpgradeCannotCancelWithOldErrorReceipt() (gas: 3456630)
51+
TestICS04Upgrade:testUpgradeCannotRecvNextUpgradePacket() (gas: 5266005)
52+
TestICS04Upgrade:testUpgradeCounterpartyAdvanceNextSequenceBeforeOpen() (gas: 5236258)
53+
TestICS04Upgrade:testUpgradeCrossingHelloIncompatibleProposals() (gas: 4407259)
54+
TestICS04Upgrade:testUpgradeFull() (gas: 57784728)
55+
TestICS04Upgrade:testUpgradeInit() (gas: 3069408)
56+
TestICS04Upgrade:testUpgradeNoChanges() (gas: 2471936)
57+
TestICS04Upgrade:testUpgradeOutOfSync() (gas: 3903220)
58+
TestICS04Upgrade:testUpgradeRelaySuccessAtCounterpartyFlushComplete() (gas: 5237770)
59+
TestICS04Upgrade:testUpgradeRelaySuccessAtFlushing() (gas: 5610957)
60+
TestICS04Upgrade:testUpgradeSendPacketFailAtFlushingOrFlushComplete() (gas: 4071025)
61+
TestICS04Upgrade:testUpgradeTimeoutAbortAck() (gas: 17681581)
62+
TestICS04Upgrade:testUpgradeTimeoutAbortConfirm() (gas: 21317741)
63+
TestICS04Upgrade:testUpgradeTimeoutUpgrade() (gas: 44264077)
64+
TestICS04Upgrade:testUpgradeToOrdered() (gas: 56509529)
65+
TestICS04Upgrade:testUpgradeToUnordered() (gas: 45113829)
6666
TestICS04UpgradeApp:testUpgradeAuthorizationChanneNotFound() (gas: 61690)
6767
TestICS04UpgradeApp:testUpgradeAuthorizationRePropose() (gas: 2565532)
6868
TestICS04UpgradeApp:testUpgradeAuthorizationRemove() (gas: 2473992)

.github/workflows/test.yml

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,17 @@ jobs:
4747
- name: Run tests with minimal solidity version
4848
run: make SOLC_VERSION=${{ env.MINIMAL_SOLC_VERSION }} test
4949

50+
slither:
51+
name: Slither analysis
52+
needs: contract-test
53+
runs-on: ubuntu-latest
54+
steps:
55+
- uses: actions/checkout@v4
56+
- uses: crytic/slither-action@v0.4.0
57+
with:
58+
node-version: 20.13
59+
slither-version: 0.10.1
60+
5061
e2e-test:
5162
name: E2E test
5263
needs: contract-test

Makefile

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,10 @@ test:
2929
coverage:
3030
@forge coverage --use solc:$(SOLC_VERSION)
3131

32+
.PHONY: slither
33+
slither:
34+
@slither .
35+
3236
######## Protobuf ########
3337

3438
.PHONY: proto-sol

contracts/core/02-client/ILightClient.sol

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,7 @@ interface ILightClient {
7878
/**
7979
* @dev verifyMembership is a generic proof verification method which verifies a proof of the existence of a value at a given CommitmentPath at the specified height.
8080
* The caller is expected to construct the full CommitmentPath from a CommitmentPrefix and a standardized path (as defined in ICS 24).
81+
* This function should not perform `call` to the IBC contract. However, `staticcall` is permitted.
8182
*/
8283
function verifyMembership(
8384
string calldata clientId,
@@ -93,6 +94,7 @@ interface ILightClient {
9394
/**
9495
* @dev verifyNonMembership is a generic proof verification method which verifies the absence of a given CommitmentPath at a specified height.
9596
* The caller is expected to construct the full CommitmentPath from a CommitmentPrefix and a standardized path (as defined in ICS 24).
97+
* This function should not perform `call` to the IBC contract. However, `staticcall` is permitted.
9698
*/
9799
function verifyNonMembership(
98100
string calldata clientId,

contracts/core/03-connection/IBCConnection.sol

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -208,6 +208,7 @@ abstract contract IBCConnection is IBCHost, IBCSelfStateValidator, IIBCConnectio
208208
bytes memory proof,
209209
bytes memory clientStateBytes
210210
) private {
211+
// slither-disable-start reentrancy-no-eth
211212
if (
212213
checkAndGetClient(connection.client_id).verifyMembership(
213214
connection.client_id,
@@ -220,6 +221,7 @@ abstract contract IBCConnection is IBCHost, IBCSelfStateValidator, IIBCConnectio
220221
clientStateBytes
221222
)
222223
) {
224+
// slither-disable-end reentrancy-no-eth
223225
return;
224226
}
225227
revert IBCConnectionFailedVerifyClientState(connection.client_id, path, clientStateBytes, proof, height);
@@ -232,6 +234,7 @@ abstract contract IBCConnection is IBCHost, IBCSelfStateValidator, IIBCConnectio
232234
bytes memory proof,
233235
bytes memory consensusStateBytes
234236
) private {
237+
// slither-disable-start reentrancy-no-eth
235238
if (
236239
checkAndGetClient(connection.client_id).verifyMembership(
237240
connection.client_id,
@@ -246,6 +249,7 @@ abstract contract IBCConnection is IBCHost, IBCSelfStateValidator, IIBCConnectio
246249
consensusStateBytes
247250
)
248251
) {
252+
// slither-disable-end reentrancy-no-eth
249253
return;
250254
}
251255
revert IBCConnectionFailedVerifyClientConsensusState(
@@ -266,6 +270,7 @@ abstract contract IBCConnection is IBCHost, IBCSelfStateValidator, IIBCConnectio
266270
string memory counterpartyConnectionId,
267271
ConnectionEnd.Data memory counterpartyConnection
268272
) private {
273+
// slither-disable-start reentrancy-no-eth
269274
if (
270275
checkAndGetClient(connection.client_id).verifyMembership(
271276
connection.client_id,
@@ -278,6 +283,7 @@ abstract contract IBCConnection is IBCHost, IBCSelfStateValidator, IIBCConnectio
278283
ConnectionEnd.encode(counterpartyConnection)
279284
)
280285
) {
286+
// slither-disable-end reentrancy-no-eth
281287
return;
282288
}
283289
revert IBCConnectionFailedVerifyConnectionState(

contracts/core/04-channel/IBCChannelHandshake.sol

Lines changed: 26 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -20,11 +20,14 @@ import {IBCChannelLib} from "./IBCChannelLib.sol";
2020
contract IBCChannelHandshake is IBCModuleManager, IIBCChannelHandshake, IIBCChannelErrors {
2121
using IBCHeight for Height.Data;
2222

23+
// ------------- IIBCChannelHandshake implementation ------------------- //
24+
2325
/**
2426
* @dev channelOpenInit is called by a module to initiate a channel opening handshake with a module on another chain.
2527
*/
2628
function channelOpenInit(IIBCChannelHandshake.MsgChannelOpenInit calldata msg_)
27-
external
29+
public
30+
override
2831
returns (string memory, string memory)
2932
{
3033
if (msg_.channel.connection_hops.length != 1) {
@@ -52,6 +55,7 @@ contract IBCChannelHandshake is IBCModuleManager, IIBCChannelHandshake, IIBCChan
5255

5356
string memory channelId = generateChannelIdentifier();
5457
initializeSequences(msg_.portId, channelId);
58+
emit GeneratedChannelIdentifier(channelId);
5559

5660
IIBCModule module = lookupModuleByPort(msg_.portId);
5761
string memory version = module.onChanOpenInit(
@@ -74,15 +78,15 @@ contract IBCChannelHandshake is IBCModuleManager, IIBCChannelHandshake, IIBCChan
7478
msg_.channel.connection_hops,
7579
version
7680
);
77-
emit GeneratedChannelIdentifier(channelId);
7881
return (channelId, version);
7982
}
8083

8184
/**
8285
* @dev channelOpenTry is called by a module to accept the first step of a channel opening handshake initiated by a module on another chain.
8386
*/
8487
function channelOpenTry(IIBCChannelHandshake.MsgChannelOpenTry calldata msg_)
85-
external
88+
public
89+
override
8690
returns (string memory, string memory)
8791
{
8892
if (msg_.channel.connection_hops.length != 1) {
@@ -127,6 +131,7 @@ contract IBCChannelHandshake is IBCModuleManager, IIBCChannelHandshake, IIBCChan
127131

128132
string memory channelId = generateChannelIdentifier();
129133
initializeSequences(msg_.portId, channelId);
134+
emit GeneratedChannelIdentifier(channelId);
130135

131136
IIBCModule module = lookupModuleByPort(msg_.portId);
132137
string memory version = module.onChanOpenTry(
@@ -149,14 +154,13 @@ contract IBCChannelHandshake is IBCModuleManager, IIBCChannelHandshake, IIBCChan
149154
msg_.channel.connection_hops,
150155
version
151156
);
152-
emit GeneratedChannelIdentifier(channelId);
153157
return (channelId, version);
154158
}
155159

156160
/**
157161
* @dev channelOpenAck is called by the handshake-originating module to acknowledge the acceptance of the initial request by the counterparty module on the other chain.
158162
*/
159-
function channelOpenAck(IIBCChannelHandshake.MsgChannelOpenAck calldata msg_) external {
163+
function channelOpenAck(IIBCChannelHandshake.MsgChannelOpenAck calldata msg_) public override {
160164
Channel.Data storage channel = channels[msg_.portId][msg_.channelId];
161165
if (channel.state != Channel.State.STATE_INIT) {
162166
revert IBCChannelUnexpectedChannelState(channel.state);
@@ -195,7 +199,7 @@ contract IBCChannelHandshake is IBCModuleManager, IIBCChannelHandshake, IIBCChan
195199
/**
196200
* @dev channelOpenConfirm is called by the counterparty module to close their end of the channel, since the other end has been closed.
197201
*/
198-
function channelOpenConfirm(IIBCChannelHandshake.MsgChannelOpenConfirm calldata msg_) external {
202+
function channelOpenConfirm(IIBCChannelHandshake.MsgChannelOpenConfirm calldata msg_) public override {
199203
Channel.Data storage channel = channels[msg_.portId][msg_.channelId];
200204
if (channel.state != Channel.State.STATE_TRYOPEN) {
201205
revert IBCChannelUnexpectedChannelState(channel.state);
@@ -227,7 +231,7 @@ contract IBCChannelHandshake is IBCModuleManager, IIBCChannelHandshake, IIBCChan
227231
/**
228232
* @dev channelCloseInit is called by either module to close their end of the channel. Once closed, channels cannot be reopened.
229233
*/
230-
function channelCloseInit(IIBCChannelHandshake.MsgChannelCloseInit calldata msg_) external {
234+
function channelCloseInit(IIBCChannelHandshake.MsgChannelCloseInit calldata msg_) public override {
231235
Channel.Data storage channel = channels[msg_.portId][msg_.channelId];
232236
if (channel.state == Channel.State.STATE_UNINITIALIZED_UNSPECIFIED) {
233237
revert IBCChannelChannelNotFound(msg_.portId, msg_.channelId);
@@ -249,7 +253,7 @@ contract IBCChannelHandshake is IBCModuleManager, IIBCChannelHandshake, IIBCChan
249253
* @dev channelCloseConfirm is called by the counterparty module to close their end of the
250254
* channel, since the other end has been closed.
251255
*/
252-
function channelCloseConfirm(IIBCChannelHandshake.MsgChannelCloseConfirm calldata msg_) external {
256+
function channelCloseConfirm(IIBCChannelHandshake.MsgChannelCloseConfirm calldata msg_) public override {
253257
Channel.Data storage channel = channels[msg_.portId][msg_.channelId];
254258
if (channel.state == Channel.State.STATE_UNINITIALIZED_UNSPECIFIED) {
255259
revert IBCChannelChannelNotFound(msg_.portId, msg_.channelId);
@@ -285,6 +289,8 @@ contract IBCChannelHandshake is IBCModuleManager, IIBCChannelHandshake, IIBCChan
285289
);
286290
}
287291

292+
// ------------- Private functions ------------------- //
293+
288294
/**
289295
* @dev writeChannel writes a channel which has successfully passed the OpenInit or OpenTry handshake step.
290296
*/
@@ -296,7 +302,7 @@ contract IBCChannelHandshake is IBCModuleManager, IIBCChannelHandshake, IIBCChan
296302
ChannelCounterparty.Data calldata counterparty,
297303
string[] calldata connectionHops,
298304
string memory version
299-
) internal {
305+
) private {
300306
Channel.Data storage channel = channels[portId][channelId];
301307
channel.state = state;
302308
channel.ordering = order;
@@ -309,13 +315,20 @@ contract IBCChannelHandshake is IBCModuleManager, IIBCChannelHandshake, IIBCChan
309315
updateChannelCommitment(portId, channelId);
310316
}
311317

318+
function initializeSequences(string memory portId, string memory channelId) internal {
319+
nextSequenceSends[portId][channelId] = 1;
320+
nextSequenceRecvs[portId][channelId] = 1;
321+
nextSequenceAcks[portId][channelId] = 1;
322+
recvStartSequences[portId][channelId].sequence = 1;
323+
commitments[IBCCommitment.nextSequenceRecvCommitmentKey(portId, channelId)] =
324+
keccak256(abi.encodePacked((bytes8(uint64(1)))));
325+
}
326+
312327
function updateChannelCommitment(string memory portId, string memory channelId) private {
313328
commitments[IBCCommitment.channelCommitmentKey(portId, channelId)] =
314329
keccak256(Channel.encode(channels[portId][channelId]));
315330
}
316331

317-
/* Verification functions */
318-
319332
function verifyChannelState(
320333
ConnectionEnd.Data storage connection,
321334
Height.Data calldata height,
@@ -324,6 +337,7 @@ contract IBCChannelHandshake is IBCModuleManager, IIBCChannelHandshake, IIBCChan
324337
string memory channelId,
325338
bytes memory channelBytes
326339
) private {
340+
// slither-disable-start reentrancy-no-eth
327341
if (
328342
checkAndGetClient(connection.client_id).verifyMembership(
329343
connection.client_id,
@@ -336,24 +350,14 @@ contract IBCChannelHandshake is IBCModuleManager, IIBCChannelHandshake, IIBCChan
336350
channelBytes
337351
)
338352
) {
353+
// slither-disable-end reentrancy-no-eth
339354
return;
340355
}
341356
revert IBCChannelFailedVerifyChannelState(
342357
connection.client_id, IBCCommitment.channelPath(portId, channelId), channelBytes, proof, height
343358
);
344359
}
345360

346-
/* Internal functions */
347-
348-
function initializeSequences(string memory portId, string memory channelId) internal {
349-
nextSequenceSends[portId][channelId] = 1;
350-
nextSequenceRecvs[portId][channelId] = 1;
351-
nextSequenceAcks[portId][channelId] = 1;
352-
recvStartSequences[portId][channelId].sequence = 1;
353-
commitments[IBCCommitment.nextSequenceRecvCommitmentKey(portId, channelId)] =
354-
keccak256(abi.encodePacked((bytes8(uint64(1)))));
355-
}
356-
357361
function getCounterpartyHops(string memory connectionId) internal view returns (string[] memory hops) {
358362
hops = new string[](1);
359363
hops[0] = connections[connectionId].counterparty.connection_id;

0 commit comments

Comments
 (0)