Bug 19877 – [dip1000] std.container.rbtree is unsafely accessing private data
Status
RESOLVED
Resolution
FIXED
Severity
major
Priority
P1
Component
druntime
Product
D
Version
D2
Platform
All
OS
All
Creation time
2019-05-15T18:12:42Z
Last change time
2022-04-03T08:33:55Z
Keywords
rejects-valid
Assigned to
No Owner
Creator
hsteoh
Comments
Comment #0 by hsteoh — 2019-05-15T18:12:42Z
Code:
------
import std.container.rbtree;
alias Grid = RedBlackTree!(GridPoint);
struct GridPoint
{
private string _srcStr;
int opCmp(in GridPoint p) const { return 0; }
}
------
Compiler output (with -preview=dip1000):
------
/usr/src/d/phobos/std/container/rbtree.d(1111): Error: @safe function std.container.rbtree.RedBlackTree!(GridPoint, "a < b", false).RedBlackTree.toHash cannot call @system function core.internal.hash.hashOf!(GridPoint).hashOf
/usr/src/d/druntime/import/core/internal/hash.d(510): core.internal.hash.hashOf!(GridPoint).hashOf is declared here
numid.d(3): Error: template instance `std.container.rbtree.RedBlackTree!(GridPoint, "a < b", false)` error instantiating
------
Compiling without -preview=dip1000 works.
The problem appears to be the 'private': removing 'private' from _srcStr makes the problem go away.
Why 'private' makes toHash un-@safe is anybody's guess.
Comment #1 by hsteoh — 2019-05-16T00:54:32Z
Here's a much more reduced case:
------
struct S {
private int _x;
}
struct RedBlackTree
{
size_t toHash() nothrow @safe
{
return .hashOf(S.init);
}
}
void main() { }
------
Comment #2 by bugzilla — 2020-03-12T09:31:34Z
The cause of this is core.internal.hash.hashOf() is getting its attributes inferred. Having it access private members of a struct causes it to be inferred as @system, and @safe RedBlackTree.toHash() is not allowed to call an @system function.
The problem is in std.container.rbtree, where the call to .toHash() should be put in an @trusted block.
Comment #3 by Ajieskola — 2021-06-07T10:40:12Z
I'd argue it behaves correctly now. Private state should be private. If `.hashOf`, or any other external funtion, needs to access it directly, it ought to fail. The correct thing to do is to add `toHash` member function to the element `struct`.
Current `-dip1000` prevents errors like this:
```
struct Ternary
{ private ubyte state;
@safe auto opEquals(const Ternary rhs) const
{ if (state >= 2) return rhs.state >= 2;
else return state == rhs.state;
}
}
@safe void main()
{ Ternary a = void, b = void;
//fails with high likelihood
if(a == b) assert(.hashOf(a) == .hashOf(b), "WAT?");
}
```
Perhaps the error message should suggest adding the `toHash` function, though.