Bug 22616 – Infinity loop instead of InvalidMemoryOperationError during GC routine

Status
RESOLVED
Resolution
FIXED
Severity
regression
Priority
P1
Component
druntime
Product
D
Version
D2
Platform
All
OS
All
Creation time
2021-12-21T12:01:33Z
Last change time
2022-12-26T03:24:12Z
Keywords
pull
Assigned to
No Owner
Creator
feklushkin.denis

Comments

Comment #0 by feklushkin.denis — 2021-12-21T12:01:33Z
Issue was introduced in v2.097.0 /+ dub.json: { "name": "test" } +/ class C { int i; ~this () { // It is wrong to allocate during GC collection, // but this failed assert allocates and must throw // InvalidMemoryOperationError. assert(i == 123); } } void main() { auto c = new C; } ``` DMD64 D Compiler v2.096.1 - OK $ dub -f --compiler=dmd --single test.d Performing "debug" build using dmd for x86_64. test ~master: building configuration "application"... Linking... Running test core.exception.InvalidMemoryOperationError@src/core/exception.d(647): Invalid memory operation ---------------- Program exited with code 1 (as expected) DMD64 D Compiler v2.097.0 - Infinity wait $ dub -f --compiler=dmd --single test.d Performing "debug" build using dmd for x86_64. test ~master: building configuration "application"... Linking... Running test (stalled) Stack after ctrl+c: #0 0x00007ffff7d05176 in __GI___wait4 (pid=289204, stat_loc=0x7fffffffc68c, options=0, usage=0x0) at ../sysdeps/unix/sysv/linux/wait4.c:30 #1 0x00005555557babb3 in std.process.Pid.performWait(bool) () #2 0x0000555555700686 in dub.generators.build.BuildGenerator.runTarget(dub.internal.vibecompat.inet.path.NativePath, in dub.compilers.buildsettings.BuildSettings, immutable(char)[][], dub.generators.generator.GeneratorSettings) () #3 0x000055555570031b in dub.generators.build.BuildGenerator.performPostGenerateActions(dub.generators.generator.GeneratorSettings, in dub.generators.generator.ProjectGenerator.TargetInfo[immutable(char)[]]) () #4 0x00005555556e8806 in dub.generators.generator.ProjectGenerator.generate(dub.generators.generator.GeneratorSettings) () #5 0x000055555577e429 in dub.commandline.GenerateCommand.execute(dub.dub.Dub, immutable(char)[][], immutable(char)[][]) () #6 0x000055555577f4ee in dub.commandline.RunCommand.execute(dub.dub.Dub, immutable(char)[][], immutable(char)[][]) () #7 0x000055555571964f in dub.commandline.runDubCommandLine(immutable(char)[][]) () #8 0x00005555557e7258 in rt.dmain2._d_run_main2(char[][], ulong, extern(C) int(char[][]) function).runAll() () #9 0x00005555557e7155 in _d_run_main2 () #10 0x00005555557e6fae in _d_run_main () #11 0x00007ffff7c627ed in __libc_start_main (main=0x555555788050 <main>, argc=5, argv=0x7fffffffe7d8, init=<optimized out>, fini=<optimized out>, rtld_fini=<optimized out>, stack_end=0x7fffffffe7c8) at ../csu/libc-start.c:332 #12 0x000055555559b2da in _start ()
Comment #1 by feklushkin.denis — 2021-12-21T12:12:41Z
Wrong stack trace attached, correct is: #0 __GI___clock_nanosleep (clock_id=clock_id@entry=0, flags=flags@entry=0, req=0x7fffffffdad0, rem=0x7fffffffdae0) at ../sysdeps/unix/sysv/linux/clock_nanosleep.c:67 #1 0x00007ffff7d053f3 in __GI___nanosleep (req=<optimized out>, rem=<optimized out>) at ../sysdeps/unix/sysv/linux/nanosleep.c:25 #2 0x00005555555ba090 in core.thread.osthread.Thread.sleep(core.time.Duration) () #3 0x00005555555bd6c9 in core.internal.spinlock.SpinLock.yield(ulong) shared () #4 0x00005555555bd67a in core.internal.spinlock.SpinLock.lock() shared () #5 0x00005555555c6dcb in core.internal.gc.impl.conservative.gc.ConservativeGC.runLocked!(core.internal.gc.impl.conservative.gc.ConservativeGC.mallocNoSync(ulong, uint, ref ulong, const(TypeInfo)), core.internal.gc.impl.conservative.gc.mallocTime, core.internal.gc.impl.conservative.gc.numMallocs, ulong, uint, ulong, const(TypeInfo)).runLocked(ref ulong, ref uint, ref ulong, ref const(TypeInfo)) () #6 0x00005555555be702 in core.internal.gc.impl.conservative.gc.ConservativeGC.malloc(ulong, uint, const(TypeInfo)) () #7 0x000055555559d3db in gc_malloc () #8 0x0000555555599875 in _d_newclass () #9 0x00005555555a3029 in core.runtime.defaultTraceHandler(void*) () #10 0x000055555559db52 in _d_traceContext () #11 0x000055555559d942 in _d_createTrace () #12 0x0000555555598cbe in _d_throwdwarf () #13 0x00005555555c327c in core.internal.gc.impl.conservative.gc.Gcx.fullcollect(bool) () #14 0x00005555555c78d4 in core.internal.gc.impl.conservative.gc.ConservativeGC.runLocked!(core.internal.gc.impl.conservative.gc.ConservativeGC.fullCollectNoStack().go(core.internal.gc.impl.conservative.gc.Gcx*), core.internal.gc.impl.conservative.gc.Gcx*).runLocked(ref core.internal.gc.impl.conservative.gc.Gcx*) () #15 0x00005555555bfc54 in core.internal.gc.impl.conservative.gc.ConservativeGC.fullCollectNoStack() () #16 0x00005555555bfbea in core.internal.gc.impl.conservative.gc.ConservativeGC.collectNoStack() () #17 0x00005555555aac3f in gc_term () #18 0x000055555559da7b in rt_term () #19 0x0000555555598acb in rt.dmain2._d_run_main2(char[][], ulong, extern(C) int(char[][]) function).runAll() () #20 0x00005555555989ad in rt.dmain2._d_run_main2(char[][], ulong, extern(C) int(char[][]) function).tryExec(scope void() delegate) () #21 0x000055555559890e in _d_run_main2 () #22 0x000055555559866a in _d_run_main () #23 0x0000555555597712 in main (argc=1, argv=0x7fffffffe228) at /usr/include/dmd/druntime/import/core/internal/entrypoint.d:29 #24 0x00007ffff7c627ed in __libc_start_main (main=0x5555555976f0 <main>, argc=1, argv=0x7fffffffe228, init=<optimized out>, fini=<optimized out>, rtld_fini=<optimized out>, stack_end=0x7fffffffe218) at ../csu/libc-start.c:332 #25 0x00005555555975ea in _start ()
Comment #2 by moonlightsentinel — 2021-12-21T15:05:22Z
Bisection points to this PR: https://github.com/dlang/druntime/pull/3476
Comment #3 by pro.mathias.lang — 2021-12-21T16:37:25Z
The key is: ``` #7 0x000055555559d3db in gc_malloc () #8 0x0000555555599875 in _d_newclass () #9 0x00005555555a3029 in core.runtime.defaultTraceHandler(void*) () #10 0x000055555559db52 in _d_traceContext () #11 0x000055555559d942 in _d_createTrace () #12 0x0000555555598cbe in _d_throwdwarf () ``` So it means we hit https://github.com/dlang/druntime/blob/fd9a45448244fb9dd4326520ad8526c540895eb0/src/core/runtime.d#L695 But that shouldn't be possible, since this function is guarded by a `GC.inFinalizer` check that would lead to it returning...
Comment #4 by moonlightsentinel — 2021-12-21T17:04:29Z
The problem here is that this line [1] is executed before the exception is rethrown - which calls `_d_throwdwarf` and ends up trying to allocate the DefaultTraceHandler. [1] https://github.com/dlang/druntime/blob/fd9a45448244fb9dd4326520ad8526c540895eb0/src/core/internal/gc/impl/conservative/gc.d#L3141
Comment #5 by schveiguy — 2022-12-18T01:17:16Z
*** Issue 23555 has been marked as a duplicate of this issue. ***
Comment #6 by dlang-bot — 2022-12-18T01:20:33Z
@schveiguy updated dlang/dmd pull request #14710 "Remove GC for allocating traceinfo" fixing this issue: - Remove GC for allocating traceinfo. Allow traces for InvalidMemoryOperationError and other sensitive errors. Fixes 20650,16641,22616. https://github.com/dlang/dmd/pull/14710
Comment #7 by dlang-bot — 2022-12-26T03:24:12Z
dlang/dmd pull request #14710 "Avoid GC for allocating traceinfo" was merged into master: - 374f20760e6c6183861dc599e1fb6379830a032a by Steven Schveighoffer: Avoid GC for allocating traceinfo. Allow traces for InvalidMemoryOperationError and other sensitive errors. Fixes 20650,16641,22616. https://github.com/dlang/dmd/pull/14710