Linea Rollup 和 TokenBridge 角色升级

本次审计是对 Consensys/linea-monorepo 代码仓库的差异审计,对比了两个 commit 标签。主要关注了在blob提交、基于角色的访问控制和设置回退操作员等方面的更改。审计发现了一些问题,并提出了改进代码一致性、可读性和 gas 效率的建议。

概要

TypeDeFiTimeline 自 2024-11-04 至 2024-11-15 语言 Solidity 总问题 11 (11 已解决) 严重程度问题 0 (0 已解决) 高严重程度问题 0 (0 已解决) 中严重程度问题 1 (1 已解决) 低严重程度问题 3 (3 已解决) 注意事项与附加信息 7 (7 已解决)

范围

我们对 Consensys/linea-monorepo 仓库 进行了差异审计,比较了标记为 contract-freeze-2024-11-01 的提交与标记为 contract-audit-2024-05-24 的提交。

更新:我们已完成对所有提交内容的审查,并最终确定了审计报告。最终审查的提交是 a83412e。我们注意到,自 adb097a 以来,范围内文件的其他更改与和我们同时进行的审计活动有关的修复有关。

此外,我们已验证已部署的字节码与 提交 df30415 的代码匹配。

以下文件在范围内:

 contracts
├── LineaRollup.sol
├── ZkEvmV2.sol
├── interfaces
│   ├── IGenericErrors.sol
│   ├── IMessageService.sol
│   ├── IPauseManager.sol
│   ├── IPermissionsManager.sol
│   ├── IRateLimiter.sol
│   ├── l1
│   │   ├── IL1MessageManager.sol
│   │   ├── IL1MessageManagerV1.sol
│   │   ├── IL1MessageService.sol
│   │   ├── ILineaRollup.sol
│   │   ├── IPlonkVerifier.sol
│   │   └── IZkEvmV2.sol
│   └── l2
│       ├── IL2MessageManager.sol
│       └── IL2MessageServiceV1.sol
├── lib
│   ├── L2MessageServicePauseManager.sol
│   ├── LineaRollupPauseManager.sol
│   ├── PauseManager.sol
│   ├── PermissionsManager.sol
│   ├── TokenBridgePauseManager.sol
│   └── Utils.sol
└── messageService
│   ├── MessageServiceBase.sol
│   ├── l1
│   │   ├── L1MessageManager.sol
│   │   ├── L1MessageService.sol
│   │   ├── TransientStorageReentrancyGuardUpgradeable.sol
│   │   └── v1
│   │       ├── L1MessageManagerV1.sol
│   │       └── L1MessageServiceV1.sol
│   ├── l2
│   │   ├── L2MessageManager.sol
│   │   ├── L2MessageService.sol
│   │   └── v1
│   │       ├── L2MessageManagerV1.sol
│   │       └── L2MessageServiceV1.sol
│   └── lib
│       ├── MessageHashing.sol
│       ├── RateLimiter.sol
│       ├── SparseMerkleTreeVerifier.sol
│       ├── TimeLock.sol
│       └── TransientStorageHelpers.sol
└── tokenBridge
    ├── TokenBridge.sol
    ├── interfaces
    │   └── ITokenBridge.sol
    └── lib
        └── StorageFiller39.sol

系统概述

审查中的系统已在 我们之前的审计报告 中进行了描述。在此差异审计中,我们审查了对代码库进行的几项重大更改,其中最显着的是以下几项:

  • 从 Blob 提交中删除区块号:从 blob 提交、事件和检查中删除了区块号。区块号仅在验证后才有效,为了降低 gas 和 calldata 成本并防止混淆而被删除。shnarf 流程现在处理诸如空 parentShnarffinalStateRootHash 之类的检查。
  • 与状态重构兼容的事件:修改了与 blob 提交和最终确定相关的事件,以支持 L2 状态重构,从而无需使用区块号。此更改简化了状态重构的数据检索。
  • 细粒度的角色重新配置和更新的 TokenBridge 角色和权限:跨合约的权限被重新设计为更细粒度的级别,以允许对访问角色进行更精细的控制。这些权限将在 LineaRollupL2MessageServiceTokenBridge 的升级事务中设置/重置。在 TokenBridge V2 中,角色和权限得到了进一步的细化。调整了存储布局,以避免升级后出现数据错位,并执行了插槽数据清理。
  • 移除没有证明的最终确定:删除了 finalizeBlocksWithoutProof 函数。现在,所有最终确定都必须包含证明。
  • 无需许可的操作员角色授予:添加了一个新的无需许可的函数。如果在过去六个月内没有发生最终确定,此函数会将 OPERATOR_ROLE 授予给预先指定的后备操作员地址。如果当前区块时间大于或等于上次最终确定的时间戳加上六个月,则任何地址都可以调用此函数将 OPERATOR_ROLE 分配给后备操作员。分配后,任何人都可以通过后备操作员地址最终确定区块。如果恢复了常规最终确定,安全理事会可以从后备操作员处撤销 OPERATOR_ROLE 以重置计数器。

对代码库进行的其他更改包括将 Solidity pragma 更改为 0.8.26、代码优化、改进错误、添加 NatSpec 以及实施之前审计中提出的建议。

