Skip to content

Commit 43f04e8

Browse files
authored
✨ LibBytes calldata checks (#1477)
1 parent 71d5107 commit 43f04e8

File tree

3 files changed

+96
-0
lines changed

3 files changed

+96
-0
lines changed

src/utils/LibBytes.sol

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -842,6 +842,32 @@ library LibBytes {
842842
}
843843
}
844844

845+
/// @dev Checks if `x` is in `a`. Assumes `a` has been checked.
846+
function checkInCalldata(bytes calldata x, bytes calldata a) internal pure {
847+
/// @solidity memory-safe-assembly
848+
assembly {
849+
if or(
850+
or(lt(x.offset, a.offset), gt(add(x.offset, x.length), add(a.length, a.offset))),
851+
shr(64, or(x.length, x.offset))
852+
) { revert(0x00, 0x00) }
853+
}
854+
}
855+
856+
/// @dev Checks if `x` is in `a`. Assumes `a` has been checked.
857+
function checkInCalldata(bytes[] calldata x, bytes calldata a) internal pure {
858+
/// @solidity memory-safe-assembly
859+
assembly {
860+
let e := sub(add(a.length, a.offset), 0x20)
861+
if or(lt(x.offset, a.offset), shr(64, x.offset)) { revert(0x00, 0x00) }
862+
for { let i := 0 } iszero(eq(x.length, i)) { i := add(i, 1) } {
863+
let o := calldataload(add(x.offset, shl(5, i)))
864+
let t := add(o, x.offset)
865+
let l := calldataload(t)
866+
if or(shr(64, or(l, o)), gt(add(t, l), e)) { revert(0x00, 0x00) }
867+
}
868+
}
869+
}
870+
845871
/// @dev Returns empty calldata bytes. For silencing the compiler.
846872
function emptyCalldata() internal pure returns (bytes calldata result) {
847873
/// @solidity memory-safe-assembly

src/utils/g/LibBytes.sol

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -846,6 +846,32 @@ library LibBytes {
846846
}
847847
}
848848

849+
/// @dev Checks if `x` is in `a`. Assumes `a` has been checked.
850+
function checkInCalldata(bytes calldata x, bytes calldata a) internal pure {
851+
/// @solidity memory-safe-assembly
852+
assembly {
853+
if or(
854+
or(lt(x.offset, a.offset), gt(add(x.offset, x.length), add(a.length, a.offset))),
855+
shr(64, or(x.length, x.offset))
856+
) { revert(0x00, 0x00) }
857+
}
858+
}
859+
860+
/// @dev Checks if `x` is in `a`. Assumes `a` has been checked.
861+
function checkInCalldata(bytes[] calldata x, bytes calldata a) internal pure {
862+
/// @solidity memory-safe-assembly
863+
assembly {
864+
let e := sub(add(a.length, a.offset), 0x20)
865+
if or(lt(x.offset, a.offset), shr(64, x.offset)) { revert(0x00, 0x00) }
866+
for { let i := 0 } iszero(eq(x.length, i)) { i := add(i, 1) } {
867+
let o := calldataload(add(x.offset, shl(5, i)))
868+
let t := add(o, x.offset)
869+
let l := calldataload(t)
870+
if or(shr(64, or(l, o)), gt(add(t, l), e)) { revert(0x00, 0x00) }
871+
}
872+
}
873+
}
874+
849875
/// @dev Returns empty calldata bytes. For silencing the compiler.
850876
function emptyCalldata() internal pure returns (bytes calldata result) {
851877
/// @solidity memory-safe-assembly

test/LibBytes.t.sol

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -373,4 +373,48 @@ contract LibBytesTest is SoladyTest {
373373
assertEq(uint160(LibBytes.msbToAddress(x)), msb);
374374
assertEq(uint160(LibBytes.lsbToAddress(x)), lsb);
375375
}
376+
377+
function testCheckInCalldata(bytes memory child) public view {
378+
this.checkInCalldata(child, abi.encode(child));
379+
}
380+
381+
function testCheckInCalldata() public pure {
382+
LibBytes.checkInCalldata(msg.data, msg.data);
383+
}
384+
385+
function checkInCalldata(bytes calldata expectedChild, bytes calldata encoded) public pure {
386+
bytes calldata child;
387+
/// @solidity memory-safe-assembly
388+
assembly {
389+
child.offset := add(0x20, add(encoded.offset, calldataload(encoded.offset)))
390+
child.length := calldataload(add(encoded.offset, calldataload(encoded.offset)))
391+
}
392+
LibBytes.checkInCalldata(child, encoded);
393+
LibBytes.checkInCalldata(child, msg.data);
394+
LibBytes.checkInCalldata(encoded, msg.data);
395+
require(keccak256(expectedChild) == keccak256(child));
396+
}
397+
398+
function testCheckInCalldata(bytes[] memory children) public view {
399+
this.checkInCalldata(children, abi.encode(children));
400+
}
401+
402+
function checkInCalldata(bytes[] calldata expectedChildren, bytes calldata encoded)
403+
public
404+
pure
405+
{
406+
bytes[] calldata children;
407+
/// @solidity memory-safe-assembly
408+
assembly {
409+
children.offset := add(0x20, add(encoded.offset, calldataload(encoded.offset)))
410+
children.length := calldataload(add(encoded.offset, calldataload(encoded.offset)))
411+
}
412+
LibBytes.checkInCalldata(children, encoded);
413+
LibBytes.checkInCalldata(expectedChildren, msg.data);
414+
LibBytes.checkInCalldata(children, msg.data);
415+
require(expectedChildren.length == children.length);
416+
for (uint256 i; i < children.length; ++i) {
417+
require(keccak256(expectedChildren[i]) == keccak256(children[i]));
418+
}
419+
}
376420
}

0 commit comments

Comments
 (0)