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

Yul interpreter: Changes required before merging Redundant store eliminator #12276

Merged
merged 1 commit into from
Jan 5, 2022

Conversation

bshastry
Copy link
Contributor

Required to fuzz #11352

@@ -94,13 +94,13 @@ string YulInterpreterTest::interpret()
state.maxExprNesting = 64;
try
{
Interpreter::run(state, EVMDialect::strictAssemblyForEVMObjects(langutil::EVMVersion{}), *m_ast);
Interpreter::run(state, EVMDialect::strictAssemblyForEVMObjects(langutil::EVMVersion{}), *m_ast, false);
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Memory tracing is enabled (last argument = false) for Yul interpreter tests. This may be changed to true if tests pass only without memory tracing.

@@ -108,13 +108,13 @@ string EwasmTranslationTest::interpret()
state.maxExprNesting = 64;
try
{
Interpreter::run(state, WasmDialect{}, *m_object->code);
Interpreter::run(state, WasmDialect{}, *m_object->code, false);
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Memory tracing is enabled (last argument = false) for Ewasm->Yul tests. This may be changed to true if tests pass only without memory tracing.

}
catch (InterpreterTerminatedGeneric const&)
{
}

stringstream result;
state.dumpTraceAndState(result);
state.dumpTraceAndState(result, false);
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Memory tracing enabled.

test/tools/ossfuzz/strictasm_diff_ossfuzz.cpp Outdated Show resolved Hide resolved
@@ -204,6 +205,7 @@ u256 EVMInstructionInterpreter::eval(
case Instruction::CALLDATASIZE:
return m_state.calldata.size();
case Instruction::CALLDATACOPY:
logTrace(_instruction, true, arg);
Copy link
Contributor Author

Choose a reason for hiding this comment

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

logTrace(instruction, true, arg) means interpret instruction I whose args are A... and tell the tracer that this instruction may write to memory.

If the interpreter is configured to disable memory tracing, this information (whether an instruction writes to memory or not) is useful in deciding whether to append the instruction to execution trace or not.

@@ -99,6 +99,7 @@ u256 EVMInstructionInterpreter::eval(
switch (_instruction)
{
case Instruction::STOP:
logTrace(_instruction, false);
Copy link
Contributor Author

Choose a reason for hiding this comment

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

For instructions that do not write to memory such as stop(), the second argument is false

Copy link
Contributor

Choose a reason for hiding this comment

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

I'm wondering - shouldn't we use some utility to infer this fact from the opcode itself?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Sounds reasonable to me. More concretely, would it be a static look up table per opcode or an annotation to the opcode itself?

Copy link
Contributor

Choose a reason for hiding this comment

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

I think SemanticInformation::memory would be suitable. It is a slight problem since we cannot test this function here anymore, but I guess it's better to use constants here all the time.

Copy link
Contributor

Choose a reason for hiding this comment

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

I think SemanticInformation::memory would be suitable. It is a slight problem since we cannot test this function here anymore, but I guess it's better to use constants here all the time.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Updated to make use of SemanticInformation::memory. If there are any mistaken assumptions, the fuzzer would report a bug which we could later investigate.

@bshastry bshastry force-pushed the fuzz-RSE branch 2 times, most recently from ecc80c6 to 9bf34de Compare November 14, 2021 17:24
@chriseth
Copy link
Contributor

Can you summarize the change? Do we at least compare the data in storage at the end?

@bshastry
Copy link
Contributor Author

Can you summarize the change? Do we at least compare the data in storage at the end?

  • Storage is compared as before (no changes there).
  • Memory changes are not recorded if the flag added in this PR is set (disableMemoryTracing = true)
  • Memory dumps are not compared if the flag added in this PR is set (disableMemoryTracing = true)

@hrkrshnn
Copy link
Member

hrkrshnn commented Jan 3, 2022

Storage is compared as before (no changes there).

@bshastry How is that done? I have a feeling that might not be enough for something like

{
    let x := calldataload(0)
    let y := sload(x)
    // both of these can be removed
    sstore(x, y)
    sstore(x, y)

    let a := x
    let b := mload(a)
    // both of these can be removed
    mstore(a, b)
    mstore(a, b)
}
// ----
// step: equalStoreEliminator
//
// {
//     let x := calldataload(0)
//     let y := sload(x)
//     let a := x
//     let b := mload(a)
// }

@bshastry
Copy link
Contributor Author

bshastry commented Jan 4, 2022

Storage is compared as before (no changes there).

@bshastry How is that done? I have a feeling that might not be enough for something like

{
    let x := calldataload(0)
    let y := sload(x)
    // both of these can be removed
    sstore(x, y)
    sstore(x, y)

    let a := x
    let b := mload(a)
    // both of these can be removed
    mstore(a, b)
    mstore(a, b)
}
// ----
// step: equalStoreEliminator
//
// {
//     let x := calldataload(0)
//     let y := sload(x)
//     let a := x
//     let b := mload(a)
// }

Essentially the following

  • sstore(a, b): an entry in map is created but not an instruction trace SSTORE(a, b)
  • mstore(a, b) is neither recorded in state nor trace

Since storage writes are always preserved in map, they may be compared pre/post optimisation. OTOH, memory may not be compared.

…ing.

Model revert in yul interpreter.
Add logTrace for a few more instructions and clear trace on revert.
state,
EVMDialect::strictAssemblyForEVMObjects(langutil::EVMVersion{}),
*m_ast,
/*disableMemoryTracing=*/false
Copy link
Contributor

Choose a reason for hiding this comment

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

Maybe avoid the negation and call it enableMemoryTracing?

@@ -339,12 +344,18 @@ u256 EVMInstructionInterpreter::eval(
case Instruction::REVERT:
accessMemory(arg[0], arg[1]);
logTrace(_instruction, arg);
m_state.storage.clear();
Copy link
Contributor

Choose a reason for hiding this comment

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

How "correct" is this interpreter? Shouldn't it only clear the changes to storage? Should we extract this change into its own PR?

@chriseth
Copy link
Contributor

chriseth commented Jan 4, 2022

Could be merged like this.

@chriseth chriseth merged commit 679f73c into develop Jan 5, 2022
@chriseth chriseth deleted the fuzz-RSE branch January 5, 2022 10:23
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants