In lib/NativeMetaTransactions.sol there is a frequently used helper msgSender:
function msgSender() internal view returns (address payable sender) {
if (msg.sender == address(this)) {
bytes memory array = msg.data;
uint256 index = msg.data.length;
assembly {
// Load the 32 bytes word from memory with the address on the lower 20 bytes, and mask those.
sender := and(
mload(add(array, index)),
0xffffffffffffffffffffffffffffffffffffffff
)
}
} else {
...
Even though only the last 20-bytes matter, the bytes memory array = msg.data; line causes the entire calldata to be copied to memory. This is exaggerated by the fact, that if msgSender() is called multiple times in a transaction, the calldata will be also copied multiple times as memory is not freed.
Proof of Concept
N/A
Tools Used
Manual review.
Recommended Mitigation Steps
There are multiple ways to avoid this:
Make use of calldata slices and conversions
Something along the lines of (untested!):
// Copy last 20 bytes
bytes calldata data = msg.data[(msg.data.length - 20):];
sender = payable(address(uint160(bytes20(data))));
Handle
axic
Vulnerability details
Impact
In
lib/NativeMetaTransactions.sol
there is a frequently used helpermsgSender
:Even though only the last 20-bytes matter, the
bytes memory array = msg.data;
line causes the entire calldata to be copied to memory. This is exaggerated by the fact, that ifmsgSender()
is called multiple times in a transaction, the calldata will be also copied multiple times as memory is not freed.Proof of Concept
N/A
Tools Used
Manual review.
Recommended Mitigation Steps
There are multiple ways to avoid this:
Something along the lines of (untested!):
The OpenZeppelin implementation (https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/metatx/ERC2771Context.sol#L21-L30) is an example of an optimised assembly version:
One must note that the pure assembly version is obviously the most gas efficient, at least today.