From cea46522e79637f6ec37c03ec3fbeb87b160a378 Mon Sep 17 00:00:00 2001 From: Damjan Marion Date: Thu, 21 May 2020 16:47:05 +0200 Subject: [PATCH] vlib: address sanitizer support for stack switch, enable clang Type: improvement Change-Id: I81df4b61d1f0b8c1df77c1ee9bebcb491e155b69 Signed-off-by: Damjan Marion --- docs/troubleshooting/sanitizer.rst | 10 +++---- src/CMakeLists.txt | 2 +- src/vlib/main.c | 23 +++++++++++---- src/vlib/main.h | 4 +++ src/vlib/node_funcs.h | 59 ++++++++++++++++++++++++++++++++++---- src/vlib/threads.c | 3 ++ src/vlib/unix/main.c | 3 ++ src/vlib/unix/plugin.h | 1 + 8 files changed, 88 insertions(+), 17 deletions(-) diff --git a/docs/troubleshooting/sanitizer.rst b/docs/troubleshooting/sanitizer.rst index 715f1b3ecd4..217f5e57182 100644 --- a/docs/troubleshooting/sanitizer.rst +++ b/docs/troubleshooting/sanitizer.rst @@ -6,7 +6,7 @@ Google Sanitizers VPP is instrumented to support `Google Sanitizers `_. As of today, only `AddressSanitizer `_ -is supported, only for GCC and only for the heap. +is supported, both for GCC and clang. AddressSanitizer ================ @@ -20,19 +20,19 @@ build option, so all VPP targets should be supported. For example: .. code-block:: console # build a debug image with ASan support: - $ make rebuild VPP_EXTRA_CMAKE_ARGS=-DVPP_ENABLE_SANITIZE_ADDR=ON CC=gcc-8 + $ make rebuild VPP_EXTRA_CMAKE_ARGS=-DVPP_ENABLE_SANITIZE_ADDR=ON .... # build a release image with ASan support: - $ make rebuild-release VPP_EXTRA_CMAKE_ARGS=-DVPP_ENABLE_SANITIZE_ADDR=ON CC=gcc-8 + $ make rebuild-release VPP_EXTRA_CMAKE_ARGS=-DVPP_ENABLE_SANITIZE_ADDR=ON .... # build packages in debug mode with ASan support: - $ make pkg-deb-debug VPP_EXTRA_CMAKE_ARGS=-DVPP_ENABLE_SANITIZE_ADDR=ON CC=gcc-8 + $ make pkg-deb-debug VPP_EXTRA_CMAKE_ARGS=-DVPP_ENABLE_SANITIZE_ADDR=ON .... # run GBP plugin tests in debug mode with ASan - $ make test-debug TEST=test_gbp VPP_EXTRA_CMAKE_ARGS=-DVPP_ENABLE_SANITIZE_ADDR=ON CC=gcc-8 + $ make test-debug TEST=test_gbp VPP_EXTRA_CMAKE_ARGS=-DVPP_ENABLE_SANITIZE_ADDR=ON .... Once VPP has been built with ASan support you can use it as usual including diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 133876c86c9..499a39fbad4 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -117,7 +117,7 @@ set(VPP_SANITIZE_ADDR_OPTIONS ) if (VPP_ENABLE_SANITIZE_ADDR) - set(CMAKE_C_FLAGS "-fsanitize=address --param asan-stack=0 -DCLIB_SANITIZE_ADDR ${CMAKE_C_FLAGS}") + set(CMAKE_C_FLAGS "-fsanitize=address -DCLIB_SANITIZE_ADDR ${CMAKE_C_FLAGS}") set(CMAKE_EXE_LINKER_FLAGS "-fsanitize=address ${CMAKE_EXE_LINKER_FLAGS}") set(CMAKE_SHARED_LINKER_FLAGS "-fsanitize=address ${CMAKE_SHARED_LINKER_FLAGS}") endif (VPP_ENABLE_SANITIZE_ADDR) diff --git a/src/vlib/main.c b/src/vlib/main.c index 2e100b24df7..2c397a29672 100644 --- a/src/vlib/main.c +++ b/src/vlib/main.c @@ -1496,6 +1496,8 @@ vlib_process_bootstrap (uword _a) vm = a->vm; p = a->process; + vlib_process_finish_switch_stack (vm); + f = a->frame; node = &p->node_runtime; @@ -1503,6 +1505,7 @@ vlib_process_bootstrap (uword _a) ASSERT (vlib_process_stack_is_valid (p)); + vlib_process_start_switch_stack (vm, 0); clib_longjmp (&p->return_longjmp, n); return n; @@ -1521,14 +1524,19 @@ vlib_process_startup (vlib_main_t * vm, vlib_process_t * p, vlib_frame_t * f) r = clib_setjmp (&p->return_longjmp, VLIB_PROCESS_RETURN_LONGJMP_RETURN); if (r == VLIB_PROCESS_RETURN_LONGJMP_RETURN) - r = clib_calljmp (vlib_process_bootstrap, pointer_to_uword (&a), - (void *) p->stack + (1 << p->log2_n_stack_bytes)); + { + vlib_process_start_switch_stack (vm, p); + r = clib_calljmp (vlib_process_bootstrap, pointer_to_uword (&a), + (void *) p->stack + (1 << p->log2_n_stack_bytes)); + } + else + vlib_process_finish_switch_stack (vm); return r; } static_always_inline uword -vlib_process_resume (vlib_process_t * p) +vlib_process_resume (vlib_main_t * vm, vlib_process_t * p) { uword r; p->flags &= ~(VLIB_PROCESS_IS_SUSPENDED_WAITING_FOR_CLOCK @@ -1536,7 +1544,12 @@ vlib_process_resume (vlib_process_t * p) | VLIB_PROCESS_RESUME_PENDING); r = clib_setjmp (&p->return_longjmp, VLIB_PROCESS_RETURN_LONGJMP_RETURN); if (r == VLIB_PROCESS_RETURN_LONGJMP_RETURN) - clib_longjmp (&p->resume_longjmp, VLIB_PROCESS_RESUME_LONGJMP_RESUME); + { + vlib_process_start_switch_stack (vm, p); + clib_longjmp (&p->resume_longjmp, VLIB_PROCESS_RESUME_LONGJMP_RESUME); + } + else + vlib_process_finish_switch_stack (vm); return r; } @@ -1655,7 +1668,7 @@ dispatch_suspended_process (vlib_main_t * vm, /* Save away current process for suspend. */ nm->current_process_index = node->runtime_index; - n_vectors = vlib_process_resume (p); + n_vectors = vlib_process_resume (vm, p); t = clib_cpu_time_now (); nm->current_process_index = ~0; diff --git a/src/vlib/main.h b/src/vlib/main.h index d6b2d1f875f..2e070aa6d64 100644 --- a/src/vlib/main.h +++ b/src/vlib/main.h @@ -280,6 +280,10 @@ typedef struct vlib_main_t u32 buffer_alloc_success_seed; f64 buffer_alloc_success_rate; +#ifdef CLIB_SANITIZE_ADDR + /* address sanitizer stack save */ + void *asan_stack_save; +#endif } vlib_main_t; /* Global main structure. */ diff --git a/src/vlib/node_funcs.h b/src/vlib/node_funcs.h index 263017d0ec7..b607ef252bd 100644 --- a/src/vlib/node_funcs.h +++ b/src/vlib/node_funcs.h @@ -48,6 +48,32 @@ #include #include +#ifdef CLIB_SANITIZE_ADDR +#include +#endif + +static_always_inline void +vlib_process_start_switch_stack (vlib_main_t * vm, vlib_process_t * p) +{ +#ifdef CLIB_SANITIZE_ADDR + void *stack = p ? (void *) p->stack : vlib_thread_stacks[vm->thread_index]; + u32 stack_bytes = p ? p->log2_n_stack_bytes : VLIB_THREAD_STACK_SIZE; + __sanitizer_start_switch_fiber (&vm->asan_stack_save, stack, stack_bytes); +#endif +} + +static_always_inline void +vlib_process_finish_switch_stack (vlib_main_t * vm) +{ +#ifdef CLIB_SANITIZE_ADDR + const void *bottom_old; + size_t size_old; + + __sanitizer_finish_switch_fiber (&vm->asan_stack_save, &bottom_old, + &size_old); +#endif +} + /** \brief Get vlib node by index. @warning This function will ASSERT if @c i is out of range. @param vm vlib_main_t pointer, varies by thread @@ -456,8 +482,11 @@ vlib_process_suspend (vlib_main_t * vm, f64 dt) { /* expiration time in 10us ticks */ p->resume_clock_interval = dt * 1e5; + vlib_process_start_switch_stack (vm, 0); clib_longjmp (&p->return_longjmp, VLIB_PROCESS_RETURN_LONGJMP_SUSPEND); } + else + vlib_process_finish_switch_stack (vm); return r; } @@ -625,8 +654,13 @@ vlib_process_wait_for_event (vlib_main_t * vm) r = clib_setjmp (&p->resume_longjmp, VLIB_PROCESS_RESUME_LONGJMP_SUSPEND); if (r == VLIB_PROCESS_RESUME_LONGJMP_SUSPEND) - clib_longjmp (&p->return_longjmp, - VLIB_PROCESS_RETURN_LONGJMP_SUSPEND); + { + vlib_process_start_switch_stack (vm, 0); + clib_longjmp (&p->return_longjmp, + VLIB_PROCESS_RETURN_LONGJMP_SUSPEND); + } + else + vlib_process_finish_switch_stack (vm); } return p->non_empty_event_type_bitmap; @@ -649,8 +683,13 @@ vlib_process_wait_for_one_time_event (vlib_main_t * vm, r = clib_setjmp (&p->resume_longjmp, VLIB_PROCESS_RESUME_LONGJMP_SUSPEND); if (r == VLIB_PROCESS_RESUME_LONGJMP_SUSPEND) - clib_longjmp (&p->return_longjmp, - VLIB_PROCESS_RETURN_LONGJMP_SUSPEND); + { + vlib_process_start_switch_stack (vm, 0); + clib_longjmp (&p->return_longjmp, + VLIB_PROCESS_RETURN_LONGJMP_SUSPEND); + } + else + vlib_process_finish_switch_stack (vm); } return vlib_process_get_events_helper (p, with_type_index, data_vector); @@ -673,8 +712,13 @@ vlib_process_wait_for_event_with_type (vlib_main_t * vm, r = clib_setjmp (&p->resume_longjmp, VLIB_PROCESS_RESUME_LONGJMP_SUSPEND); if (r == VLIB_PROCESS_RESUME_LONGJMP_SUSPEND) - clib_longjmp (&p->return_longjmp, - VLIB_PROCESS_RETURN_LONGJMP_SUSPEND); + { + vlib_process_start_switch_stack (vm, 0); + clib_longjmp (&p->return_longjmp, + VLIB_PROCESS_RETURN_LONGJMP_SUSPEND); + } + else + vlib_process_finish_switch_stack (vm); /* See if unknown event type has been signaled now. */ if (!h) @@ -715,8 +759,11 @@ vlib_process_wait_for_event_or_clock (vlib_main_t * vm, f64 dt) if (r == VLIB_PROCESS_RESUME_LONGJMP_SUSPEND) { p->resume_clock_interval = dt * 1e5; + vlib_process_start_switch_stack (vm, 0); clib_longjmp (&p->return_longjmp, VLIB_PROCESS_RETURN_LONGJMP_SUSPEND); } + else + vlib_process_finish_switch_stack (vm); /* Return amount of time still left to sleep. If <= 0 then we've been waken up by the clock (and not an event). */ diff --git a/src/vlib/threads.c b/src/vlib/threads.c index 2f141f10d05..a8c1a1a207c 100644 --- a/src/vlib/threads.c +++ b/src/vlib/threads.c @@ -581,6 +581,7 @@ vlib_worker_thread_bootstrap_fn (void *arg) __os_thread_index = w - vlib_worker_threads; + vlib_process_start_switch_stack (vlib_mains[__os_thread_index], 0); rv = (void *) clib_calljmp ((uword (*)(uword)) w->thread_function, (uword) arg, w->thread_stack + VLIB_THREAD_STACK_SIZE); @@ -1777,6 +1778,8 @@ vlib_worker_thread_fn (void *arg) vlib_main_t *vm = vlib_get_main (); clib_error_t *e; + vlib_process_finish_switch_stack (vm); + ASSERT (vm->thread_index == vlib_get_thread_index ()); vlib_worker_thread_init (w); diff --git a/src/vlib/unix/main.c b/src/vlib/unix/main.c index 0f9068c67c3..568876b2933 100644 --- a/src/vlib/unix/main.c +++ b/src/vlib/unix/main.c @@ -654,6 +654,8 @@ thread0 (uword arg) unformat_input_t input; int i; + vlib_process_finish_switch_stack (vm); + unformat_init_command_line (&input, (char **) vm->argv); i = vlib_main (vm, &input); unformat_free (&input); @@ -727,6 +729,7 @@ vlib_unix_main (int argc, char *argv[]) __os_thread_index = 0; vm->thread_index = 0; + vlib_process_start_switch_stack (vm, 0); i = clib_calljmp (thread0, (uword) vm, (void *) (vlib_thread_stacks[0] + VLIB_THREAD_STACK_SIZE)); diff --git a/src/vlib/unix/plugin.h b/src/vlib/unix/plugin.h index 30039c54d65..a52c57bef7b 100644 --- a/src/vlib/unix/plugin.h +++ b/src/vlib/unix/plugin.h @@ -122,6 +122,7 @@ u8 *vlib_get_vat_plugin_path (void); #define VLIB_PLUGIN_REGISTER() \ vlib_plugin_registration_t vlib_plugin_registration \ + CLIB_NOSANITIZE_ADDR \ __attribute__((__section__(".vlib_plugin_registration"))) /* Call a plugin init function: used for init function dependencies. */ -- 2.16.6