mirror of
https://github.com/ossrs/srs.git
synced 2025-03-09 15:49:59 +00:00
ASAN: Support coroutine context switching and stack tracing (#4153)
For coroutine, we should use `__sanitizer_start_switch_fiber` which similar to`VALGRIND_STACK_REGISTER`, see https://github.com/google/sanitizers/issues/189#issuecomment-1346243598 for details. If not fix this, asan will output warning: ``` ==72269==WARNING: ASan is ignoring requested __asan_handle_no_return: stack type: default top: 0x00016f638000; bottom 0x000106bec000; size: 0x000068a4c000 (1755627520) False positive error reports may follow For details see https://github.com/google/sanitizers/issues/189 ``` It will cause asan failed to get the stack, see `research/st/asan-switch.cpp` for example: ``` ==71611==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x000103600733 at pc 0x0001009d3d7c bp 0x000100b4bd40 sp 0x000100b4bd38 WRITE of size 1 at 0x000103600733 thread T0 #0 0x1009d3d78 in foo(void*) asan-switch.cpp:13 ``` After fix this issue, it should provide the full stack when crashing: ``` ==73437==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x000103300733 at pc 0x000100693d7c bp 0x00016f76f550 sp 0x00016f76f548 WRITE of size 1 at 0x000103300733 thread T0 #0 0x100693d78 in foo(void*) asan-switch.cpp:13 #1 0x100693df4 in main asan-switch.cpp:23 #2 0x195aa20dc (<unknown module>) ``` For primordial coroutine, if not set the stack by `st_set_primordial_stack`, then the stack is NULL and asan can't get the stack tracing. Note that it's optional and only make it fail to display the stack information, no other errors. --- Co-authored-by: john <hondaxiao@tencent.com>
This commit is contained in:
parent
55610cf689
commit
8f48a0e2d1
15 changed files with 236 additions and 15 deletions
76
trunk/3rdparty/st-srs/common.h
vendored
76
trunk/3rdparty/st-srs/common.h
vendored
|
@ -162,6 +162,10 @@ struct _st_thread {
|
|||
_st_clist_t tlink; /* For putting on thread queue */
|
||||
#endif
|
||||
|
||||
#ifdef MD_ASAN
|
||||
void *fake_stack; /* Fake stack for ASAN */
|
||||
#endif
|
||||
|
||||
st_utime_t due; /* Wakeup time when thread is sleeping */
|
||||
_st_thread_t *left; /* For putting in timeout heap */
|
||||
_st_thread_t *right; /* -- see docs/timeout_heap.txt for details */
|
||||
|
@ -314,7 +318,7 @@ extern __thread _st_eventsys_t *_st_eventsys;
|
|||
* Forward declarations
|
||||
*/
|
||||
|
||||
void _st_vp_schedule(void);
|
||||
void _st_vp_schedule(_st_thread_t *from);
|
||||
void _st_vp_check_clock(void);
|
||||
void *_st_idle_thread_start(void *arg);
|
||||
void _st_thread_main(void);
|
||||
|
@ -365,6 +369,64 @@ _st_thread_t *st_thread_create(void *(*start)(void *arg), void *arg, int joinabl
|
|||
#define ST_SWITCH_IN_CB(_thread)
|
||||
#endif
|
||||
|
||||
#ifdef MD_ASAN
|
||||
/*
|
||||
* Fiber annotation interface.
|
||||
*
|
||||
* Before switching to a different stack, one must call
|
||||
* __sanitizer_start_switch_fiber with a pointer to the bottom of the
|
||||
* destination stack and its size. When code starts running on the new stack,
|
||||
* it must call __sanitizer_finish_switch_fiber to finalize the switch.
|
||||
* The start_switch function takes a void** to store the current fake stack if
|
||||
* there is one (it is needed when detect_stack_use_after_return is enabled).
|
||||
* When restoring a stack, this pointer must be given to the finish_switch
|
||||
* function. In most cases, this void* can be stored on the stack just before
|
||||
* switching. When leaving a fiber definitely, null must be passed as first
|
||||
* argument to the start_switch function so that the fake stack is destroyed.
|
||||
* If you do not want support for stack use-after-return detection, you can
|
||||
* always pass null to these two functions.
|
||||
* Note that the fake stack mechanism is disabled during fiber switch, so if a
|
||||
* signal callback runs during the switch, it will not benefit from the stack
|
||||
* use-after-return detection.
|
||||
*
|
||||
* See https://github.com/google/sanitizers/issues/189#issuecomment-1346243598
|
||||
*/
|
||||
extern void __sanitizer_start_switch_fiber(void **fake_stack_save,
|
||||
const void *bottom, size_t size);
|
||||
|
||||
extern void __sanitizer_finish_switch_fiber(void *fake_stack_save,
|
||||
const void **bottom_old,
|
||||
size_t *size_old);
|
||||
|
||||
/* The stack for primoridal thread. */
|
||||
extern void *_st_primordial_stack_bottom;
|
||||
extern size_t _st_primordial_stack_size;
|
||||
|
||||
static inline void _st_asan_start_switch(_st_thread_t *from, _st_thread_t *thread)
|
||||
{
|
||||
/* For primordial thread, the stack is NULL, so asan can not capture it. */
|
||||
const void *stk_bottom = thread->stack ? thread->stack->stk_bottom : NULL;
|
||||
size_t stk_size = thread->stack ? thread->stack->stk_size : 0;
|
||||
|
||||
/* For primordial thread, user should setup the stack information. */
|
||||
if (!stk_bottom && (thread->flags & _ST_FL_PRIMORDIAL)) {
|
||||
stk_bottom = _st_primordial_stack_bottom;
|
||||
stk_size = _st_primordial_stack_size;
|
||||
}
|
||||
|
||||
/*
|
||||
* Save the current stack to fake_stack of from, tell asan the target stack
|
||||
* we are targeting to switch to.
|
||||
*/
|
||||
__sanitizer_start_switch_fiber(&from->fake_stack, stk_bottom, stk_size);
|
||||
}
|
||||
|
||||
static inline void _st_asan_finish_switch(_st_thread_t *thread)
|
||||
{
|
||||
__sanitizer_finish_switch_fiber(thread->fake_stack, NULL, NULL);
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Switch away from the current thread context by saving its state and
|
||||
* calling the thread scheduler
|
||||
|
@ -374,9 +436,14 @@ static inline void _st_switch_context(_st_thread_t *thread)
|
|||
ST_SWITCH_OUT_CB(thread);
|
||||
|
||||
if (!_st_md_cxt_save(thread->context)) {
|
||||
_st_vp_schedule();
|
||||
_st_vp_schedule(thread);
|
||||
}
|
||||
|
||||
#ifdef MD_ASAN
|
||||
/* Switch from other thread to this running thread. */
|
||||
_st_asan_finish_switch(thread);
|
||||
#endif
|
||||
|
||||
ST_DEBUG_ITERATE_THREADS();
|
||||
ST_SWITCH_IN_CB(thread);
|
||||
}
|
||||
|
@ -385,8 +452,11 @@ static inline void _st_switch_context(_st_thread_t *thread)
|
|||
* Restore a thread context that was saved by _st_switch_context or
|
||||
* initialized by _ST_INIT_CONTEXT
|
||||
*/
|
||||
static inline void _st_restore_context(_st_thread_t *thread)
|
||||
static inline void _st_restore_context(_st_thread_t *from, _st_thread_t *thread)
|
||||
{
|
||||
#ifdef MD_ASAN
|
||||
_st_asan_start_switch(from, thread);
|
||||
#endif
|
||||
_st_this_thread = thread;
|
||||
_st_md_cxt_restore(thread->context, 1);
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue