Fix UTF-8 sequence validation when used with array literals.

This commit is contained in:
wechman 2022-06-20 09:22:59 +02:00
parent be470c16fe
commit 82106d4655
18 changed files with 195 additions and 18 deletions

View File

@ -1229,6 +1229,15 @@ bool TypeChecker::visit(ForStatement const& _forStatement)
return false;
}
bool TypeChecker::visit(Return const& _return)
{
if (_return.expression() && _return.annotation().functionReturnParameters)
for (auto parameter: _return.annotation().functionReturnParameters->parameters())
if (parameter)
_return.expression()->annotation().resultExpectedTypes.push_back(parameter->type());
return true;
}
void TypeChecker::endVisit(Return const& _return)
{
ParameterList const* params = _return.annotation().functionReturnParameters;
@ -1342,9 +1351,13 @@ bool TypeChecker::visit(VariableDeclarationStatement const& _statement)
return false;
}
vector<ASTPointer<VariableDeclaration>> const& variables = _statement.declarations();
for (auto const& variable: variables)
if (variable)
_statement.initialValue()->annotation().resultExpectedTypes.push_back(variable->type());
// Here we have an initial value and might have to derive some types before we can visit
// the variable declaration(s).
_statement.initialValue()->accept(*this);
TypePointers valueTypes;
if (auto tupleType = dynamic_cast<TupleType const*>(type(*_statement.initialValue())))
@ -1352,7 +1365,6 @@ bool TypeChecker::visit(VariableDeclarationStatement const& _statement)
else
valueTypes = TypePointers{type(*_statement.initialValue())};
vector<ASTPointer<VariableDeclaration>> const& variables = _statement.declarations();
if (variables.empty())
// We already have an error for this in the SyntaxChecker.
solAssert(m_errorReporter.hasErrors(), "");
@ -1645,6 +1657,13 @@ bool TypeChecker::visit(TupleExpression const& _tuple)
if (!components[i])
m_errorReporter.fatalTypeError(8381_error, _tuple.location(), "Tuple component cannot be empty.");
if (TupleExpression const* componentTupleExpression = dynamic_cast<TupleExpression const*>(components[i].get());
componentTupleExpression && !_tuple.annotation().resultExpectedTypes.empty() && _tuple.isInlineArray())
{
solAssert(_tuple.annotation().resultExpectedTypes.size() == 1);
componentTupleExpression->annotation().resultExpectedTypes = {dynamic_cast<ArrayType const*>(_tuple.annotation().resultExpectedTypes.front())->baseType()};
}
components[i]->accept(*this);
types.push_back(type(*components[i]));
@ -1665,6 +1684,26 @@ bool TypeChecker::visit(TupleExpression const& _tuple)
{
solAssert(!!types[i], "Inline array cannot have empty components");
if (_tuple.annotation().resultExpectedTypes.size())
{
solAssert(_tuple.annotation().resultExpectedTypes.size() == 1);
ArrayType const* toArray = dynamic_cast<ArrayType const*>(_tuple.annotation().resultExpectedTypes.front());
Type const* componentType = type(*components[i]);
BoolResult result = componentType->isImplicitlyConvertibleTo(*toArray->baseType());
if (!result)
{
std::string const errorMessage =
"Type " +
componentType->toString(false) +
" is not implicitly convertible to expected type " +
toArray->baseType()->toString(false) +
(result.message().empty() ? "." : ". " + result.message());
m_errorReporter.typeError(6069_error, components[i]->location(), errorMessage);
}
}
if ((i == 0 || inlineArrayType) && !types[i]->mobileType())
m_errorReporter.fatalTypeError(9563_error, components[i]->location(), "Invalid mobile type.");
@ -1694,7 +1733,13 @@ bool TypeChecker::visit(TupleExpression const& _tuple)
"Type " + inlineArrayType->toString(true) + " is only valid in storage."
);
_tuple.annotation().type = TypeProvider::array(DataLocation::Memory, inlineArrayType, types.size());
if (_tuple.annotation().resultExpectedTypes.size())
{
solAssert(_tuple.annotation().resultExpectedTypes.size() == 1);
_tuple.annotation().type = TypeProvider::withLocationIfReference(DataLocation::Memory, _tuple.annotation().resultExpectedTypes.front());
}
else
_tuple.annotation().type = TypeProvider::array(DataLocation::Memory, inlineArrayType, types.size());
}
else
{
@ -3286,6 +3331,12 @@ bool TypeChecker::visit(MemberAccess const& _memberAccess)
bool TypeChecker::visit(IndexAccess const& _access)
{
_access.annotation().isConstant = false;
if (TupleExpression const* tuple = dynamic_cast<TupleExpression const*>(&_access.baseExpression()))
for (auto const& expectedType: _access.annotation().resultExpectedTypes)
_access.baseExpression().annotation().resultExpectedTypes.push_back(
TypeProvider::array(DataLocation::Memory, expectedType, tuple->components().size()));
_access.baseExpression().accept(*this);
Type const* baseType = type(_access.baseExpression());
Type const* resultType = nullptr;
@ -3829,6 +3880,7 @@ Declaration const& TypeChecker::dereference(IdentifierPath const& _path) const
bool TypeChecker::expectType(Expression const& _expression, Type const& _expectedType)
{
_expression.annotation().resultExpectedTypes = { &_expectedType };
_expression.accept(*this);
BoolResult result = type(_expression)->isImplicitlyConvertibleTo(_expectedType);
if (!result)

View File

@ -144,6 +144,7 @@ private:
void endVisit(TryStatement const& _tryStatement) override;
bool visit(WhileStatement const& _whileStatement) override;
bool visit(ForStatement const& _forStatement) override;
bool visit(Return const& _return) override;
void endVisit(Return const& _return) override;
void endVisit(EmitStatement const& _emit) override;
void endVisit(RevertStatement const& _revert) override;

View File

@ -287,6 +287,9 @@ struct ExpressionAnnotation: ASTAnnotation
/// Note that even the simplest expressions, like `(f)()`, result in an indirect call even if they consist of
/// values known at compilation time.
bool calledDirectly = false;
// Types that expression result is assigned to.
std::vector<Type const*> resultExpectedTypes;
};
struct IdentifierAnnotation: ExpressionAnnotation

View File

@ -368,11 +368,20 @@ bool ExpressionCompiler::visit(TupleExpression const& _tuple)
if (_tuple.isInlineArray())
{
ArrayType const& arrayType = dynamic_cast<ArrayType const&>(*_tuple.annotation().type);
u256 memorySize = max(u256(32u), u256(_tuple.components().size() * arrayType.baseType()->memoryHeadSize()));
solAssert(!arrayType.isDynamicallySized(), "Cannot create dynamically sized inline array.");
utils().allocateMemory(max(u256(32u), arrayType.memoryDataSize()));
if (arrayType.isDynamicallySized())
memorySize += 32;
utils().allocateMemory(memorySize);
m_context << Instruction::DUP1;
if (arrayType.isDynamicallySized())
{
m_context << u256(_tuple.components().size());
utils().storeInMemoryDynamic(*TypeProvider::uint256());
}
for (auto const& component: _tuple.components())
{
acceptAndConvert(*component, *arrayType.baseType(), true);

View File

@ -495,7 +495,6 @@ bool IRGeneratorForStatements::visit(TupleExpression const& _tuple)
if (_tuple.isInlineArray())
{
auto const& arrayType = dynamic_cast<ArrayType const&>(*_tuple.annotation().type);
solAssert(!arrayType.isDynamicallySized(), "Cannot create dynamically sized inline array.");
define(_tuple) <<
m_utils.allocateMemoryArrayFunction(arrayType) <<
"(" <<
@ -503,6 +502,19 @@ bool IRGeneratorForStatements::visit(TupleExpression const& _tuple)
")\n";
string mpos = IRVariable(_tuple).part("mpos").name();
if (arrayType.isDynamicallySized())
{
appendCode() << "// dupa\n";
appendCode() <<
m_utils.writeToMemoryFunction(*TypeProvider::uint256()) <<
"(" <<
(mpos + ", " + to_string(_tuple.components().size())) <<
")\n";
}
Type const& baseType = *arrayType.baseType();
for (size_t i = 0; i < _tuple.components().size(); i++)
{
@ -510,10 +522,15 @@ bool IRGeneratorForStatements::visit(TupleExpression const& _tuple)
component.accept(*this);
setLocation(_tuple);
IRVariable converted = convert(component, baseType);
string position =
mpos + ", " + to_string(i * arrayType.memoryStride() +
(arrayType.isDynamicallySized() ? 32 : 0));
appendCode() <<
m_utils.writeToMemoryFunction(baseType) <<
"(" <<
("add(" + mpos + ", " + to_string(i * arrayType.memoryStride()) + ")") <<
("add(" + position + ")") <<
", " <<
converted.commaSeparatedList() <<
")\n";

View File

@ -16,7 +16,7 @@ contract C {
}
// ----
// constructor()
// gas irOptimized: 237351
// gas irOptimized: 224415
// gas legacy: 221315
// gas legacyOptimized: 185247
// gas legacyOptimized: 208995
// f() -> 0

View File

@ -1,9 +1,7 @@
contract C {
function externalDefault() external returns(uint) { return 11; }
function externalView() external view returns(uint) { return 12; }
function externalPure() external pure returns(uint) { return 13; }
function internalDefault() internal returns(uint) { return 21; }
function internalView() internal view returns(uint) { return 22; }
function internalPure() internal pure returns(uint) { return 23; }
@ -41,9 +39,6 @@ contract C {
}
}
// ----
// TypeError 7407: (760-779): Type function () view external returns (uint256)[1] memory is not implicitly convertible to expected type function () external returns (uint256)[1] memory.
// TypeError 7407: (812-826): Type function () view returns (uint256)[1] memory is not implicitly convertible to expected type function () returns (uint256)[1] memory.
// TypeError 7407: (1230-1249): Type function () pure external returns (uint256)[1] memory is not implicitly convertible to expected type function () external returns (uint256)[1] memory.
// TypeError 7407: (1282-1296): Type function () pure returns (uint256)[1] memory is not implicitly convertible to expected type function () returns (uint256)[1] memory.
// TypeError 7407: (1688-1707): Type function () pure external returns (uint256)[1] memory is not implicitly convertible to expected type function () external returns (uint256)[1] memory.
// TypeError 7407: (1737-1751): Type function () pure returns (uint256)[1] memory is not implicitly convertible to expected type function () returns (uint256)[1] memory.
// testViewToDefault() -> 12, 22
// testPureToDefault() -> 13, 23
// testPureToView() -> 13, 23

View File

@ -0,0 +1,41 @@
contract C {
function externalView() external view returns(uint) { return 12; }
function externalPure() external pure returns(uint) { return 13; }
function internalView() internal view returns(uint) { return 22; }
function internalPure() internal pure returns(uint) { return 23; }
function testViewToDefault() public returns (uint, uint) {
function () external returns(uint) externalDefault;
function () internal returns(uint) internalDefault;
externalDefault = this.externalView;
internalDefault = internalView;
return (externalDefault(), internalDefault());
}
function testPureToDefault() public returns (uint, uint) {
function () external returns(uint) externalDefault;
function () internal returns(uint) internalDefault;
externalDefault = this.externalPure;
internalDefault = internalPure;
return (externalDefault(), internalDefault());
}
function testPureToView() public returns (uint, uint) {
function () external view returns(uint) externalView;
function () internal view returns(uint) internalView;
externalView = this.externalPure;
internalView = internalPure;
return (externalView(), internalView());
}
}
// ----
// testViewToDefault() -> 12, 22
// testPureToDefault() -> 13, 23
// testPureToView() -> 13, 23

View File

@ -20,6 +20,6 @@ contract C {
// ----
// g() -> 2, 6
// gas irOptimized: 178637
// gas irOptimized: 178634
// gas legacy: 180945
// gas legacyOptimized: 179460

View File

@ -6,4 +6,9 @@ contract C {
}
}
// ----
// TypeError 6069: (123-126): Type literal_string "h" is not implicitly convertible to expected type string calldata.
// TypeError 6069: (128-131): Type literal_string "e" is not implicitly convertible to expected type string calldata.
// TypeError 6069: (133-136): Type literal_string "l" is not implicitly convertible to expected type string calldata.
// TypeError 6069: (138-141): Type literal_string "l" is not implicitly convertible to expected type string calldata.
// TypeError 6069: (143-146): Type literal_string "o" is not implicitly convertible to expected type string calldata.
// TypeError 6359: (122-147): Return argument type string memory[5] memory is not implicitly convertible to expected type (type of first return variable) string calldata[5] calldata.

View File

@ -0,0 +1,8 @@
contract C {
function f() public pure {
string[2] memory a2 = [string(bytes(hex'74000001')), string(bytes(hex'c0a80101'))];
string[2] memory a1 = [hex'74000001', hex'c0a80101'];
}
}
// ----
// TypeError 6069: (182-195): Type literal_string hex"c0a80101" is not implicitly convertible to expected type string memory. Contains invalid UTF-8 sequence at position 4.

View File

@ -0,0 +1,7 @@
contract C {
function f() public pure {
string memory s = [hex'74000001', hex'c0a80101'][1];
}
}
// ----
// TypeError 6069: (86-99): Type literal_string hex"c0a80101" is not implicitly convertible to expected type string memory. Contains invalid UTF-8 sequence at position 4.

View File

@ -0,0 +1,5 @@
contract C {
string[2] data = [hex'74000001', hex'c0a80101'];
}
// ----
// TypeError 6069: (50-63): Type literal_string hex"c0a80101" is not implicitly convertible to expected type string storage ref. Contains invalid UTF-8 sequence at position 4.

View File

@ -4,4 +4,6 @@ contract C {
}
}
// ----
// TypeError 6069: (71-76): Type literal_string "foo" is not implicitly convertible to expected type uint256.
// TypeError 6069: (78-82): Type bool is not implicitly convertible to expected type uint256.
// TypeError 6378: (66-83): Unable to deduce common type for array elements.

View File

@ -0,0 +1,7 @@
contract C {
function f() public pure {
string[2][2] memory a1 = [['foo', 'bar'], [hex'74000001', hex'c0a80101']];
}
}
// ----
// TypeError 6069: (110-123): Type literal_string hex"c0a80101" is not implicitly convertible to expected type string memory. Contains invalid UTF-8 sequence at position 4.

View File

@ -0,0 +1,7 @@
contract C {
function f() public pure {
string[2] memory s = [['foo', 'bar'], [hex'74000001', hex'c0a80101']][1];
}
}
// ----
// TypeError 6069: (106-119): Type literal_string hex"c0a80101" is not implicitly convertible to expected type string memory. Contains invalid UTF-8 sequence at position 4.

View File

@ -0,0 +1,11 @@
contract C {
function f() public pure returns (string[2] memory) {
return [string(bytes(hex'74000001')), string(bytes(hex'c0a80101'))];
}
function g() public pure returns (string[2] memory) {
return [hex'74000001', hex'c0a80101'];
}
}
// ----
// TypeError 6069: (244-257): Type literal_string hex"c0a80101" is not implicitly convertible to expected type string memory. Contains invalid UTF-8 sequence at position 4.

View File

@ -0,0 +1,7 @@
contract C {
function g() public pure returns (string[2][2] memory) {
return [['foo', 'bar'], [hex'74000001', hex'c0a80101']];
}
}
// ----
// TypeError 6069: (122-135): Type literal_string hex"c0a80101" is not implicitly convertible to expected type string memory. Contains invalid UTF-8 sequence at position 4.