Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: [QS-01] revert on presence of validation hooks in deferred validation, and run validation-associated execution hooks #287

Merged
merged 9 commits into from
Nov 15, 2024

Conversation

Zer0dot
Copy link
Contributor

@Zer0dot Zer0dot commented Nov 11, 2024

Motivation

Currently, it's possible to skip userOp validation hooks if the validation function being used is also a signature validation through deferred installation. Furthermore, we're not running userOp validation-associated execution hooks, when we could be.

Solution

Revert if the validation being used includes validation hooks, and run the validation-associated pre and post hooks tied to that validation.

Copy link

octane-security-app bot commented Nov 11, 2024

Summary by Octane

New Contracts

No new contracts were added in this PR.

Updated Contracts

  • ModularAccountBase.sol: Enhanced deferred action handling by splitting signature validation, incorporating validation hooks, and improving memory management.
  • MockCountModule.sol: Updated error message to specify "mockCountModule" in the post execution hook failure notification.
  • AccountTestBase.sol: Removed the packing of final signature, streamlining deferred validation signature handling.

🔗 Commit Hash: 0810747

Copy link

github-actions bot commented Nov 11, 2024

Contract sizes:

| Contract                      | Runtime Size (B) | Initcode Size (B) | Runtime Margin (B) | Initcode Margin (B) |
|-------------------------------|------------------|-------------------|--------------------|---------------------|
| AccountFactory                |            5,921 |             6,386 |             18,655 |              42,766 |
| AllowlistModule               |            9,553 |             9,580 |             15,023 |              39,572 |
| ExecutionInstallDelegate      |            5,714 |             5,760 |             18,862 |              43,392 |
| ModularAccount                |           22,254 |            29,083 |              2,322 |              20,069 |
| NativeFunctionDelegate        |              560 |               587 |             24,016 |              48,565 |
| NativeTokenLimitModule        |            4,449 |             4,476 |             20,127 |              44,676 |
| PaymasterGuardModule          |            1,845 |             1,872 |             22,731 |              47,280 |
| SemiModularAccount7702        |           23,157 |            29,979 |              1,419 |              19,173 |
| SemiModularAccountBytecode    |           23,639 |            30,468 |                937 |              18,684 |
| SemiModularAccountStorageOnly |           24,133 |            30,962 |                443 |              18,190 |
| SingleSignerValidationModule  |            3,646 |             3,673 |             20,930 |              45,479 |
| TimeRangeModule               |            2,003 |             2,030 |             22,573 |              47,122 |
| WebAuthnValidationModule      |            7,854 |             7,881 |             16,722 |              41,271 |

Code coverage:

