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

Unable to create a calldata check immediately adjacent to an existing one #29

Closed
howlbot-integration bot opened this issue Oct 27, 2024 · 2 comments
Labels
2 (Med Risk) Assets not at direct risk, but function/availability of the protocol could be impacted or leak value bug Something isn't working duplicate-17 edited-by-warden 🤖_primary AI based primary recommendation 🤖_24_group AI based duplicate group recommendation satisfactory satisfies C4 submission criteria; eligible for awards sufficient quality report This report is of sufficient quality

Comments

@howlbot-integration
Copy link

Lines of code

https://github.com/solidity-labs-io/kleidi/blob/0d72b6cb5725c1380212dc76257da96fcfacf22f/src/Timelock.sol#L1120-L1121

Vulnerability details

Impact

This vulnerability will prevent the creation of a calldata check map as intended.

Proof of Concept

The Timelock::checkCalldata() function verifies the whitelisted status of calldata by invoking BytesHelper.getSlicedBytesHash() which internally calls BytesHelper.sliceBytes().

src/Timelock.sol#L495-L502

            require(
                calldataCheck.dataHashes.contains(
                    data.getSlicedBytesHash(
                        calldataCheck.startIndex, calldataCheck.endIndex
                    )
                ),
                "CalldataList: Calldata does not match expected value"
            );

The sliceBytes() function grabs a slice of bytes by start and end indexes.

    function sliceBytes(bytes memory toSlice, uint256 start, uint256 end)
        public
        pure
        returns (bytes memory)
    {
        ... ...

        uint256 length = end - start;
        bytes memory sliced = new bytes(length);

        for (uint256 i = 0; i < length; i++) {
            sliced[i] = toSlice[i + start];
        }

        return sliced;
    }

From the above code snippet, we can see that the function takes a slice by the range [start, end - 1].

However, when creating a calldata check map by using the Timelock::_addCalldataCheck()function, the function checks the overlapping status between new range and existing ones by the following lines:

    function _addCalldataCheck(
        address contractAddress,
        bytes4 selector,
        uint16 startIndex,
        uint16 endIndex,
        bytes[] memory data
    ) private {
        ... ...
        uint256 targetIndex = indexes.length;
        {
            bool found;
            for (uint256 i = 0; i < indexes.length; i++) {
                ... ...
                require(
                    startIndex > indexes[i].endIndex
                        || endIndex < indexes[i].startIndex,
                    "CalldataList: Partial check overlap"
                );
            }
            ... ...
        }
        ... ...
    }

Let's assume S1, E1 is an existing range. Then, the new range to be added can be put into either within [E1+1, +Inf) or [4, S1-1].

But, as sliceBytes() takes a slice from S1 to E1 - 1, the index E1 cannot be occupied by calldata checks.

If there are several ranges, their end indexes will not be included as calldata check region.

Tools Used

Manual Review

Recommended Mitigation Steps

There are 2 alternative mitigation ways:

  1. Use GTE and LTE comparison operators rather than GT and LT like the below:
    function _addCalldataCheck(
        address contractAddress,
        bytes4 selector,
        uint16 startIndex,
        uint16 endIndex,
        bytes[] memory data
    ) private {
        ... ...
        uint256 targetIndex = indexes.length;
        {
            bool found;
            for (uint256 i = 0; i < indexes.length; i++) {
                ... ...
                require(
-                   startIndex > indexes[i].endIndex
-                       || endIndex < indexes[i].startIndex,
+                   startIndex >= indexes[i].endIndex
+                       || endIndex <= indexes[i].startIndex,
                    "CalldataList: Partial check overlap"
                );
            }
            ... ...
        }
        ... ...
    }
  1. Or, the sliceBytes() function takes a slice of bytes from start to end, not to end - 1.

Assessed type

Invalid Validation

@howlbot-integration howlbot-integration bot added 2 (Med Risk) Assets not at direct risk, but function/availability of the protocol could be impacted or leak value 🤖_24_group AI based duplicate group recommendation 🤖_primary AI based primary recommendation bug Something isn't working duplicate-2 edited-by-warden sufficient quality report This report is of sufficient quality labels Oct 27, 2024
howlbot-integration bot added a commit that referenced this issue Oct 27, 2024
@c4-judge
Copy link

GalloDaSballo marked the issue as satisfactory

@c4-judge c4-judge added the satisfactory satisfies C4 submission criteria; eligible for awards label Oct 31, 2024
@GalloDaSballo
Copy link

@ElliotFriedman pls check the comment here, which seems to tie #2 to #17

Lmk if you mitigate them differently

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
2 (Med Risk) Assets not at direct risk, but function/availability of the protocol could be impacted or leak value bug Something isn't working duplicate-17 edited-by-warden 🤖_primary AI based primary recommendation 🤖_24_group AI based duplicate group recommendation satisfactory satisfies C4 submission criteria; eligible for awards sufficient quality report This report is of sufficient quality
Projects
None yet
Development

No branches or pull requests

2 participants