Skip to content
This repository was archived by the owner on Oct 12, 2022. It is now read-only.

Commit a3505e7

Browse files
authored
Merge pull request #1698 from nemanja-boric-sociomantic/fiber-protection
Add Fiber's guard page in Posix as well
2 parents c6b4c1a + 6be1794 commit a3505e7

File tree

6 files changed

+123
-15
lines changed

6 files changed

+123
-15
lines changed

changelog/fiber-configure.dd

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
Make fiber stack protection-page size configurable
2+
3+
It is now possible to change the guard page size by using
4+
the new Fiber's constructor argument - guard_page_size. It defaults to
5+
`PAGE_SIZE` (the same it used to be on Windows), and specifying 0 will
6+
turn this feature off.

changelog/fiber.dd

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
Add Fiber's stack-protection page for Posix.
2+
3+
The feature already existing for Windows' fiber implementation is now added to
4+
the systems using mmap to allocate fibers' stacks: After (or before) the last
5+
page used for the Fiber's stack, the page is allocate which is protected for
6+
any kind of access. This will cause system to trap immediately on the fiber's
7+
stack overflow. If in debugger session, one can inspect contents of the memory
8+
before or after stack pointer and it can be seen if it contains END OF FIBER
9+
string pattern.

posix.mak

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -197,7 +197,7 @@ $(DRUNTIME): $(OBJS) $(SRCS)
197197
UT_MODULES:=$(patsubst src/%.d,$(ROOT)/unittest/%,$(SRCS))
198198
HAS_ADDITIONAL_TESTS:=$(shell test -d test && echo 1)
199199
ifeq ($(HAS_ADDITIONAL_TESTS),1)
200-
ADDITIONAL_TESTS:=test/init_fini test/exceptions test/coverage test/profile test/cycles test/allocations test/typeinfo
200+
ADDITIONAL_TESTS:=test/init_fini test/exceptions test/coverage test/profile test/cycles test/allocations test/typeinfo test/thread
201201
ADDITIONAL_TESTS+=$(if $(SHARED),test/shared,)
202202
endif
203203

src/core/thread.d

Lines changed: 43 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -3976,18 +3976,21 @@ class Fiber
39763976
* Params:
39773977
* fn = The fiber function.
39783978
* sz = The stack size for this fiber.
3979+
* guardPageSize = size of the guard page to trap fiber's stack
3980+
* overflows
39793981
*
39803982
* In:
39813983
* fn must not be null.
39823984
*/
3983-
this( void function() fn, size_t sz = PAGESIZE*4 ) nothrow
3985+
this( void function() fn, size_t sz = PAGESIZE*4,
3986+
size_t guardPageSize = PAGESIZE ) nothrow
39843987
in
39853988
{
39863989
assert( fn );
39873990
}
39883991
body
39893992
{
3990-
allocStack( sz );
3993+
allocStack( sz, guardPageSize );
39913994
reset( fn );
39923995
}
39933996

@@ -3999,18 +4002,21 @@ class Fiber
39994002
* Params:
40004003
* dg = The fiber function.
40014004
* sz = The stack size for this fiber.
4005+
* guardPageSize = size of the guard page to trap fiber's stack
4006+
* overflows
40024007
*
40034008
* In:
40044009
* dg must not be null.
40054010
*/
4006-
this( void delegate() dg, size_t sz = PAGESIZE*4 ) nothrow
4011+
this( void delegate() dg, size_t sz = PAGESIZE*4,
4012+
size_t guardPageSize = PAGESIZE ) nothrow
40074013
in
40084014
{
40094015
assert( dg );
40104016
}
40114017
body
40124018
{
4013-
allocStack( sz );
4019+
allocStack( sz, guardPageSize);
40144020
reset( dg );
40154021
}
40164022

