Skip to content

Commit 13cad31

Browse files
committed
Updates to find and test stack overflow.
1 parent 4aacd68 commit 13cad31

6 files changed

Lines changed: 273 additions & 99 deletions

File tree

CMakeLists.txt

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,17 @@
77
cmake_minimum_required(VERSION 3.10)
88

99
# Read version from file
10-
file(READ "VERSION" PROJECT_VERSION_FULL)
11-
string(STRIP "${PROJECT_VERSION_FULL}" PROJECT_VERSION_FULL)
10+
set(PROJECT_VERSION_FULL "0.0.0-0")
11+
if(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/VERSION")
12+
file(READ "${CMAKE_CURRENT_SOURCE_DIR}/VERSION" VER_FILE_CONTENT)
13+
if(VER_FILE_CONTENT)
14+
string(STRIP "${VER_FILE_CONTENT}" VER_FILE_CONTENT_STRIPPED)
15+
if(VER_FILE_CONTENT_STRIPPED)
16+
set(PROJECT_VERSION_FULL "${VER_FILE_CONTENT_STRIPPED}")
17+
endif()
18+
endif()
19+
endif()
20+
1221
# CMake project version must be numeric (major.minor.patch.tweak)
1322
string(REPLACE "-" "." PROJECT_VERSION_CMAKE "${PROJECT_VERSION_FULL}")
1423
project(fcontext VERSION ${PROJECT_VERSION_CMAKE} LANGUAGES C)
@@ -195,6 +204,10 @@ set_target_properties(fcontext PROPERTIES VERSION ${PROJECT_VERSION} SOVERSION $
195204
foreach(test ${TESTS})
196205
add_executable(${test} tests/${test}.c)
197206
target_link_libraries(${test} PRIVATE fcontext)
207+
# Link dl library for test_fcontext_guard on Linux/Unix for dladdr
208+
if("${test}" STREQUAL "test_fcontext_guard" AND UNIX)
209+
target_link_libraries(${test} PRIVATE ${CMAKE_DL_LIBS})
210+
endif()
198211
endforeach()
199212

200213
# ============================================================================
@@ -212,7 +225,7 @@ foreach(test ${TESTS})
212225
endforeach()
213226

214227
# test_fcontext_guard is expected to crash (segfault) due to stack overflow
215-
set_tests_properties(test_fcontext_guard PROPERTIES WILL_FAIL TRUE)
228+
# set_tests_properties(test_fcontext_guard PROPERTIES WILL_FAIL TRUE)
216229

217230
# ============================================================================
218231
# Summary

README.md

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -423,6 +423,34 @@ This is much more efficient than pre-allocating large stacks for many fibers.
423423
424424
5. **Debugging** - Stack overflow in a fiber shows as segmentation fault, which is intentional
425425
426+
6. **Signal Handling** - To catch the `SIGSEGV` or `SIGBUS` generated by a stack overflow (guard page hit), you must register your signal handler with `SA_ONSTACK` and provide an alternate signal stack using `sigaltstack()`. Without this, the signal handler cannot run because the fiber's stack is invalid or exhausted.
427+
428+
```c
429+
#include <signal.h>
430+
431+
void setup_signal_handling() {
432+
/* 1. Allocate alternate stack */
433+
stack_t ss;
434+
ss.ss_sp = malloc(SIGSTKSZ);
435+
ss.ss_size = SIGSTKSZ;
436+
ss.ss_flags = 0;
437+
if (sigaltstack(&ss, NULL) == -1) {
438+
perror("sigaltstack");
439+
exit(1);
440+
}
441+
442+
/* 2. Register handler with SA_ONSTACK */
443+
struct sigaction sa;
444+
memset(&sa, 0, sizeof(sa));
445+
sa.sa_handler = my_signal_handler;
446+
sa.sa_flags = SA_ONSTACK; /* Run on alternate stack */
447+
sigemptyset(&sa.sa_mask);
448+
449+
sigaction(SIGSEGV, &sa, NULL);
450+
sigaction(SIGBUS, &sa, NULL);
451+
}
452+
```
453+
426454
## License
427455

428456
Derived from Boost.Context and DaoWen/fcontext, distributed under the Boost Software License 1.0.

VERSION

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
1.81.0-0

include/fcontext.h

Lines changed: 22 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -32,12 +32,12 @@
3232
#include <stdlib.h>
3333

3434
#ifndef _WIN32
35-
#include <unistd.h>
36-
#include <sys/mman.h>
35+
#include <sys/mman.h>
36+
#include <unistd.h>
3737
#endif
3838

3939
#ifdef __SANITIZE_ADDRESS__
40-
#include <sanitizer/asan_interface.h>
40+
#include <sanitizer/asan_interface.h>
4141
#endif
4242

4343
#ifdef __cplusplus
@@ -50,12 +50,12 @@ extern "C" {
5050

5151
/* Default stack size for new contexts (24KB) */
5252
#ifndef FCONTEXT_DEFAULT_STACK_SIZE
53-
#define FCONTEXT_DEFAULT_STACK_SIZE (24 * 1024)
53+
#define FCONTEXT_DEFAULT_STACK_SIZE (24 * 1024)
5454
#endif
5555

5656
/* Enable stack watermark checking for high water mark detection */
5757
#ifndef FCONTEXT_ENABLE_STACK_WATERMARK
58-
#define FCONTEXT_ENABLE_STACK_WATERMARK 1
58+
#define FCONTEXT_ENABLE_STACK_WATERMARK 1
5959
#endif
6060

6161
/* Stack watermark fill pattern (0xA5 = 10100101) */
@@ -79,8 +79,8 @@ typedef struct fcontext_opaque_t *fcontext_t;
7979
* Contains the previous context pointer and optional user data.
8080
*/
8181
typedef struct {
82-
fcontext_t prev_context; /* Context we switched away from */
83-
void *data; /* User data passed on switch */
82+
fcontext_t prev_context; /* Context we switched away from */
83+
void *data; /* User data passed on switch */
8484
} fcontext_transfer_t;
8585

8686
/**
@@ -147,8 +147,7 @@ extern fcontext_transfer_t jump_fcontext(fcontext_t const to, void *vp);
147147
* Advanced feature: fn will be called with the context transfer and can
148148
* modify the return value before control passes back.
149149
*/
150-
extern fcontext_transfer_t ontop_fcontext(fcontext_t const to, void *vp,
151-
fcontext_ontop_fn_t fn);
150+
extern fcontext_transfer_t ontop_fcontext(fcontext_t const to, void *vp, fcontext_ontop_fn_t fn);
152151

153152
/* ========================================================================
154153
* Page Size and Stack Alignment Utilities
@@ -163,12 +162,10 @@ extern fcontext_transfer_t ontop_fcontext(fcontext_t const to, void *vp,
163162
*/
164163
static inline size_t fcontext_get_page_size(void) {
165164
#ifdef _WIN32
166-
return 4096; /* Standard page size on Windows */
165+
return 4096; /* Standard page size on Windows */
167166
#else
168167
long page_size = sysconf(_SC_PAGE_SIZE);
169-
if (page_size <= 0) {
170-
return FCONTEXT_DEFAULT_STACK_SIZE;
171-
}
168+
if(page_size <= 0) { return FCONTEXT_DEFAULT_STACK_SIZE; }
172169
return (size_t)page_size;
173170
#endif
174171
}
@@ -199,9 +196,9 @@ static inline size_t fcontext_align_to_page(size_t size) {
199196
* Returns:
200197
* Aligned pointer (rounded down to 16-byte boundary)
201198
*/
202-
static inline void* fcontext_align_stack_pointer(void* ptr) {
199+
static inline void *fcontext_align_stack_pointer(void *ptr) {
203200
uintptr_t addr = (uintptr_t)ptr;
204-
return (void*)(addr & ~(FCONTEXT_STACK_ALIGNMENT - 1));
201+
return (void *)(addr & ~(FCONTEXT_STACK_ALIGNMENT - 1));
205202
}
206203

207204
/* ========================================================================
@@ -219,19 +216,16 @@ static inline void* fcontext_align_stack_pointer(void* ptr) {
219216
*
220217
* This catches stack overflow/underflow while using minimal memory.
221218
*/
222-
typedef enum {
223-
FCONTEXT_ALLOC_MMAP,
224-
FCONTEXT_ALLOC_MALLOC
225-
} fcontext_alloc_type_t;
219+
typedef enum { FCONTEXT_ALLOC_MMAP, FCONTEXT_ALLOC_MALLOC } fcontext_alloc_type_t;
226220

227221
typedef struct {
228222
fcontext_t context;
229223
fcontext_alloc_type_t alloc_type;
230-
void *alloc_base; /* Base of allocated region (mmap or malloc) */
231-
void *stack_base; /* Base of actual stack (after bottom guard page) */
232-
size_t alloc_size; /* Total size of allocated region */
233-
size_t stack_size; /* Size of actual stack (excludes guard pages) */
234-
size_t guard_size; /* Size of guard pages/zones */
224+
void *alloc_base; /* Base of allocated region (mmap or malloc) */
225+
void *stack_base; /* Base of actual stack (after bottom guard page) */
226+
size_t alloc_size; /* Total size of allocated region */
227+
size_t stack_size; /* Size of actual stack (excludes guard pages) */
228+
size_t guard_size; /* Size of guard pages/zones */
235229
} fcontext_stack_t;
236230

237231
/**
@@ -254,10 +248,10 @@ typedef struct {
254248
* - Stack is page-aligned for system page size (4KB on Linux, 16KB on macOS ARM)
255249
* - Uses mmap for efficient guard page implementation
256250
* - Guard pages catch overflow/underflow with segmentation fault
251+
* - To catch this signal, use sigaltstack() and SA_ONSTACK, as the fiber stack will be invalid
257252
* - Use fcontext_destroy() to clean up
258253
*/
259-
extern fcontext_stack_t *fcontext_create(size_t stack_size,
260-
fcontext_fn_t entry_fn);
254+
extern fcontext_stack_t *fcontext_create(size_t stack_size, fcontext_fn_t entry_fn);
261255

262256
/**
263257
* Create a new context using malloc with software guard zones.
@@ -271,8 +265,7 @@ extern fcontext_stack_t *fcontext_create(size_t stack_size,
271265
* guard_size - Size of guard zones (canaries)
272266
* entry_fn - Entry point
273267
*/
274-
extern fcontext_stack_t *fcontext_create_malloc(size_t stack_size, size_t guard_size,
275-
fcontext_fn_t entry_fn);
268+
extern fcontext_stack_t *fcontext_create_malloc(size_t stack_size, size_t guard_size, fcontext_fn_t entry_fn);
276269

277270
/**
278271
* Create a new context using mmap/VirtualAlloc with hardware guard pages.
@@ -284,8 +277,7 @@ extern fcontext_stack_t *fcontext_create_malloc(size_t stack_size, size_t guard_
284277
* nearest system page size.
285278
* entry_fn - Entry point
286279
*/
287-
extern fcontext_stack_t *fcontext_create_mmap(size_t stack_size, size_t guard_size,
288-
fcontext_fn_t entry_fn);
280+
extern fcontext_stack_t *fcontext_create_mmap(size_t stack_size, size_t guard_size, fcontext_fn_t entry_fn);
289281

290282
/**
291283
* Destroy a context created with fcontext_create().

0 commit comments

Comments
 (0)