安全模型和信任假设

对合约的更新引入了基于角色的权限,用于控制对业务关键功能的访问。这些更改已在整个代码库中实施,特别关注它们在 TokenBridge 合约中的应用,因为该更新涉及修改 TokenBridge 代理合约的存储布局。至关重要的是,假设 TokenBridge 合约中尚未设置待处理的所有者,并且在升级时桥接不会暂停。

鉴于经过审计的代码库是一个现有系统,因此新添加的初始化和重新初始化函数已包含在 LineaRollupTokenBridge 合约中。假设部署和初始化是原子性地完成的,以防止初始化过程被抢先。此外,还假设在升级期间传递了正确的值,以保持整个协议的一致性并确保基于角色的访问控制的正确初始化。

特权角色

如上所述,更新中引入了用于访问控制的广泛的基于角色的权限。这些权限影响整个代码库中的多个函数。可以在本报告的附录 A 中找到这些特权角色的完整列表。

中等严重程度

由于后备操作员角色放弃而导致最终确定区块的拒绝服务

在 rollup 合约中,如果自上次最终确定以来已经过了六个月,则 setFallbackOperator 函数 允许 fallbackOperator 承担 OPERATOR_ROLE。后备操作员旨在分配给公开可访问的地址,例如多重调用地址或众所周知的公钥,从而允许任何用户启动最终确定。至关重要的是,setFallbackOperator 函数仅用于Linea团队无法维护区块最终确定的最坏情况。从本质上讲,它是最后的后备手段。

但是,此设置引入了一个漏洞:任何用户都可以调用 finalizeBlocks,该函数会更新 currentFinalizedState 并推进最后最终确定的 L2 区块时间戳。然后,用户可以 放弃 OPERATOR_ROLE,从而使后备地址在接下来的六个月内无法重新获得 OPERATOR_ROLE。这种放弃-重置循环可以无限期地重复进行,从而有效地阻止了后备操作员维持稳定的角色,并导致最终确定出现重大延迟,每次长达六个月。在这种最坏情况的背景下,Linea 团队不太可能进行干预并设置新的操作员,从而使其成为一个永久性问题。

考虑阻止后备操作员地址调用 renounceRole

更新:已在 pull request #298 中解决。Linea 团队表示:

确认。我们已经覆盖了 renounceRole 函数,如果被撤销的角色是 fallbackOperator,则该交易现在会回滚。

低等严重程度

缺少零地址检查

当从用户提供的参数分配地址时,务必确保提供的地址不为零。将地址设置为零是有问题的,因为它具有特殊的销毁/放弃含义。此操作应由单独的函数处理,以防止在值或所有权转移期间意外丢失访问权限。

在整个代码库中,发现了多个缺少零地址检查的赋值操作实例:

  • L2MessageService.solL2MessageService 合约中的 _defaultAdmin 赋值操作
  • LineaRollup.solLineaRollup 合约中的 _fallbackOperator 赋值操作
  • TokenBridge.solTokenBridge 合约中的 _tokens 赋值操作
  • TokenBridge.solTokenBridge 合约中的 _nativeTokens 赋值操作

考虑在分配状态变量之前执行零地址检查。

更新:已在 pull request #305 中解决。Linea 团队表示:

部分确认:

  1. _defaultAdmin_fallbackOperator 没有检查。
  2. _tokens_nativeTokens 永远不会遇到空地址的情况,因为交易会在Token部署检查时回滚。
  3. setCustomContractsetReserved 已经有默认检查。
  4. 我们注意到 _tokens 没有空列表检查,现在我们也修复了它。

不正确的文档字符串

在整个代码库中,发现了多个不正确或不完整的文档字符串实例:

  • IPlonkVerifier.sol 接口中,Verify 函数具有不完整的文档字符串,其中未记录命名的返回值。

  • LineaRollup 合约中,shnarfFinalBlockNumbers 变量已替换为 blobShnarfExists。此变量的 新添加的注释 重复了 NB: NB: 字符。

考虑完成 IPlonkVerifier 接口的 Verify 函数的返回变量文档字符串,并更正 blobShnarfExists 上的其他注释。

更新:已在 pull request #304 中解决。

缺少文档字符串

在整个代码库中,发现了多个缺少文档字符串的实例:

考虑为上述状态变量添加文档字符串,并为新添加的与角色相关的常量添加文档字符串。此外,考虑在 LineaRollup 合约中的 SIX_MONTHS_IN_SECONDS 常量 中添加注释,以说明 6 个月的近似值是故意的。

总的来说,考虑彻底记录所有属于任何合约公共 API 的函数(及其参数)。即使不是公共的,实现敏感功能的函数也应明确记录。编写文档字符串时,请考虑遵循 以太坊自然规范格式 (NatSpec)。

更新:已在 pull request #307 中解决。Linea 团队表示:

确认。我们根据建议调整了建议的合约和其他区域。

注意事项与附加信息

缺少安全联系人

