Skip to content

Commit

Permalink
Expand malicious module test for all module types (#96)
Browse files Browse the repository at this point in the history
* add notes

* add placeholders

* remove selfdestruct test, it simply doesnt work due to end-of-transaction behavior of selfdestruct and the effective noop in Foundry, add check inside of endpoint tests for deprecated module reversion for completeness

* simplify external target

* clean up

* comment out note

* clean up

* complete malicious layout change check, behaviour is as expected across all module types

* clean up, fix lint issue
  • Loading branch information
zerosnacks committed Jun 21, 2023
1 parent 5d953f3 commit 367605f
Show file tree
Hide file tree
Showing 14 changed files with 387 additions and 317 deletions.
4 changes: 2 additions & 2 deletions reports/SLITHER_CHECKLIST.md
Original file line number Diff line number Diff line change
Expand Up @@ -290,9 +290,9 @@ Confidence: High
../src/ReflexModule.sol#L39

- [ ] ID-49
Variable [ReflexState.\_\_REFLEX_GAP](../src/ReflexState.sol#L90) is not in mixedCase
Variable [ReflexState.\_\_REFLEX_GAP](../src/ReflexState.sol#L91) is not in mixedCase

../src/ReflexState.sol#L90
../src/ReflexState.sol#L91

- [ ] ID-50
Variable [ReflexState.\_reentrancyStatus](../src/ReflexState.sol#L35) is not in mixedCase
Expand Down
1 change: 1 addition & 0 deletions src/ReflexState.sol
Original file line number Diff line number Diff line change
Expand Up @@ -87,5 +87,6 @@ abstract contract ReflexState is IReflexState, ReflexConstants {
* @dev Reflex occupies storage slots 0 to 49 (50 in total).
* @dev Storage slot: 6 (1408 bytes).
*/
// solhint-disable-next-line var-name-mixedcase
uint256[44] private __REFLEX_GAP;
}
2 changes: 1 addition & 1 deletion src/periphery/ReflexBatch.sol
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ abstract contract ReflexBatch is IReflexBatch, ReflexModule {
function simulateBatchCallReturn(
BatchAction[] calldata actions_
) public virtual reentrancyAllowed returns (BatchActionResponse[] memory simulation_) {
// NOTE: it is assumed user will never be able to control _modules (storage) nor _moduleId (immutable).
// NOTE: it is assumed attacker will never be able to control _modules (storage) nor _moduleId (immutable).
// TODO: gas optimization: `_unpackEndpointAddress` could be replaced by msg.sender.
// TODO: gas optimization: `success` check is strictly not necessary and may interfere.

Expand Down
1 change: 0 additions & 1 deletion test/ImplementationERC20.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
pragma solidity ^0.8.13;

// Vendor
// solhint-disable-next-line no-console
import {stdError} from "forge-std/StdError.sol";

// Interfaces
Expand Down
75 changes: 53 additions & 22 deletions test/ImplementationModuleInternal.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,15 @@
pragma solidity ^0.8.13;

// Interfaces
import {IReflexInstaller} from "../src/interfaces/IReflexInstaller.sol";
import {IReflexModule} from "../src/interfaces/IReflexModule.sol";

// Fixtures
import {ImplementationFixture} from "./fixtures/ImplementationFixture.sol";

// Mocks
import {MockImplementationDeprecatedModule} from "./mocks/MockImplementationDeprecatedModule.sol";
import {MockImplementationMaliciousModule} from "./mocks/MockImplementationMaliciousModule.sol";
import {MockImplementationMaliciousStorageModule} from "./mocks/MockImplementationMaliciousStorageModule.sol";
import {MockImplementationModule} from "./mocks/MockImplementationModule.sol";

/**
Expand Down Expand Up @@ -49,7 +50,8 @@ contract ImplementationModuleInternalTest is ImplementationFixture {
MockImplementationModule public internalModuleV1;
MockImplementationModule public internalModuleV2;
MockImplementationDeprecatedModule public internalModuleV3;
MockImplementationMaliciousModule public internalModuleV4;
MockImplementationModule public internalModuleV4;
MockImplementationMaliciousStorageModule public internalModuleMaliciousStorageV4;

// =====
// Setup
Expand Down Expand Up @@ -103,7 +105,16 @@ contract ImplementationModuleInternalTest is ImplementationFixture {
})
);

internalModuleV4 = new MockImplementationMaliciousModule(
internalModuleV4 = new MockImplementationModule(
IReflexModule.ModuleSettings({
moduleId: _MODULE_INTERNAL_ID,
moduleType: _MODULE_INTERNAL_TYPE,
moduleVersion: _MODULE_INTERNAL_VERSION_V4,
moduleUpgradeable: _MODULE_INTERNAL_UPGRADEABLE_V4
})
);

internalModuleMaliciousStorageV4 = new MockImplementationMaliciousStorageModule(
IReflexModule.ModuleSettings({
moduleId: _MODULE_INTERNAL_ID,
moduleType: _MODULE_INTERNAL_TYPE,
Expand Down Expand Up @@ -192,6 +203,14 @@ contract ImplementationModuleInternalTest is ImplementationFixture {
_MODULE_INTERNAL_VERSION_V4,
_MODULE_INTERNAL_UPGRADEABLE_V4
);

_verifyModuleConfiguration(
internalModuleMaliciousStorageV4,
_MODULE_INTERNAL_ID,
_MODULE_INTERNAL_TYPE,
_MODULE_INTERNAL_VERSION_V4,
_MODULE_INTERNAL_UPGRADEABLE_V4
);
}

function testFuzzCallInternalModule(bytes32 message_) external {
Expand Down Expand Up @@ -236,14 +255,6 @@ contract ImplementationModuleInternalTest is ImplementationFixture {

// Verify internal module.

_verifyModuleConfiguration(
singleModuleEndpoint,
_MODULE_SINGLE_ID,
_MODULE_SINGLE_TYPE,
_MODULE_SINGLE_VERSION_V1,
_MODULE_SINGLE_UPGRADEABLE_V1
);

_verifyModuleConfiguration(
IReflexModule.ModuleSettings({
moduleId: _MODULE_INTERNAL_ID,
Expand Down Expand Up @@ -308,9 +319,37 @@ contract ImplementationModuleInternalTest is ImplementationFixture {
// Verify storage is not modified by upgrades in `Dispatcher` context.

_verifyGetStateSlot(message_);

// Attempt to upgrade internal module that was marked as deprecated, this should fail.

moduleAddresses = new address[](1);
moduleAddresses[0] = address(internalModuleV4);

vm.expectRevert(
abi.encodeWithSelector(IReflexInstaller.ModuleNotUpgradeable.selector, internalModuleV4.moduleId())
);
installerEndpoint.upgradeModules(moduleAddresses);

// Verify internal module was not upgraded.

_verifyModuleConfiguration(
IReflexModule.ModuleSettings({
moduleId: _MODULE_INTERNAL_ID,
moduleType: _MODULE_INTERNAL_TYPE,
moduleVersion: _MODULE_INTERNAL_VERSION_V3,
moduleUpgradeable: _MODULE_INTERNAL_UPGRADEABLE_V3
})
);

// Verify storage is not modified by upgrades in `Dispatcher` context.

_verifyGetStateSlot(message_);
}

function testFuzzUpgradeInternalModuleToMaliciousModule(bytes32 message_, uint8 number_) external brutalizeMemory {
function testFuzzUpgradeInternalModuleToMaliciousStorageModule(
bytes32 message_,
uint8 number_
) external brutalizeMemory {
vm.assume(uint8(uint256(message_)) != number_);

// Verify storage sets in `Dispatcher` context.
Expand All @@ -319,14 +358,6 @@ contract ImplementationModuleInternalTest is ImplementationFixture {

assertEq(dispatcher.getImplementationState0(), message_);

_verifyModuleConfiguration(
singleModuleEndpoint,
_MODULE_SINGLE_ID,
_MODULE_SINGLE_TYPE,
_MODULE_SINGLE_VERSION_V1,
_MODULE_SINGLE_UPGRADEABLE_V1
);

_verifyModuleConfiguration(
IReflexModule.ModuleSettings({
moduleId: _MODULE_INTERNAL_ID,
Expand All @@ -337,7 +368,7 @@ contract ImplementationModuleInternalTest is ImplementationFixture {
);

address[] memory moduleAddresses = new address[](1);
moduleAddresses[0] = address(internalModuleV4);
moduleAddresses[0] = address(internalModuleMaliciousStorageV4);
installerEndpoint.upgradeModules(moduleAddresses);

_verifyModuleConfiguration(
Expand Down Expand Up @@ -366,7 +397,7 @@ contract ImplementationModuleInternalTest is ImplementationFixture {
number_
);

// Verify that the storage in the `Dispatcher` context has been overwritten.
// Verify that the storage in the `Dispatcher` context has been overwritten, this is disastrous.

assertEq(uint8(uint256(dispatcher.getImplementationState0())), number_);
assertFalse(dispatcher.getImplementationState0() == message_);
Expand Down
Loading

0 comments on commit 367605f

Please sign in to comment.