Bug 21097 – [REG2.083] Stack exhaustion upon large struct .destroy
Status
RESOLVED
Resolution
FIXED
Severity
regression
Priority
P1
Component
druntime
Product
D
Version
D2
Platform
All
OS
All
Creation time
2020-07-30T23:36:45Z
Last change time
2021-05-28T11:58:11Z
Keywords
industry, pull
Assigned to
No Owner
Creator
johanengelen
Comments
Comment #0 by johanengelen — 2020-07-30T23:36:45Z
Testcase:
```
import std.stdio;
struct S {
// doesn't have to be this large to exhaust the stack of course
ubyte[10_000_000] i = [1,2];
void close() { destroy(this); }
}
void main() {
auto s = new S();
s.close();
}
```
The problem is that the instantiated druntime template to write S.init to the object uses a stack allocated temporary with the same size as the object --> stack exhaustion. Prior versions used a global statically allocated variable and thus did not have the problem. (but there was duplication of data)
There's been a lot of work on the object.destroy code for structs, I can't trace back history easily.
What works: LDC 1.13.0 (dlang 2.083.1)
What doesn't: LDC 1.14 (dlang 2.084)
Comment #1 by johanengelen — 2020-07-30T23:59:42Z
Note: the 10MB symbol may lead one to believe that this is only a problem in rare cases. It isn't. At Weka, this bug is triggered with a dynamically allocated 300kb struct. Fibers do not have very large stacks.
Comment #2 by johanengelen — 2020-07-31T09:54:19Z
Two potential solutions:
```
// Avoid stack allocation, at the cost of virtual call to get the init symbol.
{
auto arr = cast(ubyte[])typeid(T).initializer();
if (arr.ptr is null) {
(cast(ubyte*)val)[0 .. T.sizeof] = ubyte(0);
} else {
// Use fill to duplicate 'arr' to work around https://issues.dlang.org/show_bug.cgi?id=16394
(cast(ubyte*)val)[0 .. T.sizeof].fillbytes(arr);
}
}
// Avoid stack allocation, at the cost of duplicating the init symbol (binary size increase)
{
import core.stdc.string : memcpy;
shared static immutable T init = T.init;
memcpy(&chunk, &init, T.sizeof);
}
```
(one cannot access the init symbol directly)
Comment #3 by dlang-bot — 2021-05-10T21:07:28Z
@JohanEngelen created dlang/druntime pull request #3467 "Fix Issue 21097 - prevent stack allocation when destroying large aggregates" fixing this issue:
- Fix Issue 21097 - Fix destroy and core.internal.lifetime.emplaceInitializer for large aggregates by preventing stack allocation
https://github.com/dlang/druntime/pull/3467
Comment #4 by dlang-bot — 2021-05-10T21:13:35Z
@JohanEngelen created dlang/dmd pull request #12509 "Add tests for the druntime fix for destroy on large aggregates (issue 21097)" mentioning this issue:
- Add tests for the druntime fix for destroy on large aggregates (issue 21097)
https://github.com/dlang/dmd/pull/12509
Comment #5 by dlang-bot — 2021-05-18T23:41:51Z
dlang/druntime pull request #3467 "Fix Issue 21097 - prevent stack allocation when destroying large aggregates" was merged into stable:
- 5981d49df518185725328538ab7b7b13879520e7 by Johan Engelen:
Fix Issue 21097 - Fix destroy and core.internal.lifetime.emplaceInitializer for large aggregates by preventing stack allocation
https://github.com/dlang/druntime/pull/3467
Comment #6 by dlang-bot — 2021-05-28T11:58:11Z
dlang/druntime pull request #3482 "merge stable" was merged into master:
- 706815547a6d7219dc39ef2f392c6139b6e943db by Johan Engelen:
Fix Issue 21097 - Fix destroy and core.internal.lifetime.emplaceInitializer for large aggregates by preventing stack allocation
https://github.com/dlang/druntime/pull/3482