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

Stricter check for "this" in constructor. #3875

Merged
merged 1 commit into from
Apr 13, 2018
Merged

Conversation

ekpyron
Copy link
Member

@ekpyron ekpyron commented Apr 12, 2018

Fixes #3843.
Fixes #3861.

@ekpyron ekpyron self-assigned this Apr 12, 2018
if (_function.isConstructor() && TypeType(type(*var)) == *m_scope->type())
m_errorReporter.typeError(
var->location(),
"Constructor arguments cannot have the contract itself as type."
Copy link
Member

@axic axic Apr 12, 2018

Choose a reason for hiding this comment

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

My first hunch was this should be a bug as you did, but after further consideration it may make sense to support this.

There were some examples in the past of splitting/forking contracts, where the reference is to another contract of the same type.

@chriseth
Copy link
Contributor

I agree, this is not the way to fix it :)

I think the condition in StaticAnalyzer.cpp:209 has to be tighter than it currently is.

@chriseth
Copy link
Contributor

I think #3861 and #3843 are basically the same thing.

@ekpyron
Copy link
Member Author

ekpyron commented Apr 12, 2018

OK, I remembered talking about this with you and concluding that such arguments would be an error in general - that's why I went ahead and did it this way :-).

Since there can only be one constructor per contract, how can a constructor with another contract of the same type ever be called in the first place :-)? I'm just wondering what such an example could look like...

@chriseth
Copy link
Contributor

ok, that's a fair point, but we also have interfaces and stuff...

@chriseth
Copy link
Contributor

I wouldn't restrict it in such a way.

@ekpyron
Copy link
Member Author

ekpyron commented Apr 12, 2018

Alright!

@axic
Copy link
Member

axic commented Apr 12, 2018

This should be valid:

contract C {
  C parent;

  function hasParent() pure internal returns (bool) {
    return address(parent) != 0;
  }

  function C(C _parent) {
    parent = _parent;
  }

  function fork() returns (C) {
    require(!hasParent(), "multiple level of forks are not allowed");
    return new C(this);
  }
}

And the the first create transaction can just pass an address 0 for the initial contract.

@ekpyron
Copy link
Member Author

ekpyron commented Apr 12, 2018

Ah OK! I hadn't thought about that!

@ekpyron ekpyron closed this Apr 12, 2018
@ekpyron ekpyron deleted the constructorSelfRef branch April 12, 2018 14:37
@axic axic restored the constructorSelfRef branch April 12, 2018 14:38
@axic axic reopened this Apr 12, 2018
@axic
Copy link
Member

axic commented Apr 12, 2018

@ekpyron can you just fix it here properly? :)

@ekpyron
Copy link
Member Author

ekpyron commented Apr 12, 2018

@axic OK - my plan was to open a new PR once it's finished, but I might as well reuse this one :-). I'll be away for an hour now, though, so it'll take until afterwards.

@ekpyron ekpyron closed this Apr 12, 2018
@ekpyron ekpyron force-pushed the constructorSelfRef branch from 5961d20 to c3dc67d Compare April 12, 2018 16:10
@ekpyron ekpyron changed the title Disallow self-referencing arguments in constructors. [WIP] Disallow self-referencing arguments in constructors. Apr 12, 2018
@ekpyron ekpyron reopened this Apr 12, 2018
@ekpyron ekpyron force-pushed the constructorSelfRef branch from cd784db to 61db349 Compare April 12, 2018 17:03
@ekpyron ekpyron changed the title [WIP] Disallow self-referencing arguments in constructors. [WIP] Stricter check for "this" in constructor. Apr 12, 2018
@ekpyron ekpyron force-pushed the constructorSelfRef branch from 61db349 to bf12e8e Compare April 12, 2018 17:42
@ekpyron
Copy link
Member Author

ekpyron commented Apr 12, 2018

I think I need some clarification on this one. I'm not sure this can be properly fixed.

Are there cases in which an occurrence of this is valid, resp. shouldn't be warned about? For example what about the following case?

contract C {
    address m_d;
    constructor(address d) public {
        m_d = d;
    }
}
contract D {
    C m_c;
    constructor() public {
        m_c = new C(this);
    }
}

The discussion in #1646 (comment) seems to indicate that this should be valid.

If this shouldn't trigger a warning, then it might be tricky to warn about cases like this:

contract C {
    address m_d;
    constructor(address d) public {
        m_d = d;
    }
    function d() public view returns (address) {
        return m_d;
    }
}
contract D {
    C m_c;
    constructor() public {
        m_c = new C(this);
        D(m_c.d()).f();
    }
    function f() public pure {
    }
}

