Bug 6376 – core.thread.thread_scanAll doesn't scan the stack due to ASLR on Mac OS X 10.7
Status
RESOLVED
Resolution
FIXED
Severity
blocker
Priority
P2
Component
druntime
Product
D
Version
D2
Platform
x86
OS
Mac OS X
Creation time
2011-07-24T15:29:00Z
Last change time
2011-08-03T20:59:33Z
Keywords
EH, patch, wrong-code
Assigned to
nobody
Creator
kennytm
Comments
Comment #0 by kennytm — 2011-07-24T15:29:07Z
Test case 1
---------------
void main() {
assert(0);
}
---------------
Running this program on 10.7 causes
Bus error: 10
while the expected output should be something like
core.exception.AssertError@y(2): Assertion failure
----------------
5 y 0x00009265 onAssertError + 65
6 y 0x000126ca _d_assertm + 30
...
Test case 2
---------------
void main() {
throw new Exception("");
}
---------------
Running this program on 10.7 causes
y(96008) malloc: *** error for object 0x14b060: pointer being freed was not allocated
*** set a breakpoint in malloc_error_break to debug
Abort trap: 6
while the expected output should be something like
[email protected](2):
----------------
5 y 0x00002320 _Dmain + 80
6 y 0x00012dc7 extern (C) int rt.dmain2.main(int, char**).void runMain() + 23
...
I suspect the stack-trace routine has some code depending on having no ASLR, so I've marked it a druntime problem.
Update: Probably *not* due to stack-trace, as the segfault happens even if the exception is caught.
Test case 3:
-------------------------
void main() {
try {
throw new Exception("");
} catch (Exception e) {
}
}
-------------------------
Comment #3 by code — 2011-07-24T16:29:15Z
I can confirm that my problems I reported on the NG seem to be caused by throwing exceptions as well.
As mentioned there, one can run »set disable-aslr off« in GDB to debug with ASLR enabled (otherwise, the crashes don't happen for me).
I have not had time to trace down what exactly is going on, but the issue seems to be related somehow to *throwing* exceptions, not catching them (I might have jumped to the wrong conclusions based off debugger output here, though).
Comment #4 by kennytm — 2011-07-25T03:45:24Z
The segfault for case 2 happens in _d_throwc:
// this is a catch handler (no finally)
auto pci = cast(DCatchInfo *)(cast(char *)handler_table + phi.cioffset);
auto ncatches = pci.ncatches;
for (int i = 0; i < ncatches; i++)
{
auto ci = **cast(ClassInfo **)h; // <--------- this line, *h == null
auto pcb = &pci.catch_block[i];
Comment #5 by kennytm — 2011-07-25T06:05:42Z
(In reply to comment #4)
> The segfault for case 2 happens in _d_throwc:
>
> // this is a catch handler (no finally)
>
> auto pci = cast(DCatchInfo *)(cast(char *)handler_table + phi.cioffset);
> auto ncatches = pci.ncatches;
> for (int i = 0; i < ncatches; i++)
> {
> auto ci = **cast(ClassInfo **)h; // <--------- this line, *h == null
>
> auto pcb = &pci.catch_block[i];
Actually this is because I compile with -debug. Without -debug, _d_throwc is not no-return, causing originally unreachable statements e.g.
extern (C) void onAssertError( string file = __FILE__, size_t line = __LINE__ )
{
if( assertHandler is null )
throw new AssertError( file, line );
assertHandler( file, line, null); // <-------- this line
}
to be run.
Comment #6 by kennytm — 2011-07-25T10:52:50Z
Actually my initial guess is right. The bug is due to corruption of the Throwable object -- in particular its classinfo -- from the defaultTraceHandler.
Using this:
extern (C) void rt_setTraceHandler(Throwable.TraceInfo function(void* ptr));
void main() {
rt_setTraceHandler(null);
// rest of main
makes the segfault go away (and the stack trace too).
The classinfo corruption is because the Throwable object is finalized prematurely. This is because the object is placed *on stack*, and the stack is not marked by the GC (!) using core.thread.thread_scanAll. Updated title to reflect the deeper cause.
Test case:
---------------
import core.stdc.stdio;
import core.thread;
void main() {
void scan(void* from, void* to) {
printf("%p -> %p\n", from, to);
}
size_t stackTop;
thread_scanAll(&scan, &stackTop);
}
---------------
With ASLR:
0x201fc0 -> 0x201fe4
Without ASLR:
0xbffff924 -> 0xc0000000
0x201fc0 -> 0x201fe4
Note that the stack is > 0xc000_0000 when ASLR is enabled.
Comment #9 by kennytm — 2011-07-26T03:22:10Z
(In reply to comment #8)
> Note that the stack is > 0xc000_0000 when ASLR is enabled.
src/rt/memory.d:
extern (C) void* rt_stackBottom()
{
...
else version( OSX )
{
return cast(void*) 0xc0000000;
}
Mehhhhhhhhhhhhhh. Now everything is clear.