/* $OpenBSD: uthread_stack.c,v 1.11 2008/12/18 09:30:32 guenther Exp $ */ /* * Copyright 1999, David Leonard. All rights reserved. * */ /* * Thread stack allocation. * * If stack pointers grow down, towards the beginning of stack storage, * the first page of the storage is protected using mprotect() so as * to generate a SIGSEGV if a thread overflows its stack. Similarly, * for stacks that grow up, the last page of the storage is protected. */ #include #include #include #include #include #include #include #include #include #include "pthread_private.h" struct stack * _thread_stack_alloc(void *base, size_t size, size_t guardsize) { struct stack *stack; size_t nbpg = (size_t)getpagesize(); /* Maintain a stack of default-sized stacks that we can re-use. */ if (base == NULL && size == PTHREAD_STACK_DEFAULT && guardsize == pthread_attr_default.guardsize_attr) { if (pthread_mutex_lock(&_gc_mutex) != 0) PANIC("Cannot lock gc mutex"); if ((stack = SLIST_FIRST(&_stackq)) != NULL) { SLIST_REMOVE_HEAD(&_stackq, qe); if (pthread_mutex_unlock(&_gc_mutex) != 0) PANIC("Cannot unlock gc mutex"); return stack; } if (pthread_mutex_unlock(&_gc_mutex) != 0) PANIC("Cannot unlock gc mutex"); } /* Allocate some storage to hold information about the stack: */ stack = (struct stack *)malloc(sizeof (struct stack)); if (stack == NULL) return NULL; if (base != NULL) { /* Use the user's storage */ stack->base = base; stack->size = size; stack->guardsize = 0; stack->redzone = NULL; stack->storage = NULL; return stack; } /* Round sizes up to closest page boundry */ size = ((size + (nbpg - 1)) / nbpg) * nbpg; guardsize = ((guardsize + (nbpg - 1)) / nbpg) * nbpg; /* overflow? */ if (SIZE_MAX - size < guardsize) { free(stack); return NULL; } /* mmap storage for the stack, possibly with page(s) for redzone */ stack->storage = mmap(NULL, size + guardsize, PROT_READ|PROT_WRITE, MAP_ANON|MAP_PRIVATE, -1, 0); if (stack->storage == MAP_FAILED) { free(stack); return NULL; } /* * Compute the location of the red zone. */ #if defined(MACHINE_STACK_GROWS_UP) /* Red zone is the last page of the storage: */ stack->redzone = (caddr_t)stack->storage + (ptrdiff_t)size; stack->base = stack->storage; stack->size = size; stack->guardsize = guardsize; #else /* Red zone is the first page of the storage: */ stack->redzone = stack->storage; stack->base = (caddr_t)stack->redzone + (ptrdiff_t)guardsize; stack->size = size; stack->guardsize = guardsize; #endif if (!guardsize) stack->redzone = NULL; else if (mprotect(stack->redzone, guardsize, PROT_NONE) == -1) PANIC("Cannot protect stack red zone"); return stack; } void _thread_stack_free(stack) struct stack *stack; { /* Cache allocated stacks of default size: */ if (stack->storage != NULL && stack->size == PTHREAD_STACK_DEFAULT && stack->guardsize == pthread_attr_default.guardsize_attr) SLIST_INSERT_HEAD(&_stackq, stack, qe); else { /* unmap storage: */ if (stack->storage) munmap(stack->storage, stack->size + stack->guardsize); /* Free stack information storage: */ free(stack); } }