Add Fiber's guard page in Posix as well#1698
Conversation
5707b74 to
0bf24b3
Compare
| // protect end of stack | ||
| if ( mprotect(guard, PAGESIZE, PROT_NONE) == -1 ) | ||
| onOutOfMemoryError(); | ||
| } |
There was a problem hiding this comment.
If valloc/malloc cases are not supported, I'd suggest putting explicit empty else clause with a comment about it (to
There was a problem hiding this comment.
(to ... ? :-)
There was a problem hiding this comment.
.. to better match previous static if block)
0bf24b3 to
68bf497
Compare
|
Updated with the |
|
Not sure why |
src/core/thread.d
Outdated
|
|
||
| // protect end of stack | ||
| if ( mprotect(guard, PAGESIZE, PROT_NONE) == -1 ) | ||
| onOutOfMemoryError(); |
There was a problem hiding this comment.
Perhaps core.internal.abort is more appropriate here.
There was a problem hiding this comment.
Yeah, but I was just following the module's style, which uses this for anything that can fail (see the same thing in Windows: https://github.com/nemanja-boric-sociomantic/druntime/blob/68bf497033575bdcb41a9392e72bdcf794c66016/src/core/thread.d#L4385-L4386)
There was a problem hiding this comment.
Well, the other ones look like they allocate sth., b/c VirtualAlloc is used to change flags, and should be changed to fail instead of using onOutOfMemoryError.
There was a problem hiding this comment.
OK, fair enough, will change to abort.
|
The DAutoTest is about testing the documentation build, and so its failure is unrelated to your pull request. |
1d17e7c to
f734164
Compare
|
Maybe we should make the guard size configurable, including a size of 0, much as the stack size is configurable. |
src/core/thread.d
Outdated
| { | ||
| // Mark end of stack | ||
| for ( ubyte* g = cast(ubyte*)guard; g < guard + PAGESIZE; g+= 32) | ||
| g[0 .. 32] = cast(ubyte[]) "END OF FIBER -- END OF FIBER -- "; |
There was a problem hiding this comment.
Not acceptable to write to that page.
The point of a guard page is that it's not accessible, not backed by real memory, and causes a (page) fault when being accessed.
There was a problem hiding this comment.
It is not a guarded at this point and writing recognizable string is the main purpose if this PR. It makes possible to easily debug stack overflow in a fiber by printing few bytes around rsp in gdb and checking if it resembles a guard string. Similar code has existed in original tango runtime and I have no idea how one is expected to work with heavy usage of fibers without it - pretty much everyone relies on it in daily workflow at Sociomantic :(
There was a problem hiding this comment.
As Dicebot said, at this point, it is not guarded at this point, and in the lack of proper exception type, instead of SIGSEGV, this is very useful.
There was a problem hiding this comment.
Not going to rediscuss why Exceptions for async failures are wrong, unwinding is hardly possible for those, and having the compiler assume that each instruction could throw requires complex unwind information or conservative (slow) code.
In any case, neither an exception nor a segfault for a stack overflow tells you much else that that the stack overflowed.
There was a problem hiding this comment.
Sorry, I wasn't referring about the D exceptions, but the operating system's exception. For example, Windows builds will, this same runtime will, raise STATUS_GUARD_PAGE exception - which is very useful - it prevents you from silently corrupting your memory and it will tell you that your fiber's stack overflowed (if you're familiar with the runtime, though).
On POSIX, I'm not sure what else I can do here. The most reliable way to report the cause of the segmentation fault here is to install SIGSEGV handler, and to compare the siginfo_t.si_addr to see if this is done by fiber stack's overflow, eventually print info to stderr and die.
It is true that segmentation fault doesn't tell you why your program failed, but it least prevents you from silently corrupting other pages of the memory, making your program fail in completely unrelated code, just because some other fiber corrupted the memory. Sadly, this is not theoretical concern (and the code is already available on Windows), but this is something that we're seeing in practice every now and then.
However, even with the segmentation fault, if the core file is generated, you can load it in debugger and simply do x/s $sp-64 and see if the contents of the memory is just END OF FIBER. This is, btw, the reason why we're filling this page with data.
There was a problem hiding this comment.
(I forgot to mention @MartinNowak to pass email filters :-) )
src/core/thread.d
Outdated
|
|
||
| // protect end of stack | ||
| if ( mprotect(guard, PAGESIZE, PROT_NONE) == -1 ) | ||
| onOutOfMemoryError(); |
There was a problem hiding this comment.
Well, the other ones look like they allocate sth., b/c VirtualAlloc is used to change flags, and should be changed to fail instead of using onOutOfMemoryError.
Ok, sure. Do you prefer to do this via druntime config argument? |
f734164 to
006855b
Compare
|
Updated with the:
|
f9dad10 to
c912db6
Compare
|
Ah, FreeBSD failed because by the time it got to testing, changelog.dd started containing merge confict. |
c912db6 to
dac1103
Compare
|
Updated. @wilzbach could you advise if I got the new changelog format right? |
The idea is at that you will never have to rebase again due to changelog conflicts ;-)
Yup - the idea was also that it's less complicated ;-) |
|
Thanks! |
|
Sorry haven't looked at this for a while.
|
I'm not sure I follow this - the guard page is both write and read protected (see here: https://github.com/dlang/druntime/pull/1698/files#diff-8bb12ed976acf0a5132e877ec5a01ea8R4488) I'll take a look later at other issues mentioned. Thanks! |
dac1103 to
2458702
Compare
I've updated the code so that this is configurable using Fiber's contructor's argument (with an ability to turn it off by setting |
src/core/thread.d
Outdated
| { | ||
| // Mark end of stack | ||
| for ( ubyte* g = cast(ubyte*)guard; g < guard + guard_page_size; g+= 32) | ||
| g[0 .. 32] = cast(ubyte[]) "END OF FIBER -- END OF FIBER -- "; |
There was a problem hiding this comment.
Here you're writing to the guard page which will cause it to actually get wired. Seems unnecessary, when the page is write protected below.
There was a problem hiding this comment.
Yeah, the intention is to easily check if the stackoverflow has happened, simply by inspecting the memory pointed by sp in the debugger. I guess one could also check the mappings from /proc to confirm that the page is actually protected. If this writing is a deal breaker I would remove it.
src/core/thread.d
Outdated
| * Params: | ||
| * fn = The fiber function. | ||
| * sz = The stack size for this fiber. | ||
| * guard_page_size = size of the guard page to trap fiber's stack |
There was a problem hiding this comment.
Change to guardPageSize to match our naming.
There was a problem hiding this comment.
Will do. Thanks for the review!
|
Updated with your PR rebased. |
08d7552 to
35b1c80
Compare
35b1c80 to
6a2b951
Compare
6a2b951 to
98b907a
Compare
98b907a to
3ca3d41
Compare
Windows Fiber implementation already uses the fiber stack guard page. This brings the similar protection for mmaped fiber stacks, using mprotect.
This allows configuring the size or turning off completely the fiber's stack's guard page. It can be configured using new Fiber's constructor's parameter `guard_page_size`.
- no point in creating a dirty write protected page
0ca13da to
e6c6ec3
Compare
e6c6ec3 to
6be1794
Compare
|
@MartinNowak I've pushed a small commit with a trivial tidying up the test case, if you don't mind. |
|
Green! |
During the review of GitHub dlang#1698, the "END OF FIBRE" marker data was removed to avoid dirtying the guard page. Hence, the corresponding comment in the release notes doesn't apply anymore.
| page used for the Fiber's stack, the page is allocate which is protected for | ||
| any kind of access. This will cause system to trap immediately on the fiber's | ||
| stack overflow. If in debugger session, one can inspect contents of the memory | ||
| before or after stack pointer and it can be seen if it contains END OF FIBER |
During the review of GitHub dlang/druntime#1698, the "END OF FIBER" marker data was removed to avoid dirtying the guard page. Hence, the corresponding comment in the release notes doesn't apply anymore. See dlang/druntime#1821
Windows Fiber implementation already uses the fiber stack guard page.
This brings the similar protection for mmaped fiber stacks, using
mprotect.