在智能合约中提供特定的安全联系人(例如电子邮件或 ENS 名称)可以大大简化个人在代码中发现漏洞时的沟通过程。这种做法非常有益,因为它允许代码所有者指定漏洞披露的沟通渠道,从而消除了由于缺乏如何操作的知识而导致沟通不畅或未能报告的风险。此外,如果合约包含第三方库,并且这些库中出现错误,则其维护者可以更轻松地联系到关于该问题的合适人员并提供缓解说明。

在整个代码库中,除了 Utils 之外,所有合约都有安全联系人。

考虑在库定义上方添加包含安全联系人的 NatSpec 注释,该注释符合整个代码库中使用的样式:@custom:security-contact security-report@linea.build

更新:已在 pull request #301 中解决。Linea 团队表示:

确认。我们已添加库标题和安全联系人的 NatSpec。

缺少索引事件参数

IPauseManager.sol 接口中,某些事件没有索引参数:

为了提高链下服务搜索和筛选特定事件的能力,请考虑 索引事件参数

更新:已在 pull request #302 中解决。Linea 团队表示:

确认。这些事件现在已索引。

不一致的整数基数

在汇编中,可以使用十进制或十六进制声明内存偏移量。但是,重要的是使用一致的表示法以避免混淆,并使其易于理解这些大小的用途和用法。

LineaRollup 合约中,内存偏移量的声明不一致。一个内存偏移量已使用十进制声明,而其余的已使用十六进制声明。

考虑使用一致的表示法来表示整个代码库中的内存偏移量。

更新:已在 pull request #300 中解决。Linea 团队表示:

确认。我们现在使用 0x20 而不是 32 值。

代码中的魔术数字

在整个 TokenBridge 合约中,发现了多个具有不明含义的文字值实例:

  • TokenBridge.sol 中的 32 文字数字
  • TokenBridge.sol 中的 64 文字数字
  • TokenBridge.sol 中的 32 文字数字
  • TokenBridge.sol 中的 32 文字数字

考虑定义和使用 constant 变量而不是使用文字,或添加代码注释(在适当的地方)以提高代码库的可读性。

更新:已在 pull request #303 中解决。Linea 团队表示:

确认。这已通过 3 个常量值修复。

调用可能会被抢先交易

对 EIP-2612 的 permit() 或 OpenZeppelin Governance 的 delegateBySig() 函数的调用可能会被恶意行为者抢先交易。因此,其调用已被抢先交易的用户将无法在调用结束后执行任何操作,因为他们的交易将回滚。TokenBridge.sol 中的 IERC20PermitUpgradeable(_token).permit(msg.sender, address(this), amount, deadline, v, r, s) 调用容易受到此类 DoS 攻击。

考虑正确地将易受攻击的调用包装在 try-catch 块中,以避免 DoS 攻击。

更新:已在 pull request #306 中解决。Linea 团队表示:

确认。我们已经通过另一种方法修复了这个问题,即只有在没有足够的 allowance 时才使用 permit。如果是抢先交易,则Token桥将具有 allowance,并且不会使用 permit 调用。

函数可见性过于宽松

在整个代码库中,发现了多个函数可见性过于宽松的实例:

  • TokenBridge.sol 中具有 public 可见性的 setMessageService 函数可以限制为 external
  • TokenBridge.sol 中具有 public 可见性的 setReserved 函数可以限制为 external

为了更好地传达函数的预期用途并有可能实现一些额外的 gas 节省,请考虑更改函数的可见性,使其仅在需要的范围内宽松。

更新:已在 pull request #299 中解决。

不一致地使用 whenTypeAndGeneralNotPaused 修饰符

TokenBridge 合约中,对 whenTypeAndGeneralNotPaused 的检查是在 bridgeToken 函数 上手动执行的,而不是使用 whenTypeAndGeneralNotPaused 修饰符。这会在代码库中引入不一致,因为修饰符用于在合约的其他部分强制执行相同的检查。

考虑一致地使用 whenTypeAndGeneralNotPaused 修饰符,以提高代码的清晰度和可读性。

更新:已解决。Linea 团队表示:

由于 nonZeroAddress(_token) nonZeroAddress(_recipient) nonZeroAmount(_amount) nonReentrant 和 3 个函数参数,堆栈太深,无法正常工作。

结论

本次审计涵盖了标记为 contract-audit-05-24contract-freeze-2024-11-01 的提交之间的差异。这些更改涉及代码库中的多个添加和优化,最显着的是关于 blob 提交、基于角色的访问控制和设置后备操作员。

审计发现了几个问题,并提出了旨在提高代码一致性、可读性和 gas 效率的建议。我们要感谢 Linea 团队提供关于所做更改的全面文档,以及他们对审计团队提出的问题做出的及时响应。

附录 A

对代码库的更新在代码库中引入了多个特权角色。该列表包括但不限于:

请求审计

  • 原文链接: openzeppelin.com/news/li...
  • 登链社区 AI 助手,为大家转译优秀英文文章,如有翻译不通的地方,还请包涵~
点赞 0
收藏 0
分享
本文参与登链社区写作激励计划 ,好文好收益,欢迎正在阅读的你也加入。

0 条评论

请先 登录 后评论
OpenZeppelin
OpenZeppelin
江湖只有他的大名,没有他的介绍。