{ 跳至内容 }

嵌套 calldata 数组 ABI 重新编码中的大小检查错误

发布于 2022年5月17日 由 Solidity 团队

安全警报

2022年4月7日,Certora 开发团队的 John Toman 报告了 Solidity 代码生成器中的一个错误。Certora 的错误披露帖子可以在这里找到这里.

此错误已在2022年5月17日发布的 Solidity 版本 0.8.14中修复。此错误最早在 Solidity 版本 0.5.8 中引入。

我们为该错误分配了“极低”的严重级别。

哪些合约受到影响?

如果您将嵌套数组直接传递给另一个外部函数调用或使用abi.encode 对其进行编码,则可能会受到影响。

如果 calldata 以某种方式格式错误,则调用可能不会像预期的那样回退。相反,在末尾附加额外零的数据将传递给被调用的合约或 abi.encode 函数。

技术细节

嵌套动态类型的 calldata 验证被推迟到第一次访问嵌套值时。例如,此类访问可能是复制到内存或对外部类型的索引或成员访问。虽然在大多数此类访问中,calldata 验证会正确检查嵌套数组的数据区域(请参阅ABI 编码规范) 完全包含在传递的 calldata 中(即在范围 [0, calldatasize()] 内),但是当再次直接从 calldata 对此类嵌套类型进行 ABI 编码时,此检查可能不会执行。

例如,如果 calldata 中具有嵌套动态数组的值被传递给外部调用、在 abi.encode 中使用或作为事件发出,则可能会发生这种情况。在这种情况下,如果嵌套数组的数据区域超出 calldatasize(),则对其进行 ABI 编码不会回退,而是继续读取超出 calldatasize() 的值(即零值)。

例如,在此合约中

contract C {
	event e(uint[][]);
	function g(uint[][] calldata) external { ... }
	function f(uint[][] calldata a) external pure {
		bytes memory data = abi.encode(a);
		this.g(a);
		emit e(a);
	}
}

f 的调用,其 calldata 已损坏,其中 a 的一个元素具有超出 calldatasize 的数据区域,将不会回退。

但是,以下每个案例都会针对 calldatasize 进行正确验证,并且在 calldata 损坏时会回退。这是因为 calldata 数组仅被解码,而不是在单个操作中解码并重新编码。编译器中的另一条代码路径处理了这种情况,并且不受此错误的影响

contract C {
	function f1(uint[][] calldata a) external pure {
		a[0]; // Where a[0] is the element that extends beyond ``calldatasize``.
	}
	uint[][] s;
	function f2(uint[][] calldata a) external pure {
		s = a;
	}
	function f2(uint[][] calldata a) external pure {
		uint[][] memory x = a;
	}
}

上一篇

下一篇

参与进来

GitHub

推特

Mastodon

矩阵

了解更多

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

2024Solidity 团队

安全策略

行为准则