[Issue 15573] New: @safe code using TLS works in debug; crashes in release
via Digitalmars-d-bugs
digitalmars-d-bugs at puremagic.com
Sat Jan 16 16:51:35 PST 2016
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 at puremagic.com
Reporter: thomas.bockman at 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));
}
}
}
--
More information about the Digitalmars-d-bugs
mailing list