{ 跳至内容 }

将脏字节数组复制到存储中的错误

发布于 2022 年 6 月 15 日,Solidity 团队

安全警报

2021 年 7 月 1 日,通过差异模糊测试发现了 Solidity 代码生成器中的一个错误。该错误导致旧版代码生成管道生成代码,该代码在复制时可能会将脏值写入存储bytes 数组来自 calldatamemory

最初,假设存储中的脏值仅使用内联汇编可见。但是,使用空 .push() 调整 bytes 数组的大小,而无需实际向其写入值,可以在不使用任何内联汇编的情况下暴露脏字节。

该错误在编译器早期版本中 bytes 数组的初始实现中就已经存在。尽管如此,它从未被外部利用或报告,因此我们将其严重程度评定为“低”。

哪些合约受到影响?

memorycalldata 复制 bytes 数组到 storage 的大多数情况都可能受到影响,因为它们可能会写入脏值。只有在 bytes 数组中存在空 .push()(在旧版本中还包括对其 .length 字段的赋值)并且结果元素被假设为零而没有实际写入它们时,存储中的这些脏值才会变得可见。

例如,以下操作以前会导致 h() 中新 .push() 的数组元素不为零

contract C {
    event ev(uint[], uint);
    bytes s;
    constructor() {
        // The following event emission involves writing to temporary memory at the current location
        // of the free memory pointer. Several other operations (e.g. certain keccak256 calls) will
        // use temporary memory in a similar manner.
        // In this particular case, the length of the passed array will be written to temporary memory
        // exactly such that the byte after the 63 bytes allocated below will be 0x02. This dirty byte
        // will then be written to storage during the assignment and become visible with the push in ``h``.
        emit ev(new uint[](2), 0);
        bytes memory m = new bytes(63);
        s = m;
    }
    function h() external returns (bytes memory) {
        s.push();
        return s;
      }
}

类似地,来自 calldata 的脏值最终可能会被复制到存储中

contract C {
    bytes public s;
    function f(bytes calldata c) external returns (bytes memory) {
        s = c;
        s.push();
        return s;
    }
}

此处的函数 f 可以使用 c 长度后的脏字节在 calldata 中调用,这些字节将被复制到 s 并在 s.push() 之后变得可见。

但是,这个错误直到现在才被发现的事实表明,实际项目似乎并不依赖于空 .push()bytes 数组应该添加一个零值新元素这一事实。

上一篇

下一篇

参与进来

GitHub

Twitter

Mastodon

Matrix

了解更多

博客文档用例贡献关于论坛

2024Solidity 团队

安全策略

行为准则