File % Lines % Statements % Branches % Funcs
src/account/AccountBase.sol 100.00% (8/8) 100.00% (7/7) 100.00% (2/2) 100.00% (4/4)
src/account/AccountStorageInitializable.sol 100.00% (19/19) 100.00% (26/26) 100.00% (5/5) 100.00% (2/2)
src/account/ModularAccount.sol 100.00% (2/2) 100.00% (2/2) 100.00% (0/0) 100.00% (2/2)
src/account/ModularAccountBase.sol 99.04% (310/313) 96.45% (380/394) 78.69% (48/61) 97.30% (36/37)
src/account/ModularAccountView.sol 100.00% (24/24) 100.00% (28/28) 100.00% (2/2) 100.00% (4/4)
src/account/ModuleManagerInternals.sol 95.08% (58/61) 96.20% (76/79) 62.50% (5/8) 100.00% (3/3)
src/account/SemiModularAccount7702.sol 0.00% (0/6) 0.00% (0/6) 0.00% (0/1) 0.00% (0/3)
src/account/SemiModularAccountBase.sol 89.06% (57/64) 92.31% (84/91) 68.75% (11/16) 100.00% (15/15)
src/account/SemiModularAccountBytecode.sol 100.00% (6/6) 100.00% (7/7) 100.00% (1/1) 100.00% (2/2)
src/account/SemiModularAccountStorageOnly.sol 80.00% (4/5) 83.33% (5/6) 100.00% (0/0) 50.00% (1/2)
src/account/TokenReceiver.sol 33.33% (1/3) 33.33% (1/3) 100.00% (0/0) 33.33% (1/3)
src/factory/AccountFactory.sol 75.56% (34/45) 80.33% (49/61) 50.00% (3/6) 60.00% (9/15)
src/helpers/ExecutionInstallDelegate.sol 92.59% (50/54) 92.96% (66/71) 40.00% (2/5) 100.00% (7/7)
src/helpers/NativeFunctionDelegate.sol 100.00% (22/22) 100.00% (42/42) 100.00% (0/0) 100.00% (1/1)
src/libraries/ExecutionLib.sol 99.64% (276/277) 98.89% (268/271) 90.91% (30/33) 100.00% (24/24)
src/libraries/KnownSelectorsLib.sol 100.00% (16/16) 100.00% (34/34) 100.00% (0/0) 100.00% (2/2)
src/libraries/LinkedListSetLib.sol 94.00% (47/50) 96.25% (77/80) 66.67% (4/6) 100.00% (8/8)
src/libraries/MemManagementLib.sol 100.00% (54/54) 100.00% (70/70) 100.00% (0/0) 100.00% (12/12)
src/libraries/ModuleInstallCommonsLib.sol 57.14% (8/14) 42.11% (8/19) 75.00% (3/4) 100.00% (3/3)
src/modules/ModuleBase.sol 100.00% (13/13) 94.12% (16/17) 100.00% (2/2) 100.00% (3/3)
src/modules/permissions/AllowlistModule.sol 86.05% (74/86) 85.71% (96/112) 78.26% (18/23) 50.00% (9/18)
src/modules/permissions/NativeTokenLimitModule.sol 90.91% (40/44) 93.22% (55/59) 100.00% (13/13) 66.67% (8/12)
src/modules/permissions/PaymasterGuardModule.sol 83.33% (10/12) 82.35% (14/17) 66.67% (2/3) 71.43% (5/7)
src/modules/permissions/TimeRangeModule.sol 85.71% (12/14) 81.82% (18/22) 100.00% (2/2) 87.50% (7/8)
src/modules/validation/SingleSignerValidationModule.sol 92.00% (23/25) 81.58% (31/38) 62.50% (5/8) 90.00% (9/10)
src/modules/validation/WebAuthnValidationModule.sol 61.11% (11/18) 66.67% (18/27) 100.00% (3/3) 60.00% (6/10)
Total 93.94% (1179/1255) 93.01% (1478/1589) 78.92% (161/204) 84.33% (183/217)

Copy link

octane-security-app bot commented Nov 11, 2024

Overview

Vulnerabilities found: 2                                                                                

Detailed findings

src/account/ModularAccountBase.sol


🔗 Commit Hash: 0810747
🛡️ Octane Dashboard: All vulnerabilities

@@ -413,12 +414,18 @@ abstract contract ModularAccountBase is
/// [(33 + deferredActionSigLength + encodedDataLength):] : bytes, userOpSignature. This is the
/// signature passed to the inner validation.
if (hasDeferredAction) {
// Because this bypasses UO validation hooks, we require that the validation used does not include any
// validation hooks.
if (!getAccountStorage().validationStorage[validationFunction].validationHooks.isEmpty()) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should document this during validation installation.

If you want to use a validation to approve deferred actions in the future, it should not have validation hooks.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Where do you think this fits? As a comment in installValidation()? Or perhaps in the documentation for integrators/SDK?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In the documentation notion doc for now to later transfer to the doc site is enough probably.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added a section in the documentation, feel free to give it a peek!

@Zer0dot Zer0dot changed the base branch from develop to adam/fix-execute-user-op-with-def-action November 12, 2024 22:08
@adamegyed adamegyed force-pushed the adam/fix-execute-user-op-with-def-action branch 2 times, most recently from 67305ea to 3973328 Compare November 13, 2024 18:54
@adamegyed adamegyed force-pushed the adam/fix-execute-user-op-with-def-action branch from 3973328 to 6180cf4 Compare November 14, 2024 20:34
Base automatically changed from adam/fix-execute-user-op-with-def-action to develop November 14, 2024 21:19
Copy link
Contributor

@adamegyed adamegyed left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good overall, have a few questions about code structure

Comment on lines 448 to 451
ExecutionLib.callBubbleOnRevertTransient(address(this), 0, encodedData[63:]);

// Do the cached post hooks
ExecutionLib.doCachedPostHooks(postHookData);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does it make sense to move this section into the _validateDeferredActionAndSetNonce function? It seems like the functionality is leaking out from the function, as it runs the pre-exec hooks there. Or otherwise reorganize to do the pre-exec hooks + self-call + post exec hooks together?

(Side note: this is a case where we can manually clear the memory after running with freezeFMP / restoreFMP, because this action isn't reused for the user op validation later.)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should be good to go now, it's all in a _handleDeferredAction() function!


// Clear the memory after performing signature validation
MemSnapshot memSnapshot = MemManagementLib.freezeFMP();
if (_isValidSignature(defActionSigValidation.moduleEntity(), typedDataHash, sig) != _1271_MAGIC_VALUE) {
if (_isValidSignature(defActionValidationModuleEntity, typedDataHash, sig) != _1271_MAGIC_VALUE) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Given that we assert there are no pre-validation hooks at all, we can also assert there are no pre-signature validation hooks, which means we will never have any per-hook data. Given that, I think we should be good to replace this call to _isValidSignature with _exec1271Validation, and omit the signature segment decoding. Wdyt?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sounds good to me!

@@ -711,6 +711,21 @@ abstract contract ModularAccountBase is
return ExecutionLib.invokeUserOpCallBuffer(callBuffer, userOpValidationFunction, signatureSegment);
}

function _runValidationAssociatedExecHooks(ModuleEntity validationFunction, bytes calldata callData)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, this may be a good place to wrap all of {pre exec hooks, the transient self-call, and the post-exec hooks} while wrapping with the memory deallocator.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I went ahead and wrapped the entire call to _handleDeferredAction() which is a new function that basically does everything except the initial decoding in _validateUserOp(). It looks good to me, but let me know if there's any memory-related issue you spot.

@@ -413,13 +414,12 @@ abstract contract ModularAccountBase is
/// [(33 + deferredActionSigLength + encodedDataLength):] : bytes, userOpSignature. This is the
/// signature passed to the inner validation.
if (hasDeferredAction) {
// Use outer validation to validate the UO, and the inner validation as 1271 validation for the
// deferred action.
// Use outer validation as a 1271 validation, then use the inner validation to validate the UO.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This comment shouldn't be switched, right?

Comment on lines +747 to +750
// Check that the passed nonce isn't already invalidated.
if (getAccountStorage().deferredActionNonceUsed[nonce]) {
revert DeferredActionNonceInvalid();
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is correct to do in the workflow, but it isn't described by the function name - can we refactor this out to the parent function, or update the function naming?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Function name changed, it now handles the entire signature flow

Copy link
Contributor

@adamegyed adamegyed left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good, 1 small storage optimization


// Because this bypasses UO validation hooks, we require that the validation used does not include any
// validation hooks.
if (!getAccountStorage().validationStorage[defActionValidationModuleEntity].validationHooks.isEmpty()) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should use validationStorage[defActionValidationModuleEntity].validationHookCount != 0, to prevent the cold sload here.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

On it!

@Zer0dot Zer0dot merged commit 48a77f0 into develop Nov 15, 2024
6 checks passed
@Zer0dot Zer0dot deleted the zer0dot/fix-qs-01 branch November 15, 2024 21:05
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants