Skip to content

Commit

Permalink
Use mmap to allocate procedure stack buffers on E2K
Browse files Browse the repository at this point in the history
(fix of commit 990dcdb)

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.
  • Loading branch information
ivmai committed Feb 3, 2022
1 parent 6c8923e commit 73b67ba
Show file tree
Hide file tree
Showing 4 changed files with 83 additions and 18 deletions.
64 changes: 52 additions & 12 deletions include/private/gc_priv.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 */
Expand Down
22 changes: 20 additions & 2 deletions mach_dep.c
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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)
{
Expand Down
3 changes: 2 additions & 1 deletion mark_rts.c
Original file line number Diff line number Diff line change
Expand Up @@ -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 */
Expand Down
12 changes: 9 additions & 3 deletions pthread_stop_world.c
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand All @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -833,6 +835,10 @@ GC_INNER void GC_push_all_stacks(void)
THREAD_EQUAL(p -> id, self),
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
}
}
Expand Down

0 comments on commit 73b67ba

Please sign in to comment.