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

cyclic constructor calls have undefined behavior but are accepted in @safe code #19408

Open
dlangBugzillaToGithub opened this issue Mar 12, 2018 · 5 comments

Comments

@dlangBugzillaToGithub
Copy link

ag0aep6g reported this on 2018-03-12T10:29:36Z

Transferred from https://issues.dlang.org/show_bug.cgi?id=18598

CC List

Description

On class constructors, the spec says [1]:

> It is illegal for constructors to mutually call each other, although
> the compiler is not required to detect it. It will result in undefined
> behavior.

But DMD accepts this:

----
class C
{
    this() @safe { this(1); }
    this(int i) @safe { this(); }
}

void main() @safe
{
    auto c = new C;
}
----

According to the spec, the code has undefined behavior, so it shouldn't be accepted with the @safe attribute.

Also according to the spec, "the compiler is not required to detect" this, but that can't apply to @safe code, because the compiler is required to ensure that there is "no possibility of undefined behavior" in @safe code [2].

(As always, this can be fixed by letting DMD reject the code, or by changing the spec to give the code defined behavior.)


[1] https://dlang.org/spec/class.html#constructors
[2] https://dlang.org/spec/function.html#function-safety
@dlangBugzillaToGithub
Copy link
Author

bugzilla (@WalterBright) commented on 2018-03-20T07:03:43Z

I don't know a way to assign defined behavior to overflowing the stack :-(

This can, however, be statically detected by the compiler by keeping track of the calls between constructors. (They can't be virtual, so that'll work as long as all the constructor bodies are visible to the compiler.)

@dlangBugzillaToGithub
Copy link
Author

ag0aep6g commented on 2018-03-20T12:25:27Z

(In reply to Walter Bright from comment #1)
> I don't know a way to assign defined behavior to overflowing the stack :-(

Is that a problem for normal (non-constructor) functions as well?

----
void f() @safe { g(); }
void g() @safe { f(); }
void main() @safe { f(); } /* Undefined behavior? */
----

@dlangBugzillaToGithub
Copy link
Author

bugzilla (@WalterBright) commented on 2018-03-21T07:11:09Z

(In reply to ag0aep6g from comment #2)
> Is that a problem for normal (non-constructor) functions as well?

Yes.

I'm open to advice on what to do about it.

@dlangBugzillaToGithub
Copy link
Author

ag0aep6g commented on 2018-03-25T14:16:27Z

(In reply to Walter Bright from comment #3)
> I'm open to advice on what to do about it.

I'm by no means an expert here, but don't we have a guaranteed guard page beyond the stack? If we have, stack overflow is guaranteed to fail with a segfault, as long as the access doesn't jump over guard page (cf. issue 17566).

The situation is very similar to null dereferences then. A null dereference also hits a guard page, as long as the offset from null isn't too large (cf. issue 5176).

In both cases, the compiler has to detect offsets that are so large that they would jump over the guard page, and then it has to inject code that makes an earlier access to trigger the segfault. If I got the term right, for the stack that's called "stack probing".

@dlangBugzillaToGithub
Copy link
Author

razvan.nitu1305 commented on 2023-05-09T15:01:37Z

Well, infinite recursion also leads to a segfault due to stack overflow however it is still accepted in @safe code [1]. @safe typically refers to undefined behavior caused by memory corruption. I don't see any memory corruption happening here, since the OS guards against that, rather a programming mistake that is beyond of what the @safe checking mechanism can discover.

[1]  void fun() @safe { fun();

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

No branches or pull requests

1 participant