Commit b4f0767d authored by David Gibson's avatar David Gibson

coroutine: Stack allocation

At present, coroutine stacks must be allocated explicitly by the user,
then initialized with coroutine_stack_init().  This adds a new
coroutine_stack_alloc() function which allocates a stack, making life
easier for users.  coroutine_stack_release() will automatically determine
if the given stack was set up with _init() or alloc() and act
accordingly.

The stacks are allocate with mmap() rather than a plain malloc(), and a
guard page is added, so an overflow of the stack should result in a
relatively debuggable SEGV instead of random data corruption.
Signed-off-by: default avatarDavid Gibson <david@gibson.dropbear.id.au>
parent fe3995b4
......@@ -5,6 +5,9 @@
#include <inttypes.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/mman.h>
#include <ccan/ptrint/ptrint.h>
#include <ccan/compiler/compiler.h>
#include <ccan/build_assert/build_assert.h>
......@@ -70,23 +73,90 @@ struct coroutine_stack *coroutine_stack_init(void *buf, size_t bufsize,
((char *)buf + bufsize - metasize) - 1;
#endif
stack->magic = COROUTINE_STACK_MAGIC;
stack->magic = COROUTINE_STACK_MAGIC_BUF;
stack->size = size;
vg_register_stack(stack);
return stack;
}
struct coroutine_stack *coroutine_stack_alloc(size_t totalsize, size_t metasize)
{
struct coroutine_stack *stack;
size_t pgsz = getpagesize();
size_t mapsize;
char *map, *guard;
int rc;
mapsize = ((totalsize + (pgsz - 1)) & ~(pgsz - 1)) + pgsz;
map = mmap(NULL, mapsize, PROT_READ | PROT_WRITE,
MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
if (map == MAP_FAILED)
return NULL;
#if HAVE_STACK_GROWS_UPWARDS
guard = map + mapsize - pgsz;
stack = (struct coroutine_stack *)(guard - totalsize + metasize);
#else
guard = map;
stack = (struct coroutine_stack *)(map + pgsz + totalsize - metasize)
- 1;
#endif
rc = mprotect(guard, pgsz, PROT_NONE);
if (rc != 0) {
munmap(map, mapsize);
return NULL;
}
stack->magic = COROUTINE_STACK_MAGIC_ALLOC;
stack->size = totalsize - sizeof(*stack) - metasize;
vg_register_stack(stack);
return stack;
}
static void coroutine_stack_free(struct coroutine_stack *stack, size_t metasize)
{
void *map;
size_t pgsz = getpagesize();
size_t totalsize = stack->size + sizeof(*stack) + metasize;
size_t mapsize = ((totalsize + (pgsz - 1)) & ~(pgsz - 1)) + pgsz;
#if HAVE_STACK_GROWS_UPWARDS
map = (char *)(stack + 1) + stack->size + pgsz - mapsize;
#else
map = (char *)stack - stack->size - pgsz;
#endif
munmap(map, mapsize);
}
void coroutine_stack_release(struct coroutine_stack *stack, size_t metasize)
{
vg_deregister_stack(stack);
memset(stack, 0, sizeof(*stack));
switch (stack->magic) {
case COROUTINE_STACK_MAGIC_BUF:
memset(stack, 0, sizeof(*stack));
break;
case COROUTINE_STACK_MAGIC_ALLOC:
coroutine_stack_free(stack, metasize);
break;
default:
abort();
}
}
struct coroutine_stack *coroutine_stack_check(struct coroutine_stack *stack,
const char *abortstr)
{
if (stack && vg_addressable(stack, sizeof(*stack))
&& (stack->magic == COROUTINE_STACK_MAGIC)
&& ((stack->magic == COROUTINE_STACK_MAGIC_BUF)
|| (stack->magic == COROUTINE_STACK_MAGIC_ALLOC))
&& (stack->size >= COROUTINE_MIN_STKSZ))
return stack;
......
......@@ -53,10 +53,16 @@ struct coroutine_state;
#define COROUTINE_MIN_STKSZ 2048
/**
* COROUTINE_STACK_MAGIC - Magic number for coroutine stacks
* COROUTINE_STACK_MAGIC_BUF - Magic number for coroutine stacks in a user
* supplied buffer
*/
#define COROUTINE_STACK_MAGIC 0xc040c040574c574c
#define COROUTINE_STACK_MAGIC_BUF 0xc040c040574cb00f
/**
* COROUTINE_STACK_MAGIC_ALLOC - Magic number for coroutine stacks
* allocated by this module
*/
#define COROUTINE_STACK_MAGIC_ALLOC 0xc040c040574ca110
/**
* coroutine_stack_init - Prepare a coroutine stack in an existing buffer
......@@ -75,6 +81,23 @@ struct coroutine_state;
struct coroutine_stack *coroutine_stack_init(void *buf, size_t bufsize,
size_t metasize);
/**
* coroutine_stack_alloc - Allocate a coroutine stack
* @totalsize: total size to allocate
* @metasize: size of metadata to add to the stack (not including
* coroutine internal overhead)
*
* Allocates a coroutine stack of size @totalsize, including both
* internal overhead (COROUTINE_STK_OVERHEAD) and metadata of size
* @metasize. Where available this will also create a guard page, so
* that overruning the stack will result in an immediate crash, rather
* than data corruption.
*
* This will fail if the totalsize < (COROUTINE_MIN_STKSZ +
* COROUTINE_STK_OVERHEAD + metasize).
*/
struct coroutine_stack *coroutine_stack_alloc(size_t bufsize, size_t metasize);
/**
* coroutine_stack_release - Stop using a coroutine stack
* @stack: coroutine stack to release
......
......@@ -3,6 +3,8 @@
#include <ccan/coroutine/coroutine.h>
#include <ccan/tap/tap.h>
#define STKSZ (COROUTINE_MIN_STKSZ + COROUTINE_STK_OVERHEAD)
static int global = 0;
static void trivial_fn(void *p)
......@@ -18,6 +20,10 @@ static void test_trivial(struct coroutine_stack *stack)
{
struct coroutine_state t, master;
ok1(stack != NULL);
ok1(coroutine_stack_check(stack, NULL) == stack);
ok1(coroutine_stack_size(stack) == COROUTINE_MIN_STKSZ);
if (!COROUTINE_AVAILABLE) {
skip(1, "Coroutines not available");
return;
......@@ -30,22 +36,22 @@ static void test_trivial(struct coroutine_stack *stack)
}
static char buf[COROUTINE_MIN_STKSZ + COROUTINE_STK_OVERHEAD];
static char buf[STKSZ];
int main(void)
{
struct coroutine_stack *stack;
/* This is how many tests you plan to run */
plan_tests(4);
plan_tests(2 * 4 + 1);
stack = coroutine_stack_init(buf, sizeof(buf), 0);
ok1(stack != NULL);
ok1(coroutine_stack_check(stack, NULL) == stack);
ok1(coroutine_stack_size(stack) == COROUTINE_MIN_STKSZ);
test_trivial(stack);
coroutine_stack_release(stack, 0);
ok1(!coroutine_stack_check(stack, NULL));
stack = coroutine_stack_alloc(STKSZ, 0);
test_trivial(stack);
coroutine_stack_release(stack, 0);
/* This exits depending on whether all tests passed */
......
......@@ -59,20 +59,13 @@ static void f2(void *p)
static void test1(size_t bufsz)
{
void *buf1, *buf2;
struct coroutine_stack *stack1, *stack2;
buf1 = malloc(bufsz);
ok1(buf1 != NULL);
stack1 = coroutine_stack_init(buf1, bufsz, 0);
diag("buf1=%p stack1=%p bufsz=0x%zx overhead=0x%zx\n",
buf1, stack1, bufsz, COROUTINE_STK_OVERHEAD);
stack1 = coroutine_stack_alloc(bufsz, 0);
ok1(coroutine_stack_check(stack1, NULL) == stack1);
ok1(coroutine_stack_size(stack1) == bufsz - COROUTINE_STK_OVERHEAD);
buf2 = malloc(bufsz);
ok1(buf2 != NULL);
stack2 = coroutine_stack_init(buf2, bufsz, 0);
stack2 = coroutine_stack_alloc(bufsz, 0);
ok1(coroutine_stack_check(stack2, NULL) == stack2);
ok1(coroutine_stack_size(stack2) == bufsz - COROUTINE_STK_OVERHEAD);
......@@ -90,18 +83,14 @@ static void test1(size_t bufsz)
ok(1, "Completed test1");
coroutine_stack_release(stack1, 0);
ok1(coroutine_stack_check(stack1, NULL) == NULL);
free(buf1);
coroutine_stack_release(stack2, 0);
ok1(coroutine_stack_check(stack2, NULL) == NULL);
free(buf2);
}
int main(void)
{
/* This is how many tests you plan to run */
plan_tests(14);
plan_tests(10);
test1(8192);
......
......@@ -38,6 +38,11 @@ static void test_metadata(struct coroutine_stack *stack)
{
struct metadata *meta;
ok1(stack != NULL);
ok1(coroutine_stack_check(stack, NULL) == stack);
ok1(coroutine_stack_size(stack)
== BUFSIZE - COROUTINE_STK_OVERHEAD - sizeof(struct metadata));
meta = coroutine_stack_to_metadata(stack, sizeof(*meta));
ok1(coroutine_stack_from_metadata(meta, sizeof(*meta)) == stack);
......@@ -68,22 +73,19 @@ int main(void)
struct coroutine_stack *stack;
/* This is how many tests you plan to run */
plan_tests(10);
plan_tests(1 + 2 * 9);
/* Fix seed so we get consistent, though pseudo-random results */
srandom(0);
stack = coroutine_stack_alloc(BUFSIZE, sizeof(struct metadata));
test_metadata(stack);
coroutine_stack_release(stack, sizeof(struct metadata));
buf = malloc(BUFSIZE);
ok1(buf != NULL);
stack = coroutine_stack_init(buf, BUFSIZE, sizeof(struct metadata));
ok1(stack != NULL);
ok1(coroutine_stack_check(stack, NULL) == stack);
ok1(coroutine_stack_size(stack)
== BUFSIZE - COROUTINE_STK_OVERHEAD - sizeof(struct metadata));
test_metadata(stack);
coroutine_stack_release(stack, sizeof(struct metadata));
free(buf);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment