X-Git-Url: https://git.proxmox.com/?a=blobdiff_plain;f=util%2Fcoroutine-ucontext.c;h=904b375192cab6d24c2bdea46d8c53beedb9b296;hb=1af26ce67082727aeb109708f2c75d5fc5364e23;hp=bd593e61bc0d743df7dbd3bd8b8ba228e3a97016;hpb=a8d2532645cf5ce4f75981f81dfe363efc35d05c;p=mirror_qemu.git diff --git a/util/coroutine-ucontext.c b/util/coroutine-ucontext.c index bd593e61bc..904b375192 100644 --- a/util/coroutine-ucontext.c +++ b/util/coroutine-ucontext.c @@ -37,12 +37,26 @@ #endif #endif +#ifdef CONFIG_TSAN +#include +#endif + typedef struct { Coroutine base; void *stack; size_t stack_size; +#ifdef CONFIG_SAFESTACK + /* Need an unsafe stack for each coroutine */ + void *unsafe_stack; + size_t unsafe_stack_size; +#endif sigjmp_buf env; +#ifdef CONFIG_TSAN + void *tsan_co_fiber; + void *tsan_caller_fiber; +#endif + #ifdef CONFIG_VALGRIND_H unsigned int valgrind_stack_id; #endif @@ -65,7 +79,22 @@ union cc_arg { int i[2]; }; -static void finish_switch_fiber(void *fake_stack_save) +/* + * QEMU_ALWAYS_INLINE only does so if __OPTIMIZE__, so we cannot use it. + * always_inline is required to avoid TSan runtime fatal errors. + */ +static inline __attribute__((always_inline)) +void on_new_fiber(CoroutineUContext *co) +{ +#ifdef CONFIG_TSAN + co->tsan_co_fiber = __tsan_create_fiber(0); /* flags: sync on switch */ + co->tsan_caller_fiber = __tsan_get_current_fiber(); +#endif +} + +/* always_inline is required to avoid TSan runtime fatal errors. */ +static inline __attribute__((always_inline)) +void finish_switch_fiber(void *fake_stack_save) { #ifdef CONFIG_ASAN const void *bottom_old; @@ -78,13 +107,41 @@ static void finish_switch_fiber(void *fake_stack_save) leader.stack_size = size_old; } #endif +#ifdef CONFIG_TSAN + if (fake_stack_save) { + __tsan_release(fake_stack_save); + __tsan_switch_to_fiber(fake_stack_save, 0); /* 0=synchronize */ + } +#endif } -static void start_switch_fiber(void **fake_stack_save, - const void *bottom, size_t size) +/* always_inline is required to avoid TSan runtime fatal errors. */ +static inline __attribute__((always_inline)) +void start_switch_fiber_asan(CoroutineAction action, void **fake_stack_save, + const void *bottom, size_t size) { #ifdef CONFIG_ASAN - __sanitizer_start_switch_fiber(fake_stack_save, bottom, size); + __sanitizer_start_switch_fiber( + action == COROUTINE_TERMINATE ? NULL : fake_stack_save, + bottom, size); +#endif +} + +/* always_inline is required to avoid TSan runtime fatal errors. */ +static inline __attribute__((always_inline)) +void start_switch_fiber_tsan(void **fake_stack_save, + CoroutineUContext *co, + bool caller) +{ +#ifdef CONFIG_TSAN + void *new_fiber = caller ? + co->tsan_caller_fiber : + co->tsan_co_fiber; + void *curr_fiber = __tsan_get_current_fiber(); + __tsan_acquire(curr_fiber); + + *fake_stack_save = curr_fiber; + __tsan_switch_to_fiber(new_fiber, 0); /* 0=synchronize */ #endif } @@ -104,8 +161,9 @@ static void coroutine_trampoline(int i0, int i1) /* Initialize longjmp environment and switch back the caller */ if (!sigsetjmp(self->env, 0)) { - start_switch_fiber(&fake_stack_save, - leader.stack, leader.stack_size); + start_switch_fiber_asan(COROUTINE_YIELD, &fake_stack_save, leader.stack, + leader.stack_size); + start_switch_fiber_tsan(&fake_stack_save, self, true); /* true=caller */ siglongjmp(*(sigjmp_buf *)co->entry_arg, 1); } @@ -140,6 +198,10 @@ Coroutine *qemu_coroutine_new(void) co = g_malloc0(sizeof(*co)); co->stack_size = COROUTINE_STACK_SIZE; co->stack = qemu_alloc_stack(&co->stack_size); +#ifdef CONFIG_SAFESTACK + co->unsafe_stack_size = COROUTINE_STACK_SIZE; + co->unsafe_stack = qemu_alloc_stack(&co->unsafe_stack_size); +#endif co->base.entry_arg = &old_env; /* stash away our jmp_buf */ uc.uc_link = &old_uc; @@ -154,12 +216,32 @@ Coroutine *qemu_coroutine_new(void) arg.p = co; + on_new_fiber(co); makecontext(&uc, (void (*)(void))coroutine_trampoline, 2, arg.i[0], arg.i[1]); /* swapcontext() in, siglongjmp() back out */ if (!sigsetjmp(old_env, 0)) { - start_switch_fiber(&fake_stack_save, co->stack, co->stack_size); + start_switch_fiber_asan(COROUTINE_YIELD, &fake_stack_save, co->stack, + co->stack_size); + start_switch_fiber_tsan(&fake_stack_save, + co, false); /* false=not caller */ + +#ifdef CONFIG_SAFESTACK + /* + * Before we swap the context, set the new unsafe stack + * The unsafe stack grows just like the normal stack, so start from + * the last usable location of the memory area. + * NOTE: we don't have to re-set the usp afterwards because we are + * coming back to this context through a siglongjmp. + * The compiler already wrapped the corresponding sigsetjmp call with + * code that saves the usp on the (safe) stack before the call, and + * restores it right after (which is where we return with siglongjmp). + */ + void *usp = co->unsafe_stack + co->unsafe_stack_size; + __safestack_unsafe_stack_ptr = usp; +#endif + swapcontext(&old_uc, &uc); } @@ -169,8 +251,8 @@ Coroutine *qemu_coroutine_new(void) } #ifdef CONFIG_VALGRIND_H -#if defined(CONFIG_PRAGMA_DIAGNOSTIC_AVAILABLE) && !defined(__clang__) /* Work around an unused variable in the valgrind.h macro... */ +#if !defined(__clang__) #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wunused-but-set-variable" #endif @@ -178,7 +260,7 @@ static inline void valgrind_stack_deregister(CoroutineUContext *co) { VALGRIND_STACK_DEREGISTER(co->valgrind_stack_id); } -#if defined(CONFIG_PRAGMA_DIAGNOSTIC_AVAILABLE) && !defined(__clang__) +#if !defined(__clang__) #pragma GCC diagnostic pop #endif #endif @@ -192,6 +274,9 @@ void qemu_coroutine_delete(Coroutine *co_) #endif qemu_free_stack(co->stack, co->stack_size); +#ifdef CONFIG_SAFESTACK + qemu_free_stack(co->unsafe_stack, co->unsafe_stack_size); +#endif g_free(co); } @@ -216,8 +301,10 @@ qemu_coroutine_switch(Coroutine *from_, Coroutine *to_, ret = sigsetjmp(from->env, 0); if (ret == 0) { - start_switch_fiber(action == COROUTINE_TERMINATE ? - NULL : &fake_stack_save, to->stack, to->stack_size); + start_switch_fiber_asan(action, &fake_stack_save, to->stack, + to->stack_size); + start_switch_fiber_tsan(&fake_stack_save, + to, false); /* false=not caller */ siglongjmp(to->env, action); } @@ -231,6 +318,11 @@ Coroutine *qemu_coroutine_self(void) if (!current) { current = &leader.base; } +#ifdef CONFIG_TSAN + if (!leader.tsan_co_fiber) { + leader.tsan_co_fiber = __tsan_get_current_fiber(); + } +#endif return current; }