Platform: Code4rena
Start Date: 02/10/2023
Pot Size: $1,100,000 USDC
Total HM: 28
Participants: 64
Period: 21 days
Judge: GalloDaSballo
Total Solo HM: 13
Id: 292
League: ETH
Rank: 43/64
Findings: 1
Award: $656.33
š Selected for report: 0
š Solo Findings: 0
š Selected for report: zero-idea
Also found by: 0x1337, 0xTheC0der, 0xstochasticparrot, Audittens, HE1M, Jeiwan, erebus, gumgumzum, leviticus1129, lsaudit, quarkslab, rvierdiiev
656.3255 USDC - $656.33
https://github.com/code-423n4/2023-10-zksync/blob/main/code/system-contracts/contracts/Constants.sol#L30-L35 https://github.com/code-423n4/2023-10-zksync/blob/main/code/system-contracts/contracts/AccountCodeStorage.sol#L86-L139
New EcAdd
and EcMul
precompiles were added but CURRENT_MAX_PRECOMPILE_ADDRESS was not updated.
This will cause AccountCodeStorage@getCodeHash to not return EMPTY_STRING_KECCAK
and AccountCodeStorage@getCodeSize to not return 0
leading to EVM invariants not being preserved.
The test below was ran on a modified era-test-node that includes EcAdd
and EcMul
.
It also includes a test for extcodesize
and extcodehash
since both are translated (based on this) to AccountCodeStorage@getCodeSize
and AccountCodeStorage@getCodeHash
calls respectively.
import { expect } from 'chai'; import { AccountCodeStorage__factory, CodeHelper } from '../typechain-types'; import { ACCOUNT_CODE_STORAGE_SYSTEM_CONTRACT, EMPTY_STRING_KECCAK } from './shared/constants'; import { Wallet } from 'zksync-web3'; import { deployContract, getWallets } from './shared/utils'; const ECADD = "0x0000000000000000000000000000000000000006"; const ECMUL = "0x0000000000000000000000000000000000000007"; describe('Precompiles', function () { let wallet: Wallet; before(async () => { wallet = getWallets()[0]; }); describe('Account Code Storage', function () { describe('getCodeSize', function () { it('should return 0 for EcAdd', async () => { expect( (await AccountCodeStorage__factory.connect(ACCOUNT_CODE_STORAGE_SYSTEM_CONTRACT, wallet) .getCodeSize(ECADD)).eq(0) ).to.be.true; }); it('should return 0 for EcMul', async () => { expect( (await AccountCodeStorage__factory.connect(ACCOUNT_CODE_STORAGE_SYSTEM_CONTRACT, wallet) .getCodeSize(ECMUL)).eq(0) ).to.be.true; }); }); describe('getCodeHash', function () { it('should return the empty string keccak hash for EcAdd', async () => { expect( (await AccountCodeStorage__factory.connect(ACCOUNT_CODE_STORAGE_SYSTEM_CONTRACT, wallet) .getCodeHash(ECADD)) ).to.be.eq(EMPTY_STRING_KECCAK); }); it('should return the empty string keccak hash for EcMul', async () => { expect( (await AccountCodeStorage__factory.connect(ACCOUNT_CODE_STORAGE_SYSTEM_CONTRACT, wallet) .getCodeHash(ECMUL)) ).to.be.eq(EMPTY_STRING_KECCAK); }); }); }); describe('Code Helper', function () { let codeHelper: CodeHelper; before(async () => { codeHelper = await deployContract('CodeHelper') as CodeHelper; }); describe('getCodeSize', function () { it('should return 0 for EcAdd', async () => { expect( (await codeHelper .getCodeSize(ECADD)).eq(0) ).to.be.true; }); it('should return 0 for EcMul', async () => { expect( (await codeHelper .getCodeSize(ECMUL)).eq(0) ).to.be.true; }); }); describe('getCodeHash', function () { it('should return the empty string keccak hash for EcAdd', async () => { expect( (await codeHelper .getCodeHash(ECADD)) ).to.be.eq(EMPTY_STRING_KECCAK); }); it('should return the empty string keccak hash for EcMul', async () => { expect( (await codeHelper .getCodeHash(ECMUL)) ).to.be.eq(EMPTY_STRING_KECCAK); }); }); }); });
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; contract CodeHelper { function getCodeSize(address of_) external view returns (uint256 s) { assembly { s := extcodesize(of_) } } function getCodeHash(address of_) external view returns (bytes32 h) { assembly { h := extcodehash(of_) } } }
Precompiles Account Code Storage getCodeSize 1) should return 0 for EcAdd 2) should return 0 for EcMul getCodeHash 3) should return the empty string keccak hash for EcAdd 4) should return the empty string keccak hash for EcMul Code Helper getCodeSize 5) should return 0 for EcAdd 6) should return 0 for EcMul getCodeHash 7) should return the empty string keccak hash for EcAdd 8) should return the empty string keccak hash for EcMul 0 passing (300ms) 8 failing 1) Precompiles Account Code Storage getCodeSize should return 0 for EcAdd: AssertionError: expected false to be true + expected - actual -false +true 2) Precompiles Account Code Storage getCodeSize should return 0 for EcMul: AssertionError: expected false to be true + expected - actual -false +true 3) Precompiles Account Code Storage getCodeHash should return the empty string keccak hash for EcAdd: AssertionError: expected '0x010000c56c054a0de4a36b133d3c114ec51ā¦' to equal '0xc5d2460186f7233c927e7db2dcc703c0e50ā¦' + expected - actual -0x010000c56c054a0de4a36b133d3c114ec514c3ce0334ad7759c202392386a913 +0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470 4) Precompiles Account Code Storage getCodeHash should return the empty string keccak hash for EcMul: AssertionError: expected '0x010001378d31273c8e58caa12bcf1a5694eā¦' to equal '0xc5d2460186f7233c927e7db2dcc703c0e50ā¦' + expected - actual -0x010001378d31273c8e58caa12bcf1a5694e66a0aefdba2504adb8e3eb02b21c7 +0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470 5) Precompiles Code Helper getCodeSize should return 0 for EcAdd: AssertionError: expected false to be true + expected - actual -false +true 6) Precompiles Code Helper getCodeSize should return 0 for EcMul: AssertionError: expected false to be true + expected - actual -false +true 7) Precompiles Code Helper getCodeHash should return the empty string keccak hash for EcAdd: AssertionError: expected '0x010000c56c054a0de4a36b133d3c114ec51ā¦' to equal '0xc5d2460186f7233c927e7db2dcc703c0e50ā¦' + expected - actual -0x010000c56c054a0de4a36b133d3c114ec514c3ce0334ad7759c202392386a913 +0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470 8) Precompiles Code Helper getCodeHash should return the empty string keccak hash for EcMul: AssertionError: expected '0x010001378d31273c8e58caa12bcf1a5694eā¦' to equal '0xc5d2460186f7233c927e7db2dcc703c0e50ā¦' + expected - actual -0x010001378d31273c8e58caa12bcf1a5694e66a0aefdba2504adb8e3eb02b21c7 +0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470
Manual Review
Update CURRENT_MAX_PRECOMPILE_ADDRESS to uint256(uint160(ECMUL_SYSTEM_CONTRACT))
but this will also change the behavior for RIPEMD-160
, identity
and modexp
addresses so the check for precompiles might need to be changed to account for gaps.
Other
#0 - c4-pre-sort
2023-10-31T06:51:19Z
bytes032 marked the issue as duplicate of #142
#1 - c4-judge
2023-11-23T19:31:09Z
GalloDaSballo marked the issue as satisfactory