@@ -4355,7 +4361,7 @@ private:
43554361
//
43564362
// Allocate a new stack for this fiber.
43574363
//
4358-
final void allocStack( size_t sz ) nothrow
4364+
final void allocStack( size_t sz, size_t guardPageSize ) nothrow
43594365
in
43604366
{
43614367
assert( !m_pmem && !m_ctxt );
@@ -4380,15 +4386,15 @@ private:
43804386
{
43814387
// reserve memory for stack
43824388
m_pmem = VirtualAlloc( null,
4383-
sz + PAGESIZE,
4389+
sz + guardPageSize,
43844390
MEM_RESERVE,
43854391
PAGE_NOACCESS );
43864392
if( !m_pmem )
43874393
onOutOfMemoryError();
43884394

43894395
version( StackGrowsDown )
43904396
{
4391-
void* stack = m_pmem + PAGESIZE;
4397+
void* stack = m_pmem + guardPageSize;
43924398
void* guard = m_pmem;
43934399
void* pbase = stack + sz;
43944400
}
@@ -4407,13 +4413,16 @@ private:
44074413
if( !stack )
44084414
onOutOfMemoryError();
44094415

4410-
// allocate reserved guard page
4411-
guard = VirtualAlloc( guard,
4412-
PAGESIZE,
4413-
MEM_COMMIT,
4414-
PAGE_READWRITE | PAGE_GUARD );
4415-
if( !guard )
4416-
onOutOfMemoryError();
4416+
if (guardPageSize)
4417+
{
4418+
// allocate reserved guard page
4419+
guard = VirtualAlloc( guard,
4420+
guardPageSize,
4421+
MEM_COMMIT,
4422+
PAGE_READWRITE | PAGE_GUARD );
4423+
if( !guard )
4424+
onOutOfMemoryError();
4425+
}
44174426

44184427
m_ctxt.bstack = pbase;
44194428
m_ctxt.tstack = pbase;
@@ -4429,6 +4438,9 @@ private:
44294438

44304439
static if( __traits( compiles, mmap ) )
44314440
{
4441+
// Allocate more for the memory guard
4442+
sz += guardPageSize;
4443+
44324444
m_pmem = mmap( null,
44334445
sz,
44344446
PROT_READ | PROT_WRITE,
@@ -4458,13 +4470,30 @@ private:
44584470
{
44594471
m_ctxt.bstack = m_pmem + sz;
44604472
m_ctxt.tstack = m_pmem + sz;
4473+
void* guard = m_pmem;
44614474
}
44624475
else
44634476
{
44644477
m_ctxt.bstack = m_pmem;
44654478
m_ctxt.tstack = m_pmem;
4479+
void* guard = m_pmem + sz - guardPageSize;
44664480
}
44674481
m_size = sz;
4482+
4483+
static if( __traits( compiles, mmap ) )
4484+
{
4485+
if (guardPageSize)
4486+
{
4487+
// protect end of stack
4488+
if ( mprotect(guard, guardPageSize, PROT_NONE) == -1 )
4489+
abort();
4490+
}
4491+
}
4492+
else
4493+
{
4494+
// Supported only for mmap allocated memory - results are
4495+
// undefined if applied to memory not obtained by mmap
4496+
}
44684497
}
44694498

44704499
Thread.add( m_ctxt );

test/thread/Makefile

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
include ../common.mak
2+
3+
TESTS:=fiber_guard_page
4+
5+
.PHONY: all clean
6+
all: $(addprefix $(ROOT)/,$(addsuffix .done,$(TESTS)))
7+
8+
# segfault || bus error (OSX)
9+
$(ROOT)/fiber_guard_page.done: $(ROOT)/%.done : $(ROOT)/%
10+
@echo Testing $*
11+
$(QUIET)$(TIMELIMIT)$(ROOT)/$* $(RUN_ARGS); rc=$$?; [ $$rc -eq 139 ] || [ $$rc -eq 138 ]
12+
@touch $@
13+
14+
$(ROOT)/%: $(SRC)/%.d
15+
$(QUIET)$(DMD) $(DFLAGS) -of$@ $<
16+
17+
clean:
18+
rm -rf $(GENERATED)

test/thread/src/fiber_guard_page.d

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
import core.thread;
2+
import core.sys.posix.sys.mman;
3+
4+
// this should be true for most architectures
5+
// (taken from core.thread)
6+
version = StackGrowsDown;
7+
8+
enum stackSize = 4096;
9+
10+
// Simple method that causes a stack overflow
11+
void stackMethod()
12+
{
13+
// Over the stack size, so it overflows the stack
14+
int[stackSize/int.sizeof+100] x;
15+
}
16+
17+
void main()
18+
{
19+
auto test_fiber = new Fiber(&stackMethod, stackSize);
20+
21+
// allocate a page below (above) the fiber's stack to make stack overflows possible (w/o segfaulting)
22+
version (StackGrowsDown)
23+
{
24+
static assert(__traits(identifier, test_fiber.tupleof[8]) == "m_pmem");
25+
auto stackBottom = test_fiber.tupleof[8];
26+
auto p = mmap(stackBottom - 8 * stackSize, 8 * stackSize,
27+
PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON, -1, 0);
28+
assert(p !is null, "failed to allocate page");
29+
}
30+
else
31+
{
32+
auto m_sz = test_fiber.tupleof[7];
33+
auto m_pmem = test_fiber.tupleof[8];
34+
static assert(__traits(identifier, test_fiber.tupleof[7]) == "m_size");
35+
static assert(__traits(identifier, test_fiber.tupleof[8]) == "m_pmem");
36+
37+
auto stackTop = m_pmem + m_sz;
38+
auto p = mmap(stackTop, 8 * stackSize,
39+
PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON, -1, 0);
40+
assert(p !is null, "failed to allocate page");
41+
}
42+
43+
// the guard page should prevent a mem corruption by stack
44+
// overflow and cause a segfault instead (or generate SIGBUS on *BSD flavors)
45+
test_fiber.call();
46+
}

0 commit comments

Comments
 (0)