aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--docs/contracts.rst6
-rw-r--r--docs/miscellaneous.rst29
-rw-r--r--test/libsolidity/SolidityEndToEndTest.cpp25
3 files changed, 53 insertions, 7 deletions
diff --git a/docs/contracts.rst b/docs/contracts.rst
index faef3fc2..0d1b029b 100644
--- a/docs/contracts.rst
+++ b/docs/contracts.rst
@@ -485,7 +485,11 @@ Functions can be declared ``view`` in which case they promise not to modify the
.. note::
If the compiler's EVM target is Byzantium or newer (default) the opcode
``STATICCALL`` is used for ``view`` functions which enforces the state
- to stay unmodified as part of the EVM execution.
+ to stay unmodified as part of the EVM execution. For library ``view`` functions
+ ``DELEGATECALL`` is used, because there is no combined ``DELEGATECALL`` and ``STATICCALL``.
+ This means library ``view`` functions do not have run-time checks that prevent state
+ modifications. This should not impact security negatively because library code is
+ usually known at compile-time and the static checker performs compile-time checks.
The following statements are considered modifying the state:
diff --git a/docs/miscellaneous.rst b/docs/miscellaneous.rst
index 12603f2e..8cc52c8f 100644
--- a/docs/miscellaneous.rst
+++ b/docs/miscellaneous.rst
@@ -34,18 +34,20 @@ Statically-sized variables (everything except mapping and dynamically-sized arra
The elements of structs and arrays are stored after each other, just as if they were given explicitly.
+Mappings and Dynamic Arrays
+===========================
+
Due to their unpredictable size, mapping and dynamically-sized array types use a Keccak-256 hash
computation to find the starting position of the value or the array data. These starting positions are always full stack slots.
-The mapping or the dynamic array itself
-occupies an (unfilled) slot in storage at some position ``p`` according to the above rule (or by
-recursively applying this rule for mappings to mappings or arrays of arrays). For a dynamic array, this slot stores the number of elements in the array (byte arrays and strings are an exception here, see below). For a mapping, the slot is unused (but it is needed so that two equal mappings after each other will use a different hash distribution).
-Array data is located at ``keccak256(p)`` and the value corresponding to a mapping key
+The mapping or the dynamic array itself occupies a slot in storage at some position ``p``
+according to the above rule (or by recursively applying this rule for mappings of mappings or arrays of arrays). For dynamic arrays,
+this slot stores the number of elements in the array (byte arrays and strings are an exception, see :ref:`below <bytes-and-string>`).
+For mappings, the slot is unused (but it is needed so that two equal mappings after each other will use a different
+hash distribution). Array data is located at ``keccak256(p)`` and the value corresponding to a mapping key
``k`` is located at ``keccak256(k . p)`` where ``.`` is concatenation. If the value is again a
non-elementary type, the positions are found by adding an offset of ``keccak256(k . p)``.
-``bytes`` and ``string`` store their data in the same slot where also the length is stored if they are short. In particular: If the data is at most ``31`` bytes long, it is stored in the higher-order bytes (left aligned) and the lowest-order byte stores ``length * 2``. If it is longer, the main slot stores ``length * 2 + 1`` and the data is stored as usual in ``keccak256(slot)``.
-
So for the following contract snippet::
pragma solidity >=0.4.0 <0.6.0;
@@ -58,6 +60,21 @@ So for the following contract snippet::
The position of ``data[4][9].b`` is at ``keccak256(uint256(9) . keccak256(uint256(4) . uint256(1))) + 1``.
+.. _bytes-and-string:
+
+``bytes`` and ``string``
+------------------------
+
+``bytes`` and ``string`` are encoded identically. For short byte arrays, they store their data in the same
+slot where the length is also stored. In particular: if the data is at most ``31`` bytes long, it is stored
+in the higher-order bytes (left aligned) and the lowest-order byte stores ``length * 2``.
+For byte arrays that store data which is ``32`` or more bytes long, the main slot stores ``length * 2 + 1`` and the data is
+stored as usual in ``keccak256(slot)``. This means that you can distinguish a short array from a long array
+by checking if the lowest bit is set: short (not set) and long (set).
+
+.. note::
+ Handling invalidly encoded slots is currently not supported but may be added in the future.
+
.. index: memory layout
****************
diff --git a/test/libsolidity/SolidityEndToEndTest.cpp b/test/libsolidity/SolidityEndToEndTest.cpp
index f65c8b27..7a496e64 100644
--- a/test/libsolidity/SolidityEndToEndTest.cpp
+++ b/test/libsolidity/SolidityEndToEndTest.cpp
@@ -4519,6 +4519,31 @@ BOOST_AUTO_TEST_CASE(library_call_protection)
ABI_CHECK(callContractFunction("pu()"), encodeArgs(2));
}
+
+BOOST_AUTO_TEST_CASE(library_staticcall_delegatecall)
+{
+ char const* sourceCode = R"(
+ library Lib {
+ function x() public view returns (uint) {
+ return 1;
+ }
+ }
+ contract Test {
+ uint t;
+ function f() public returns (uint) {
+ t = 2;
+ return this.g();
+ }
+ function g() public view returns (uint) {
+ return Lib.x();
+ }
+ }
+ )";
+ compileAndRun(sourceCode, 0, "Lib");
+ compileAndRun(sourceCode, 0, "Test", bytes(), map<string, Address>{{"Lib", m_contractAddress}});
+ ABI_CHECK(callContractFunction("f()"), encodeArgs(1));
+}
+
BOOST_AUTO_TEST_CASE(store_bytes)
{
// this test just checks that the copy loop does not mess up the stack