zkPC' = doJMP * (finalJmpAddr - nextNoJmpZkPC) + elseJMP * (finalElseAddr - nextNoJmpZkPC) + nextNoJmpZkPC;

https://github.com/0xPolygonHermez/zkevm-rom/commit/d1a2936649d82d912bff51315db397be2f832bc2#diff-388aa2d51760e0d46ac2b556f46a39e7e893b223b4c3604fa804e29557078ffaL21

Untitled

$ => B :MLOAD(originCTX), JMPZ(handleGas)

问题在于这句话,如果IDENTITY是被直接调用的originCTX是0,这里就会跳转到handleGas以结束当前交易。一般来说不会是0,因为预编译合约基本都是其他合约调用的。

在这里如果跳转到了handleGas,由于CTX依然是GLOBAL会在GLOBAL的第一个成员用于txGasLimit

由于对应的成员是

VAR GLOBAL oldStateRoot ; Previous state-tree root

所以最优gas到手的值会贼大。

zkASM 全局变量 https://github.com/0xPolygonHermez/zkevm-rom/blob/877328eff50070426f040a8843b7d6eb3140ef22/main/vars.zkasm

这个漏洞的修复方案是将handleGas 放到originCTX保存之后

REF

通过控制isNeg 使其不是0/1 来zkPC’指向其他地方