Skip to content
This repository was archived by the owner on Oct 12, 2022. It is now read-only.
/ druntime Public archive
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions changelog/fiber-configure.dd
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
Make fiber stack protection-page size configurable

It is now possible to change the guard page size by using
the new Fiber's constructor argument - guard_page_size. It defaults to
`PAGE_SIZE` (the same it used to be on Windows), and specifying 0 will
turn this feature off.
9 changes: 9 additions & 0 deletions changelog/fiber.dd
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
Add Fiber's stack-protection page for Posix.

The feature already existing for Windows' fiber implementation is now added to
the systems using mmap to allocate fibers' stacks: After (or before) the last
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
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This isn't true anymore, see #1821 for a fix.

string pattern.
2 changes: 1 addition & 1 deletion posix.mak
Original file line number Diff line number Diff line change
Expand Up @@ -197,7 +197,7 @@ $(DRUNTIME): $(OBJS) $(SRCS)
UT_MODULES:=$(patsubst src/%.d,$(ROOT)/unittest/%,$(SRCS))
HAS_ADDITIONAL_TESTS:=$(shell test -d test && echo 1)
ifeq ($(HAS_ADDITIONAL_TESTS),1)
ADDITIONAL_TESTS:=test/init_fini test/exceptions test/coverage test/profile test/cycles test/allocations test/typeinfo
ADDITIONAL_TESTS:=test/init_fini test/exceptions test/coverage test/profile test/cycles test/allocations test/typeinfo test/thread
ADDITIONAL_TESTS+=$(if $(SHARED),test/shared,)
endif

Expand Down
57 changes: 43 additions & 14 deletions src/core/thread.d
Original file line number Diff line number Diff line change
Expand Up @@ -3976,18 +3976,21 @@ class Fiber
* Params:
* fn = The fiber function.
* sz = The stack size for this fiber.
* guardPageSize = size of the guard page to trap fiber's stack
* overflows
*
* In:
* fn must not be null.
*/
this( void function() fn, size_t sz = PAGESIZE*4 ) nothrow
this( void function() fn, size_t sz = PAGESIZE*4,
size_t guardPageSize = PAGESIZE ) nothrow
in
{
assert( fn );
}
body
{
allocStack( sz );
allocStack( sz, guardPageSize );
reset( fn );
}

Expand All @@ -3999,18 +4002,21 @@ class Fiber
* Params:
* dg = The fiber function.
* sz = The stack size for this fiber.
* guardPageSize = size of the guard page to trap fiber's stack
* overflows
*
* In:
* dg must not be null.
*/
this( void delegate() dg, size_t sz = PAGESIZE*4 ) nothrow
this( void delegate() dg, size_t sz = PAGESIZE*4,
size_t guardPageSize = PAGESIZE ) nothrow
in
{
assert( dg );
}
body
{
allocStack( sz );
allocStack( sz, guardPageSize);
reset( dg );
}

Expand Down Expand Up @@ -4355,7 +4361,7 @@ private:
//
// Allocate a new stack for this fiber.
//
final void allocStack( size_t sz ) nothrow
final void allocStack( size_t sz, size_t guardPageSize ) nothrow
in
{
assert( !m_pmem && !m_ctxt );
Expand All @@ -4380,15 +4386,15 @@ private:
{
// reserve memory for stack
m_pmem = VirtualAlloc( null,
sz + PAGESIZE,
sz + guardPageSize,
MEM_RESERVE,
PAGE_NOACCESS );
if( !m_pmem )
onOutOfMemoryError();

version( StackGrowsDown )
{
void* stack = m_pmem + PAGESIZE;
void* stack = m_pmem + guardPageSize;
void* guard = m_pmem;
void* pbase = stack + sz;
}
Expand All @@ -4407,13 +4413,16 @@ private:
if( !stack )
onOutOfMemoryError();

// allocate reserved guard page
guard = VirtualAlloc( guard,
PAGESIZE,
MEM_COMMIT,
PAGE_READWRITE | PAGE_GUARD );
if( !guard )
onOutOfMemoryError();
if (guardPageSize)
{
// allocate reserved guard page
guard = VirtualAlloc( guard,
guardPageSize,
MEM_COMMIT,
PAGE_READWRITE | PAGE_GUARD );
if( !guard )
onOutOfMemoryError();
}

m_ctxt.bstack = pbase;
m_ctxt.tstack = pbase;
Expand All @@ -4429,6 +4438,9 @@ private:

static if( __traits( compiles, mmap ) )
{
// Allocate more for the memory guard
sz += guardPageSize;

m_pmem = mmap( null,
sz,
PROT_READ | PROT_WRITE,
Expand Down Expand Up @@ -4458,13 +4470,30 @@ private:
{
m_ctxt.bstack = m_pmem + sz;
m_ctxt.tstack = m_pmem + sz;
void* guard = m_pmem;
}
else
{
m_ctxt.bstack = m_pmem;
m_ctxt.tstack = m_pmem;
void* guard = m_pmem + sz - guardPageSize;
}
m_size = sz;

static if( __traits( compiles, mmap ) )
{
if (guardPageSize)
{
// protect end of stack
if ( mprotect(guard, guardPageSize, PROT_NONE) == -1 )
abort();
}
}
else
{
// Supported only for mmap allocated memory - results are
// undefined if applied to memory not obtained by mmap
}
}

Thread.add( m_ctxt );
Expand Down
18 changes: 18 additions & 0 deletions test/thread/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
include ../common.mak

TESTS:=fiber_guard_page

.PHONY: all clean
all: $(addprefix $(ROOT)/,$(addsuffix .done,$(TESTS)))

# segfault || bus error (OSX)
$(ROOT)/fiber_guard_page.done: $(ROOT)/%.done : $(ROOT)/%
@echo Testing $*
$(QUIET)$(TIMELIMIT)$(ROOT)/$* $(RUN_ARGS); rc=$$?; [ $$rc -eq 139 ] || [ $$rc -eq 138 ]
@touch $@

$(ROOT)/%: $(SRC)/%.d
$(QUIET)$(DMD) $(DFLAGS) -of$@ $<

clean:
rm -rf $(GENERATED)
46 changes: 46 additions & 0 deletions test/thread/src/fiber_guard_page.d
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import core.thread;
import core.sys.posix.sys.mman;

// this should be true for most architectures
// (taken from core.thread)
version = StackGrowsDown;

enum stackSize = 4096;

// Simple method that causes a stack overflow
void stackMethod()
{
// Over the stack size, so it overflows the stack
int[stackSize/int.sizeof+100] x;
}

void main()
{
auto test_fiber = new Fiber(&stackMethod, stackSize);

// allocate a page below (above) the fiber's stack to make stack overflows possible (w/o segfaulting)
version (StackGrowsDown)
{
static assert(__traits(identifier, test_fiber.tupleof[8]) == "m_pmem");
auto stackBottom = test_fiber.tupleof[8];
auto p = mmap(stackBottom - 8 * stackSize, 8 * stackSize,
PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON, -1, 0);
assert(p !is null, "failed to allocate page");
}
else
{
auto m_sz = test_fiber.tupleof[7];
auto m_pmem = test_fiber.tupleof[8];
static assert(__traits(identifier, test_fiber.tupleof[7]) == "m_size");
static assert(__traits(identifier, test_fiber.tupleof[8]) == "m_pmem");

auto stackTop = m_pmem + m_sz;
auto p = mmap(stackTop, 8 * stackSize,
PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON, -1, 0);
assert(p !is null, "failed to allocate page");
}

// the guard page should prevent a mem corruption by stack
// overflow and cause a segfault instead (or generate SIGBUS on *BSD flavors)
test_fiber.call();
}