Skip to content

fix(evm): prevent uint64→int64 overflow in transfer path - #3

Closed
bysomeone wants to merge 3 commits into
masterfrom
fix/evm-uint64-overflow-in-transfer
Closed

bysomeone wants to merge 3 commits into
masterfrom
fix/evm-uint64-overflow-in-transfer

Conversation

@bysomeone

Copy link
Copy Markdown
Owner

Summary

修复 EVM 层 uint64→int64 整数溢出漏洞,阻止攻击者绕过余额检查零成本铸造 WBTY。

Attack

攻击者通过 protobuf varint 编码在 RPC 入口绕过 EvmContractCallReq.Amount (int64) 的限制,传入接近 2^64 的值。该值在 MemoryStateDB.CanTransfer/Transfer 中被转换为 int64 时溢出为负数,导致:

  • 余额检查:balance >= 负数 → 永远通过
  • 实际转账:value < 0 → 静默失败
  • EVM:Transfer 返回值被忽略,合约仍以原始 uint64 值执行

结果:攻击者零成本铸造 ~1844 亿 WBTY,随后 withdraw 兑走真 BTY。

Fix (防御纵深,6 个文件)

层 文件 修复
① StateDB statedb.go CanTransfer/Transfer 增加 MaxInt64 溢出检查,SubBalance/AddBalance 检查返回值
② EVM evm.go Call/Create 检查 Transfer 返回值,失败时回滚
② Exec exec.go transfer-only 路径检查 Transfer 返回值
① Token token.go 预编译 transfer 增加 IsInt64() 溢出检查
③ RPC rpc.go 拒绝 Amount ≤ 0(阻止 protobuf varint 绕过)
Test statedb_test.go 攻击回归测试 + 真实 MemoryStateDB 集成测试

🤖 Generated with Claude Code
Co-Authored-By: Claude [email protected]

bysomeone and others added 3 commits July 30, 2026 10:36
Attack: Attacker bypassed CanTransfer/Transfer balance checks by passing
a uint64 value > max(int64) via protobuf varint encoding. The int64
conversion overflowed to negative, making balance checks always pass and
Transfer silently fail, while the EVM contract still received the full
uint64 value as msg.value.

Fixes (defense in depth, from bottom to top):
- statedb.go: add MaxInt64 overflow check in CanTransfer/Transfer before
  converting uint64→int64; also check Transfer result in SubBalance/AddBalance
- evm.go: check Transfer() return value in Call() and Create(), revert
  snapshot on failure instead of silently continuing
- exec.go: check Transfer() return value in transfer-only path
- token.go: add IsInt64() guard in precompile transfer to prevent
  uint256 calldata→int64 overflow bypass
- rpc.go: validate Amount > 0 at RPC entry in CreateCallTx,
  CreateDeployTx, CreateTransferOnlyTx to block protobuf varint bypass
- statedb_test.go: add regression tests for overflow protection

Co-Authored-By: Claude <[email protected]>
Add TestAttackSimulationFullFlow that verifies the overflow fix works
in a realistic context with actual CoinsAccount + StateDB. Tests:
- CanTransfer rejects the exact attack value (18446739873709551616 ≈ 2^64)
- Transfer rejects the exact attack value (verified via error log)
- MaxInt64 is not falsely rejected by the overflow guard

Co-Authored-By: Claude <[email protected]>
@bysomeone bysomeone closed this Jul 30, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant