diff options
author | Ivan Maidanski <ivmai@mail.ru> | 2022-02-02 01:12:43 +0300 |
---|---|---|
committer | Ivan Maidanski <ivmai@mail.ru> | 2022-02-04 00:29:23 +0300 |
commit | 73b67bab7ecc943abd2e3253a26127212442833a (patch) | |
tree | 75de90140f8c78edb5637ebb38a73e4bf74705d0 | |
parent | 6c8923eef141c0de98d369af5ab270caa7d51b67 (diff) | |
download | bdwgc-73b67bab7ecc943abd2e3253a26127212442833a.tar.gz |
Use mmap to allocate procedure stack buffers on E2K
(fix of commit 990dcdba1)
Issue #411 (bdwgc).
This commit prevents stack overflow in case of a big procedure stack
to be saved.
If a procedure stack requires less than 1 memory page for the buffer,
then the buffer is still allocated on the local stack.
* include/private/gc_priv.h [E2K] (PS_ALLOCA_BUF, ALLOCA_SAFE_LIMIT,
FREE_PROCEDURE_STACK_LOCAL): Define macro.
* include/private/gc_priv.h [E2K] (PROCEDURE_STACK_ALLOCA_AND_STORE):
Rename to GET_PROCEDURE_STACK_LOCAL; update comment; modify
implementation to call GC_mmap_procedure_stack_buf (and
GC_unmap_procedure_stack_buf) if size > ALLOCA_SAFE_LIMIT.
* include/private/gc_priv.h [E2K] (GC_mmap_procedure_stack_buf,
GC_unmap_procedure_stack_buf): Declare function.
* include/private/gc_priv.h [E2K && THREADS]
(GC_alloc_and_get_procedure_stack): Refine comment.
* mach_dep.c [E2K]: Include sys/mman.h.
* mach_dep.c [E2K] (GC_mmap_procedure_stack_buf,
GC_unmap_procedure_stack_buf): Implement.
* mark_rts.c [E2K && !THREADS] (GC_push_current_stack): Rename
PROCEDURE_STACK_ALLOCA_AND_STORE to GET_PROCEDURE_STACK_LOCAL; call
FREE_PROCEDURE_STACK_LOCAL() when bs_lo is not needed.
* pthread_stop_world.c [!GC_OPENBSD_UTHREADS && !NACL && E2K]
(GC_suspend_handler_inner, GC_push_all_stacks): Likewise.
-rw-r--r-- | include/private/gc_priv.h | 64 | ||||
-rw-r--r-- | mach_dep.c | 22 | ||||
-rw-r--r-- | mark_rts.c | 3 | ||||
-rw-r--r-- | pthread_stop_world.c | 12 |
4 files changed, 83 insertions, 18 deletions
diff --git a/include/private/gc_priv.h b/include/private/gc_priv.h index d274fcc4..80200259 100644 --- a/include/private/gc_priv.h +++ b/include/private/gc_priv.h @@ -1988,25 +1988,65 @@ GC_INNER void GC_with_callee_saves_pushed(void (*fn)(ptr_t, void *), /* May be called from a signal handler. */ GC_INNER size_t GC_get_procedure_stack(ptr_t, size_t); - /* Copy procedure (register) stack to a stack-allocated buffer. */ - /* The buffer is freed automatically on the function return. */ - /* May be used from a signal handler. */ -# define PROCEDURE_STACK_ALLOCA_AND_STORE(pbuf, psz) \ +# if defined(CPPCHECK) +# define PS_ALLOCA_BUF(sz) NULL +# define ALLOCA_SAFE_LIMIT 0 +# else +# define PS_ALLOCA_BUF(sz) alloca(sz) /* cannot return NULL */ +# ifndef ALLOCA_SAFE_LIMIT +# define ALLOCA_SAFE_LIMIT (GC_page_size-16U) +# endif +# endif /* !CPPCHECK */ + + /* Copy procedure (register) stack to a stack-allocated or */ + /* memory-mapped buffer. Usable from a signal handler. */ + /* FREE_PROCEDURE_STACK_LOCAL() must be called with the same */ + /* *pbuf and *psz values before the caller function returns */ + /* (thus, the buffer is valid only within the function). */ +# define GET_PROCEDURE_STACK_LOCAL(pbuf, psz) \ do { \ - size_t buf_sz = 0; \ - for (*(pbuf) = NULL; ; buf_sz = *(psz)) { \ - *(psz) = GC_get_procedure_stack(*(pbuf), buf_sz); \ - if (*(psz) <= buf_sz) break; \ - *(pbuf) = alloca(*(psz)); /* cannot return NULL */ \ + size_t capacity = 0; \ + GC_ASSERT(GC_page_size != 0); \ + for (*(pbuf) = NULL; ; capacity = *(psz)) { \ + *(psz) = GC_get_procedure_stack(*(pbuf), capacity); \ + if (*(psz) <= capacity) break; \ + if (*(psz) > ALLOCA_SAFE_LIMIT \ + || EXPECT(capacity != 0, FALSE)) { \ + /* Deallocate old buffer if any. */ \ + if (EXPECT(capacity > ALLOCA_SAFE_LIMIT, FALSE)) \ + GC_unmap_procedure_stack_buf(*(pbuf),capacity); \ + *(psz) = ROUNDUP_PAGESIZE(*(psz)); \ + *(pbuf) = GC_mmap_procedure_stack_buf(*(psz)); \ + } else { \ + /* Allocate buffer on the stack if not large. */ \ + *(pbuf) = PS_ALLOCA_BUF(*(psz)); \ + } \ + } \ + if (capacity > ALLOCA_SAFE_LIMIT \ + && EXPECT(((capacity - *(psz)) \ + & ~(GC_page_size-1)) != 0, FALSE)) { \ + /* Ensure sz value passed to munmap() later */ \ + /* matches that passed to mmap() above. */ \ + *(psz) = capacity - (GC_page_size - 1); \ } \ } while (0) + /* Indicate that the buffer with copied procedure stack is not needed. */ +# define FREE_PROCEDURE_STACK_LOCAL(buf, sz) \ + (void)((sz) > ALLOCA_SAFE_LIMIT \ + ? (GC_unmap_procedure_stack_buf(buf, sz), 0) : 0) + + GC_INNER ptr_t GC_mmap_procedure_stack_buf(size_t); + GC_INNER void GC_unmap_procedure_stack_buf(ptr_t, size_t); + # ifdef THREADS /* Allocate a buffer in the GC heap (as an atomic object) and copy */ /* procedure stack there. Assumes the GC allocation lock is held. */ - /* The buffer should be freed with GC_INTERNAL_FREE later when not */ - /* needed (or, alternatively, it could be just garbage-collected). */ - /* Similar to PROCEDURE_STACK_ALLOCA_AND_STORE in other aspects. */ + /* May trigger a collection (thus, cannot be used in GC_push_roots */ + /* or in a signal handler). The buffer should be freed with */ + /* GC_INTERNAL_FREE later when not needed (or, alternatively, it */ + /* could be just garbage-collected). */ + /* Similar to GET_PROCEDURE_STACK_LOCAL in other aspects. */ GC_INNER size_t GC_alloc_and_get_procedure_stack(ptr_t *pbuf); # endif #endif /* E2K */ @@ -28,9 +28,9 @@ #ifdef E2K # include <errno.h> -# include <sys/syscall.h> - # include <asm/e2k_syswork.h> +# include <sys/mman.h> +# include <sys/syscall.h> # define VA_SIZE 48 # define E2K_PSHTP_SIZE 12 @@ -103,6 +103,24 @@ return NULL; } + GC_INNER ptr_t GC_mmap_procedure_stack_buf(size_t aligned_sz) + { + void *buf = mmap(NULL, aligned_sz, PROT_READ | PROT_WRITE, + MAP_PRIVATE | MAP_ANON, 0 /* fd */, 0 /* offset */); + if (MAP_FAILED == buf) + ABORT_ARG2("Could not map memory for procedure stack", + ": requested %lu bytes, errno= %d", + (unsigned long)aligned_sz, errno); + return (ptr_t)buf; + } + + GC_INNER void GC_unmap_procedure_stack_buf(ptr_t buf, size_t sz) + { + if (munmap(buf, ROUNDUP_PAGESIZE(sz)) == -1) + ABORT_ARG1("munmap failed (for procedure stack space)", + ": errno= %d", errno); + } + # ifdef THREADS GC_INNER size_t GC_alloc_and_get_procedure_stack(ptr_t *pbuf) { @@ -840,10 +840,11 @@ STATIC void GC_push_current_stack(ptr_t cold_gc_frame, ptr_t bs_lo; size_t stack_size; - PROCEDURE_STACK_ALLOCA_AND_STORE(&bs_lo, &stack_size); + GET_PROCEDURE_STACK_LOCAL(&bs_lo, &stack_size); GC_push_all_register_sections(bs_lo, bs_lo + stack_size, TRUE /* eager */, GC_traced_stack_sect); + FREE_PROCEDURE_STACK_LOCAL(bs_lo, stack_size); } # endif # endif /* !THREADS */ diff --git a/pthread_stop_world.c b/pthread_stop_world.c index 83b70839..c7bbc2bd 100644 --- a/pthread_stop_world.c +++ b/pthread_stop_world.c @@ -340,7 +340,7 @@ STATIC void GC_suspend_handler_inner(ptr_t dummy GC_ATTR_UNUSED, if (ao_load_async(&me->suspended_ext)) { GC_store_stack_ptr(me); # ifdef E2K - PROCEDURE_STACK_ALLOCA_AND_STORE(&bs_lo, &stack_size); + GET_PROCEDURE_STACK_LOCAL(&bs_lo, &stack_size); me -> backing_store_end = bs_lo; me -> backing_store_ptr = bs_lo + stack_size; # endif @@ -350,6 +350,7 @@ STATIC void GC_suspend_handler_inner(ptr_t dummy GC_ATTR_UNUSED, GC_log_printf("Continuing %p on GC_resume_thread\n", (void *)self); # endif # ifdef E2K + FREE_PROCEDURE_STACK_LOCAL(bs_lo, stack_size); me -> backing_store_ptr = NULL; me -> backing_store_end = NULL; # endif @@ -369,7 +370,7 @@ STATIC void GC_suspend_handler_inner(ptr_t dummy GC_ATTR_UNUSED, } GC_store_stack_ptr(me); # ifdef E2K - PROCEDURE_STACK_ALLOCA_AND_STORE(&bs_lo, &stack_size); + GET_PROCEDURE_STACK_LOCAL(&bs_lo, &stack_size); me -> backing_store_end = bs_lo; me -> backing_store_ptr = bs_lo + stack_size; # endif @@ -414,6 +415,7 @@ STATIC void GC_suspend_handler_inner(ptr_t dummy GC_ATTR_UNUSED, # endif # ifdef E2K GC_ASSERT(me -> backing_store_end == bs_lo); + FREE_PROCEDURE_STACK_LOCAL(bs_lo, stack_size); me -> backing_store_ptr = NULL; me -> backing_store_end = NULL; # endif @@ -757,7 +759,7 @@ GC_INNER void GC_push_all_stacks(void) # elif defined(E2K) GC_ASSERT(NULL == p -> backing_store_end); (void)GC_save_regs_in_stack(); - PROCEDURE_STACK_ALLOCA_AND_STORE(&bs_lo, &stack_size); + GET_PROCEDURE_STACK_LOCAL(&bs_lo, &stack_size); bs_hi = bs_lo + stack_size; # endif # endif @@ -834,6 +836,10 @@ GC_INNER void GC_push_all_stacks(void) traced_stack_sect); total_size += bs_hi - bs_lo; /* bs_lo <= bs_hi */ # endif +# ifdef E2K + if (THREAD_EQUAL(p -> id, self)) + FREE_PROCEDURE_STACK_LOCAL(bs_lo, (size_t)(bs_hi - bs_lo)); +# endif } } GC_VERBOSE_LOG_PRINTF("Pushed %d thread stacks\n", (int)nthreads); |