Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[WIP] fix(fuzz): strip metadata when collecting push bytes #8139

Draft
wants to merge 1 commit into
base: master
Choose a base branch
from

Conversation

grandizzy
Copy link
Collaborator

@grandizzy grandizzy commented Jun 12, 2024

Motivation

Closes #8115
When we collect values from push bytes we don't strip metadata from contract bytecode. This results in irrelevant values added to fuzz dictionary and it also affects forge snapshot (each time an comment is added / removed the snapshot values are changed)

Would be nice to have such method for getting bytecode without metadata in Bytecode revm primitives (and use in verify code as well)

Solution

  • proposed solution is looking up the metadata len as last 2 bytes from original_byte_slice (prev we were using bytes_slice which is padded with 32 zero bytes) and then remove metadata
  • the bytecode len is checked to be greater than 2 (cheatcode address have a len of 1) and greater than extracted metadata (the default create2 deployer does not contain metadata so it panics).
    TBD: should we ignore these addresses? check should be preserved in order to avoid panics from other edge cases
  • tested with https://github.com/cruzdanilo/foundry-snapshot-repro by adding comments to SnapshotTest contract (test is too big to be included as unit test and I wasn't able to simplify it and reproduce issue)

@grandizzy grandizzy changed the title fix(fuzz): strip metadata when push bytes collect fix(fuzz): strip metadata when collecting push bytes Jun 12, 2024
@grandizzy grandizzy marked this pull request as ready for review June 12, 2024 18:02
@grandizzy grandizzy requested a review from klkvr June 12, 2024 18:02
Copy link
Member

@mattsse mattsse left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

not an expert on bytecode layout, but this explainer made sense to me.

if this is something that holds true, we could upstream this to revm perhaps

pending @DaniPopes

return;
}

// Remove metadata by looking up the last two bytes of original bytecode.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is this always true?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For initcode, no. Sometimes e.g. long strings in a constructor are after the metadata hash.

For runtime code, yes, IF the metadata hash is present. When bytecode_hash = none there is still a metadata hash containing the solidity version, so the last two bytes are still the length. When cbor_metadata = false then there is no bytecode hash at all and the last two bytes are code

To know for certain if what you have is a metadata hash, without knowing the compilation settings, you'd assume the last two bytes are the length of the metadata hash, then try CBOR decoding that hash. If it decodes successfully, it's a metadata hash, otherwise it's code.

Here is some old, probably bad, rust code I had that did this using ciborium: https://github.com/ScopeLift/cove-backend/blob/d8c875c5a64bf89a874892bc8ed57751c47b1233/src/bytecode.rs#L53-L97

if code.len() > 2 {
let metadata_len = &code[code.len() - 2..];
let metadata_len = u16::from_be_bytes([metadata_len[0], metadata_len[1]]).into();
if code.len() > metadata_len {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this could still fail due to the -2, but not sure if possible

perhaps metadata_offset = metadata_len + 2

&code[..code.len() - metadata_offset];

@grandizzy grandizzy marked this pull request as draft June 13, 2024 02:25
@grandizzy grandizzy changed the title fix(fuzz): strip metadata when collecting push bytes [WIP] fix(fuzz): strip metadata when collecting push bytes Jun 13, 2024
@zerosnacks zerosnacks added this to the v1.0.0 milestone Jul 31, 2024
@zerosnacks zerosnacks added the A-testing Area: testing label Jul 31, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-testing Area: testing
Projects
None yet
Development

Successfully merging this pull request may close these issues.

feat(fuzz): do not populate dictionary with bytecode metadata
4 participants