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

ICE on push to array with storage mapping #7410

Closed
erak opened this issue Sep 11, 2019 · 13 comments · Fixed by #9702
Closed

ICE on push to array with storage mapping #7410

erak opened this issue Sep 11, 2019 · 13 comments · Fixed by #9702

Comments

@erak
Copy link
Collaborator

erak commented Sep 11, 2019

Description

The following internal compiler error is thrown in the scenario below:

InternalCompilerError: Invalid non-value type for assignment.
            data.map.push(storageMapping);
            ^---------------------------^

Steps to Reproduce

contract C {
    struct Data {
        mapping(uint=>uint)[] map;
    }
    
    Data data;
    mapping(uint=>uint) storageMapping;
    
    constructor() public {
        for (uint i = 0; i < 6; i++)
            data.map.push(storageMapping);
    }
}
@erak erak added the bug 🐛 label Sep 11, 2019
@erak erak changed the title ICE on push to array with uninitialized storage mapping ICE on push to array with storage mapping Sep 12, 2019
@leonardoalt
Copy link
Member

A bit simplified:

contract C {
    mapping(uint=>uint)[] array;
    mapping(uint=>uint) map;
    function f() public {
        array.push(map);
    }
}

@dddejan
Copy link

dddejan commented Oct 16, 2019

If you manage to push the map to the array, popping it will cause another internal compiler error.

In general, supporting mappings within reference types seems to raise many issues and unexpected results. It would seem more consistent to just disallow it except as a top-level storage member, with no assignment and no delete.

@leonardoalt
Copy link
Member

@dddejan the ICE when popping should have been fixed by #7431 (#7378) and released in 0.5.12. Do you still see that behavior?

@dddejan
Copy link

dddejan commented Oct 17, 2019

@leonardoalt Yes, it seem like no ICE i 0.5.12. I'm still not a big fan. The semantics are bizarre and will eventually get someone in trouble. Like example below (using resize instead for push).

contract C {

    mapping (address => int)[] m;

    function whats_going_on() public {
        // Put something in the array
        m.length = 1;
        m[0][msg.sender] = 1;
        // Get the reference to first elemnet
        mapping (address => int) storage ref = m[0]; 
        // Oh, let's pop the element
        m.pop();
        assert(m.length == 0);
        // Reference still works? 
        assert(ref[msg.sender] == 1); 
        // Seems so, let's put something in
        ref[msg.sender] = 2;
        ref[address(0)] = 3;
        // Let's put something in the array and see what's in the new mapping?
        m.length = 1;
        assert(m[0][msg.sender] == 2);
        assert(m[0][address(0)] == 3);
    }

}

@leonardoalt
Copy link
Member

I agree that this is dangerous. The tests added in https://github.com/ethereum/solidity/pull/7431/files make this behavior explicit.
For now we added some security considerations in the documentation (https://solidity.readthedocs.io/en/v0.5.12/security-considerations.html#clearing-mappings) explaining this, and we're considering disallowing it.

@erak
Copy link
Collaborator Author

erak commented Oct 18, 2019

I'd also tend to re-think this behavior. Assigning to length will be disallowed with #7350 already.

@axic
Copy link
Member

axic commented May 7, 2020

Still a problem:

/Users/alex/Projects/solidity/libsolidity/codegen/LValue.cpp(403): Throw in function virtual void solidity::frontend::StorageItem::storeValue(const solidity::frontend::Type &, const solidity::langutil::SourceLocation &, bool) const
Dynamic exception type: boost::wrapexcept<solidity::langutil::InternalCompilerError>
std::exception::what: Invalid non-value type for assignment.
[solidity::util::tag_comment*] = Invalid non-value type for assignment.
[solidity::langutil::tag_sourceLocation*] = 7410.sol[213,242]

@chriseth
Copy link
Contributor

chriseth commented May 7, 2020

Maybe related to #8278

@hrkrshnn hrkrshnn self-assigned this May 19, 2020
@hrkrshnn
Copy link
Member

hrkrshnn commented May 28, 2020

This can be fixed in TypeChecker for ArrayPush operator. (State variable mappings cannot be assigned to.)

But I actually wonder if push makes sense for mapping types?

The following code should be valid:

contract C {
	mapping (uint => uint) map1;
	mapping (uint => mapping(uint => uint)[]) mapToMapArray;

	function f() public {
		mapping (uint => uint)[] storage maps = mapToMapArray[1];
		maps.push(map1);
	}
}

But there is an exception:

/solidity/libsolidity/codegen/LValue.cpp(400): Throw in function virtual void solidity::frontend::StorageItem::storeValue(const solidity::frontend::Type&, const solidity::langutil::SourceLocation&, bool) const
Dynamic exception type: boost::wrapexcept<solidity::langutil::InternalCompilerError>
std::exception::what: Invalid non-value type for assignment.
[solidity::util::tag_comment*] = Invalid non-value type for assignment.
[solidity::langutil::tag_sourceLocation*] = /tmp/mapping.sol[187,202]

There are two three ways to proceed.

  1. TypeChecker disallows when storage mapping types are assigned during a push. Implement push for local storage mapping type.
  2. TypeChecker will disallow push to any mapping type.
  3. Dissallow arrays with (nested) mapping types. Will need to check if external contracts uses this somehow.

What's a good fix for this? @chriseth @leonardoalt

@leonardoalt
Copy link
Member

I thought the idea was to disallow arrays of mappings in general?

@chriseth
Copy link
Contributor

@leonardoalt did we reach a conclusion there yet?

@leonardoalt
Copy link
Member

@chriseth I thought so, but maybe it was just me wanting it :p

@cameel
Copy link
Member

cameel commented Sep 2, 2020

Related: #8535

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging a pull request may close this issue.

8 participants