Recognizing that D(m_c.d()) will evaluate to this during the analysis phase is non-trivial (and impossible in general, since it may depend on run-time state in more complex examples).

The original discussion in #583 is rather inconclusive as well...

The current state of the PR warns about these cases:

contract C {
  function f() public {
  }
  constructor() {
    this.f();
    (this).f();
  }
}

But will already fail to warn for this case:

contract C {
  function f() public {
  }
  constructor() {
    C c = this;
    c.f();
  }
}

So currently I'm not sure - it doesn't seem that we can warn in all cases that warrant a warning, but I'm not sure which subset of them I should try to catch...

@ekpyron ekpyron force-pushed the constructorSelfRef branch from bf12e8e to 96cabbe Compare April 13, 2018 08:30
@ekpyron ekpyron changed the title [WIP] Stricter check for "this" in constructor. Stricter check for "this" in constructor. Apr 13, 2018
@ekpyron ekpyron requested a review from chriseth April 13, 2018 08:58
@ekpyron ekpyron changed the title Stricter check for "this" in constructor. [WIP] Stricter check for "this" in constructor. Apr 13, 2018
@ekpyron
Copy link
Member Author

ekpyron commented Apr 13, 2018

Sorry, I think this is still not a good solution. I'll improve it now and comment after I'm done.

@@ -152,6 +156,8 @@ bool StaticAnalyzer::visit(MemberAccess const& _memberAccess)
{
bool const v050 = m_currentContract->sourceUnit().annotation().experimentalFeatures.count(ExperimentalFeature::V050);

m_memberAccess = true;
Copy link
Contributor

Choose a reason for hiding this comment

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

While it is nice that we can catch (this).f() using this mechanism, I think it would also generate an error on f(this).f(), which should in general not be the case. Constructors cannot have nested non-constructor functions, but a member access expression can, so I don't think this can be solved by using stateful AST traversal.

Copy link
Member Author

Choose a reason for hiding this comment

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

This was exactly the problem I was just thinking about. However, "f(this).f()" does not seem to trigger the warning - I just added a test for that.

Copy link
Member Author

Choose a reason for hiding this comment

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

Just trying to think of a case in which this will in fact fail.
Otherwise I could just check for this in the member access again - either while also checking for (this) or while ignoring cases like (this)...

Copy link
Member Author

Choose a reason for hiding this comment

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

Actually I expected test/libsolidity/syntaxTests/parsing/constructor_allowed_this.sol to cause a warning because of this, but as a matter of fact it doesn't...

Copy link
Member Author

Choose a reason for hiding this comment

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

Ah, ok, now I got one that fails - I'll revert back to checking in member access.

}

function x() pure internal {
require(false);
Copy link
Contributor

Choose a reason for hiding this comment

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

Please reduce the test case to its bare minimum.

Copy link
Contributor

Choose a reason for hiding this comment

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

And perhaps also add a comment that this tests a former bug in the detection of this.f().

@ekpyron ekpyron force-pushed the constructorSelfRef branch from 96cabbe to 5cafdfa Compare April 13, 2018 09:08
@ekpyron ekpyron changed the title [WIP] Stricter check for "this" in constructor. Stricter check for "this" in constructor. Apr 13, 2018
@ekpyron ekpyron force-pushed the constructorSelfRef branch 4 times, most recently from 2da1350 to 7abe92c Compare April 13, 2018 09:36
@ekpyron ekpyron force-pushed the constructorSelfRef branch 2 times, most recently from 24a0284 to d2e1d14 Compare April 13, 2018 09:39
@ekpyron
Copy link
Member Author

ekpyron commented Apr 13, 2018

Now it should be fine - although IMO it's not particularly nice this way.

I'm really starting to think that we should consider #3878.

if (auto id = dynamic_cast<Identifier const*>(expr))
{
if (id->name() == "this")
m_errorReporter.warning(id->location(), "\"this\" used in constructor.");
Copy link
Contributor

Choose a reason for hiding this comment

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

Perhaps we should also extend the message a little: \"this\" used in constructor. Note that external functions of a contract cannot be called while it is being constructed. or something like that.

@ekpyron ekpyron force-pushed the constructorSelfRef branch from d2e1d14 to be37e3a Compare April 13, 2018 13:57
@chriseth chriseth merged commit 95c49b3 into develop Apr 13, 2018
@axic axic deleted the constructorSelfRef branch April 14, 2018 22:50
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.

3 participants