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

Fix Option pointer bug #570

Merged
merged 12 commits into from
Nov 10, 2020
Merged

Fix Option pointer bug #570

merged 12 commits into from
Nov 10, 2020

Conversation

cmichi
Copy link
Collaborator

@cmichi cmichi commented Nov 7, 2020

Closes #568.

The bug was in the the implementation of SpreadLayout for Option:

  • On push_spread, clear_spread, pull_spread no further forwarding to the inner value push_spread, clear_spread, pull_spread functions happened if the Option was None.

  • This is problematic because these methods advance the ptr by one ‒ which happened if the Option was Some(…). For None it didn't happen, thus causing havoc for all objects that subsequently tried to push, pull or clear their content from storage.

@cmichi cmichi changed the title Cmichi fix option ptr bug Fix Option Pointer bug Nov 7, 2020
@cmichi cmichi changed the title Fix Option Pointer bug Fix Option pointer bug Nov 7, 2020
@cmichi cmichi force-pushed the cmichi-fix-option-ptr-bug branch 2 times, most recently from 1f29702 to 2ca833a Compare November 7, 2020 16:17
@codecov-io
Copy link

codecov-io commented Nov 7, 2020

Codecov Report

Merging #570 (3bc442e) into master (eee8dd1) will decrease coverage by 14.72%.
The diff coverage is 80.00%.

Impacted file tree graph

@@             Coverage Diff             @@
##           master     #570       +/-   ##
===========================================
- Coverage   82.47%   67.75%   -14.73%     
===========================================
  Files         155      155               
  Lines        6820     6826        +6     
===========================================
- Hits         5625     4625     -1000     
- Misses       1195     2201     +1006     
Impacted Files Coverage Δ
crates/storage/src/lazy/lazy_cell.rs 98.30% <ø> (ø)
crates/storage/src/traits/impls/prims.rs 90.32% <80.00%> (-1.06%) ⬇️
crates/lang/codegen/src/traits.rs 0.00% <0.00%> (-100.00%) ⬇️
crates/lang/ir/src/ir/contract.rs 0.00% <0.00%> (-100.00%) ⬇️
crates/lang/codegen/src/generator/env.rs 0.00% <0.00%> (-100.00%) ⬇️
crates/lang/codegen/src/generator/storage.rs 0.00% <0.00%> (-100.00%) ⬇️
crates/lang/codegen/src/generator/contract.rs 0.00% <0.00%> (-100.00%) ⬇️
crates/lang/codegen/src/generator/trait_def.rs 0.00% <0.00%> (-100.00%) ⬇️
crates/lang/codegen/src/generator/dispatch.rs 0.00% <0.00%> (-98.27%) ⬇️
crates/lang/codegen/src/generator/item_impls.rs 0.00% <0.00%> (-97.20%) ⬇️
... and 19 more

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update eee8dd1...3bc442e. Read the comment docs.

@Robbepop
Copy link
Collaborator

Robbepop commented Nov 7, 2020

Closes #568.

So I'm still working on adding a regression test and making it prettier, but I think we have it.

The bug was in the the implementation of SpreadLayout for Option:

* On `push_spread`, `clear_spread`, `pull_spread` no further forwarding to the inner value `push_spread`, `clear_spread`, `pull_spread` functions happened if the `Option` was `None`.

* This is problematic because these methods advance the ptr by one ‒ which happened if the `Option` was `Some(…)`. For `None` it didn't happen, thus causing havoc for all objects that subsequently tried to push, pull or clear their content from storage.

(I don't want to commit the contract ofc, will revert that commit before merging).

Thanks for fixing! This is exactly what I thought was wrong. :)

@cmichi cmichi requested a review from Robbepop November 9, 2020 18:45
Copy link
Collaborator

@Robbepop Robbepop left a comment

Choose a reason for hiding this comment

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

Thanks for the bug fix and clippy suggestion improvement.

crates/storage/src/lazy/lazy_cell.rs Outdated Show resolved Hide resolved
crates/storage/src/lazy/lazy_cell.rs Outdated Show resolved Hide resolved
crates/storage/src/lazy/lazy_cell.rs Outdated Show resolved Hide resolved
crates/storage/src/lazy/lazy_cell.rs Outdated Show resolved Hide resolved
@@ -545,4 +545,63 @@ mod tests {
Ok(())
})
}

#[test]
fn regression_test_for_issue_570() -> ink_env::Result<()> {
Copy link
Collaborator

Choose a reason for hiding this comment

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

The entire test can be heavily simplified. You only need to push a (Option, i32) tuple with values (None, 1) to the contract storage and then read it again in the next step and assert that the first value is None and the second value is 1. Done.

Copy link
Collaborator

Choose a reason for hiding this comment

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

Or am I missing something?

Copy link
Collaborator Author

@cmichi cmichi Nov 10, 2020

Choose a reason for hiding this comment

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

So that's a clever idea, I guess you thought the 1 would be written out to the first pointer and on the next pull_spread we would try to read a Some(...) there and fail. It doesn't work unfortunately because of this: push_spread for Option writes self.is_some() as u8 in the first ptr slot and increments the pointer. Hence on the next pull_spread we immediately return the None and continue pulling the i32. So no failure would occur here.

Copy link
Collaborator Author

@cmichi cmichi Nov 10, 2020

Choose a reason for hiding this comment

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

Using a tuple also doesn't simplify the test significantly, because for each tuple element the entire push_spread recursion is called. So storage is basically overwritten until I introduce LazyCell into the tuple. (Note that in my test only the Option is pushed in step 2).

I mean the whole test could also be simplified way more drastically to something like

assert_eq!(ptr_after_pushing_none, ptr_after_pushing_some);
assert_eq!(ptr_after_pulling_none, ptr_after_pulling_some);

(I only thought of this now)

Copy link
Collaborator

Choose a reason for hiding this comment

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

Thanks for the elaboration!

Copy link
Collaborator

@Robbepop Robbepop left a comment

Choose a reason for hiding this comment

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

Thanks a lot for this fix! We will probably release an ink! 3.0-rc3 soon with it.

@@ -545,4 +545,63 @@ mod tests {
Ok(())
})
}

#[test]
fn regression_test_for_issue_570() -> ink_env::Result<()> {
Copy link
Collaborator

Choose a reason for hiding this comment

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

Thanks for the elaboration!

@cmichi cmichi merged commit 1d086a3 into master Nov 10, 2020
@cmichi cmichi deleted the cmichi-fix-option-ptr-bug branch November 10, 2020 14:32
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.

[Bug] Contract Trapped Issue due to Ordering of Struct
3 participants