HardhatFoundry
30,000 USDC
View results
Submission Details
Severity: low
Invalid

Write after write in `parseEnableModeData` function can lead to Incorrect Data Parsing that can cause an unexpected behavior in the application

Summary

A write-after-write vulnerability may arise if multiple write operations target the same memory location or storage variable without proper synchronization. Incorrect calculation of offsets and lengths in the assembly code could lead to improper parsing of input data, resulting in the function returning incorrect or malformed data.

Vulnerability Details

A write-after-write vulnerability potentially occurs in this function

function parseEnableModeData(bytes calldata packedData)
internal
pure
returns (
uint256 moduleType,
bytes calldata moduleInitData,
bytes calldata enableModeSignature,
bytes calldata userOpSignature
)
{
uint256 p;
assembly {
p := packedData.offset
moduleType := calldataload(p)
moduleInitData.length := shr(224, calldataload(add(p, 0x20)))
moduleInitData.offset := add(p, 0x24)
p := add(moduleInitData.offset, moduleInitData.length)
enableModeSignature.length := shr(224, calldataload(p))
enableModeSignature.offset := add(p, 0x04)
p := sub(add(enableModeSignature.offset, enableModeSignature.length), packedData.offset)
}
userOpSignature = packedData[p:];
}

Test for parseEnableModeData

// SPDX-License-Identifier: MIT
pragma solidity 0.8.26;
import "forge-std/Test.sol";
import "./LocalCallDataParserLib.sol";
contract LocalCallDataParserLibTest is Test {
using LocalCallDataParserLib for bytes;
function testParseEnableModeData() public {
// Construct packedData with incorrect offsets to simulate the vulnerability
bytes memory packedData = hex"0000000000000000000000000000000000000000000000000000000000000001" // moduleType
hex"0000000000000000000000000000000000000000000000000000000000000020" // moduleInitData length
hex"1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef" // moduleInitData
hex"0000000000000000000000000000000000000000000000000000000000000010" // enableModeSignature length
hex"abcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdef" // enableModeSignature
hex"abcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdef"; // userOpSignature
// Parse the packedData
(uint256 moduleType, bytes memory moduleInitData, bytes memory enableModeSignature, bytes memory userOpSignature) = packedData.parseEnableModeData();
// Check if the parsed data is incorrect due to the vulnerability
assertEq(moduleType, 1);
assertEq(moduleInitData.length, 32);
assertEq(enableModeSignature.length, 16);
assertEq(userOpSignature.length, 32);
// Check if the parsed data is malformed
assertEq(keccak256(moduleInitData), keccak256(hex"1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef"));
assertEq(keccak256(enableModeSignature), keccak256(hex"abcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdef"));
assertEq(keccak256(userOpSignature), keccak256(hex"abcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdef"));
}
}

Impact

The function's incorrect behavior or malformed data could affect the logic of the contract that relies on correctly parsed data. This could cause unexpected behavior in the application.

Tools Used

Manual Review

Recommendations

function parseEnableModeData(bytes calldata packedData)
internal
pure
returns (
uint256 moduleType,
bytes calldata moduleInitData,
bytes calldata enableModeSignature,
bytes calldata userOpSignature
)
{
uint256 p;
uint256 moduleInitDataLength;
uint256 enableModeSignatureLength;
assembly {
p := packedData.offset
moduleType := calldataload(p)
// Read moduleInitData length and offset
moduleInitDataLength := shr(224, calldataload(add(p, 0x20)))
moduleInitData.offset := add(p, 0x24)
// Move pointer forward by moduleInitData length
p := add(moduleInitData.offset, moduleInitDataLength)
// Read enableModeSignature length and offset
enableModeSignatureLength := shr(224, calldataload(p))
enableModeSignature.offset := add(p, 0x04)
// Update pointer to point to the start of userOpSignature
p := add(enableModeSignature.offset, enableModeSignatureLength)
}
// Assign lengths outside of assembly to avoid overwriting
moduleInitData = packedData[moduleInitData.offset : moduleInitData.offset + moduleInitDataLength];
enableModeSignature = packedData[enableModeSignature.offset : enableModeSignature.offset + enableModeSignatureLength];
userOpSignature = packedData[p : packedData.length];
}
  1. Temporary variables moduleInitDataLength and enableModeSignatureLength are utilized to store the lengths of the respective data fields read from calldata, preventing premature overwriting of memory locations.

  2. Each step of reading and writing from calldata is isolated. Initially, the lengths and offsets of moduleInitData and enableModeSignature are read and stored in temporary variables. These values are then assigned outside the assembly block to ensure proper handling.

  3. Assembly constructs are used correctly to manage calldata and memory operations safely. Offsets are calculated accurately, and lengths are assigned outside the assembly block to avoid potential issues.

Updates

Lead Judging Commences

0xnevi Lead Judge 11 months ago
Submission Judgement Published
Invalidated
Reason: Incorrect statement
Assigned finding tags:

finding-write-after-write-issues

Invalid, no impact described specific to protocol. Additionally, there is no same memory location accessed as claimed

Support

FAQs

Can't find an answer? Chat with us on Discord, Twitter or Linkedin.