Skip to content
This repository was archived by the owner on Oct 12, 2022. It is now read-only.
/ druntime Public archive
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions src/core/internal/dassert.d
Original file line number Diff line number Diff line change
Expand Up @@ -377,7 +377,7 @@ private string miniFormat(V)(const scope ref V v)
import core.atomic : atomicLoad;

// Inverts a comparison token for use in _d_assert_fail
private string invertCompToken(string comp) pure nothrow @nogc @safe
private string invertCompToken(scope string comp) pure nothrow @nogc @safe
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 not sure if this should compile. Later in the function, we have [comp]. I.e., comp is copied to the heap. Usually, that's not allowed for scope variables.

Copy link
Contributor

@MoonlightSentinel MoonlightSentinel May 14, 2021

Choose a reason for hiding this comment

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

I assume it works here because the array is assigned to a scope parameter and hence promoted to the stack.

Copy link
Contributor Author

@dkorpel dkorpel May 14, 2021

Choose a reason for hiding this comment

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

The [comp] is passed to parameter const scope string[] valB of combine, so the literal is allocated on the stack
Edit: that's what Florian said, didn't see his message when I posted

Copy link
Contributor

Choose a reason for hiding this comment

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

I assume it works here because the array is assigned to a scope parameter and hence promoted to the stack.

That might be DMD's reasoning. But it's unsound. It actually doesn't matter if the array is on the heap or on the stack. A scope is being lost.

comp.ptr is a scope pointer. valB.ptr is also a scope pointer. valB[0].ptr is not a scope pointer, because scope is not transitive. So valB = [comp]; cannot be allowed. It's assigning a scope pointer to a non-scope pointer.

Reduced code demonstrating memory corruption:

string invertCompToken(scope string comp) @safe
{
    return combine([comp]); /* Should not compile. */
}
string combine(const scope string[] valB) @safe
{
    return valB[0];
}
string f() @safe
{
    immutable char[3] eq = "foo";
    return invertCompToken(eq);
}
void main() @safe
{
    import std.stdio;
    string s = f();
    writeln(s); /* prints garbage */
}

Copy link
Contributor

@MoonlightSentinel MoonlightSentinel May 14, 2021

Choose a reason for hiding this comment

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

Fair enough, that's just another oddity of scope not being transitive.
But there's no corruption here because combine doesn't escape the string. So it should be fine to wrap the call in a @trusted lambda to avoid future problems.

EDIT: The current solution is even better

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I put the operator in the middle argument of combine, side-stepping the issue.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Btw nice catch @aG0aep6G! I think the relevant bugzilla issue is this:
https://issues.dlang.org/show_bug.cgi?id=20505

{
switch (comp)
{
Expand All @@ -402,7 +402,7 @@ private string invertCompToken(string comp) pure nothrow @nogc @safe
case "!in":
return "in";
default:
assert(0, combine(["Invalid comparison operator"], "-", [comp]));
assert(0, combine(["Invalid comparison operator '"], comp, ["'"]));
}
}

Expand Down