`super` resolution ignores visibility and can select an `external` function
Reported to us confidentially by @cyberthirst. We analysed it and determined it is not a security issue, so we are filing it publicly here as an ordinary compiler bug. Thanks to @cyberthirst for finding and reporting it.
A super.f() call is type-checked against the candidate set of the contract that
lexically contains it, but resolved again at code generation time over the
linearization of the most derived contract. FunctionDefinition::resolveVirtual()
matches candidates on name and parameter types only, it does not check
isVisibleInDerivedContracts(), so an external function is an eligible target.
But super is an internal call and can never legitimately reach an external function
body.
Currently:
- legacy codegen incorrectly emits an internal
JUMPinto theexternalfunction's body. Miscompile. - via-IR hits an assertion failure. Which is better, but not nice.
Reproducer
poc.sol:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
contract A {
function f() public virtual returns (uint256) { return 1; }
}
contract X {
function f() external virtual returns (uint256) { return 100; }
}
contract B is A {
function f() public virtual override returns (uint256) { return super.f(); }
}
// C3 linearization of D: [D, B, X, A]
contract D is A, X, B {
function f() public override(A, X, B) returns (uint256) { return super.f(); }
}Expected behaviour
Should be a compile error,m with nice warning to user.
Source: argotorg/solidity