On March 10th, 2022, the Solidity team discovered a bug in the implementation of
abi.encodeCall
when used together with fixed-length bytes literals.
It was introduced together with abi.encodeCall
in Solidity 0.8.11 and is fixed in 0.8.13.
We assigned the bug a severity of “very low”.
Which Contracts are Affected?
You might be affected if you use abi.encodeCall(f, (...))
where f
takes a
bytesNN
parameter and you provide the value for that parameter either as a
hex literal (0x1234
or hex"abcd"
) or
as a string literal ("abcd"
).
If you only pass variables to abi.encodeCall
, your code is not affected.
Technical Details
The compiler did check that the types of the values are all implicitly convertible
to the types of the parameters of the function (this was the main advantage of
abi.encodeCall
over abi.encode
and the reason for adding the feature),
but it did not take the types properly into account when generating the code for
this function call. The generated code was still the same as the one for
abi.encode
, which just encodes according to the types of the arguments.
The problem is that literals like 0x1234
and "abcd"
can be implicitly converted to
different types: The first can be converted to both bytes2
and uint16
and the
second can be converted to bytes4
and bytes memory
.
In the first case, the compiler chose to encode 0x1234
as a uint16
. This means it
right-aligned it instead of left-aligning it for bytes2
. In the second case, it
was encoded as bytes memory
, which is a dynamic type, and thus at the place of
the bytes4
value, an offset pointer was stored and additional data was added at the end.
Since the bug is only present for literals (actual variables have proper types and implicit conversions are always no-ops), it should be easy to detect with simple testing.