2021年3月20日,我们团队在solidity 字节码优化器发现了一个Bug。

危害等级为中危,影响版本为 version < 0.8.3

技术细节

字节码优化器会重用内联汇编中的keccak256哈希结果。

solidity的字节码优化器有一个优化步骤: 如果keccak256的哈希结果在余下的编译过程中用到了,则直接使用缓存结果。但这个步骤有一个bug,相同内容但长度不同的message,会被认为有相同的keccak256哈希结果。

下面的代码是一个小例子:

**contract** C {
  **function** bug() **public** **returns** (**uint** a, **uint** b) {
    **assembly** {
      mstore(0, 0)
**      a **:=** keccak256(0, 32)
**      b **:=** keccak256(0, 23)
    }
  }
}

在上面的代码中,编译时会计算keccak256

下面的代码在编译过程中不会计算keccak256。但效果都一样,都会被优化器判为相等哈希。

**contract** C {
  **function** bug(**string** **memory** s) **public** **returns** (**bool** ret) {
    **assembly** {
      **let** a **:=** keccak256(s, 32)
      **let** b **:=** keccak256(s, 8)
**      ret **:=** eq(a, b)
    }
  }
}
**contract** C {
  **function** bug() **public** **view** **returns** (**bool** ret) {
    **assembly** {
      **let** x **:=** calldataload(0)
      mstore(0, x)
      mstore(0x20, x)
      **let** a **:=** keccak256(0, 4)
**      **let** b **:=** keccak256(0x20, 8)
**      ret **:=** eq(a, b)
    }
  }
}

如果 length1 和 length2 做四舍五入后相等,则keccak256(mpos, length1) 和 keccak256(mpos, length2) 相等。

不受影响的情况

以下特征的代码,不会受影响:

注意,除了手写keccak256,编译器还会自动生成一些keccak256 ,比如