aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--docs/control-structures.rst4
-rw-r--r--libsolidity/codegen/ExpressionCompiler.cpp7
-rw-r--r--test/libsolidity/SolidityEndToEndTest.cpp26
3 files changed, 37 insertions, 0 deletions
diff --git a/docs/control-structures.rst b/docs/control-structures.rst
index a6daccac..5b78d099 100644
--- a/docs/control-structures.rst
+++ b/docs/control-structures.rst
@@ -69,6 +69,10 @@ this does not execute a constructor. We could also have used ``function setFeed(
only (locally) sets the value and amount of gas sent with the function call and only the
parentheses at the end perform the actual call.
+Function calls cause exceptions if the called contract does not exist (in the
+sense that the account does not contain code) or if the called contract itself
+throws an exception or goes out of gas.
+
.. warning::
Any interaction with another contract imposes a potential danger, especially
if the source code of the contract is not known in advance. The current
diff --git a/libsolidity/codegen/ExpressionCompiler.cpp b/libsolidity/codegen/ExpressionCompiler.cpp
index 4695ff96..4a81e27d 100644
--- a/libsolidity/codegen/ExpressionCompiler.cpp
+++ b/libsolidity/codegen/ExpressionCompiler.cpp
@@ -1524,6 +1524,13 @@ void ExpressionCompiler::appendExternalFunctionCall(
m_context << u256(0);
m_context << dupInstruction(m_context.baseToCurrentStackOffset(contractStackPos));
+ // Check the the target contract exists (has code) for non-low-level calls.
+ if (funKind == FunctionKind::External || funKind == FunctionKind::CallCode || funKind == FunctionKind::DelegateCall)
+ {
+ m_context << Instruction::DUP1 << Instruction::EXTCODESIZE << Instruction::ISZERO;
+ m_context.appendConditionalJumpTo(m_context.errorTag());
+ }
+
if (_functionType.gasSet())
m_context << dupInstruction(m_context.baseToCurrentStackOffset(gasStackPos));
else
diff --git a/test/libsolidity/SolidityEndToEndTest.cpp b/test/libsolidity/SolidityEndToEndTest.cpp
index f20ea2cb..4e6f68b0 100644
--- a/test/libsolidity/SolidityEndToEndTest.cpp
+++ b/test/libsolidity/SolidityEndToEndTest.cpp
@@ -7049,6 +7049,32 @@ BOOST_AUTO_TEST_CASE(failing_ecrecover_invalid_input)
BOOST_CHECK(callContractFunction("f()") == encodeArgs(u256(0)));
}
+BOOST_AUTO_TEST_CASE(calling_nonexisting_contract_throws)
+{
+ char const* sourceCode = R"(
+ contract D { function g(); }
+ contract C {
+ D d = D(0x1212);
+ function f() returns (uint) {
+ d.g();
+ return 7;
+ }
+ function g() returns (uint) {
+ d.g.gas(200)();
+ return 7;
+ }
+ function h() returns (uint) {
+ d.call(); // this does not throw (low-level)
+ return 7;
+ }
+ }
+ )";
+ compileAndRun(sourceCode, 0, "C");
+ BOOST_CHECK(callContractFunction("f()") == encodeArgs());
+ BOOST_CHECK(callContractFunction("g()") == encodeArgs());
+ BOOST_CHECK(callContractFunction("h()") == encodeArgs(u256(7)));
+}
+
BOOST_AUTO_TEST_SUITE_END()
}