digitalmars.D.bugs - [Issue 15573] New: safe code using TLS works in debug; crashes in
- via Digitalmars-d-bugs (159/159) Jan 16 2016 https://issues.dlang.org/show_bug.cgi?id=15573
https://issues.dlang.org/show_bug.cgi?id=15573 Issue ID: 15573 Summary: safe code using TLS works in debug; crashes in release Product: D Version: D2 Hardware: x86_64 OS: Linux Status: NEW Severity: major Priority: P1 Component: dmd Assignee: nobody puremagic.com Reporter: thomas.bockman gmail.com This is a serious bug, but a little bit complicated to explain; sorry I wasn't able to reduce it further. CONTEXT: The code below was reduced from some test code in my checkedint project. It's original purpose was to compare my checked integer math operations to the built in floating-point operations, to verify exhaustively that all the problematic corner cases (such as division by zero) are handled correctly. One extra test I threw in (and am now very glad I did!) was a consistency check - given the same inputs, a basic operation like division should always return the same value. `safeDiv()` isn't actually `pure`, because of the way it does error reporting. Nevertheless, it should be consistent since it never reads the TLS variable `intFlag`; it just overwrites it sometimes. THE PROBLEM: On LDC and GDC, this code works great. It also works in debug or unittest builds with DMD. However, when built in 64-bit release mode with DMD (tested with master, 2.069.2, and 2.068.2), it fails catastrophically. DUB command: dub run -b release -a x86_64 --compiler=dmd A large number of combinations of `n` and `m` fail the "consistent" check with output like this: byte n = -128 byte m = -2 real theory = +64 int practice1 = +0 int practice2 = +64 intFlag = {} FAILS: consistent A few combinations fail the "correct" check with output like this: byte n = -1 byte m = -2 real theory = +0 int practice = +0 intFlag = {divide by zero} FAILS: correct Finally, the program crashes with this unhelpful message: Program exited with code -8 I haven't yet been able to determine exactly what "code -8" means, although it seems to indicate that the program did something bad enough that Linux forcibly terminated the process. A HINT: I wasn't able to reduce it further, but I did discover that the problem goes away if `intFlag` is put anywhere other than thread local storage. SYSTEM DETAILS: Linux Mint 17.3 64-bit running kernel 4.2.0-23-lowlatency Intel Xeon E3-1225 v3 (Haswell quad-core) DUB CONFIG: Since this bug may be dependant on the compiler options, here's my reduced DUB config: { "name": "bug", "description": "Bug demonstrator.", "copyright": "Copyright © 2015, Thomas Stuart Bockman", "authors": ["Thomas Stuart Bockman"], "license": "BSL-1.0", "targetType": "executable", "mainSourceFile": "source/app.d", } SOURCE CODE: // source/app.d module app; import std.math, std.stdio, std.traits; safe: enum ulong[] naturals = function() { ulong[34 + 3*(64 - 5) - 2] nats; size_t n = 0; while(n <= 33) { nats[n] = n; ++n; } int sh = 6; while(sh < 64) { nats[n++] = (1uL << sh) -1; nats[n++] = (1uL << sh); nats[n++] = (1uL << sh) + 1; ++sh; } nats[n] = ulong.max; return nats; }(); struct TestValues { ptrdiff_t index = -38L; property bool empty() const { return index > 37L; } property byte front() const { if(index < 0) return -cast(byte)naturals[-index]; return cast(byte)naturals[index]; } property void popFront() { ++index; } } enum IntFlag : uint { NULL = 0, div0 = 1, posOver = 2 } auto intFlag = IntFlag.NULL; int safeDiv(const byte left, const byte right) { const div0 = (right == 0); const posOver = (left == int.min) && (right == -1); if(div0 || posOver) { intFlag = (posOver? IntFlag.posOver : IntFlag.div0); return 0; // Prevent unrecoverable FPE } else return mixin("left / right"); } void main() { foreach(const m; TestValues()) { foreach(const n; TestValues()) { const theory = trunc(cast(real)n / cast(real)m); bool thrInval; thrInval = theory.isNaN; intFlag = IntFlag.NULL; const practice1 = safeDiv(n, m); int practice2 = practice1; void require(string name, const bool success) { if(success) return; writeln(); writefln("\t" ~ byte.stringof ~ " n = %+d", n); writefln("\t" ~ byte.stringof ~ " m = %+d", m); writefln("\treal theory = %+.22g", theory); if(practice1 == practice2) writefln("\tint practice = %+d", practice1); else { writefln("\tint practice1 = %+d", practice1); writefln("\tint practice2 = %+d", practice2); } writefln("\tintFlag = %s", ["{}", "{divide by zero}", "{positive overflow}"][cast(uint)intFlag]); write("\tFAILS: "); writefln(name); } require("correct", (!thrInval && (theory == practice1)) ^ (intFlag != IntFlag.NULL)); intFlag = IntFlag.posOver; practice2 = safeDiv(n, m); require("sticky", (intFlag != IntFlag.NULL)); intFlag = IntFlag.NULL; require("consistent", (practice2 == practice1)); } } } --
Jan 16 2016