* [dpdk-dev] [PATCH] Enable AddressSanitizer feature on DPDK @ 2021-09-10 2:01 zhihongx.peng 2021-09-10 2:47 ` Stephen Hemminger ` (2 more replies) 0 siblings, 3 replies; 117+ messages in thread From: zhihongx.peng @ 2021-09-10 2:01 UTC (permalink / raw) To: anatoly.burakov, konstantin.ananyev, stephen Cc: dev, xueqin.lin, Zhihong Peng From: Zhihong Peng <zhihongx.peng@intel.com> AddressSanitizer (ASan) is a google memory error detect standard tool. It could help to detect use-after-free and {heap,stack,global}-buffer overflow bugs in C/C++ programs, print detailed error information when error happens, large improve debug efficiency. By referring to its implementation algorithm (https://github.com/google/sanitizers/wiki/AddressSanitizerAlgorithm), enable heap-buffer-overflow and use-after-free functions on dpdk. Here is an example of heap-buffer-overflow bug: ...... char *p = rte_zmalloc(NULL, 7, 0); p[7] = 'a'; ...... Here is an example of use-after-free bug: ...... char *p = rte_zmalloc(NULL, 7, 0); rte_free(p); *p = 'a'; ...... If you want to use this feature, you need to add below compilation options when compiling code: -Dbuildtype=debug -Db_lundef=false -Db_sanitize=address "-Dbuildtype=debug": Display code information when coredump occurs in the program. "-Db_lundef=false": It is enabled by default, and needs to be disabled when using asan. Signed-off-by: Xueqin Lin <xueqin.lin@intel.com> Signed-off-by: Zhihong Peng <zhihongx.peng@intel.com> --- app/test/meson.build | 2 + app/test/test_asan_heap_buffer_overflow.c | 30 ++++ app/test/test_asan_use_after_free.c | 31 ++++ doc/guides/prog_guide/asan.rst | 130 ++++++++++++++++ doc/guides/prog_guide/index.rst | 1 + lib/eal/common/malloc_elem.c | 26 +++- lib/eal/common/malloc_elem.h | 172 +++++++++++++++++++++- lib/eal/common/malloc_heap.c | 12 ++ lib/eal/common/meson.build | 4 + lib/eal/common/rte_malloc.c | 9 +- lib/pipeline/rte_swx_pipeline.c | 4 +- 11 files changed, 416 insertions(+), 5 deletions(-) create mode 100644 app/test/test_asan_heap_buffer_overflow.c create mode 100644 app/test/test_asan_use_after_free.c create mode 100644 doc/guides/prog_guide/asan.rst diff --git a/app/test/meson.build b/app/test/meson.build index a7611686ad..12a366bcec 100644 --- a/app/test/meson.build +++ b/app/test/meson.build @@ -150,6 +150,8 @@ test_sources = files( 'test_trace_perf.c', 'test_version.c', 'virtual_pmd.c', + 'test_asan_heap_buffer_overflow.c', + 'test_asan_use_after_free.c', ) test_deps = [ diff --git a/app/test/test_asan_heap_buffer_overflow.c b/app/test/test_asan_heap_buffer_overflow.c new file mode 100644 index 0000000000..001e2853f8 --- /dev/null +++ b/app/test/test_asan_heap_buffer_overflow.c @@ -0,0 +1,30 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * Copyright(c) <2021> Intel Corporation + */ + +#include <stdint.h> +#include <stdio.h> +#include <stdarg.h> +#include <errno.h> +#include <string.h> +#include <rte_per_lcore.h> +#include <rte_errno.h> +#include <rte_string_fns.h> +#include <rte_malloc.h> + +#include "test.h" + +static int +asan_heap_buffer_overflow(void) +{ + char *p = rte_zmalloc(NULL, 9, 0); + if (!p) { + printf("rte_zmalloc error."); + return -1; + } + p[9] = 'a'; + + return 0; +} + +REGISTER_TEST_COMMAND(asan_heap_buffer_overflow_autotest, asan_heap_buffer_overflow); diff --git a/app/test/test_asan_use_after_free.c b/app/test/test_asan_use_after_free.c new file mode 100644 index 0000000000..b9c4ec8eba --- /dev/null +++ b/app/test/test_asan_use_after_free.c @@ -0,0 +1,31 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * Copyright(c) <2021> Intel Corporation + */ + +#include <stdint.h> +#include <stdio.h> +#include <stdarg.h> +#include <errno.h> +#include <string.h> +#include <rte_per_lcore.h> +#include <rte_errno.h> +#include <rte_string_fns.h> +#include <rte_malloc.h> + +#include "test.h" + +static int +asan_use_after_free(void) +{ + char *p = rte_zmalloc(NULL, 9, 0); + if (!p) { + printf("rte_zmalloc error."); + return -1; + } + rte_free(p); + *p = 'a'; + + return 0; +} + +REGISTER_TEST_COMMAND(asan_use_after_free_autotest, asan_use_after_free); diff --git a/doc/guides/prog_guide/asan.rst b/doc/guides/prog_guide/asan.rst new file mode 100644 index 0000000000..721a99b946 --- /dev/null +++ b/doc/guides/prog_guide/asan.rst @@ -0,0 +1,130 @@ +.. Copyright (c) <2021>, Intel Corporation + All rights reserved. + +Memory error detect standard tool - AddressSanitizer(Asan) +========================================================== + +AddressSanitizer (ASan) is a google memory error detect +standard tool. It could help to detect use-after-free and +{heap,stack,global}-buffer overflow bugs in C/C++ programs, +print detailed error information when error happens, large +improve debug efficiency. + +By referring to its implementation algorithm +(https://github.com/google/sanitizers/wiki/AddressSanitizerAlgorithm), +enabled heap-buffer-overflow and use-after-free functions on dpdk. +DPDK ASAN function currently only supports on Linux x86_64. + +AddressSanitizer is a part of LLVM(3.1+)and GCC(4.8+). + +Example heap-buffer-overflow error +---------------------------------- + +Following error was reported when Asan was enabled:: + + app/test/test_asan_heap_buffer_overflow.c:25: Applied 9 bytes + of memory, but accessed the 10th byte of memory, so heap-buffer-overflow + appeared. + +Below code results in this error:: + + char *p = rte_zmalloc(NULL, 9, 0); + if (!p) { + printf("rte_zmalloc error."); + return -1; + } + p[9] = 'a'; + +The error log:: + + ==49433==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x7f773fafa249 at pc 0x5556b13bdae4 bp 0x7ffeb4965e40 sp 0x7ffeb4965e30 WRITE of size 1 at 0x7f773fafa249 thread T0 + #0 0x5556b13bdae3 in asan_heap_buffer_overflow ../app/test/test_asan_heap_buffer_overflow.c:25 + #1 0x5556b043e9d4 in cmd_autotest_parsed ../app/test/commands.c:71 + #2 0x5556b1cdd4b0 in cmdline_parse ../lib/cmdline/cmdline_parse.c:290 + #3 0x5556b1cd8987 in cmdline_valid_buffer ../lib/cmdline/cmdline.c:26 + #4 0x5556b1ce477a in rdline_char_in ../lib/cmdline/cmdline_rdline.c:421 + #5 0x5556b1cd923e in cmdline_in ../lib/cmdline/cmdline.c:149 + #6 0x5556b1cd9769 in cmdline_interact ../lib/cmdline/cmdline.c:223 + #7 0x5556b045f53b in main ../app/test/test.c:234 + #8 0x7f7f1eba90b2 in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x270b2) + #9 0x5556b043e70d in _start (/home/pzh/yyy/x86_64-native-linuxapp-gcc/app/test/dpdk-test+0x7ce70d) + + Address 0x7f773fafa249 is a wild pointer. + SUMMARY: AddressSanitizer: heap-buffer-overflow ../app/test/test_asan_heap_buffer_overflow.c:25 in asan_heap_buffer_overflow + Shadow bytes around the buggy address: + 0x0fef67f573f0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 0x0fef67f57400: fa fa 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 0x0fef67f57410: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 0x0fef67f57420: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 0x0fef67f57430: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + =>0x0fef67f57440: 00 00 00 00 00 00 fa fa 00[01]fa 00 00 00 00 00 + 0x0fef67f57450: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 0x0fef67f57460: 00 00 00 00 00 00 fa fa 00 00 00 00 00 00 00 00 + 0x0fef67f57470: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 0x0fef67f57480: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + +Example use-after-free error +---------------------------- + +Following error was reported when Asan was enabled:: + + app/test/test_asan_use_after_free.c:26: Applied for 9 bytes of + memory, and accessed the first byte after released, so + heap-use-after-free appeared. + +Below code results in this error:: + + char *p = rte_zmalloc(NULL, 9, 0); + if (!p) { + printf("rte_zmalloc error."); + return -1; + } + rte_free(p); + *p = 'a'; + +The error log:: + + ==49478==ERROR: AddressSanitizer: heap-use-after-free on address 0x7fe2ffafa240 at pc 0x56409b084bc8 bp 0x7ffef62c57d0 sp 0x7ffef62c57c0 WRITE of size 1 at 0x7fe2ffafa240 thread T0 + #0 0x56409b084bc7 in asan_use_after_free ../app/test/test_asan_use_after_free.c:26 + #1 0x56409a1059d4 in cmd_autotest_parsed ../app/test/commands.c:71 + #2 0x56409b9a44b0 in cmdline_parse ../lib/cmdline/cmdline_parse.c:290 + #3 0x56409b99f987 in cmdline_valid_buffer ../lib/cmdline/cmdline.c:26 + #4 0x56409b9ab77a in rdline_char_in ../lib/cmdline/cmdline_rdline.c:421 + #5 0x56409b9a023e in cmdline_in ../lib/cmdline/cmdline.c:149 + #6 0x56409b9a0769 in cmdline_interact ../lib/cmdline/cmdline.c:223 + #7 0x56409a12653b in main ../app/test/test.c:234 + #8 0x7feafafc20b2 in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x270b2) + #9 0x56409a10570d in _start (/home/pzh/yyy/x86_64-native-linuxapp-gcc/app/test/dpdk-test+0x7ce70d) + + Address 0x7fe2ffafa240 is a wild pointer. + SUMMARY: AddressSanitizer: heap-use-after-free ../app/test/test_asan_use_after_free.c:26 in asan_use_after_free + Shadow bytes around the buggy address: + 0x0ffcdff573f0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 0x0ffcdff57400: fa fa 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 0x0ffcdff57410: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 0x0ffcdff57420: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 0x0ffcdff57430: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + =>0x0ffcdff57440: 00 00 00 00 00 00 00 00[fd]fd fd fd fd fd fd fd + 0x0ffcdff57450: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 0x0ffcdff57460: 00 00 00 00 00 00 fa fa 00 00 00 00 00 00 00 00 + 0x0ffcdff57470: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 0x0ffcdff57480: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 0x0ffcdff57490: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + +Usage +----- + +meson build +^^^^^^^^^^^ + +To enable Asan in meson build system, use following meson build command: + +Example usage:: + + meson build -Dbuildtype=debug -Db_lundef=false -Db_sanitize=address + ninja -C build + +.. Note:: + + Centos8 needs to install libasan separately. + If the program uses cmdline, when a memory bug occurs, need to execute the "stty echo" command. diff --git a/doc/guides/prog_guide/index.rst b/doc/guides/prog_guide/index.rst index 2dce507f46..df8a4b93e1 100644 --- a/doc/guides/prog_guide/index.rst +++ b/doc/guides/prog_guide/index.rst @@ -71,3 +71,4 @@ Programmer's Guide lto profile_app glossary + asan diff --git a/lib/eal/common/malloc_elem.c b/lib/eal/common/malloc_elem.c index c2c9461f1d..bdd20a162e 100644 --- a/lib/eal/common/malloc_elem.c +++ b/lib/eal/common/malloc_elem.c @@ -446,6 +446,8 @@ malloc_elem_alloc(struct malloc_elem *elem, size_t size, unsigned align, struct malloc_elem *new_free_elem = RTE_PTR_ADD(new_elem, size + MALLOC_ELEM_OVERHEAD); + asan_clear_split_alloczone(new_free_elem); + split_elem(elem, new_free_elem); malloc_elem_free_list_insert(new_free_elem); @@ -458,6 +460,8 @@ malloc_elem_alloc(struct malloc_elem *elem, size_t size, unsigned align, elem->state = ELEM_BUSY; elem->pad = old_elem_size; + asan_clear_alloczone(elem); + /* put a dummy header in padding, to point to real element header */ if (elem->pad > 0) { /* pad will be at least 64-bytes, as everything * is cache-line aligned */ @@ -470,12 +474,18 @@ malloc_elem_alloc(struct malloc_elem *elem, size_t size, unsigned align, return new_elem; } + asan_clear_split_alloczone(new_elem); + /* we are going to split the element in two. The original element * remains free, and the new element is the one allocated. * Re-insert original element, in case its new size makes it * belong on a different list. */ + split_elem(elem, new_elem); + + asan_clear_alloczone(new_elem); + new_elem->state = ELEM_BUSY; malloc_elem_free_list_insert(elem); @@ -601,6 +611,8 @@ malloc_elem_hide_region(struct malloc_elem *elem, void *start, size_t len) if (next && next_elem_is_adjacent(elem)) { len_after = RTE_PTR_DIFF(next, hide_end); if (len_after >= MALLOC_ELEM_OVERHEAD + MIN_DATA_SIZE) { + asan_clear_split_alloczone(hide_end); + /* split after */ split_elem(elem, hide_end); @@ -615,6 +627,8 @@ malloc_elem_hide_region(struct malloc_elem *elem, void *start, size_t len) if (prev && prev_elem_is_adjacent(elem)) { len_before = RTE_PTR_DIFF(hide_start, elem); if (len_before >= MALLOC_ELEM_OVERHEAD + MIN_DATA_SIZE) { + asan_clear_split_alloczone(hide_start); + /* split before */ split_elem(elem, hide_start); @@ -628,6 +642,8 @@ malloc_elem_hide_region(struct malloc_elem *elem, void *start, size_t len) } } + asan_clear_alloczone(elem); + remove_elem(elem); } @@ -641,8 +657,10 @@ malloc_elem_resize(struct malloc_elem *elem, size_t size) const size_t new_size = size + elem->pad + MALLOC_ELEM_OVERHEAD; /* if we request a smaller size, then always return ok */ - if (elem->size >= new_size) + if (elem->size >= new_size) { + asan_clear_alloczone(elem); return 0; + } /* check if there is a next element, it's free and adjacent */ if (!elem->next || elem->next->state != ELEM_FREE || @@ -661,9 +679,15 @@ malloc_elem_resize(struct malloc_elem *elem, size_t size) /* now we have a big block together. Lets cut it down a bit, by splitting */ struct malloc_elem *split_pt = RTE_PTR_ADD(elem, new_size); split_pt = RTE_PTR_ALIGN_CEIL(split_pt, RTE_CACHE_LINE_SIZE); + + asan_clear_split_alloczone(split_pt); + split_elem(elem, split_pt); malloc_elem_free_list_insert(split_pt); } + + asan_clear_alloczone(elem); + return 0; } diff --git a/lib/eal/common/malloc_elem.h b/lib/eal/common/malloc_elem.h index a1e5f7f02c..0e91ace17f 100644 --- a/lib/eal/common/malloc_elem.h +++ b/lib/eal/common/malloc_elem.h @@ -36,10 +36,20 @@ struct malloc_elem { uint64_t header_cookie; /* Cookie marking start of data */ /* trailer cookie at start + size */ #endif +#ifdef RTE_MALLOC_ASAN + size_t user_size; + uint64_t asan_cookie[2]; /*must be next to header_cookie*/ +#endif } __rte_cache_aligned; +static const unsigned MALLOC_ELEM_HEADER_LEN = sizeof(struct malloc_elem); + #ifndef RTE_MALLOC_DEBUG +#ifdef RTE_MALLOC_ASAN +static const unsigned MALLOC_ELEM_TRAILER_LEN = RTE_CACHE_LINE_SIZE; +#else static const unsigned MALLOC_ELEM_TRAILER_LEN = 0; +#endif /* dummy function - just check if pointer is non-null */ static inline int @@ -90,9 +100,169 @@ malloc_elem_cookies_ok(const struct malloc_elem *elem) #endif -static const unsigned MALLOC_ELEM_HEADER_LEN = sizeof(struct malloc_elem); #define MALLOC_ELEM_OVERHEAD (MALLOC_ELEM_HEADER_LEN + MALLOC_ELEM_TRAILER_LEN) +#ifdef RTE_MALLOC_ASAN + +#define ASAN_SHADOW_GRAIN_SIZE 8 +#define ASAN_MEM_FREE_FLAG 0xfd +#define ASAN_MEM_REDZONE_FLAG 0xfa +#define ASAN_MEM_TO_SHADOW(mem) (((mem) >> 3) + 0x00007fff8000) + +#if defined(__clang__) +__attribute__((no_sanitize("address", "hwaddress"))) +#else +__attribute__((no_sanitize_address)) +#endif +static inline void +asan_set_shadow(void *addr, char val) +{ + *(char *)addr = val; +} + +static inline void +asan_set_zone(void *ptr, size_t len, uint32_t val) +{ + size_t offset; + char *shadow; + size_t zone_len = len / ASAN_SHADOW_GRAIN_SIZE; + if (len % ASAN_SHADOW_GRAIN_SIZE != 0) + zone_len += 1; + + for (size_t i = 0; i < zone_len; i++) { + offset = i * ASAN_SHADOW_GRAIN_SIZE; + shadow = (char *)ASAN_MEM_TO_SHADOW(((int64_t)ptr + offset)); + asan_set_shadow(shadow, val); + } +} + +/* + * When the memory is released, the release mark is + * set in the corresponding range of the shadow area. + */ +static inline void +asan_set_freezone(void *ptr, size_t size) +{ + asan_set_zone(ptr, size, ASAN_MEM_FREE_FLAG); +} + +/* + * When the memory is allocated, memory state must set as accessible. + */ +static inline void +asan_clear_alloczone(struct malloc_elem *elem) +{ + asan_set_zone((void *)elem, elem->size, 0x0); +} + +static inline void +asan_clear_split_alloczone(struct malloc_elem *elem) +{ + void *ptr = RTE_PTR_SUB(elem, MALLOC_ELEM_TRAILER_LEN); + asan_set_zone(ptr, MALLOC_ELEM_OVERHEAD, 0x0); +} + +/* + * When the memory is allocated, the memory boundary is + * marked in the corresponding range of the shadow area. + */ +static inline void +asan_set_redzone(struct malloc_elem *elem, size_t user_size) +{ + uint64_t ptr; + char *shadow; + if (elem != NULL) { + if (elem->state != ELEM_PAD) + elem = RTE_PTR_ADD(elem, elem->pad); + + elem->user_size = user_size; + + /* Set mark before the start of the allocated memory */ + ptr = (uint64_t)RTE_PTR_ADD(elem, MALLOC_ELEM_HEADER_LEN) + - ASAN_SHADOW_GRAIN_SIZE; + shadow = (char *)ASAN_MEM_TO_SHADOW(ptr); + asan_set_shadow(shadow, ASAN_MEM_REDZONE_FLAG); + shadow = (char *)ASAN_MEM_TO_SHADOW(ptr + - ASAN_SHADOW_GRAIN_SIZE); + asan_set_shadow(shadow, ASAN_MEM_REDZONE_FLAG); + + /* Set mark after the end of the allocated memory */ + ptr = (uint64_t)RTE_PTR_ADD(elem, MALLOC_ELEM_HEADER_LEN + + elem->user_size); + shadow = (char *)ASAN_MEM_TO_SHADOW(ptr); + uint32_t val = (ptr % ASAN_SHADOW_GRAIN_SIZE); + val = (val == 0) ? ASAN_MEM_REDZONE_FLAG : val; + asan_set_shadow(shadow, val); + shadow = (char *)ASAN_MEM_TO_SHADOW(ptr + + ASAN_SHADOW_GRAIN_SIZE); + asan_set_shadow(shadow, ASAN_MEM_REDZONE_FLAG); + } +} + +/* + * When the memory is released, the mark of the memory boundary + * in the corresponding range of the shadow area is cleared. + */ +static inline void +asan_clear_redzone(struct malloc_elem *elem) +{ + uint64_t ptr; + char *shadow; + if (elem != NULL) { + elem = RTE_PTR_ADD(elem, elem->pad); + + /* Clear mark before the start of the allocated memory */ + ptr = (uint64_t)RTE_PTR_ADD(elem, MALLOC_ELEM_HEADER_LEN) + - ASAN_SHADOW_GRAIN_SIZE; + shadow = (char *)ASAN_MEM_TO_SHADOW(ptr); + asan_set_shadow(shadow, 0x00); + shadow = (char *)ASAN_MEM_TO_SHADOW(ptr + - ASAN_SHADOW_GRAIN_SIZE); + asan_set_shadow(shadow, 0x00); + + /* Clear mark after the end of the allocated memory */ + ptr = (uint64_t)RTE_PTR_ADD(elem, MALLOC_ELEM_HEADER_LEN + + elem->user_size); + shadow = (char *)ASAN_MEM_TO_SHADOW(ptr); + asan_set_shadow(shadow, 0x00); + shadow = (char *)ASAN_MEM_TO_SHADOW(ptr + + ASAN_SHADOW_GRAIN_SIZE); + asan_set_shadow(shadow, 0x00); + } +} + +static inline size_t +old_malloc_size(struct malloc_elem *elem) +{ + if (elem->state != ELEM_PAD) + elem = RTE_PTR_ADD(elem, elem->pad); + + return elem->user_size; +} +#else +static inline void +asan_set_freezone(void *ptr __rte_unused, size_t size __rte_unused) { } + +static inline void +asan_clear_alloczone(struct malloc_elem *elem __rte_unused) { } + +static inline void +asan_clear_split_alloczone(struct malloc_elem *elem __rte_unused) { } + +static inline void +asan_set_redzone(struct malloc_elem *elem __rte_unused, + size_t user_size __rte_unused) { } + +static inline void +asan_clear_redzone(struct malloc_elem *elem __rte_unused) { } + +static inline size_t +old_malloc_size(struct malloc_elem *elem) +{ + return elem->size - elem->pad - MALLOC_ELEM_OVERHEAD; +} +#endif + /* * Given a pointer to the start of a memory block returned by malloc, get * the actual malloc_elem header for that block. diff --git a/lib/eal/common/malloc_heap.c b/lib/eal/common/malloc_heap.c index ee400f38ec..775d6789df 100644 --- a/lib/eal/common/malloc_heap.c +++ b/lib/eal/common/malloc_heap.c @@ -237,6 +237,7 @@ heap_alloc(struct malloc_heap *heap, const char *type __rte_unused, size_t size, unsigned int flags, size_t align, size_t bound, bool contig) { struct malloc_elem *elem; + size_t user_size = size; size = RTE_CACHE_LINE_ROUNDUP(size); align = RTE_CACHE_LINE_ROUNDUP(align); @@ -250,6 +251,8 @@ heap_alloc(struct malloc_heap *heap, const char *type __rte_unused, size_t size, /* increase heap's count of allocated elements */ heap->alloc_count++; + + asan_set_redzone(elem, user_size); } return elem == NULL ? NULL : (void *)(&elem[1]); @@ -270,6 +273,8 @@ heap_alloc_biggest(struct malloc_heap *heap, const char *type __rte_unused, /* increase heap's count of allocated elements */ heap->alloc_count++; + + asan_set_redzone(elem, size); } return elem == NULL ? NULL : (void *)(&elem[1]); @@ -841,6 +846,8 @@ malloc_heap_free(struct malloc_elem *elem) if (!malloc_elem_cookies_ok(elem) || elem->state != ELEM_BUSY) return -1; + asan_clear_redzone(elem); + /* elem may be merged with previous element, so keep heap address */ heap = elem->heap; msl = elem->msl; @@ -848,6 +855,9 @@ malloc_heap_free(struct malloc_elem *elem) rte_spinlock_lock(&(heap->lock)); + void *asan_ptr = RTE_PTR_ADD(elem, MALLOC_ELEM_HEADER_LEN + elem->pad); + size_t asan_data_len = elem->size - MALLOC_ELEM_OVERHEAD - elem->pad; + /* mark element as free */ elem->state = ELEM_FREE; @@ -1001,6 +1011,8 @@ malloc_heap_free(struct malloc_elem *elem) rte_mcfg_mem_write_unlock(); free_unlock: + asan_set_freezone(asan_ptr, asan_data_len); + rte_spinlock_unlock(&(heap->lock)); return ret; } diff --git a/lib/eal/common/meson.build b/lib/eal/common/meson.build index edfca77779..2f786841d0 100644 --- a/lib/eal/common/meson.build +++ b/lib/eal/common/meson.build @@ -5,6 +5,10 @@ includes += include_directories('.') cflags += [ '-DABI_VERSION="@0@"'.format(abi_version) ] +if get_option('b_sanitize').startswith('address') + cflags += '-DRTE_MALLOC_ASAN' +endif + if is_windows sources += files( 'eal_common_bus.c', diff --git a/lib/eal/common/rte_malloc.c b/lib/eal/common/rte_malloc.c index 9d39e58c08..d0bec26920 100644 --- a/lib/eal/common/rte_malloc.c +++ b/lib/eal/common/rte_malloc.c @@ -162,6 +162,8 @@ rte_calloc(const char *type, size_t num, size_t size, unsigned align) void * rte_realloc_socket(void *ptr, size_t size, unsigned int align, int socket) { + size_t user_size; + if (ptr == NULL) return rte_malloc_socket(NULL, size, align, socket); @@ -171,6 +173,8 @@ rte_realloc_socket(void *ptr, size_t size, unsigned int align, int socket) return NULL; } + user_size = size; + size = RTE_CACHE_LINE_ROUNDUP(size), align = RTE_CACHE_LINE_ROUNDUP(align); /* check requested socket id and alignment matches first, and if ok, @@ -181,6 +185,9 @@ rte_realloc_socket(void *ptr, size_t size, unsigned int align, int socket) RTE_PTR_ALIGN(ptr, align) == ptr && malloc_heap_resize(elem, size) == 0) { rte_eal_trace_mem_realloc(size, align, socket, ptr); + + asan_set_redzone(elem, user_size); + return ptr; } @@ -192,7 +199,7 @@ rte_realloc_socket(void *ptr, size_t size, unsigned int align, int socket) if (new_ptr == NULL) return NULL; /* elem: |pad|data_elem|data|trailer| */ - const size_t old_size = elem->size - elem->pad - MALLOC_ELEM_OVERHEAD; + const size_t old_size = old_malloc_size(elem); rte_memcpy(new_ptr, ptr, old_size < size ? old_size : size); rte_free(ptr); diff --git a/lib/pipeline/rte_swx_pipeline.c b/lib/pipeline/rte_swx_pipeline.c index 8eb978a30c..aaa0107d02 100644 --- a/lib/pipeline/rte_swx_pipeline.c +++ b/lib/pipeline/rte_swx_pipeline.c @@ -6340,7 +6340,7 @@ instr_meter_translate(struct rte_swx_pipeline *p, return 0; } - CHECK(0, EINVAL); + return -EINVAL; } static inline struct meter * @@ -8025,7 +8025,7 @@ instr_translate(struct rte_swx_pipeline *p, instr, data); - CHECK(0, EINVAL); + return -EINVAL; } static struct instruction_data * -- 2.25.1 ^ permalink raw reply [flat|nested] 117+ messages in thread
* Re: [dpdk-dev] [PATCH] Enable AddressSanitizer feature on DPDK 2021-09-10 2:01 [dpdk-dev] [PATCH] Enable AddressSanitizer feature on DPDK zhihongx.peng @ 2021-09-10 2:47 ` Stephen Hemminger 2021-09-13 5:27 ` Peng, ZhihongX 2021-09-10 17:58 ` David Christensen 2021-09-16 1:38 ` [dpdk-dev] [PATCH v2] " zhihongx.peng 2 siblings, 1 reply; 117+ messages in thread From: Stephen Hemminger @ 2021-09-10 2:47 UTC (permalink / raw) To: zhihongx.peng; +Cc: anatoly.burakov, konstantin.ananyev, dev, xueqin.lin On Fri, 10 Sep 2021 02:01:47 +0000 zhihongx.peng@intel.com wrote: > > +if get_option('b_sanitize').startswith('address') > + cflags += '-DRTE_MALLOC_ASAN' > +endif > + This looks great, but can we make it just do-the-right-thing and get rid of the nerd knobs (i.e no meson configure). The address sanitizer already has a way to detect if enabled. GCC uses: __SANITIZE_ADDRESS__ Clang uses: #if defined(__has_feature) # if __has_feature(address_sanitizer) ^ permalink raw reply [flat|nested] 117+ messages in thread
* Re: [dpdk-dev] [PATCH] Enable AddressSanitizer feature on DPDK 2021-09-10 2:47 ` Stephen Hemminger @ 2021-09-13 5:27 ` Peng, ZhihongX 2021-09-13 15:05 ` Stephen Hemminger 0 siblings, 1 reply; 117+ messages in thread From: Peng, ZhihongX @ 2021-09-13 5:27 UTC (permalink / raw) To: Stephen Hemminger; +Cc: Burakov, Anatoly, Ananyev, Konstantin, dev, Lin, Xueqin > -----Original Message----- > From: Stephen Hemminger <stephen@networkplumber.org> > Sent: Friday, September 10, 2021 10:48 AM > To: Peng, ZhihongX <zhihongx.peng@intel.com> > Cc: Burakov, Anatoly <anatoly.burakov@intel.com>; Ananyev, Konstantin > <konstantin.ananyev@intel.com>; dev@dpdk.org; Lin, Xueqin > <xueqin.lin@intel.com> > Subject: Re: [PATCH] Enable AddressSanitizer feature on DPDK > > On Fri, 10 Sep 2021 02:01:47 +0000 > zhihongx.peng@intel.com wrote: > > > > > +if get_option('b_sanitize').startswith('address') > > + cflags += '-DRTE_MALLOC_ASAN' > > +endif > > + > > This looks great, but can we make it just do-the-right-thing and get rid of the > nerd knobs (i.e no meson configure). > > The address sanitizer already has a way to detect if enabled. > > GCC uses: > __SANITIZE_ADDRESS__ > > Clang uses: > #if defined(__has_feature) > # if __has_feature(address_sanitizer) Tried this method you said. It can run successfully. Because gcc and clang have different Methods for determining whether to turn on the asan function, so if you judge the two methods in the code, it feels not simple to judge in meson. ^ permalink raw reply [flat|nested] 117+ messages in thread
* Re: [dpdk-dev] [PATCH] Enable AddressSanitizer feature on DPDK 2021-09-13 5:27 ` Peng, ZhihongX @ 2021-09-13 15:05 ` Stephen Hemminger 2021-09-13 15:22 ` Bruce Richardson 0 siblings, 1 reply; 117+ messages in thread From: Stephen Hemminger @ 2021-09-13 15:05 UTC (permalink / raw) To: Peng, ZhihongX; +Cc: Burakov, Anatoly, Ananyev, Konstantin, dev, Lin, Xueqin On Mon, 13 Sep 2021 05:27:12 +0000 "Peng, ZhihongX" <zhihongx.peng@intel.com> wrote: > > -----Original Message----- > > From: Stephen Hemminger <stephen@networkplumber.org> > > Sent: Friday, September 10, 2021 10:48 AM > > To: Peng, ZhihongX <zhihongx.peng@intel.com> > > Cc: Burakov, Anatoly <anatoly.burakov@intel.com>; Ananyev, Konstantin > > <konstantin.ananyev@intel.com>; dev@dpdk.org; Lin, Xueqin > > <xueqin.lin@intel.com> > > Subject: Re: [PATCH] Enable AddressSanitizer feature on DPDK > > > > On Fri, 10 Sep 2021 02:01:47 +0000 > > zhihongx.peng@intel.com wrote: > > > > > > > > +if get_option('b_sanitize').startswith('address') > > > + cflags += '-DRTE_MALLOC_ASAN' > > > +endif > > > + > > > > This looks great, but can we make it just do-the-right-thing and get rid of the > > nerd knobs (i.e no meson configure). > > > > The address sanitizer already has a way to detect if enabled. > > > > GCC uses: > > __SANITIZE_ADDRESS__ > > > > Clang uses: > > #if defined(__has_feature) > > # if __has_feature(address_sanitizer) > > Tried this method you said. It can run successfully. Because gcc and clang have different > Methods for determining whether to turn on the asan function, so if you judge the two > methods in the code, it feels not simple to judge in meson. There is already compiler specific #ifdef's why not do this contained in one header file? The point is DPDK is trying to get away from having configuration settings if at all possible. Configuration creates dependency nightmares and also leaves many code paths as never tested. ^ permalink raw reply [flat|nested] 117+ messages in thread
* Re: [dpdk-dev] [PATCH] Enable AddressSanitizer feature on DPDK 2021-09-13 15:05 ` Stephen Hemminger @ 2021-09-13 15:22 ` Bruce Richardson 2021-09-13 15:45 ` Stephen Hemminger 2021-09-14 3:11 ` Lin, Xueqin 0 siblings, 2 replies; 117+ messages in thread From: Bruce Richardson @ 2021-09-13 15:22 UTC (permalink / raw) To: Stephen Hemminger Cc: Peng, ZhihongX, Burakov, Anatoly, Ananyev, Konstantin, dev, Lin, Xueqin On Mon, Sep 13, 2021 at 08:05:58AM -0700, Stephen Hemminger wrote: > On Mon, 13 Sep 2021 05:27:12 +0000 > "Peng, ZhihongX" <zhihongx.peng@intel.com> wrote: > > > > -----Original Message----- > > > From: Stephen Hemminger <stephen@networkplumber.org> > > > Sent: Friday, September 10, 2021 10:48 AM > > > To: Peng, ZhihongX <zhihongx.peng@intel.com> > > > Cc: Burakov, Anatoly <anatoly.burakov@intel.com>; Ananyev, Konstantin > > > <konstantin.ananyev@intel.com>; dev@dpdk.org; Lin, Xueqin > > > <xueqin.lin@intel.com> > > > Subject: Re: [PATCH] Enable AddressSanitizer feature on DPDK > > > > > > On Fri, 10 Sep 2021 02:01:47 +0000 > > > zhihongx.peng@intel.com wrote: > > > > > > > > > > > +if get_option('b_sanitize').startswith('address') > > > > + cflags += '-DRTE_MALLOC_ASAN' > > > > +endif > > > > + > > > > > > This looks great, but can we make it just do-the-right-thing and get rid of the > > > nerd knobs (i.e no meson configure). > > > There are no new meson options being added here. Turning on/off address sanitizing is a built-in meson option that is there already. > > > The address sanitizer already has a way to detect if enabled. > > > > > > GCC uses: > > > __SANITIZE_ADDRESS__ > > > > > > Clang uses: > > > #if defined(__has_feature) > > > # if __has_feature(address_sanitizer) > > > > Tried this method you said. It can run successfully. Because gcc and clang have different > > Methods for determining whether to turn on the asan function, so if you judge the two > > methods in the code, it feels not simple to judge in meson. > > There is already compiler specific #ifdef's why not do this contained in one header file? > > The point is DPDK is trying to get away from having configuration settings if at all > possible. Configuration creates dependency nightmares and also leaves many code paths > as never tested. Not sure I follow your point here. We need some macro to easily tell if we are running with address sanitization enabled or not, so as to avoid having the multi-compiler detection rules all over the place. The only question is where it's better to have this in a header file or a meson.build file. Given your objection and the fact that the meson.build code above looks a little awkward, I'd suggest putting the conditional checks in malloc_elem.h. Is something like the following what you had in mind? #ifdef __SANITIZE_ADDRESS__ #define RTE_MALLOC_ASAN #elif defined(__has_feature) && __has_feature(address_sanitizer) #define RTE_MALLOC_ASAN #endif /Bruce ^ permalink raw reply [flat|nested] 117+ messages in thread
* Re: [dpdk-dev] [PATCH] Enable AddressSanitizer feature on DPDK 2021-09-13 15:22 ` Bruce Richardson @ 2021-09-13 15:45 ` Stephen Hemminger 2021-09-14 3:17 ` Lin, Xueqin 2021-09-14 3:11 ` Lin, Xueqin 1 sibling, 1 reply; 117+ messages in thread From: Stephen Hemminger @ 2021-09-13 15:45 UTC (permalink / raw) To: Bruce Richardson Cc: Peng, ZhihongX, Burakov, Anatoly, Ananyev, Konstantin, dev, Lin, Xueqin On Mon, 13 Sep 2021 16:22:13 +0100 Bruce Richardson <bruce.richardson@intel.com> wrote: > On Mon, Sep 13, 2021 at 08:05:58AM -0700, Stephen Hemminger wrote: > > On Mon, 13 Sep 2021 05:27:12 +0000 > > "Peng, ZhihongX" <zhihongx.peng@intel.com> wrote: > > > > > > -----Original Message----- > > > > From: Stephen Hemminger <stephen@networkplumber.org> > > > > Sent: Friday, September 10, 2021 10:48 AM > > > > To: Peng, ZhihongX <zhihongx.peng@intel.com> > > > > Cc: Burakov, Anatoly <anatoly.burakov@intel.com>; Ananyev, Konstantin > > > > <konstantin.ananyev@intel.com>; dev@dpdk.org; Lin, Xueqin > > > > <xueqin.lin@intel.com> > > > > Subject: Re: [PATCH] Enable AddressSanitizer feature on DPDK > > > > > > > > On Fri, 10 Sep 2021 02:01:47 +0000 > > > > zhihongx.peng@intel.com wrote: > > > > > > > > > > > > > > +if get_option('b_sanitize').startswith('address') > > > > > + cflags += '-DRTE_MALLOC_ASAN' > > > > > +endif > > > > > + > > > > > > > > This looks great, but can we make it just do-the-right-thing and get rid of the > > > > nerd knobs (i.e no meson configure). > > > > > > There are no new meson options being added here. Turning on/off address > sanitizing is a built-in meson option that is there already. > > > > > The address sanitizer already has a way to detect if enabled. > > > > > > > > GCC uses: > > > > __SANITIZE_ADDRESS__ > > > > > > > > Clang uses: > > > > #if defined(__has_feature) > > > > # if __has_feature(address_sanitizer) > > > > > > Tried this method you said. It can run successfully. Because gcc and clang have different > > > Methods for determining whether to turn on the asan function, so if you judge the two > > > methods in the code, it feels not simple to judge in meson. > > > > There is already compiler specific #ifdef's why not do this contained in one header file? > > > > The point is DPDK is trying to get away from having configuration settings if at all > > possible. Configuration creates dependency nightmares and also leaves many code paths > > as never tested. > > Not sure I follow your point here. We need some macro to easily tell if we > are running with address sanitization enabled or not, so as to avoid having > the multi-compiler detection rules all over the place. The only question is > where it's better to have this in a header file or a meson.build file. > Given your objection and the fact that the meson.build code above looks a > little awkward, I'd suggest putting the conditional checks in malloc_elem.h. NVM working of meson as commn base seems like good option. ^ permalink raw reply [flat|nested] 117+ messages in thread
* Re: [dpdk-dev] [PATCH] Enable AddressSanitizer feature on DPDK 2021-09-13 15:45 ` Stephen Hemminger @ 2021-09-14 3:17 ` Lin, Xueqin 0 siblings, 0 replies; 117+ messages in thread From: Lin, Xueqin @ 2021-09-14 3:17 UTC (permalink / raw) To: Stephen Hemminger, Richardson, Bruce Cc: Peng, ZhihongX, Burakov, Anatoly, Ananyev, Konstantin, dev > -----Original Message----- > From: Stephen Hemminger <stephen@networkplumber.org> > Sent: Monday, September 13, 2021 11:46 PM > To: Richardson, Bruce <bruce.richardson@intel.com> > Cc: Peng, ZhihongX <zhihongx.peng@intel.com>; Burakov, Anatoly > <anatoly.burakov@intel.com>; Ananyev, Konstantin > <konstantin.ananyev@intel.com>; dev@dpdk.org; Lin, Xueqin > <xueqin.lin@intel.com> > Subject: Re: [dpdk-dev] [PATCH] Enable AddressSanitizer feature on DPDK > > On Mon, 13 Sep 2021 16:22:13 +0100 > Bruce Richardson <bruce.richardson@intel.com> wrote: > > > On Mon, Sep 13, 2021 at 08:05:58AM -0700, Stephen Hemminger wrote: > > > On Mon, 13 Sep 2021 05:27:12 +0000 > > > "Peng, ZhihongX" <zhihongx.peng@intel.com> wrote: > > > > > > > > -----Original Message----- > > > > > From: Stephen Hemminger <stephen@networkplumber.org> > > > > > Sent: Friday, September 10, 2021 10:48 AM > > > > > To: Peng, ZhihongX <zhihongx.peng@intel.com> > > > > > Cc: Burakov, Anatoly <anatoly.burakov@intel.com>; Ananyev, > > > > > Konstantin <konstantin.ananyev@intel.com>; dev@dpdk.org; Lin, > > > > > Xueqin <xueqin.lin@intel.com> > > > > > Subject: Re: [PATCH] Enable AddressSanitizer feature on DPDK > > > > > > > > > > On Fri, 10 Sep 2021 02:01:47 +0000 zhihongx.peng@intel.com > > > > > wrote: > > > > > > > > > > > > > > > > > +if get_option('b_sanitize').startswith('address') > > > > > > + cflags += '-DRTE_MALLOC_ASAN' > > > > > > +endif > > > > > > + > > > > > > > > > > This looks great, but can we make it just do-the-right-thing and > > > > > get rid of the nerd knobs (i.e no meson configure). > > > > > > > > > There are no new meson options being added here. Turning on/off > > address sanitizing is a built-in meson option that is there already. > > > > > > > The address sanitizer already has a way to detect if enabled. > > > > > > > > > > GCC uses: > > > > > __SANITIZE_ADDRESS__ > > > > > > > > > > Clang uses: > > > > > #if defined(__has_feature) > > > > > # if __has_feature(address_sanitizer) > > > > > > > > Tried this method you said. It can run successfully. Because gcc > > > > and clang have different Methods for determining whether to turn > > > > on the asan function, so if you judge the two methods in the code, it > feels not simple to judge in meson. > > > > > > There is already compiler specific #ifdef's why not do this contained in > one header file? > > > > > > The point is DPDK is trying to get away from having configuration > > > settings if at all possible. Configuration creates dependency > > > nightmares and also leaves many code paths as never tested. > > > > Not sure I follow your point here. We need some macro to easily tell > > if we are running with address sanitization enabled or not, so as to > > avoid having the multi-compiler detection rules all over the place. > > The only question is where it's better to have this in a header file or a > meson.build file. > > Given your objection and the fact that the meson.build code above > > looks a little awkward, I'd suggest putting the conditional checks in > malloc_elem.h. > > NVM working of meson as commn base seems like good option. +1, good option for the tool enable, thanks a lot for your guide and positive feedback. We will update them in V2 and send V2 soon. High appreciate that you can ACK this feature patch if no other objection, thanks. ^ permalink raw reply [flat|nested] 117+ messages in thread
* Re: [dpdk-dev] [PATCH] Enable AddressSanitizer feature on DPDK 2021-09-13 15:22 ` Bruce Richardson 2021-09-13 15:45 ` Stephen Hemminger @ 2021-09-14 3:11 ` Lin, Xueqin 1 sibling, 0 replies; 117+ messages in thread From: Lin, Xueqin @ 2021-09-14 3:11 UTC (permalink / raw) To: Richardson, Bruce, Stephen Hemminger Cc: Peng, ZhihongX, Burakov, Anatoly, Ananyev, Konstantin, dev > -----Original Message----- > From: Richardson, Bruce <bruce.richardson@intel.com> > Sent: Monday, September 13, 2021 11:22 PM > To: Stephen Hemminger <stephen@networkplumber.org> > Cc: Peng, ZhihongX <zhihongx.peng@intel.com>; Burakov, Anatoly > <anatoly.burakov@intel.com>; Ananyev, Konstantin > <konstantin.ananyev@intel.com>; dev@dpdk.org; Lin, Xueqin > <xueqin.lin@intel.com> > Subject: Re: [dpdk-dev] [PATCH] Enable AddressSanitizer feature on DPDK > > On Mon, Sep 13, 2021 at 08:05:58AM -0700, Stephen Hemminger wrote: > > On Mon, 13 Sep 2021 05:27:12 +0000 > > "Peng, ZhihongX" <zhihongx.peng@intel.com> wrote: > > > > > > -----Original Message----- > > > > From: Stephen Hemminger <stephen@networkplumber.org> > > > > Sent: Friday, September 10, 2021 10:48 AM > > > > To: Peng, ZhihongX <zhihongx.peng@intel.com> > > > > Cc: Burakov, Anatoly <anatoly.burakov@intel.com>; Ananyev, > > > > Konstantin <konstantin.ananyev@intel.com>; dev@dpdk.org; Lin, > > > > Xueqin <xueqin.lin@intel.com> > > > > Subject: Re: [PATCH] Enable AddressSanitizer feature on DPDK > > > > > > > > On Fri, 10 Sep 2021 02:01:47 +0000 zhihongx.peng@intel.com wrote: > > > > > > > > > > > > > > +if get_option('b_sanitize').startswith('address') > > > > > + cflags += '-DRTE_MALLOC_ASAN' > > > > > +endif > > > > > + > > > > > > > > This looks great, but can we make it just do-the-right-thing and > > > > get rid of the nerd knobs (i.e no meson configure). > > > > > > There are no new meson options being added here. Turning on/off address > sanitizing is a built-in meson option that is there already. > > > > > The address sanitizer already has a way to detect if enabled. > > > > > > > > GCC uses: > > > > __SANITIZE_ADDRESS__ > > > > > > > > Clang uses: > > > > #if defined(__has_feature) > > > > # if __has_feature(address_sanitizer) > > > > > > Tried this method you said. It can run successfully. Because gcc and > > > clang have different Methods for determining whether to turn on the > > > asan function, so if you judge the two methods in the code, it feels not > simple to judge in meson. > > > > There is already compiler specific #ifdef's why not do this contained in one > header file? > > > > The point is DPDK is trying to get away from having configuration > > settings if at all possible. Configuration creates dependency > > nightmares and also leaves many code paths as never tested. > > Not sure I follow your point here. We need some macro to easily tell if we > are running with address sanitization enabled or not, so as to avoid having > the multi-compiler detection rules all over the place. The only question is > where it's better to have this in a header file or a meson.build file. > Given your objection and the fact that the meson.build code above looks a > little awkward, I'd suggest putting the conditional checks in malloc_elem.h. > > Is something like the following what you had in mind? > > #ifdef __SANITIZE_ADDRESS__ > #define RTE_MALLOC_ASAN > #elif defined(__has_feature) && __has_feature(address_sanitizer) > #define RTE_MALLOC_ASAN > #endif > > /Bruce Agree, we will update it in V2 version and send it soon. Thanks Bruce for your review and guide. If no other objections, need your help to ACK this feature patch. ^ permalink raw reply [flat|nested] 117+ messages in thread
* Re: [dpdk-dev] [PATCH] Enable AddressSanitizer feature on DPDK 2021-09-10 2:01 [dpdk-dev] [PATCH] Enable AddressSanitizer feature on DPDK zhihongx.peng 2021-09-10 2:47 ` Stephen Hemminger @ 2021-09-10 17:58 ` David Christensen 2021-09-13 5:35 ` Peng, ZhihongX 2021-09-16 1:38 ` [dpdk-dev] [PATCH v2] " zhihongx.peng 2 siblings, 1 reply; 117+ messages in thread From: David Christensen @ 2021-09-10 17:58 UTC (permalink / raw) To: zhihongx.peng, anatoly.burakov, konstantin.ananyev, stephen Cc: dev, xueqin.lin > From: Zhihong Peng <zhihongx.peng@intel.com> > > AddressSanitizer (ASan) is a google memory error detect > standard tool. It could help to detect use-after-free and > {heap,stack,global}-buffer overflow bugs in C/C++ programs, > print detailed error information when error happens, large > improve debug efficiency. > > By referring to its implementation algorithm > (https://github.com/google/sanitizers/wiki/AddressSanitizerAlgorithm), > enable heap-buffer-overflow and use-after-free functions on dpdk. > > Here is an example of heap-buffer-overflow bug: > ...... > char *p = rte_zmalloc(NULL, 7, 0); > p[7] = 'a'; > ...... > > Here is an example of use-after-free bug: > ...... > char *p = rte_zmalloc(NULL, 7, 0); > rte_free(p); > *p = 'a'; > ...... > > If you want to use this feature, > you need to add below compilation options when compiling code: > -Dbuildtype=debug -Db_lundef=false -Db_sanitize=address > "-Dbuildtype=debug": Display code information when coredump occurs > in the program. > "-Db_lundef=false": It is enabled by default, and needs to be > disabled when using asan. On initial inspection, it appears ASAN functionality doesn't work with DPDK on PPC architecture. I tested the patch with several compiler versions (gcc 8.3.1 from RHEL 8.3 through gcc 11.2.1 from the IBM Advanced Toolchain 15.0) and observed the following error when running testpmd with ASAN enabled: AddressSanitizer:DEADLYSIGNAL ================================================================= ==49246==ERROR: AddressSanitizer: SEGV on unknown address 0x0000a0077bd0 (pc 0x000010b4eca4 bp 0x7fffffffe150 sp 0x7fffffffe150 T0) ==49246==The signal is caused by a UNKNOWN memory access. #0 0x10b4eca4 in asan_set_shadow ../lib/eal/common/malloc_elem.h:120 #1 0x10b4ed68 in asan_set_zone ../lib/eal/common/malloc_elem.h:135 #2 0x10b4ee90 in asan_clear_split_alloczone ../lib/eal/common/malloc_elem.h:162 #3 0x10b51f84 in malloc_elem_alloc ../lib/eal/common/malloc_elem.c:477 ... Can you incorporate an exception for PPC architecture with this patch while I look into the problem further? Dave ^ permalink raw reply [flat|nested] 117+ messages in thread
* Re: [dpdk-dev] [PATCH] Enable AddressSanitizer feature on DPDK 2021-09-10 17:58 ` David Christensen @ 2021-09-13 5:35 ` Peng, ZhihongX 2021-09-17 20:50 ` David Christensen 0 siblings, 1 reply; 117+ messages in thread From: Peng, ZhihongX @ 2021-09-13 5:35 UTC (permalink / raw) To: David Christensen, Burakov, Anatoly, Ananyev, Konstantin, stephen Cc: dev, Lin, Xueqin > -----Original Message----- > From: David Christensen <drc@linux.vnet.ibm.com> > Sent: Saturday, September 11, 2021 1:59 AM > To: Peng, ZhihongX <zhihongx.peng@intel.com>; Burakov, Anatoly > <anatoly.burakov@intel.com>; Ananyev, Konstantin > <konstantin.ananyev@intel.com>; stephen@networkplumber.org > Cc: dev@dpdk.org; Lin, Xueqin <xueqin.lin@intel.com> > Subject: Re: [dpdk-dev] [PATCH] Enable AddressSanitizer feature on DPDK > > > From: Zhihong Peng <zhihongx.peng@intel.com> > > > > AddressSanitizer (ASan) is a google memory error detect standard tool. > > It could help to detect use-after-free and {heap,stack,global}-buffer > > overflow bugs in C/C++ programs, print detailed error information when > > error happens, large improve debug efficiency. > > > > By referring to its implementation algorithm > > (https://github.com/google/sanitizers/wiki/AddressSanitizerAlgorithm), > > enable heap-buffer-overflow and use-after-free functions on dpdk. > > > > Here is an example of heap-buffer-overflow bug: > > ...... > > char *p = rte_zmalloc(NULL, 7, 0); > > p[7] = 'a'; > > ...... > > > > Here is an example of use-after-free bug: > > ...... > > char *p = rte_zmalloc(NULL, 7, 0); > > rte_free(p); > > *p = 'a'; > > ...... > > > > If you want to use this feature, > > you need to add below compilation options when compiling code: > > -Dbuildtype=debug -Db_lundef=false -Db_sanitize=address > > "-Dbuildtype=debug": Display code information when coredump occurs in > > the program. > > "-Db_lundef=false": It is enabled by default, and needs to be disabled > > when using asan. > > On initial inspection, it appears ASAN functionality doesn't work with DPDK > on PPC architecture. I tested the patch with several compiler versions (gcc > 8.3.1 from RHEL 8.3 through gcc 11.2.1 from the IBM Advanced Toolchain 15.0) > and observed the following error when running testpmd with ASAN enabled: > > AddressSanitizer:DEADLYSIGNAL > ========================================================== > ======= > ==49246==ERROR: AddressSanitizer: SEGV on unknown address > 0x0000a0077bd0 (pc 0x000010b4eca4 bp 0x7fffffffe150 sp 0x7fffffffe150 T0) > ==49246==The signal is caused by a UNKNOWN memory access. > #0 0x10b4eca4 in asan_set_shadow ../lib/eal/common/malloc_elem.h:120 > #1 0x10b4ed68 in asan_set_zone ../lib/eal/common/malloc_elem.h:135 > #2 0x10b4ee90 in asan_clear_split_alloczone > ../lib/eal/common/malloc_elem.h:162 > #3 0x10b51f84 in malloc_elem_alloc ../lib/eal/common/malloc_elem.c:477 > ... > > Can you incorporate an exception for PPC architecture with this patch while I > look into the problem further? > > Dave We do not have a ppc platform, so there is no adaptation. doc/guides/prog_guide/asan.rst has stated that we currently only support Linux x86_64. You can adapt according to the following documents, the main work is to modify the base address according to the platform. Documents: https://github.com/google/sanitizers/wiki/AddressSanitizer https://github.com/llvm/llvm-project/tree/main/compiler-rt ^ permalink raw reply [flat|nested] 117+ messages in thread
* Re: [dpdk-dev] [PATCH] Enable AddressSanitizer feature on DPDK 2021-09-13 5:35 ` Peng, ZhihongX @ 2021-09-17 20:50 ` David Christensen 2021-09-18 7:21 ` Peng, ZhihongX 0 siblings, 1 reply; 117+ messages in thread From: David Christensen @ 2021-09-17 20:50 UTC (permalink / raw) To: Peng, ZhihongX, Burakov, Anatoly, Ananyev, Konstantin, stephen Cc: dev, Lin, Xueqin >>> If you want to use this feature, >>> you need to add below compilation options when compiling code: >>> -Dbuildtype=debug -Db_lundef=false -Db_sanitize=address >>> "-Dbuildtype=debug": Display code information when coredump occurs in >>> the program. >>> "-Db_lundef=false": It is enabled by default, and needs to be disabled >>> when using asan. >> >> On initial inspection, it appears ASAN functionality doesn't work with DPDK >> on PPC architecture. I tested the patch with several compiler versions (gcc >> 8.3.1 from RHEL 8.3 through gcc 11.2.1 from the IBM Advanced Toolchain 15.0) >> and observed the following error when running testpmd with ASAN enabled: >> >> AddressSanitizer:DEADLYSIGNAL >> ========================================================== >> ======= >> ==49246==ERROR: AddressSanitizer: SEGV on unknown address >> 0x0000a0077bd0 (pc 0x000010b4eca4 bp 0x7fffffffe150 sp 0x7fffffffe150 T0) >> ==49246==The signal is caused by a UNKNOWN memory access. >> #0 0x10b4eca4 in asan_set_shadow ../lib/eal/common/malloc_elem.h:120 >> #1 0x10b4ed68 in asan_set_zone ../lib/eal/common/malloc_elem.h:135 >> #2 0x10b4ee90 in asan_clear_split_alloczone >> ../lib/eal/common/malloc_elem.h:162 >> #3 0x10b51f84 in malloc_elem_alloc ../lib/eal/common/malloc_elem.c:477 >> ... >> >> Can you incorporate an exception for PPC architecture with this patch while I >> look into the problem further? >> >> Dave > > We do not have a ppc platform, so there is no adaptation. doc/guides/prog_guide/asan.rst > has stated that we currently only support Linux x86_64. You can adapt according to the > following documents, the main work is to modify the base address according to the platform. > Documents: > https://github.com/google/sanitizers/wiki/AddressSanitizer > https://github.com/llvm/llvm-project/tree/main/compiler-rt Understand you don't have such a platform. I looked into it and suggest the following change in lib/eal/common/malloc_elem.h: #define ASAN_SHADOW_GRAIN_SIZE 8 #define ASAN_SHADOW_SCALE 3 #ifdef RTE_ARCH_PPC_64 #define ASAN_SHADOW_OFFSET 0x020000000000 #else #define ASAN_SHADOW_OFFSET 0x00007fff8000 #endif #define ASAN_MEM_FREE_FLAG 0xfd #define ASAN_MEM_REDZONE_FLAG 0xfa #define ASAN_MEM_TO_SHADOW(mem) (((mem) >> ASAN_SHADOW_SCALE) + ASAN_SHADOW_OFFSET) This resolves the segmentation error I receive. Dave P.S. FYI, here's the ASAN mapping I observe on x86 vs. POWER: x86 results: ----------------------------------------------------------------------------------- ASAN_OPTIONS=verbosity=1 ./a.out ==141271==AddressSanitizer: libc interceptors initialized || `[0x1000_7fff_8000, 0x7fff_ffff_ffff]` || HighMem || || `[0x0200_8fff_7000, 0x1000_7fff_7fff]` || HighShadow || || `[0x0000_8fff_7000, 0x0200_8fff_6fff]` || ShadowGap || || `[0x0000_7fff_8000, 0x0000_8fff_6fff]` || LowShadow || || `[0x0000_0000_0000, 0x0000_7fff_7fff]` || LowMem || MemToShadow(shadow): 0x0000_8fff_7000 0x0000_91ff_6dff 0x0040_91ff_6e00 0x0200_8fff_6fff redzone=16 max_redzone=2048 quarantine_size_mb=256M thread_local_quarantine_size_kb=1024K malloc_context_size=30 SHADOW_SCALE: 3 SHADOW_GRANULARITY: 8 SHADOW_OFFSET: 0x7fff_8000 POWER results: ----------------------------------------------------------------------------------- ASAN_OPTIONS=verbosity=1 ./a.out ... ==93284==AddressSanitizer: libc interceptors initialized || `[0x1200_0000_0000, 0x7fff_ffff_ffff]` || HighMem || || `[0x0440_0000_0000, 0x11ff_ffff_ffff]` || HighShadow || || `[0x0240_0000_0000, 0x043f_ffff_ffff]` || ShadowGap || || `[0x0200_0000_0000, 0x023f_ffff_ffff]` || LowShadow || || `[0x0000_0000_0000, 0x01ff_ffff_ffff]` || LowMem || MemToShadow(shadow): 0x0240_0000_0000 0x0247_ffff_ffff 0x0288_0000_0000 0x043f_ffff_ffff redzone=16 max_redzone=2048 quarantine_size_mb=256M thread_local_quarantine_size_kb=1024K malloc_context_size=30 SHADOW_SCALE: 3 SHADOW_GRANULARITY: 8 SHADOW_OFFSET: 0x200_0000_0000 ^ permalink raw reply [flat|nested] 117+ messages in thread
* Re: [dpdk-dev] [PATCH] Enable AddressSanitizer feature on DPDK 2021-09-17 20:50 ` David Christensen @ 2021-09-18 7:21 ` Peng, ZhihongX 2021-09-20 19:41 ` David Christensen 0 siblings, 1 reply; 117+ messages in thread From: Peng, ZhihongX @ 2021-09-18 7:21 UTC (permalink / raw) To: David Christensen, Burakov, Anatoly, Ananyev, Konstantin, stephen Cc: dev, Lin, Xueqin > -----Original Message----- > From: David Christensen <drc@linux.vnet.ibm.com> > Sent: Saturday, September 18, 2021 4:51 AM > To: Peng, ZhihongX <zhihongx.peng@intel.com>; Burakov, Anatoly > <anatoly.burakov@intel.com>; Ananyev, Konstantin > <konstantin.ananyev@intel.com>; stephen@networkplumber.org > Cc: dev@dpdk.org; Lin, Xueqin <xueqin.lin@intel.com> > Subject: Re: [dpdk-dev] [PATCH] Enable AddressSanitizer feature on DPDK > > >>> If you want to use this feature, > >>> you need to add below compilation options when compiling code: > >>> -Dbuildtype=debug -Db_lundef=false -Db_sanitize=address > >>> "-Dbuildtype=debug": Display code information when coredump occurs > >>> in the program. > >>> "-Db_lundef=false": It is enabled by default, and needs to be > >>> disabled when using asan. > >> > >> On initial inspection, it appears ASAN functionality doesn't work > >> with DPDK on PPC architecture. I tested the patch with several > >> compiler versions (gcc > >> 8.3.1 from RHEL 8.3 through gcc 11.2.1 from the IBM Advanced > >> Toolchain 15.0) and observed the following error when running testpmd > with ASAN enabled: > >> > >> AddressSanitizer:DEADLYSIGNAL > >> > ========================================================== > >> ======= > >> ==49246==ERROR: AddressSanitizer: SEGV on unknown address > >> 0x0000a0077bd0 (pc 0x000010b4eca4 bp 0x7fffffffe150 sp 0x7fffffffe150 > >> T0) ==49246==The signal is caused by a UNKNOWN memory access. > >> #0 0x10b4eca4 in > asan_set_shadow ../lib/eal/common/malloc_elem.h:120 > >> #1 0x10b4ed68 in > asan_set_zone ../lib/eal/common/malloc_elem.h:135 > >> #2 0x10b4ee90 in asan_clear_split_alloczone > >> ../lib/eal/common/malloc_elem.h:162 > >> #3 0x10b51f84 in malloc_elem_alloc > >> ../lib/eal/common/malloc_elem.c:477 > >> ... > >> > >> Can you incorporate an exception for PPC architecture with this patch > >> while I look into the problem further? > >> > >> Dave > > > > We do not have a ppc platform, so there is no adaptation. > > doc/guides/prog_guide/asan.rst has stated that we currently only > > support Linux x86_64. You can adapt according to the following documents, > the main work is to modify the base address according to the platform. > > Documents: > > https://github.com/google/sanitizers/wiki/AddressSanitizer > > https://github.com/llvm/llvm-project/tree/main/compiler-rt > > Understand you don't have such a platform. I looked into it and suggest the > following change in lib/eal/common/malloc_elem.h: > > #define ASAN_SHADOW_GRAIN_SIZE 8 > #define ASAN_SHADOW_SCALE 3 > #ifdef RTE_ARCH_PPC_64 > #define ASAN_SHADOW_OFFSET 0x020000000000 #else #define > ASAN_SHADOW_OFFSET 0x00007fff8000 #endif > #define ASAN_MEM_FREE_FLAG 0xfd > #define ASAN_MEM_REDZONE_FLAG 0xfa > #define ASAN_MEM_TO_SHADOW(mem) (((mem) >> > ASAN_SHADOW_SCALE) + > ASAN_SHADOW_OFFSET) > > > This resolves the segmentation error I receive. > > Dave > Great, good information for dpdk asan tool. Because we can't do many tests, so when this patch is merged into the main line, you can submit the ppc architecture patch. > P.S. FYI, here's the ASAN mapping I observe on x86 vs. POWER: > > x86 results: > ----------------------------------------------------------------------------------- > ASAN_OPTIONS=verbosity=1 ./a.out > ==141271==AddressSanitizer: libc interceptors initialized > || `[0x1000_7fff_8000, 0x7fff_ffff_ffff]` || HighMem || > || `[0x0200_8fff_7000, 0x1000_7fff_7fff]` || HighShadow || > || `[0x0000_8fff_7000, 0x0200_8fff_6fff]` || ShadowGap || > || `[0x0000_7fff_8000, 0x0000_8fff_6fff]` || LowShadow || > || `[0x0000_0000_0000, 0x0000_7fff_7fff]` || LowMem || > MemToShadow(shadow): 0x0000_8fff_7000 0x0000_91ff_6dff > 0x0040_91ff_6e00 > 0x0200_8fff_6fff > redzone=16 > max_redzone=2048 > quarantine_size_mb=256M > thread_local_quarantine_size_kb=1024K > malloc_context_size=30 > SHADOW_SCALE: 3 > SHADOW_GRANULARITY: 8 > SHADOW_OFFSET: 0x7fff_8000 > > POWER results: > ----------------------------------------------------------------------------------- > ASAN_OPTIONS=verbosity=1 ./a.out > ... > ==93284==AddressSanitizer: libc interceptors initialized > || `[0x1200_0000_0000, 0x7fff_ffff_ffff]` || HighMem || > || `[0x0440_0000_0000, 0x11ff_ffff_ffff]` || HighShadow || > || `[0x0240_0000_0000, 0x043f_ffff_ffff]` || ShadowGap || > || `[0x0200_0000_0000, 0x023f_ffff_ffff]` || LowShadow || > || `[0x0000_0000_0000, 0x01ff_ffff_ffff]` || LowMem || > MemToShadow(shadow): 0x0240_0000_0000 0x0247_ffff_ffff > 0x0288_0000_0000 > 0x043f_ffff_ffff > redzone=16 > max_redzone=2048 > quarantine_size_mb=256M > thread_local_quarantine_size_kb=1024K > malloc_context_size=30 > SHADOW_SCALE: 3 > SHADOW_GRANULARITY: 8 > SHADOW_OFFSET: 0x200_0000_0000 Peng, ZhihongX ^ permalink raw reply [flat|nested] 117+ messages in thread
* Re: [dpdk-dev] [PATCH] Enable AddressSanitizer feature on DPDK 2021-09-18 7:21 ` Peng, ZhihongX @ 2021-09-20 19:41 ` David Christensen 2021-09-21 8:29 ` David Marchand 0 siblings, 1 reply; 117+ messages in thread From: David Christensen @ 2021-09-20 19:41 UTC (permalink / raw) To: Peng, ZhihongX, Burakov, Anatoly, Ananyev, Konstantin, stephen Cc: dev, Lin, Xueqin On 9/18/21 12:21 AM, Peng, ZhihongX wrote: >> -----Original Message----- >> From: David Christensen <drc@linux.vnet.ibm.com> >> Sent: Saturday, September 18, 2021 4:51 AM >> To: Peng, ZhihongX <zhihongx.peng@intel.com>; Burakov, Anatoly >> <anatoly.burakov@intel.com>; Ananyev, Konstantin >> <konstantin.ananyev@intel.com>; stephen@networkplumber.org >> Cc: dev@dpdk.org; Lin, Xueqin <xueqin.lin@intel.com> >> Subject: Re: [dpdk-dev] [PATCH] Enable AddressSanitizer feature on DPDK >> >>>>> If you want to use this feature, >>>>> you need to add below compilation options when compiling code: >>>>> -Dbuildtype=debug -Db_lundef=false -Db_sanitize=address >>>>> "-Dbuildtype=debug": Display code information when coredump occurs >>>>> in the program. >>>>> "-Db_lundef=false": It is enabled by default, and needs to be >>>>> disabled when using asan. >>>> >>>> On initial inspection, it appears ASAN functionality doesn't work >>>> with DPDK on PPC architecture. I tested the patch with several >>>> compiler versions (gcc >>>> 8.3.1 from RHEL 8.3 through gcc 11.2.1 from the IBM Advanced >>>> Toolchain 15.0) and observed the following error when running testpmd >> with ASAN enabled: >>>> >>>> AddressSanitizer:DEADLYSIGNAL >>>> >> ========================================================== >>>> ======= >>>> ==49246==ERROR: AddressSanitizer: SEGV on unknown address >>>> 0x0000a0077bd0 (pc 0x000010b4eca4 bp 0x7fffffffe150 sp 0x7fffffffe150 >>>> T0) ==49246==The signal is caused by a UNKNOWN memory access. >>>> #0 0x10b4eca4 in >> asan_set_shadow ../lib/eal/common/malloc_elem.h:120 >>>> #1 0x10b4ed68 in >> asan_set_zone ../lib/eal/common/malloc_elem.h:135 >>>> #2 0x10b4ee90 in asan_clear_split_alloczone >>>> ../lib/eal/common/malloc_elem.h:162 >>>> #3 0x10b51f84 in malloc_elem_alloc >>>> ../lib/eal/common/malloc_elem.c:477 >>>> ... >>>> >>>> Can you incorporate an exception for PPC architecture with this patch >>>> while I look into the problem further? >>>> >>>> Dave >>> >>> We do not have a ppc platform, so there is no adaptation. >>> doc/guides/prog_guide/asan.rst has stated that we currently only >>> support Linux x86_64. You can adapt according to the following documents, >> the main work is to modify the base address according to the platform. >>> Documents: >>> https://github.com/google/sanitizers/wiki/AddressSanitizer >>> https://github.com/llvm/llvm-project/tree/main/compiler-rt >> >> Understand you don't have such a platform. I looked into it and suggest the >> following change in lib/eal/common/malloc_elem.h: >> >> #define ASAN_SHADOW_GRAIN_SIZE 8 >> #define ASAN_SHADOW_SCALE 3 >> #ifdef RTE_ARCH_PPC_64 >> #define ASAN_SHADOW_OFFSET 0x020000000000 #else #define >> ASAN_SHADOW_OFFSET 0x00007fff8000 #endif >> #define ASAN_MEM_FREE_FLAG 0xfd >> #define ASAN_MEM_REDZONE_FLAG 0xfa >> #define ASAN_MEM_TO_SHADOW(mem) (((mem) >> >> ASAN_SHADOW_SCALE) + >> ASAN_SHADOW_OFFSET) >> >> >> This resolves the segmentation error I receive. >> >> Dave >> > > Great, good information for dpdk asan tool. Because we can't do many tests, > so when this patch is merged into the main line, you can submit the ppc > architecture patch. If your argument is that this is x86 only then please ensure it can't be enabled on non-x86 platforms such as ARM and PPC. I can then easily submit a follow-on patch to enable for PPC. As the patch currently stands it enables ASAN on a non-tested platform and provides an unexpected error for some users when it can easily be avoided. I'd advise not accepting the patch as currently presented. Dave ^ permalink raw reply [flat|nested] 117+ messages in thread
* Re: [dpdk-dev] [PATCH] Enable AddressSanitizer feature on DPDK 2021-09-20 19:41 ` David Christensen @ 2021-09-21 8:29 ` David Marchand 2021-09-21 10:15 ` Jerin Jacob 2021-09-22 7:32 ` Peng, ZhihongX 0 siblings, 2 replies; 117+ messages in thread From: David Marchand @ 2021-09-21 8:29 UTC (permalink / raw) To: Peng, ZhihongX Cc: Burakov, Anatoly, David Christensen, Ananyev, Konstantin, stephen, dev, Lin, Xueqin On Mon, Sep 20, 2021 at 9:41 PM David Christensen <drc@linux.vnet.ibm.com> wrote: > >>> We do not have a ppc platform, so there is no adaptation. > >>> doc/guides/prog_guide/asan.rst has stated that we currently only > >>> support Linux x86_64. You can adapt according to the following documents, > >> the main work is to modify the base address according to the platform. > >>> Documents: > >>> https://github.com/google/sanitizers/wiki/AddressSanitizer > >>> https://github.com/llvm/llvm-project/tree/main/compiler-rt > >> > >> Understand you don't have such a platform. I looked into it and suggest the > >> following change in lib/eal/common/malloc_elem.h: > >> > >> #define ASAN_SHADOW_GRAIN_SIZE 8 > >> #define ASAN_SHADOW_SCALE 3 > >> #ifdef RTE_ARCH_PPC_64 > >> #define ASAN_SHADOW_OFFSET 0x020000000000 #else #define > >> ASAN_SHADOW_OFFSET 0x00007fff8000 #endif > >> #define ASAN_MEM_FREE_FLAG 0xfd > >> #define ASAN_MEM_REDZONE_FLAG 0xfa > >> #define ASAN_MEM_TO_SHADOW(mem) (((mem) >> > >> ASAN_SHADOW_SCALE) + > >> ASAN_SHADOW_OFFSET) > >> > >> > >> This resolves the segmentation error I receive. > >> > >> Dave > >> > > > > Great, good information for dpdk asan tool. Because we can't do many tests, > > so when this patch is merged into the main line, you can submit the ppc > > architecture patch. > > If your argument is that this is x86 only then please ensure it can't be > enabled on non-x86 platforms such as ARM and PPC. I can then easily > submit a follow-on patch to enable for PPC. > > As the patch currently stands it enables ASAN on a non-tested platform > and provides an unexpected error for some users when it can easily be > avoided. I'd advise not accepting the patch as currently presented. Please make sure only x86_64 gets this code enabled. I'll wait for a new revision, thanks. -- David Marchand ^ permalink raw reply [flat|nested] 117+ messages in thread
* Re: [dpdk-dev] [PATCH] Enable AddressSanitizer feature on DPDK 2021-09-21 8:29 ` David Marchand @ 2021-09-21 10:15 ` Jerin Jacob 2021-09-22 7:32 ` Peng, ZhihongX 1 sibling, 0 replies; 117+ messages in thread From: Jerin Jacob @ 2021-09-21 10:15 UTC (permalink / raw) To: David Marchand Cc: Peng, ZhihongX, Burakov, Anatoly, David Christensen, Ananyev, Konstantin, stephen, dev, Lin, Xueqin On Tue, Sep 21, 2021 at 1:59 PM David Marchand <david.marchand@redhat.com> wrote: > > On Mon, Sep 20, 2021 at 9:41 PM David Christensen > <drc@linux.vnet.ibm.com> wrote: > > >>> We do not have a ppc platform, so there is no adaptation. > > >>> doc/guides/prog_guide/asan.rst has stated that we currently only > > >>> support Linux x86_64. You can adapt according to the following documents, > > >> the main work is to modify the base address according to the platform. > > >>> Documents: > > >>> https://github.com/google/sanitizers/wiki/AddressSanitizer > > >>> https://github.com/llvm/llvm-project/tree/main/compiler-rt > > >> > > >> Understand you don't have such a platform. I looked into it and suggest the > > >> following change in lib/eal/common/malloc_elem.h: > > >> > > >> #define ASAN_SHADOW_GRAIN_SIZE 8 > > >> #define ASAN_SHADOW_SCALE 3 > > >> #ifdef RTE_ARCH_PPC_64 > > >> #define ASAN_SHADOW_OFFSET 0x020000000000 #else #define > > >> ASAN_SHADOW_OFFSET 0x00007fff8000 #endif > > >> #define ASAN_MEM_FREE_FLAG 0xfd > > >> #define ASAN_MEM_REDZONE_FLAG 0xfa > > >> #define ASAN_MEM_TO_SHADOW(mem) (((mem) >> > > >> ASAN_SHADOW_SCALE) + > > >> ASAN_SHADOW_OFFSET) > > >> > > >> > > >> This resolves the segmentation error I receive. > > >> > > >> Dave > > >> > > > > > > Great, good information for dpdk asan tool. Because we can't do many tests, > > > so when this patch is merged into the main line, you can submit the ppc > > > architecture patch. > > > > If your argument is that this is x86 only then please ensure it can't be > > enabled on non-x86 platforms such as ARM and PPC. I can then easily > > submit a follow-on patch to enable for PPC. > > > > As the patch currently stands it enables ASAN on a non-tested platform > > and provides an unexpected error for some users when it can easily be > > avoided. I'd advise not accepting the patch as currently presented. > > Please make sure only x86_64 gets this code enabled. I think, we need to opt out only the PPC. According https://developer.android.com/ndk/guides/asan, Arm64 and x86 are supported. I suggest resping by opting out PPC, I can test the next version and confirm the behavior on arm64 and give Ack. > I'll wait for a new revision, thanks. > > > -- > David Marchand > ^ permalink raw reply [flat|nested] 117+ messages in thread
* Re: [dpdk-dev] [PATCH] Enable AddressSanitizer feature on DPDK 2021-09-21 8:29 ` David Marchand 2021-09-21 10:15 ` Jerin Jacob @ 2021-09-22 7:32 ` Peng, ZhihongX 1 sibling, 0 replies; 117+ messages in thread From: Peng, ZhihongX @ 2021-09-22 7:32 UTC (permalink / raw) To: David Marchand Cc: Burakov, Anatoly, David Christensen, Ananyev, Konstantin, stephen, dev, Lin, Xueqin > -----Original Message----- > From: David Marchand <david.marchand@redhat.com> > Sent: Tuesday, September 21, 2021 4:30 PM > To: Peng, ZhihongX <zhihongx.peng@intel.com> > Cc: Burakov, Anatoly <anatoly.burakov@intel.com>; David Christensen > <drc@linux.vnet.ibm.com>; Ananyev, Konstantin > <konstantin.ananyev@intel.com>; stephen@networkplumber.org; > dev@dpdk.org; Lin, Xueqin <xueqin.lin@intel.com> > Subject: Re: [dpdk-dev] [PATCH] Enable AddressSanitizer feature on DPDK > > On Mon, Sep 20, 2021 at 9:41 PM David Christensen > <drc@linux.vnet.ibm.com> wrote: > > >>> We do not have a ppc platform, so there is no adaptation. > > >>> doc/guides/prog_guide/asan.rst has stated that we currently only > > >>> support Linux x86_64. You can adapt according to the following > > >>> documents, > > >> the main work is to modify the base address according to the platform. > > >>> Documents: > > >>> https://github.com/google/sanitizers/wiki/AddressSanitizer > > >>> https://github.com/llvm/llvm-project/tree/main/compiler-rt > > >> > > >> Understand you don't have such a platform. I looked into it and > > >> suggest the following change in lib/eal/common/malloc_elem.h: > > >> > > >> #define ASAN_SHADOW_GRAIN_SIZE 8 > > >> #define ASAN_SHADOW_SCALE 3 > > >> #ifdef RTE_ARCH_PPC_64 > > >> #define ASAN_SHADOW_OFFSET 0x020000000000 #else #define > > >> ASAN_SHADOW_OFFSET 0x00007fff8000 #endif > > >> #define ASAN_MEM_FREE_FLAG 0xfd > > >> #define ASAN_MEM_REDZONE_FLAG 0xfa > > >> #define ASAN_MEM_TO_SHADOW(mem) (((mem) >> > > >> ASAN_SHADOW_SCALE) + > > >> ASAN_SHADOW_OFFSET) > > >> > > >> > > >> This resolves the segmentation error I receive. > > >> > > >> Dave > > >> > > > > > > Great, good information for dpdk asan tool. Because we can't do many > > > tests, so when this patch is merged into the main line, you can > > > submit the ppc architecture patch. > > > > If your argument is that this is x86 only then please ensure it can't > > be enabled on non-x86 platforms such as ARM and PPC. I can then > > easily submit a follow-on patch to enable for PPC. > > > > As the patch currently stands it enables ASAN on a non-tested platform > > and provides an unexpected error for some users when it can easily be > > avoided. I'd advise not accepting the patch as currently presented. > > Please make sure only x86_64 gets this code enabled. > I'll wait for a new revision, thanks. V4 will enable platform macro switch. > > -- > David Marchand ^ permalink raw reply [flat|nested] 117+ messages in thread
* [dpdk-dev] [PATCH v2] Enable AddressSanitizer feature on DPDK 2021-09-10 2:01 [dpdk-dev] [PATCH] Enable AddressSanitizer feature on DPDK zhihongx.peng 2021-09-10 2:47 ` Stephen Hemminger 2021-09-10 17:58 ` David Christensen @ 2021-09-16 1:38 ` zhihongx.peng 2021-09-17 8:23 ` David Marchand 2021-09-18 7:41 ` [dpdk-dev] [PATCH v3] " zhihongx.peng 2 siblings, 2 replies; 117+ messages in thread From: zhihongx.peng @ 2021-09-16 1:38 UTC (permalink / raw) To: anatoly.burakov, konstantin.ananyev, stephen Cc: dev, xueqin.lin, Zhihong Peng From: Zhihong Peng <zhihongx.peng@intel.com> AddressSanitizer (ASan) is a google memory error detect standard tool. It could help to detect use-after-free and {heap,stack,global}-buffer overflow bugs in C/C++ programs, print detailed error information when error happens, large improve debug efficiency. By referring to its implementation algorithm (https://github.com/google/sanitizers/wiki/AddressSanitizerAlgorithm), enable heap-buffer-overflow and use-after-free functions on dpdk. DPDK ASAN function currently only supports on Linux x86_64. Here is an example of heap-buffer-overflow bug: ...... char *p = rte_zmalloc(NULL, 7, 0); p[7] = 'a'; ...... Here is an example of use-after-free bug: ...... char *p = rte_zmalloc(NULL, 7, 0); rte_free(p); *p = 'a'; ...... If you want to use this feature, you need to add below compilation options when compiling code: -Dbuildtype=debug -Db_lundef=false -Db_sanitize=address "-Dbuildtype=debug": Display code information when coredump occurs in the program. "-Db_lundef=false": It is enabled by default, and needs to be disabled when using asan. Signed-off-by: Xueqin Lin <xueqin.lin@intel.com> Signed-off-by: Zhihong Peng <zhihongx.peng@intel.com> --- app/test/meson.build | 2 + app/test/test_asan_heap_buffer_overflow.c | 32 ++++ app/test/test_asan_use_after_free.c | 33 ++++ doc/guides/prog_guide/asan.rst | 130 +++++++++++++++ doc/guides/prog_guide/index.rst | 1 + lib/eal/common/malloc_elem.c | 26 ++- lib/eal/common/malloc_elem.h | 184 +++++++++++++++++++++- lib/eal/common/malloc_heap.c | 12 ++ lib/eal/common/rte_malloc.c | 9 +- lib/pipeline/rte_swx_pipeline.c | 4 +- 10 files changed, 426 insertions(+), 7 deletions(-) create mode 100644 app/test/test_asan_heap_buffer_overflow.c create mode 100644 app/test/test_asan_use_after_free.c create mode 100644 doc/guides/prog_guide/asan.rst diff --git a/app/test/meson.build b/app/test/meson.build index a7611686ad..12a366bcec 100644 --- a/app/test/meson.build +++ b/app/test/meson.build @@ -150,6 +150,8 @@ test_sources = files( 'test_trace_perf.c', 'test_version.c', 'virtual_pmd.c', + 'test_asan_heap_buffer_overflow.c', + 'test_asan_use_after_free.c', ) test_deps = [ diff --git a/app/test/test_asan_heap_buffer_overflow.c b/app/test/test_asan_heap_buffer_overflow.c new file mode 100644 index 0000000000..10a8f43a9b --- /dev/null +++ b/app/test/test_asan_heap_buffer_overflow.c @@ -0,0 +1,32 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * Copyright(c) <2021> Intel Corporation + */ + +#include <stdint.h> +#include <stdio.h> +#include <stdarg.h> +#include <errno.h> +#include <string.h> +#include <rte_per_lcore.h> +#include <rte_errno.h> +#include <rte_string_fns.h> +#include <rte_malloc.h> + +#include "test.h" + +static int +asan_heap_buffer_overflow(void) +{ + uint32_t malloc_size = 9; + + char *p = rte_zmalloc(NULL, malloc_size, 0); + if (!p) { + printf("rte_zmalloc error."); + return -1; + } + p[9] = 'a'; + + return 0; +} + +REGISTER_TEST_COMMAND(asan_heap_buffer_overflow_autotest, asan_heap_buffer_overflow); diff --git a/app/test/test_asan_use_after_free.c b/app/test/test_asan_use_after_free.c new file mode 100644 index 0000000000..8ea598ea25 --- /dev/null +++ b/app/test/test_asan_use_after_free.c @@ -0,0 +1,33 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * Copyright(c) <2021> Intel Corporation + */ + +#include <stdint.h> +#include <stdio.h> +#include <stdarg.h> +#include <errno.h> +#include <string.h> +#include <rte_per_lcore.h> +#include <rte_errno.h> +#include <rte_string_fns.h> +#include <rte_malloc.h> + +#include "test.h" + +static int +asan_use_after_free(void) +{ + uint32_t malloc_size = 9; + + char *p = rte_zmalloc(NULL, malloc_size, 0); + if (!p) { + printf("rte_zmalloc error."); + return -1; + } + rte_free(p); + *p = 'a'; + + return 0; +} + +REGISTER_TEST_COMMAND(asan_use_after_free_autotest, asan_use_after_free); diff --git a/doc/guides/prog_guide/asan.rst b/doc/guides/prog_guide/asan.rst new file mode 100644 index 0000000000..721a99b946 --- /dev/null +++ b/doc/guides/prog_guide/asan.rst @@ -0,0 +1,130 @@ +.. Copyright (c) <2021>, Intel Corporation + All rights reserved. + +Memory error detect standard tool - AddressSanitizer(Asan) +========================================================== + +AddressSanitizer (ASan) is a google memory error detect +standard tool. It could help to detect use-after-free and +{heap,stack,global}-buffer overflow bugs in C/C++ programs, +print detailed error information when error happens, large +improve debug efficiency. + +By referring to its implementation algorithm +(https://github.com/google/sanitizers/wiki/AddressSanitizerAlgorithm), +enabled heap-buffer-overflow and use-after-free functions on dpdk. +DPDK ASAN function currently only supports on Linux x86_64. + +AddressSanitizer is a part of LLVM(3.1+)and GCC(4.8+). + +Example heap-buffer-overflow error +---------------------------------- + +Following error was reported when Asan was enabled:: + + app/test/test_asan_heap_buffer_overflow.c:25: Applied 9 bytes + of memory, but accessed the 10th byte of memory, so heap-buffer-overflow + appeared. + +Below code results in this error:: + + char *p = rte_zmalloc(NULL, 9, 0); + if (!p) { + printf("rte_zmalloc error."); + return -1; + } + p[9] = 'a'; + +The error log:: + + ==49433==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x7f773fafa249 at pc 0x5556b13bdae4 bp 0x7ffeb4965e40 sp 0x7ffeb4965e30 WRITE of size 1 at 0x7f773fafa249 thread T0 + #0 0x5556b13bdae3 in asan_heap_buffer_overflow ../app/test/test_asan_heap_buffer_overflow.c:25 + #1 0x5556b043e9d4 in cmd_autotest_parsed ../app/test/commands.c:71 + #2 0x5556b1cdd4b0 in cmdline_parse ../lib/cmdline/cmdline_parse.c:290 + #3 0x5556b1cd8987 in cmdline_valid_buffer ../lib/cmdline/cmdline.c:26 + #4 0x5556b1ce477a in rdline_char_in ../lib/cmdline/cmdline_rdline.c:421 + #5 0x5556b1cd923e in cmdline_in ../lib/cmdline/cmdline.c:149 + #6 0x5556b1cd9769 in cmdline_interact ../lib/cmdline/cmdline.c:223 + #7 0x5556b045f53b in main ../app/test/test.c:234 + #8 0x7f7f1eba90b2 in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x270b2) + #9 0x5556b043e70d in _start (/home/pzh/yyy/x86_64-native-linuxapp-gcc/app/test/dpdk-test+0x7ce70d) + + Address 0x7f773fafa249 is a wild pointer. + SUMMARY: AddressSanitizer: heap-buffer-overflow ../app/test/test_asan_heap_buffer_overflow.c:25 in asan_heap_buffer_overflow + Shadow bytes around the buggy address: + 0x0fef67f573f0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 0x0fef67f57400: fa fa 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 0x0fef67f57410: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 0x0fef67f57420: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 0x0fef67f57430: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + =>0x0fef67f57440: 00 00 00 00 00 00 fa fa 00[01]fa 00 00 00 00 00 + 0x0fef67f57450: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 0x0fef67f57460: 00 00 00 00 00 00 fa fa 00 00 00 00 00 00 00 00 + 0x0fef67f57470: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 0x0fef67f57480: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + +Example use-after-free error +---------------------------- + +Following error was reported when Asan was enabled:: + + app/test/test_asan_use_after_free.c:26: Applied for 9 bytes of + memory, and accessed the first byte after released, so + heap-use-after-free appeared. + +Below code results in this error:: + + char *p = rte_zmalloc(NULL, 9, 0); + if (!p) { + printf("rte_zmalloc error."); + return -1; + } + rte_free(p); + *p = 'a'; + +The error log:: + + ==49478==ERROR: AddressSanitizer: heap-use-after-free on address 0x7fe2ffafa240 at pc 0x56409b084bc8 bp 0x7ffef62c57d0 sp 0x7ffef62c57c0 WRITE of size 1 at 0x7fe2ffafa240 thread T0 + #0 0x56409b084bc7 in asan_use_after_free ../app/test/test_asan_use_after_free.c:26 + #1 0x56409a1059d4 in cmd_autotest_parsed ../app/test/commands.c:71 + #2 0x56409b9a44b0 in cmdline_parse ../lib/cmdline/cmdline_parse.c:290 + #3 0x56409b99f987 in cmdline_valid_buffer ../lib/cmdline/cmdline.c:26 + #4 0x56409b9ab77a in rdline_char_in ../lib/cmdline/cmdline_rdline.c:421 + #5 0x56409b9a023e in cmdline_in ../lib/cmdline/cmdline.c:149 + #6 0x56409b9a0769 in cmdline_interact ../lib/cmdline/cmdline.c:223 + #7 0x56409a12653b in main ../app/test/test.c:234 + #8 0x7feafafc20b2 in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x270b2) + #9 0x56409a10570d in _start (/home/pzh/yyy/x86_64-native-linuxapp-gcc/app/test/dpdk-test+0x7ce70d) + + Address 0x7fe2ffafa240 is a wild pointer. + SUMMARY: AddressSanitizer: heap-use-after-free ../app/test/test_asan_use_after_free.c:26 in asan_use_after_free + Shadow bytes around the buggy address: + 0x0ffcdff573f0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 0x0ffcdff57400: fa fa 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 0x0ffcdff57410: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 0x0ffcdff57420: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 0x0ffcdff57430: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + =>0x0ffcdff57440: 00 00 00 00 00 00 00 00[fd]fd fd fd fd fd fd fd + 0x0ffcdff57450: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 0x0ffcdff57460: 00 00 00 00 00 00 fa fa 00 00 00 00 00 00 00 00 + 0x0ffcdff57470: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 0x0ffcdff57480: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 0x0ffcdff57490: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + +Usage +----- + +meson build +^^^^^^^^^^^ + +To enable Asan in meson build system, use following meson build command: + +Example usage:: + + meson build -Dbuildtype=debug -Db_lundef=false -Db_sanitize=address + ninja -C build + +.. Note:: + + Centos8 needs to install libasan separately. + If the program uses cmdline, when a memory bug occurs, need to execute the "stty echo" command. diff --git a/doc/guides/prog_guide/index.rst b/doc/guides/prog_guide/index.rst index 2dce507f46..df8a4b93e1 100644 --- a/doc/guides/prog_guide/index.rst +++ b/doc/guides/prog_guide/index.rst @@ -71,3 +71,4 @@ Programmer's Guide lto profile_app glossary + asan diff --git a/lib/eal/common/malloc_elem.c b/lib/eal/common/malloc_elem.c index c2c9461f1d..bdd20a162e 100644 --- a/lib/eal/common/malloc_elem.c +++ b/lib/eal/common/malloc_elem.c @@ -446,6 +446,8 @@ malloc_elem_alloc(struct malloc_elem *elem, size_t size, unsigned align, struct malloc_elem *new_free_elem = RTE_PTR_ADD(new_elem, size + MALLOC_ELEM_OVERHEAD); + asan_clear_split_alloczone(new_free_elem); + split_elem(elem, new_free_elem); malloc_elem_free_list_insert(new_free_elem); @@ -458,6 +460,8 @@ malloc_elem_alloc(struct malloc_elem *elem, size_t size, unsigned align, elem->state = ELEM_BUSY; elem->pad = old_elem_size; + asan_clear_alloczone(elem); + /* put a dummy header in padding, to point to real element header */ if (elem->pad > 0) { /* pad will be at least 64-bytes, as everything * is cache-line aligned */ @@ -470,12 +474,18 @@ malloc_elem_alloc(struct malloc_elem *elem, size_t size, unsigned align, return new_elem; } + asan_clear_split_alloczone(new_elem); + /* we are going to split the element in two. The original element * remains free, and the new element is the one allocated. * Re-insert original element, in case its new size makes it * belong on a different list. */ + split_elem(elem, new_elem); + + asan_clear_alloczone(new_elem); + new_elem->state = ELEM_BUSY; malloc_elem_free_list_insert(elem); @@ -601,6 +611,8 @@ malloc_elem_hide_region(struct malloc_elem *elem, void *start, size_t len) if (next && next_elem_is_adjacent(elem)) { len_after = RTE_PTR_DIFF(next, hide_end); if (len_after >= MALLOC_ELEM_OVERHEAD + MIN_DATA_SIZE) { + asan_clear_split_alloczone(hide_end); + /* split after */ split_elem(elem, hide_end); @@ -615,6 +627,8 @@ malloc_elem_hide_region(struct malloc_elem *elem, void *start, size_t len) if (prev && prev_elem_is_adjacent(elem)) { len_before = RTE_PTR_DIFF(hide_start, elem); if (len_before >= MALLOC_ELEM_OVERHEAD + MIN_DATA_SIZE) { + asan_clear_split_alloczone(hide_start); + /* split before */ split_elem(elem, hide_start); @@ -628,6 +642,8 @@ malloc_elem_hide_region(struct malloc_elem *elem, void *start, size_t len) } } + asan_clear_alloczone(elem); + remove_elem(elem); } @@ -641,8 +657,10 @@ malloc_elem_resize(struct malloc_elem *elem, size_t size) const size_t new_size = size + elem->pad + MALLOC_ELEM_OVERHEAD; /* if we request a smaller size, then always return ok */ - if (elem->size >= new_size) + if (elem->size >= new_size) { + asan_clear_alloczone(elem); return 0; + } /* check if there is a next element, it's free and adjacent */ if (!elem->next || elem->next->state != ELEM_FREE || @@ -661,9 +679,15 @@ malloc_elem_resize(struct malloc_elem *elem, size_t size) /* now we have a big block together. Lets cut it down a bit, by splitting */ struct malloc_elem *split_pt = RTE_PTR_ADD(elem, new_size); split_pt = RTE_PTR_ALIGN_CEIL(split_pt, RTE_CACHE_LINE_SIZE); + + asan_clear_split_alloczone(split_pt); + split_elem(elem, split_pt); malloc_elem_free_list_insert(split_pt); } + + asan_clear_alloczone(elem); + return 0; } diff --git a/lib/eal/common/malloc_elem.h b/lib/eal/common/malloc_elem.h index a1e5f7f02c..01a739f2ea 100644 --- a/lib/eal/common/malloc_elem.h +++ b/lib/eal/common/malloc_elem.h @@ -7,6 +7,14 @@ #include <stdbool.h> +#ifdef __SANITIZE_ADDRESS__ +#define RTE_MALLOC_ASAN +#elif defined(__has_feature) +# if __has_feature(address_sanitizer) +#define RTE_MALLOC_ASAN +# endif +#endif + #define MIN_DATA_SIZE (RTE_CACHE_LINE_SIZE) /* dummy definition of struct so we can use pointers to it in malloc_elem struct */ @@ -36,10 +44,20 @@ struct malloc_elem { uint64_t header_cookie; /* Cookie marking start of data */ /* trailer cookie at start + size */ #endif +#ifdef RTE_MALLOC_ASAN + size_t user_size; + uint64_t asan_cookie[2]; /*must be next to header_cookie*/ +#endif } __rte_cache_aligned; +static const unsigned int MALLOC_ELEM_HEADER_LEN = sizeof(struct malloc_elem); + #ifndef RTE_MALLOC_DEBUG -static const unsigned MALLOC_ELEM_TRAILER_LEN = 0; +#ifdef RTE_MALLOC_ASAN +static const unsigned int MALLOC_ELEM_TRAILER_LEN = RTE_CACHE_LINE_SIZE; +#else +static const unsigned int MALLOC_ELEM_TRAILER_LEN; +#endif /* dummy function - just check if pointer is non-null */ static inline int @@ -55,7 +73,7 @@ set_trailer(struct malloc_elem *elem __rte_unused){ } #else -static const unsigned MALLOC_ELEM_TRAILER_LEN = RTE_CACHE_LINE_SIZE; +static const unsigned int MALLOC_ELEM_TRAILER_LEN = RTE_CACHE_LINE_SIZE; #define MALLOC_HEADER_COOKIE 0xbadbadbadadd2e55ULL /**< Header cookie. */ #define MALLOC_TRAILER_COOKIE 0xadd2e55badbadbadULL /**< Trailer cookie.*/ @@ -90,9 +108,169 @@ malloc_elem_cookies_ok(const struct malloc_elem *elem) #endif -static const unsigned MALLOC_ELEM_HEADER_LEN = sizeof(struct malloc_elem); #define MALLOC_ELEM_OVERHEAD (MALLOC_ELEM_HEADER_LEN + MALLOC_ELEM_TRAILER_LEN) +#ifdef RTE_MALLOC_ASAN + +#define ASAN_SHADOW_GRAIN_SIZE 8 +#define ASAN_MEM_FREE_FLAG 0xfd +#define ASAN_MEM_REDZONE_FLAG 0xfa +#define ASAN_MEM_TO_SHADOW(mem) (((mem) >> 3) + 0x00007fff8000) + +#if defined(__clang__) +__attribute__((no_sanitize("address", "hwaddress"))) +#else +__attribute__((no_sanitize_address)) +#endif +static inline void +asan_set_shadow(void *addr, char val) +{ + *(char *)addr = val; +} + +static inline void +asan_set_zone(void *ptr, size_t len, uint32_t val) +{ + size_t offset; + char *shadow; + size_t zone_len = len / ASAN_SHADOW_GRAIN_SIZE; + if (len % ASAN_SHADOW_GRAIN_SIZE != 0) + zone_len += 1; + + for (size_t i = 0; i < zone_len; i++) { + offset = i * ASAN_SHADOW_GRAIN_SIZE; + shadow = (char *)ASAN_MEM_TO_SHADOW(((int64_t)ptr + offset)); + asan_set_shadow(shadow, val); + } +} + +/* + * When the memory is released, the release mark is + * set in the corresponding range of the shadow area. + */ +static inline void +asan_set_freezone(void *ptr, size_t size) +{ + asan_set_zone(ptr, size, ASAN_MEM_FREE_FLAG); +} + +/* + * When the memory is allocated, memory state must set as accessible. + */ +static inline void +asan_clear_alloczone(struct malloc_elem *elem) +{ + asan_set_zone((void *)elem, elem->size, 0x0); +} + +static inline void +asan_clear_split_alloczone(struct malloc_elem *elem) +{ + void *ptr = RTE_PTR_SUB(elem, MALLOC_ELEM_TRAILER_LEN); + asan_set_zone(ptr, MALLOC_ELEM_OVERHEAD, 0x0); +} + +/* + * When the memory is allocated, the memory boundary is + * marked in the corresponding range of the shadow area. + */ +static inline void +asan_set_redzone(struct malloc_elem *elem, size_t user_size) +{ + uint64_t ptr; + char *shadow; + if (elem != NULL) { + if (elem->state != ELEM_PAD) + elem = RTE_PTR_ADD(elem, elem->pad); + + elem->user_size = user_size; + + /* Set mark before the start of the allocated memory */ + ptr = (uint64_t)RTE_PTR_ADD(elem, MALLOC_ELEM_HEADER_LEN) + - ASAN_SHADOW_GRAIN_SIZE; + shadow = (char *)ASAN_MEM_TO_SHADOW(ptr); + asan_set_shadow(shadow, ASAN_MEM_REDZONE_FLAG); + shadow = (char *)ASAN_MEM_TO_SHADOW(ptr + - ASAN_SHADOW_GRAIN_SIZE); + asan_set_shadow(shadow, ASAN_MEM_REDZONE_FLAG); + + /* Set mark after the end of the allocated memory */ + ptr = (uint64_t)RTE_PTR_ADD(elem, MALLOC_ELEM_HEADER_LEN + + elem->user_size); + shadow = (char *)ASAN_MEM_TO_SHADOW(ptr); + uint32_t val = (ptr % ASAN_SHADOW_GRAIN_SIZE); + val = (val == 0) ? ASAN_MEM_REDZONE_FLAG : val; + asan_set_shadow(shadow, val); + shadow = (char *)ASAN_MEM_TO_SHADOW(ptr + + ASAN_SHADOW_GRAIN_SIZE); + asan_set_shadow(shadow, ASAN_MEM_REDZONE_FLAG); + } +} + +/* + * When the memory is released, the mark of the memory boundary + * in the corresponding range of the shadow area is cleared. + */ +static inline void +asan_clear_redzone(struct malloc_elem *elem) +{ + uint64_t ptr; + char *shadow; + if (elem != NULL) { + elem = RTE_PTR_ADD(elem, elem->pad); + + /* Clear mark before the start of the allocated memory */ + ptr = (uint64_t)RTE_PTR_ADD(elem, MALLOC_ELEM_HEADER_LEN) + - ASAN_SHADOW_GRAIN_SIZE; + shadow = (char *)ASAN_MEM_TO_SHADOW(ptr); + asan_set_shadow(shadow, 0x00); + shadow = (char *)ASAN_MEM_TO_SHADOW(ptr + - ASAN_SHADOW_GRAIN_SIZE); + asan_set_shadow(shadow, 0x00); + + /* Clear mark after the end of the allocated memory */ + ptr = (uint64_t)RTE_PTR_ADD(elem, MALLOC_ELEM_HEADER_LEN + + elem->user_size); + shadow = (char *)ASAN_MEM_TO_SHADOW(ptr); + asan_set_shadow(shadow, 0x00); + shadow = (char *)ASAN_MEM_TO_SHADOW(ptr + + ASAN_SHADOW_GRAIN_SIZE); + asan_set_shadow(shadow, 0x00); + } +} + +static inline size_t +old_malloc_size(struct malloc_elem *elem) +{ + if (elem->state != ELEM_PAD) + elem = RTE_PTR_ADD(elem, elem->pad); + + return elem->user_size; +} +#else +static inline void +asan_set_freezone(void *ptr __rte_unused, size_t size __rte_unused) { } + +static inline void +asan_clear_alloczone(struct malloc_elem *elem __rte_unused) { } + +static inline void +asan_clear_split_alloczone(struct malloc_elem *elem __rte_unused) { } + +static inline void +asan_set_redzone(struct malloc_elem *elem __rte_unused, + size_t user_size __rte_unused) { } + +static inline void +asan_clear_redzone(struct malloc_elem *elem __rte_unused) { } + +static inline size_t +old_malloc_size(struct malloc_elem *elem) +{ + return elem->size - elem->pad - MALLOC_ELEM_OVERHEAD; +} +#endif + /* * Given a pointer to the start of a memory block returned by malloc, get * the actual malloc_elem header for that block. diff --git a/lib/eal/common/malloc_heap.c b/lib/eal/common/malloc_heap.c index ee400f38ec..775d6789df 100644 --- a/lib/eal/common/malloc_heap.c +++ b/lib/eal/common/malloc_heap.c @@ -237,6 +237,7 @@ heap_alloc(struct malloc_heap *heap, const char *type __rte_unused, size_t size, unsigned int flags, size_t align, size_t bound, bool contig) { struct malloc_elem *elem; + size_t user_size = size; size = RTE_CACHE_LINE_ROUNDUP(size); align = RTE_CACHE_LINE_ROUNDUP(align); @@ -250,6 +251,8 @@ heap_alloc(struct malloc_heap *heap, const char *type __rte_unused, size_t size, /* increase heap's count of allocated elements */ heap->alloc_count++; + + asan_set_redzone(elem, user_size); } return elem == NULL ? NULL : (void *)(&elem[1]); @@ -270,6 +273,8 @@ heap_alloc_biggest(struct malloc_heap *heap, const char *type __rte_unused, /* increase heap's count of allocated elements */ heap->alloc_count++; + + asan_set_redzone(elem, size); } return elem == NULL ? NULL : (void *)(&elem[1]); @@ -841,6 +846,8 @@ malloc_heap_free(struct malloc_elem *elem) if (!malloc_elem_cookies_ok(elem) || elem->state != ELEM_BUSY) return -1; + asan_clear_redzone(elem); + /* elem may be merged with previous element, so keep heap address */ heap = elem->heap; msl = elem->msl; @@ -848,6 +855,9 @@ malloc_heap_free(struct malloc_elem *elem) rte_spinlock_lock(&(heap->lock)); + void *asan_ptr = RTE_PTR_ADD(elem, MALLOC_ELEM_HEADER_LEN + elem->pad); + size_t asan_data_len = elem->size - MALLOC_ELEM_OVERHEAD - elem->pad; + /* mark element as free */ elem->state = ELEM_FREE; @@ -1001,6 +1011,8 @@ malloc_heap_free(struct malloc_elem *elem) rte_mcfg_mem_write_unlock(); free_unlock: + asan_set_freezone(asan_ptr, asan_data_len); + rte_spinlock_unlock(&(heap->lock)); return ret; } diff --git a/lib/eal/common/rte_malloc.c b/lib/eal/common/rte_malloc.c index 9d39e58c08..d0bec26920 100644 --- a/lib/eal/common/rte_malloc.c +++ b/lib/eal/common/rte_malloc.c @@ -162,6 +162,8 @@ rte_calloc(const char *type, size_t num, size_t size, unsigned align) void * rte_realloc_socket(void *ptr, size_t size, unsigned int align, int socket) { + size_t user_size; + if (ptr == NULL) return rte_malloc_socket(NULL, size, align, socket); @@ -171,6 +173,8 @@ rte_realloc_socket(void *ptr, size_t size, unsigned int align, int socket) return NULL; } + user_size = size; + size = RTE_CACHE_LINE_ROUNDUP(size), align = RTE_CACHE_LINE_ROUNDUP(align); /* check requested socket id and alignment matches first, and if ok, @@ -181,6 +185,9 @@ rte_realloc_socket(void *ptr, size_t size, unsigned int align, int socket) RTE_PTR_ALIGN(ptr, align) == ptr && malloc_heap_resize(elem, size) == 0) { rte_eal_trace_mem_realloc(size, align, socket, ptr); + + asan_set_redzone(elem, user_size); + return ptr; } @@ -192,7 +199,7 @@ rte_realloc_socket(void *ptr, size_t size, unsigned int align, int socket) if (new_ptr == NULL) return NULL; /* elem: |pad|data_elem|data|trailer| */ - const size_t old_size = elem->size - elem->pad - MALLOC_ELEM_OVERHEAD; + const size_t old_size = old_malloc_size(elem); rte_memcpy(new_ptr, ptr, old_size < size ? old_size : size); rte_free(ptr); diff --git a/lib/pipeline/rte_swx_pipeline.c b/lib/pipeline/rte_swx_pipeline.c index 8eb978a30c..aaa0107d02 100644 --- a/lib/pipeline/rte_swx_pipeline.c +++ b/lib/pipeline/rte_swx_pipeline.c @@ -6340,7 +6340,7 @@ instr_meter_translate(struct rte_swx_pipeline *p, return 0; } - CHECK(0, EINVAL); + return -EINVAL; } static inline struct meter * @@ -8025,7 +8025,7 @@ instr_translate(struct rte_swx_pipeline *p, instr, data); - CHECK(0, EINVAL); + return -EINVAL; } static struct instruction_data * -- 2.25.1 ^ permalink raw reply [flat|nested] 117+ messages in thread
* Re: [dpdk-dev] [PATCH v2] Enable AddressSanitizer feature on DPDK 2021-09-16 1:38 ` [dpdk-dev] [PATCH v2] " zhihongx.peng @ 2021-09-17 8:23 ` David Marchand 2021-09-17 9:12 ` Peng, ZhihongX 2021-09-18 7:41 ` [dpdk-dev] [PATCH v3] " zhihongx.peng 1 sibling, 1 reply; 117+ messages in thread From: David Marchand @ 2021-09-17 8:23 UTC (permalink / raw) To: Zhihong Peng Cc: Burakov, Anatoly, Ananyev, Konstantin, Stephen Hemminger, dev, Xueqin Lin On Thu, Sep 16, 2021 at 3:47 AM <zhihongx.peng@intel.com> wrote: > > From: Zhihong Peng <zhihongx.peng@intel.com> > > AddressSanitizer (ASan) is a google memory error detect > standard tool. It could help to detect use-after-free and > {heap,stack,global}-buffer overflow bugs in C/C++ programs, > print detailed error information when error happens, large > improve debug efficiency. > > By referring to its implementation algorithm > (https://github.com/google/sanitizers/wiki/AddressSanitizerAlgorithm), > enable heap-buffer-overflow and use-after-free functions on dpdk. > DPDK ASAN function currently only supports on Linux x86_64. > > Here is an example of heap-buffer-overflow bug: > ...... > char *p = rte_zmalloc(NULL, 7, 0); > p[7] = 'a'; > ...... > > Here is an example of use-after-free bug: > ...... > char *p = rte_zmalloc(NULL, 7, 0); > rte_free(p); > *p = 'a'; > ...... > Unfortunately, this won't build with some (too smart) compilers. Can you look at the CI report? Thanks. > If you want to use this feature, > you need to add below compilation options when compiling code: > -Dbuildtype=debug -Db_lundef=false -Db_sanitize=address > "-Dbuildtype=debug": Display code information when coredump occurs > in the program. > "-Db_lundef=false": It is enabled by default, and needs to be > disabled when using asan. > > Signed-off-by: Xueqin Lin <xueqin.lin@intel.com> > Signed-off-by: Zhihong Peng <zhihongx.peng@intel.com> -- David Marchand ^ permalink raw reply [flat|nested] 117+ messages in thread
* Re: [dpdk-dev] [PATCH v2] Enable AddressSanitizer feature on DPDK 2021-09-17 8:23 ` David Marchand @ 2021-09-17 9:12 ` Peng, ZhihongX 2021-09-17 14:58 ` Stephen Hemminger 0 siblings, 1 reply; 117+ messages in thread From: Peng, ZhihongX @ 2021-09-17 9:12 UTC (permalink / raw) To: David Marchand Cc: Burakov, Anatoly, Ananyev, Konstantin, Stephen Hemminger, dev, Lin, Xueqin > -----Original Message----- > From: David Marchand <david.marchand@redhat.com> > Sent: Friday, September 17, 2021 4:23 PM > To: Peng, ZhihongX <zhihongx.peng@intel.com> > Cc: Burakov, Anatoly <anatoly.burakov@intel.com>; Ananyev, Konstantin > <konstantin.ananyev@intel.com>; Stephen Hemminger > <stephen@networkplumber.org>; dev <dev@dpdk.org>; Lin, Xueqin > <xueqin.lin@intel.com> > Subject: Re: [dpdk-dev] [PATCH v2] Enable AddressSanitizer feature on DPDK > > On Thu, Sep 16, 2021 at 3:47 AM <zhihongx.peng@intel.com> wrote: > > > > From: Zhihong Peng <zhihongx.peng@intel.com> > > > > AddressSanitizer (ASan) is a google memory error detect standard tool. > > It could help to detect use-after-free and {heap,stack,global}-buffer > > overflow bugs in C/C++ programs, print detailed error information when > > error happens, large improve debug efficiency. > > > > By referring to its implementation algorithm > > (https://github.com/google/sanitizers/wiki/AddressSanitizerAlgorithm), > > enable heap-buffer-overflow and use-after-free functions on dpdk. > > DPDK ASAN function currently only supports on Linux x86_64. > > > > Here is an example of heap-buffer-overflow bug: > > ...... > > char *p = rte_zmalloc(NULL, 7, 0); > > p[7] = 'a'; > > ...... > > > > Here is an example of use-after-free bug: > > ...... > > char *p = rte_zmalloc(NULL, 7, 0); > > rte_free(p); > > *p = 'a'; > > ...... > > > > Unfortunately, this won't build with some (too smart) compilers. > Can you look at the CI report? > Thanks. I am trying to solve this issue.If there is no good solution, this test case will be removed. Thanks. > > > > If you want to use this feature, > > you need to add below compilation options when compiling code: > > -Dbuildtype=debug -Db_lundef=false -Db_sanitize=address > > "-Dbuildtype=debug": Display code information when coredump occurs in > > the program. > > "-Db_lundef=false": It is enabled by default, and needs to be disabled > > when using asan. > > > > Signed-off-by: Xueqin Lin <xueqin.lin@intel.com> > > Signed-off-by: Zhihong Peng <zhihongx.peng@intel.com> > > > > -- > David Marchand ^ permalink raw reply [flat|nested] 117+ messages in thread
* Re: [dpdk-dev] [PATCH v2] Enable AddressSanitizer feature on DPDK 2021-09-17 9:12 ` Peng, ZhihongX @ 2021-09-17 14:58 ` Stephen Hemminger 2021-09-18 6:36 ` Peng, ZhihongX 0 siblings, 1 reply; 117+ messages in thread From: Stephen Hemminger @ 2021-09-17 14:58 UTC (permalink / raw) To: Peng, ZhihongX Cc: David Marchand, Burakov, Anatoly, Ananyev, Konstantin, dev, Lin, Xueqin On Fri, 17 Sep 2021 09:12:07 +0000 "Peng, ZhihongX" <zhihongx.peng@intel.com> wrote: > > > By referring to its implementation algorithm > > > (https://github.com/google/sanitizers/wiki/AddressSanitizerAlgorithm), > > > enable heap-buffer-overflow and use-after-free functions on dpdk. > > > DPDK ASAN function currently only supports on Linux x86_64. We are using ASAN on Arm64 without problems, don't know why the patch shouldn't work there as well. ^ permalink raw reply [flat|nested] 117+ messages in thread
* Re: [dpdk-dev] [PATCH v2] Enable AddressSanitizer feature on DPDK 2021-09-17 14:58 ` Stephen Hemminger @ 2021-09-18 6:36 ` Peng, ZhihongX 0 siblings, 0 replies; 117+ messages in thread From: Peng, ZhihongX @ 2021-09-18 6:36 UTC (permalink / raw) To: Stephen Hemminger Cc: David Marchand, Burakov, Anatoly, Ananyev, Konstantin, dev, Lin, Xueqin > -----Original Message----- > From: Stephen Hemminger <stephen@networkplumber.org> > Sent: Friday, September 17, 2021 10:59 PM > To: Peng, ZhihongX <zhihongx.peng@intel.com> > Cc: David Marchand <david.marchand@redhat.com>; Burakov, Anatoly > <anatoly.burakov@intel.com>; Ananyev, Konstantin > <konstantin.ananyev@intel.com>; dev <dev@dpdk.org>; Lin, Xueqin > <xueqin.lin@intel.com> > Subject: Re: [dpdk-dev] [PATCH v2] Enable AddressSanitizer feature on DPDK > > On Fri, 17 Sep 2021 09:12:07 +0000 > "Peng, ZhihongX" <zhihongx.peng@intel.com> wrote: > > > > > By referring to its implementation algorithm > > > > (https://github.com/google/sanitizers/wiki/AddressSanitizerAlgorit > > > > hm), enable heap-buffer-overflow and use-after-free functions on > > > > dpdk. > > > > DPDK ASAN function currently only supports on Linux x86_64. > > We are using ASAN on Arm64 without problems, don't know why the patch > shouldn't work there as well. Google asan cannot support DPDK heap memory detection. This patch adapts to google asan so that it can check DPDK heap memory bugs. For each platform, it is necessary to adapt the base address and do a lot of testing. We don't have an arm platform here, so we didn't adapt it. Peng, ZhihongX ^ permalink raw reply [flat|nested] 117+ messages in thread
* [dpdk-dev] [PATCH v3] Enable AddressSanitizer feature on DPDK 2021-09-16 1:38 ` [dpdk-dev] [PATCH v2] " zhihongx.peng 2021-09-17 8:23 ` David Marchand @ 2021-09-18 7:41 ` zhihongx.peng 2021-09-20 10:03 ` David Marchand ` (2 more replies) 1 sibling, 3 replies; 117+ messages in thread From: zhihongx.peng @ 2021-09-18 7:41 UTC (permalink / raw) To: anatoly.burakov, konstantin.ananyev, stephen Cc: dev, xueqin.lin, Zhihong Peng From: Zhihong Peng <zhihongx.peng@intel.com> AddressSanitizer (ASan) is a google memory error detect standard tool. It could help to detect use-after-free and {heap,stack,global}-buffer overflow bugs in C/C++ programs, print detailed error information when error happens, large improve debug efficiency. By referring to its implementation algorithm (https://github.com/google/sanitizers/wiki/AddressSanitizerAlgorithm), enable heap-buffer-overflow and use-after-free functions on dpdk. DPDK ASAN function currently only supports on Linux x86_64. Here is an example of heap-buffer-overflow bug: ...... char *p = rte_zmalloc(NULL, 7, 0); p[7] = 'a'; ...... Here is an example of use-after-free bug: ...... char *p = rte_zmalloc(NULL, 7, 0); rte_free(p); *p = 'a'; ...... If you want to use this feature, you need to add below compilation options when compiling code: -Dbuildtype=debug -Db_lundef=false -Db_sanitize=address "-Dbuildtype=debug": Display code information when coredump occurs in the program. "-Db_lundef=false": It is enabled by default, and needs to be disabled when using asan. Signed-off-by: Xueqin Lin <xueqin.lin@intel.com> Signed-off-by: Zhihong Peng <zhihongx.peng@intel.com> --- doc/guides/prog_guide/asan.rst | 130 ++++++++++++++++++++++ doc/guides/prog_guide/index.rst | 1 + lib/eal/common/malloc_elem.c | 26 ++++- lib/eal/common/malloc_elem.h | 184 +++++++++++++++++++++++++++++++- lib/eal/common/malloc_heap.c | 12 +++ lib/eal/common/rte_malloc.c | 9 +- lib/pipeline/rte_swx_pipeline.c | 4 +- 7 files changed, 359 insertions(+), 7 deletions(-) create mode 100644 doc/guides/prog_guide/asan.rst diff --git a/doc/guides/prog_guide/asan.rst b/doc/guides/prog_guide/asan.rst new file mode 100644 index 0000000000..a0589d9b8a --- /dev/null +++ b/doc/guides/prog_guide/asan.rst @@ -0,0 +1,130 @@ +.. Copyright (c) <2021>, Intel Corporation + All rights reserved. + +Memory error detect standard tool - AddressSanitizer(Asan) +========================================================== + +AddressSanitizer (ASan) is a google memory error detect +standard tool. It could help to detect use-after-free and +{heap,stack,global}-buffer overflow bugs in C/C++ programs, +print detailed error information when error happens, large +improve debug efficiency. + +By referring to its implementation algorithm +(https://github.com/google/sanitizers/wiki/AddressSanitizerAlgorithm), +enabled heap-buffer-overflow and use-after-free functions on dpdk. +DPDK ASAN function currently only supports on Linux x86_64. + +AddressSanitizer is a part of LLVM(3.1+)and GCC(4.8+). + +Example heap-buffer-overflow error +---------------------------------- + +Following error was reported when Asan was enabled:: + + Applied 9 bytes of memory, but accessed the 10th byte of memory, + so heap-buffer-overflow appeared. + +Below code results in this error:: + + char *p = rte_zmalloc(NULL, 9, 0); + if (!p) { + printf("rte_zmalloc error."); + return -1; + } + p[9] = 'a'; + +The error log:: + + ==49433==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x7f773fafa249 at pc 0x5556b13bdae4 bp 0x7ffeb4965e40 sp 0x7ffeb4965e30 WRITE of size 1 at 0x7f773fafa249 thread T0 + #0 0x5556b13bdae3 in asan_heap_buffer_overflow ../app/test/test_asan_heap_buffer_overflow.c:25 + #1 0x5556b043e9d4 in cmd_autotest_parsed ../app/test/commands.c:71 + #2 0x5556b1cdd4b0 in cmdline_parse ../lib/cmdline/cmdline_parse.c:290 + #3 0x5556b1cd8987 in cmdline_valid_buffer ../lib/cmdline/cmdline.c:26 + #4 0x5556b1ce477a in rdline_char_in ../lib/cmdline/cmdline_rdline.c:421 + #5 0x5556b1cd923e in cmdline_in ../lib/cmdline/cmdline.c:149 + #6 0x5556b1cd9769 in cmdline_interact ../lib/cmdline/cmdline.c:223 + #7 0x5556b045f53b in main ../app/test/test.c:234 + #8 0x7f7f1eba90b2 in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x270b2) + #9 0x5556b043e70d in _start (/home/pzh/yyy/x86_64-native-linuxapp-gcc/app/test/dpdk-test+0x7ce70d) + + Address 0x7f773fafa249 is a wild pointer. + SUMMARY: AddressSanitizer: heap-buffer-overflow ../app/test/test_asan_heap_buffer_overflow.c:25 in asan_heap_buffer_overflow + Shadow bytes around the buggy address: + 0x0fef67f573f0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 0x0fef67f57400: fa fa 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 0x0fef67f57410: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 0x0fef67f57420: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 0x0fef67f57430: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + =>0x0fef67f57440: 00 00 00 00 00 00 fa fa 00[01]fa 00 00 00 00 00 + 0x0fef67f57450: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 0x0fef67f57460: 00 00 00 00 00 00 fa fa 00 00 00 00 00 00 00 00 + 0x0fef67f57470: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 0x0fef67f57480: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + +Example use-after-free error +---------------------------- + +Following error was reported when Asan was enabled:: + + Applied for 9 bytes of memory, and accessed the first byte after + released, so heap-use-after-free appeared. + +Below code results in this error:: + + char *p = rte_zmalloc(NULL, 9, 0); + if (!p) { + printf("rte_zmalloc error."); + return -1; + } + rte_free(p); + *p = 'a'; + +The error log:: + + ==49478==ERROR: AddressSanitizer: heap-use-after-free on address 0x7fe2ffafa240 at pc 0x56409b084bc8 bp 0x7ffef62c57d0 sp 0x7ffef62c57c0 WRITE of size 1 at 0x7fe2ffafa240 thread T0 + #0 0x56409b084bc7 in asan_use_after_free ../app/test/test_asan_use_after_free.c:26 + #1 0x56409a1059d4 in cmd_autotest_parsed ../app/test/commands.c:71 + #2 0x56409b9a44b0 in cmdline_parse ../lib/cmdline/cmdline_parse.c:290 + #3 0x56409b99f987 in cmdline_valid_buffer ../lib/cmdline/cmdline.c:26 + #4 0x56409b9ab77a in rdline_char_in ../lib/cmdline/cmdline_rdline.c:421 + #5 0x56409b9a023e in cmdline_in ../lib/cmdline/cmdline.c:149 + #6 0x56409b9a0769 in cmdline_interact ../lib/cmdline/cmdline.c:223 + #7 0x56409a12653b in main ../app/test/test.c:234 + #8 0x7feafafc20b2 in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x270b2) + #9 0x56409a10570d in _start (/home/pzh/yyy/x86_64-native-linuxapp-gcc/app/test/dpdk-test+0x7ce70d) + + Address 0x7fe2ffafa240 is a wild pointer. + SUMMARY: AddressSanitizer: heap-use-after-free ../app/test/test_asan_use_after_free.c:26 in asan_use_after_free + Shadow bytes around the buggy address: + 0x0ffcdff573f0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 0x0ffcdff57400: fa fa 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 0x0ffcdff57410: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 0x0ffcdff57420: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 0x0ffcdff57430: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + =>0x0ffcdff57440: 00 00 00 00 00 00 00 00[fd]fd fd fd fd fd fd fd + 0x0ffcdff57450: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 0x0ffcdff57460: 00 00 00 00 00 00 fa fa 00 00 00 00 00 00 00 00 + 0x0ffcdff57470: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 0x0ffcdff57480: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 0x0ffcdff57490: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + +Usage +----- + +meson build +^^^^^^^^^^^ + +To enable Asan in meson build system, use following meson build command: + +Example usage:: + + meson build -Dbuildtype=debug -Db_lundef=false -Db_sanitize=address + ninja -C build + +.. Note:: + + a) The issue of asan wild pointer is that dpdk asan tool is not fully adapted to google asan. + For example: Address 0x7fe2ffafa240 is a wild pointer. + b) Centos8 needs to install libasan separately. + c) If the program uses cmdline, when a memory bug occurs, need to execute the "stty echo" command. diff --git a/doc/guides/prog_guide/index.rst b/doc/guides/prog_guide/index.rst index 2dce507f46..df8a4b93e1 100644 --- a/doc/guides/prog_guide/index.rst +++ b/doc/guides/prog_guide/index.rst @@ -71,3 +71,4 @@ Programmer's Guide lto profile_app glossary + asan diff --git a/lib/eal/common/malloc_elem.c b/lib/eal/common/malloc_elem.c index c2c9461f1d..bdd20a162e 100644 --- a/lib/eal/common/malloc_elem.c +++ b/lib/eal/common/malloc_elem.c @@ -446,6 +446,8 @@ malloc_elem_alloc(struct malloc_elem *elem, size_t size, unsigned align, struct malloc_elem *new_free_elem = RTE_PTR_ADD(new_elem, size + MALLOC_ELEM_OVERHEAD); + asan_clear_split_alloczone(new_free_elem); + split_elem(elem, new_free_elem); malloc_elem_free_list_insert(new_free_elem); @@ -458,6 +460,8 @@ malloc_elem_alloc(struct malloc_elem *elem, size_t size, unsigned align, elem->state = ELEM_BUSY; elem->pad = old_elem_size; + asan_clear_alloczone(elem); + /* put a dummy header in padding, to point to real element header */ if (elem->pad > 0) { /* pad will be at least 64-bytes, as everything * is cache-line aligned */ @@ -470,12 +474,18 @@ malloc_elem_alloc(struct malloc_elem *elem, size_t size, unsigned align, return new_elem; } + asan_clear_split_alloczone(new_elem); + /* we are going to split the element in two. The original element * remains free, and the new element is the one allocated. * Re-insert original element, in case its new size makes it * belong on a different list. */ + split_elem(elem, new_elem); + + asan_clear_alloczone(new_elem); + new_elem->state = ELEM_BUSY; malloc_elem_free_list_insert(elem); @@ -601,6 +611,8 @@ malloc_elem_hide_region(struct malloc_elem *elem, void *start, size_t len) if (next && next_elem_is_adjacent(elem)) { len_after = RTE_PTR_DIFF(next, hide_end); if (len_after >= MALLOC_ELEM_OVERHEAD + MIN_DATA_SIZE) { + asan_clear_split_alloczone(hide_end); + /* split after */ split_elem(elem, hide_end); @@ -615,6 +627,8 @@ malloc_elem_hide_region(struct malloc_elem *elem, void *start, size_t len) if (prev && prev_elem_is_adjacent(elem)) { len_before = RTE_PTR_DIFF(hide_start, elem); if (len_before >= MALLOC_ELEM_OVERHEAD + MIN_DATA_SIZE) { + asan_clear_split_alloczone(hide_start); + /* split before */ split_elem(elem, hide_start); @@ -628,6 +642,8 @@ malloc_elem_hide_region(struct malloc_elem *elem, void *start, size_t len) } } + asan_clear_alloczone(elem); + remove_elem(elem); } @@ -641,8 +657,10 @@ malloc_elem_resize(struct malloc_elem *elem, size_t size) const size_t new_size = size + elem->pad + MALLOC_ELEM_OVERHEAD; /* if we request a smaller size, then always return ok */ - if (elem->size >= new_size) + if (elem->size >= new_size) { + asan_clear_alloczone(elem); return 0; + } /* check if there is a next element, it's free and adjacent */ if (!elem->next || elem->next->state != ELEM_FREE || @@ -661,9 +679,15 @@ malloc_elem_resize(struct malloc_elem *elem, size_t size) /* now we have a big block together. Lets cut it down a bit, by splitting */ struct malloc_elem *split_pt = RTE_PTR_ADD(elem, new_size); split_pt = RTE_PTR_ALIGN_CEIL(split_pt, RTE_CACHE_LINE_SIZE); + + asan_clear_split_alloczone(split_pt); + split_elem(elem, split_pt); malloc_elem_free_list_insert(split_pt); } + + asan_clear_alloczone(elem); + return 0; } diff --git a/lib/eal/common/malloc_elem.h b/lib/eal/common/malloc_elem.h index a1e5f7f02c..01a739f2ea 100644 --- a/lib/eal/common/malloc_elem.h +++ b/lib/eal/common/malloc_elem.h @@ -7,6 +7,14 @@ #include <stdbool.h> +#ifdef __SANITIZE_ADDRESS__ +#define RTE_MALLOC_ASAN +#elif defined(__has_feature) +# if __has_feature(address_sanitizer) +#define RTE_MALLOC_ASAN +# endif +#endif + #define MIN_DATA_SIZE (RTE_CACHE_LINE_SIZE) /* dummy definition of struct so we can use pointers to it in malloc_elem struct */ @@ -36,10 +44,20 @@ struct malloc_elem { uint64_t header_cookie; /* Cookie marking start of data */ /* trailer cookie at start + size */ #endif +#ifdef RTE_MALLOC_ASAN + size_t user_size; + uint64_t asan_cookie[2]; /*must be next to header_cookie*/ +#endif } __rte_cache_aligned; +static const unsigned int MALLOC_ELEM_HEADER_LEN = sizeof(struct malloc_elem); + #ifndef RTE_MALLOC_DEBUG -static const unsigned MALLOC_ELEM_TRAILER_LEN = 0; +#ifdef RTE_MALLOC_ASAN +static const unsigned int MALLOC_ELEM_TRAILER_LEN = RTE_CACHE_LINE_SIZE; +#else +static const unsigned int MALLOC_ELEM_TRAILER_LEN; +#endif /* dummy function - just check if pointer is non-null */ static inline int @@ -55,7 +73,7 @@ set_trailer(struct malloc_elem *elem __rte_unused){ } #else -static const unsigned MALLOC_ELEM_TRAILER_LEN = RTE_CACHE_LINE_SIZE; +static const unsigned int MALLOC_ELEM_TRAILER_LEN = RTE_CACHE_LINE_SIZE; #define MALLOC_HEADER_COOKIE 0xbadbadbadadd2e55ULL /**< Header cookie. */ #define MALLOC_TRAILER_COOKIE 0xadd2e55badbadbadULL /**< Trailer cookie.*/ @@ -90,9 +108,169 @@ malloc_elem_cookies_ok(const struct malloc_elem *elem) #endif -static const unsigned MALLOC_ELEM_HEADER_LEN = sizeof(struct malloc_elem); #define MALLOC_ELEM_OVERHEAD (MALLOC_ELEM_HEADER_LEN + MALLOC_ELEM_TRAILER_LEN) +#ifdef RTE_MALLOC_ASAN + +#define ASAN_SHADOW_GRAIN_SIZE 8 +#define ASAN_MEM_FREE_FLAG 0xfd +#define ASAN_MEM_REDZONE_FLAG 0xfa +#define ASAN_MEM_TO_SHADOW(mem) (((mem) >> 3) + 0x00007fff8000) + +#if defined(__clang__) +__attribute__((no_sanitize("address", "hwaddress"))) +#else +__attribute__((no_sanitize_address)) +#endif +static inline void +asan_set_shadow(void *addr, char val) +{ + *(char *)addr = val; +} + +static inline void +asan_set_zone(void *ptr, size_t len, uint32_t val) +{ + size_t offset; + char *shadow; + size_t zone_len = len / ASAN_SHADOW_GRAIN_SIZE; + if (len % ASAN_SHADOW_GRAIN_SIZE != 0) + zone_len += 1; + + for (size_t i = 0; i < zone_len; i++) { + offset = i * ASAN_SHADOW_GRAIN_SIZE; + shadow = (char *)ASAN_MEM_TO_SHADOW(((int64_t)ptr + offset)); + asan_set_shadow(shadow, val); + } +} + +/* + * When the memory is released, the release mark is + * set in the corresponding range of the shadow area. + */ +static inline void +asan_set_freezone(void *ptr, size_t size) +{ + asan_set_zone(ptr, size, ASAN_MEM_FREE_FLAG); +} + +/* + * When the memory is allocated, memory state must set as accessible. + */ +static inline void +asan_clear_alloczone(struct malloc_elem *elem) +{ + asan_set_zone((void *)elem, elem->size, 0x0); +} + +static inline void +asan_clear_split_alloczone(struct malloc_elem *elem) +{ + void *ptr = RTE_PTR_SUB(elem, MALLOC_ELEM_TRAILER_LEN); + asan_set_zone(ptr, MALLOC_ELEM_OVERHEAD, 0x0); +} + +/* + * When the memory is allocated, the memory boundary is + * marked in the corresponding range of the shadow area. + */ +static inline void +asan_set_redzone(struct malloc_elem *elem, size_t user_size) +{ + uint64_t ptr; + char *shadow; + if (elem != NULL) { + if (elem->state != ELEM_PAD) + elem = RTE_PTR_ADD(elem, elem->pad); + + elem->user_size = user_size; + + /* Set mark before the start of the allocated memory */ + ptr = (uint64_t)RTE_PTR_ADD(elem, MALLOC_ELEM_HEADER_LEN) + - ASAN_SHADOW_GRAIN_SIZE; + shadow = (char *)ASAN_MEM_TO_SHADOW(ptr); + asan_set_shadow(shadow, ASAN_MEM_REDZONE_FLAG); + shadow = (char *)ASAN_MEM_TO_SHADOW(ptr + - ASAN_SHADOW_GRAIN_SIZE); + asan_set_shadow(shadow, ASAN_MEM_REDZONE_FLAG); + + /* Set mark after the end of the allocated memory */ + ptr = (uint64_t)RTE_PTR_ADD(elem, MALLOC_ELEM_HEADER_LEN + + elem->user_size); + shadow = (char *)ASAN_MEM_TO_SHADOW(ptr); + uint32_t val = (ptr % ASAN_SHADOW_GRAIN_SIZE); + val = (val == 0) ? ASAN_MEM_REDZONE_FLAG : val; + asan_set_shadow(shadow, val); + shadow = (char *)ASAN_MEM_TO_SHADOW(ptr + + ASAN_SHADOW_GRAIN_SIZE); + asan_set_shadow(shadow, ASAN_MEM_REDZONE_FLAG); + } +} + +/* + * When the memory is released, the mark of the memory boundary + * in the corresponding range of the shadow area is cleared. + */ +static inline void +asan_clear_redzone(struct malloc_elem *elem) +{ + uint64_t ptr; + char *shadow; + if (elem != NULL) { + elem = RTE_PTR_ADD(elem, elem->pad); + + /* Clear mark before the start of the allocated memory */ + ptr = (uint64_t)RTE_PTR_ADD(elem, MALLOC_ELEM_HEADER_LEN) + - ASAN_SHADOW_GRAIN_SIZE; + shadow = (char *)ASAN_MEM_TO_SHADOW(ptr); + asan_set_shadow(shadow, 0x00); + shadow = (char *)ASAN_MEM_TO_SHADOW(ptr + - ASAN_SHADOW_GRAIN_SIZE); + asan_set_shadow(shadow, 0x00); + + /* Clear mark after the end of the allocated memory */ + ptr = (uint64_t)RTE_PTR_ADD(elem, MALLOC_ELEM_HEADER_LEN + + elem->user_size); + shadow = (char *)ASAN_MEM_TO_SHADOW(ptr); + asan_set_shadow(shadow, 0x00); + shadow = (char *)ASAN_MEM_TO_SHADOW(ptr + + ASAN_SHADOW_GRAIN_SIZE); + asan_set_shadow(shadow, 0x00); + } +} + +static inline size_t +old_malloc_size(struct malloc_elem *elem) +{ + if (elem->state != ELEM_PAD) + elem = RTE_PTR_ADD(elem, elem->pad); + + return elem->user_size; +} +#else +static inline void +asan_set_freezone(void *ptr __rte_unused, size_t size __rte_unused) { } + +static inline void +asan_clear_alloczone(struct malloc_elem *elem __rte_unused) { } + +static inline void +asan_clear_split_alloczone(struct malloc_elem *elem __rte_unused) { } + +static inline void +asan_set_redzone(struct malloc_elem *elem __rte_unused, + size_t user_size __rte_unused) { } + +static inline void +asan_clear_redzone(struct malloc_elem *elem __rte_unused) { } + +static inline size_t +old_malloc_size(struct malloc_elem *elem) +{ + return elem->size - elem->pad - MALLOC_ELEM_OVERHEAD; +} +#endif + /* * Given a pointer to the start of a memory block returned by malloc, get * the actual malloc_elem header for that block. diff --git a/lib/eal/common/malloc_heap.c b/lib/eal/common/malloc_heap.c index ee400f38ec..775d6789df 100644 --- a/lib/eal/common/malloc_heap.c +++ b/lib/eal/common/malloc_heap.c @@ -237,6 +237,7 @@ heap_alloc(struct malloc_heap *heap, const char *type __rte_unused, size_t size, unsigned int flags, size_t align, size_t bound, bool contig) { struct malloc_elem *elem; + size_t user_size = size; size = RTE_CACHE_LINE_ROUNDUP(size); align = RTE_CACHE_LINE_ROUNDUP(align); @@ -250,6 +251,8 @@ heap_alloc(struct malloc_heap *heap, const char *type __rte_unused, size_t size, /* increase heap's count of allocated elements */ heap->alloc_count++; + + asan_set_redzone(elem, user_size); } return elem == NULL ? NULL : (void *)(&elem[1]); @@ -270,6 +273,8 @@ heap_alloc_biggest(struct malloc_heap *heap, const char *type __rte_unused, /* increase heap's count of allocated elements */ heap->alloc_count++; + + asan_set_redzone(elem, size); } return elem == NULL ? NULL : (void *)(&elem[1]); @@ -841,6 +846,8 @@ malloc_heap_free(struct malloc_elem *elem) if (!malloc_elem_cookies_ok(elem) || elem->state != ELEM_BUSY) return -1; + asan_clear_redzone(elem); + /* elem may be merged with previous element, so keep heap address */ heap = elem->heap; msl = elem->msl; @@ -848,6 +855,9 @@ malloc_heap_free(struct malloc_elem *elem) rte_spinlock_lock(&(heap->lock)); + void *asan_ptr = RTE_PTR_ADD(elem, MALLOC_ELEM_HEADER_LEN + elem->pad); + size_t asan_data_len = elem->size - MALLOC_ELEM_OVERHEAD - elem->pad; + /* mark element as free */ elem->state = ELEM_FREE; @@ -1001,6 +1011,8 @@ malloc_heap_free(struct malloc_elem *elem) rte_mcfg_mem_write_unlock(); free_unlock: + asan_set_freezone(asan_ptr, asan_data_len); + rte_spinlock_unlock(&(heap->lock)); return ret; } diff --git a/lib/eal/common/rte_malloc.c b/lib/eal/common/rte_malloc.c index 9d39e58c08..d0bec26920 100644 --- a/lib/eal/common/rte_malloc.c +++ b/lib/eal/common/rte_malloc.c @@ -162,6 +162,8 @@ rte_calloc(const char *type, size_t num, size_t size, unsigned align) void * rte_realloc_socket(void *ptr, size_t size, unsigned int align, int socket) { + size_t user_size; + if (ptr == NULL) return rte_malloc_socket(NULL, size, align, socket); @@ -171,6 +173,8 @@ rte_realloc_socket(void *ptr, size_t size, unsigned int align, int socket) return NULL; } + user_size = size; + size = RTE_CACHE_LINE_ROUNDUP(size), align = RTE_CACHE_LINE_ROUNDUP(align); /* check requested socket id and alignment matches first, and if ok, @@ -181,6 +185,9 @@ rte_realloc_socket(void *ptr, size_t size, unsigned int align, int socket) RTE_PTR_ALIGN(ptr, align) == ptr && malloc_heap_resize(elem, size) == 0) { rte_eal_trace_mem_realloc(size, align, socket, ptr); + + asan_set_redzone(elem, user_size); + return ptr; } @@ -192,7 +199,7 @@ rte_realloc_socket(void *ptr, size_t size, unsigned int align, int socket) if (new_ptr == NULL) return NULL; /* elem: |pad|data_elem|data|trailer| */ - const size_t old_size = elem->size - elem->pad - MALLOC_ELEM_OVERHEAD; + const size_t old_size = old_malloc_size(elem); rte_memcpy(new_ptr, ptr, old_size < size ? old_size : size); rte_free(ptr); diff --git a/lib/pipeline/rte_swx_pipeline.c b/lib/pipeline/rte_swx_pipeline.c index 8eb978a30c..aaa0107d02 100644 --- a/lib/pipeline/rte_swx_pipeline.c +++ b/lib/pipeline/rte_swx_pipeline.c @@ -6340,7 +6340,7 @@ instr_meter_translate(struct rte_swx_pipeline *p, return 0; } - CHECK(0, EINVAL); + return -EINVAL; } static inline struct meter * @@ -8025,7 +8025,7 @@ instr_translate(struct rte_swx_pipeline *p, instr, data); - CHECK(0, EINVAL); + return -EINVAL; } static struct instruction_data * -- 2.25.1 ^ permalink raw reply [flat|nested] 117+ messages in thread
* Re: [dpdk-dev] [PATCH v3] Enable AddressSanitizer feature on DPDK 2021-09-18 7:41 ` [dpdk-dev] [PATCH v3] " zhihongx.peng @ 2021-09-20 10:03 ` David Marchand 2021-09-22 7:26 ` Peng, ZhihongX 2021-09-24 2:20 ` [dpdk-dev] [PATCH v4 1/2] Enable ASan for memory detector " zhihongx.peng 2021-09-27 12:02 ` [dpdk-dev] [PATCH v3] Enable AddressSanitizer feature " Burakov, Anatoly 2 siblings, 1 reply; 117+ messages in thread From: David Marchand @ 2021-09-20 10:03 UTC (permalink / raw) To: Zhihong Peng Cc: Burakov, Anatoly, Ananyev, Konstantin, Stephen Hemminger, dev, Xueqin Lin On Sat, Sep 18, 2021 at 9:51 AM <zhihongx.peng@intel.com> wrote: > > From: Zhihong Peng <zhihongx.peng@intel.com> - The title is too vague. I am not sure what the best title is, but my current idea is: mem: instrument allocator with ASan - This is a nice feature that must be announced in the release notes. - How should we spell it? Asan ? ASAN ? ASan ? Please update devtools/words-case.txt and fix inconsistencies in this patch. > > AddressSanitizer (ASan) is a google memory error detect > standard tool. It could help to detect use-after-free and > {heap,stack,global}-buffer overflow bugs in C/C++ programs, > print detailed error information when error happens, large > improve debug efficiency. > > By referring to its implementation algorithm > (https://github.com/google/sanitizers/wiki/AddressSanitizerAlgorithm), > enable heap-buffer-overflow and use-after-free functions on dpdk. > DPDK ASAN function currently only supports on Linux x86_64. If you don't intend to update other arches, at least explain in the commitlog what should be done: so that other arches know what to do to add support. > > Here is an example of heap-buffer-overflow bug: > ...... > char *p = rte_zmalloc(NULL, 7, 0); > p[7] = 'a'; > ...... > > Here is an example of use-after-free bug: > ...... > char *p = rte_zmalloc(NULL, 7, 0); > rte_free(p); > *p = 'a'; > ...... > > If you want to use this feature, > you need to add below compilation options when compiling code: > -Dbuildtype=debug -Db_lundef=false -Db_sanitize=address ASAN is triggered by -Db_sanitize=address, it is the only *needed* option afaiu. > "-Dbuildtype=debug": Display code information when coredump occurs > in the program. In ASan context, there is no coredump. ASan displays a backtrace which is easier to read when debug symbols are available. You can suggest building with debug, but this is *not needed*. > "-Db_lundef=false": It is enabled by default, and needs to be > disabled when using asan. This is an issue with meson and clang. Tweaking b_lundef is needed with clang, gcc looks fine. But still, on RHEL with gcc, I need to install libasan. Maybe we can add libasan at a requirement at project level, did you try it? > > Signed-off-by: Xueqin Lin <xueqin.lin@intel.com> > Signed-off-by: Zhihong Peng <zhihongx.peng@intel.com> > --- > doc/guides/prog_guide/asan.rst | 130 ++++++++++++++++++++++ > doc/guides/prog_guide/index.rst | 1 + > lib/eal/common/malloc_elem.c | 26 ++++- > lib/eal/common/malloc_elem.h | 184 +++++++++++++++++++++++++++++++- > lib/eal/common/malloc_heap.c | 12 +++ > lib/eal/common/rte_malloc.c | 9 +- > lib/pipeline/rte_swx_pipeline.c | 4 +- This change on pipeline has no explanation, and looks out of place wrt to current change. > 7 files changed, 359 insertions(+), 7 deletions(-) > create mode 100644 doc/guides/prog_guide/asan.rst > > diff --git a/doc/guides/prog_guide/asan.rst b/doc/guides/prog_guide/asan.rst > new file mode 100644 > index 0000000000..a0589d9b8a > --- /dev/null > +++ b/doc/guides/prog_guide/asan.rst > @@ -0,0 +1,130 @@ > +.. Copyright (c) <2021>, Intel Corporation > + All rights reserved. > + > +Memory error detect standard tool - AddressSanitizer(Asan) > +========================================================== > + > +AddressSanitizer (ASan) is a google memory error detect > +standard tool. It could help to detect use-after-free and > +{heap,stack,global}-buffer overflow bugs in C/C++ programs, > +print detailed error information when error happens, large > +improve debug efficiency. > + > +By referring to its implementation algorithm > +(https://github.com/google/sanitizers/wiki/AddressSanitizerAlgorithm), > +enabled heap-buffer-overflow and use-after-free functions on dpdk. > +DPDK ASAN function currently only supports on Linux x86_64. > + > +AddressSanitizer is a part of LLVM(3.1+)and GCC(4.8+). > + > +Example heap-buffer-overflow error > +---------------------------------- > + > +Following error was reported when Asan was enabled:: > + > + Applied 9 bytes of memory, but accessed the 10th byte of memory, > + so heap-buffer-overflow appeared. > + > +Below code results in this error:: > + > + char *p = rte_zmalloc(NULL, 9, 0); > + if (!p) { > + printf("rte_zmalloc error."); > + return -1; > + } > + p[9] = 'a'; > + > +The error log:: > + > + ==49433==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x7f773fafa249 at pc 0x5556b13bdae4 bp 0x7ffeb4965e40 sp 0x7ffeb4965e30 WRITE of size 1 at 0x7f773fafa249 thread T0 > + #0 0x5556b13bdae3 in asan_heap_buffer_overflow ../app/test/test_asan_heap_buffer_overflow.c:25 > + #1 0x5556b043e9d4 in cmd_autotest_parsed ../app/test/commands.c:71 > + #2 0x5556b1cdd4b0 in cmdline_parse ../lib/cmdline/cmdline_parse.c:290 > + #3 0x5556b1cd8987 in cmdline_valid_buffer ../lib/cmdline/cmdline.c:26 > + #4 0x5556b1ce477a in rdline_char_in ../lib/cmdline/cmdline_rdline.c:421 > + #5 0x5556b1cd923e in cmdline_in ../lib/cmdline/cmdline.c:149 > + #6 0x5556b1cd9769 in cmdline_interact ../lib/cmdline/cmdline.c:223 > + #7 0x5556b045f53b in main ../app/test/test.c:234 > + #8 0x7f7f1eba90b2 in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x270b2) > + #9 0x5556b043e70d in _start (/home/pzh/yyy/x86_64-native-linuxapp-gcc/app/test/dpdk-test+0x7ce70d) > + > + Address 0x7f773fafa249 is a wild pointer. > + SUMMARY: AddressSanitizer: heap-buffer-overflow ../app/test/test_asan_heap_buffer_overflow.c:25 in asan_heap_buffer_overflow > + Shadow bytes around the buggy address: > + 0x0fef67f573f0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 > + 0x0fef67f57400: fa fa 00 00 00 00 00 00 00 00 00 00 00 00 00 00 > + 0x0fef67f57410: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 > + 0x0fef67f57420: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 > + 0x0fef67f57430: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 > + =>0x0fef67f57440: 00 00 00 00 00 00 fa fa 00[01]fa 00 00 00 00 00 > + 0x0fef67f57450: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 > + 0x0fef67f57460: 00 00 00 00 00 00 fa fa 00 00 00 00 00 00 00 00 > + 0x0fef67f57470: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 > + 0x0fef67f57480: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 > + > +Example use-after-free error > +---------------------------- > + > +Following error was reported when Asan was enabled:: > + > + Applied for 9 bytes of memory, and accessed the first byte after > + released, so heap-use-after-free appeared. > + > +Below code results in this error:: > + > + char *p = rte_zmalloc(NULL, 9, 0); > + if (!p) { > + printf("rte_zmalloc error."); > + return -1; > + } > + rte_free(p); > + *p = 'a'; > + > +The error log:: > + > + ==49478==ERROR: AddressSanitizer: heap-use-after-free on address 0x7fe2ffafa240 at pc 0x56409b084bc8 bp 0x7ffef62c57d0 sp 0x7ffef62c57c0 WRITE of size 1 at 0x7fe2ffafa240 thread T0 > + #0 0x56409b084bc7 in asan_use_after_free ../app/test/test_asan_use_after_free.c:26 > + #1 0x56409a1059d4 in cmd_autotest_parsed ../app/test/commands.c:71 > + #2 0x56409b9a44b0 in cmdline_parse ../lib/cmdline/cmdline_parse.c:290 > + #3 0x56409b99f987 in cmdline_valid_buffer ../lib/cmdline/cmdline.c:26 > + #4 0x56409b9ab77a in rdline_char_in ../lib/cmdline/cmdline_rdline.c:421 > + #5 0x56409b9a023e in cmdline_in ../lib/cmdline/cmdline.c:149 > + #6 0x56409b9a0769 in cmdline_interact ../lib/cmdline/cmdline.c:223 > + #7 0x56409a12653b in main ../app/test/test.c:234 > + #8 0x7feafafc20b2 in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x270b2) > + #9 0x56409a10570d in _start (/home/pzh/yyy/x86_64-native-linuxapp-gcc/app/test/dpdk-test+0x7ce70d) > + > + Address 0x7fe2ffafa240 is a wild pointer. > + SUMMARY: AddressSanitizer: heap-use-after-free ../app/test/test_asan_use_after_free.c:26 in asan_use_after_free > + Shadow bytes around the buggy address: > + 0x0ffcdff573f0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 > + 0x0ffcdff57400: fa fa 00 00 00 00 00 00 00 00 00 00 00 00 00 00 > + 0x0ffcdff57410: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 > + 0x0ffcdff57420: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 > + 0x0ffcdff57430: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 > + =>0x0ffcdff57440: 00 00 00 00 00 00 00 00[fd]fd fd fd fd fd fd fd > + 0x0ffcdff57450: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 > + 0x0ffcdff57460: 00 00 00 00 00 00 fa fa 00 00 00 00 00 00 00 00 > + 0x0ffcdff57470: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 > + 0x0ffcdff57480: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 > + 0x0ffcdff57490: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 > + > +Usage > +----- > + > +meson build > +^^^^^^^^^^^ > + > +To enable Asan in meson build system, use following meson build command: > + > +Example usage:: > + > + meson build -Dbuildtype=debug -Db_lundef=false -Db_sanitize=address > + ninja -C build > + > +.. Note:: > + > + a) The issue of asan wild pointer is that dpdk asan tool is not fully adapted to google asan. > + For example: Address 0x7fe2ffafa240 is a wild pointer. I can't understand what the "wild pointer" means in this context. This comment belongs to the traces in the section before. > + b) Centos8 needs to install libasan separately. See my previous comment on b_lundef. > + c) If the program uses cmdline, when a memory bug occurs, need to execute the "stty echo" command. Yes, this is annoying when executing failing unit tests. That is something to handle better in the cmdline library, maybe in the future. Like "wild pointer", I don't think this comment belongs here. > diff --git a/doc/guides/prog_guide/index.rst b/doc/guides/prog_guide/index.rst > index 2dce507f46..df8a4b93e1 100644 > --- a/doc/guides/prog_guide/index.rst > +++ b/doc/guides/prog_guide/index.rst > @@ -71,3 +71,4 @@ Programmer's Guide > lto > profile_app > glossary > + asan > diff --git a/lib/eal/common/malloc_elem.c b/lib/eal/common/malloc_elem.c > index c2c9461f1d..bdd20a162e 100644 > --- a/lib/eal/common/malloc_elem.c > +++ b/lib/eal/common/malloc_elem.c > @@ -446,6 +446,8 @@ malloc_elem_alloc(struct malloc_elem *elem, size_t size, unsigned align, > struct malloc_elem *new_free_elem = > RTE_PTR_ADD(new_elem, size + MALLOC_ELEM_OVERHEAD); > > + asan_clear_split_alloczone(new_free_elem); > + > split_elem(elem, new_free_elem); > malloc_elem_free_list_insert(new_free_elem); > > @@ -458,6 +460,8 @@ malloc_elem_alloc(struct malloc_elem *elem, size_t size, unsigned align, > elem->state = ELEM_BUSY; > elem->pad = old_elem_size; > > + asan_clear_alloczone(elem); > + > /* put a dummy header in padding, to point to real element header */ > if (elem->pad > 0) { /* pad will be at least 64-bytes, as everything > * is cache-line aligned */ > @@ -470,12 +474,18 @@ malloc_elem_alloc(struct malloc_elem *elem, size_t size, unsigned align, > return new_elem; > } > > + asan_clear_split_alloczone(new_elem); > + > /* we are going to split the element in two. The original element > * remains free, and the new element is the one allocated. > * Re-insert original element, in case its new size makes it > * belong on a different list. > */ > + > split_elem(elem, new_elem); > + > + asan_clear_alloczone(new_elem); > + > new_elem->state = ELEM_BUSY; > malloc_elem_free_list_insert(elem); > > @@ -601,6 +611,8 @@ malloc_elem_hide_region(struct malloc_elem *elem, void *start, size_t len) > if (next && next_elem_is_adjacent(elem)) { > len_after = RTE_PTR_DIFF(next, hide_end); > if (len_after >= MALLOC_ELEM_OVERHEAD + MIN_DATA_SIZE) { > + asan_clear_split_alloczone(hide_end); > + > /* split after */ > split_elem(elem, hide_end); > > @@ -615,6 +627,8 @@ malloc_elem_hide_region(struct malloc_elem *elem, void *start, size_t len) > if (prev && prev_elem_is_adjacent(elem)) { > len_before = RTE_PTR_DIFF(hide_start, elem); > if (len_before >= MALLOC_ELEM_OVERHEAD + MIN_DATA_SIZE) { > + asan_clear_split_alloczone(hide_start); > + > /* split before */ > split_elem(elem, hide_start); > > @@ -628,6 +642,8 @@ malloc_elem_hide_region(struct malloc_elem *elem, void *start, size_t len) > } > } > > + asan_clear_alloczone(elem); > + > remove_elem(elem); > } > > @@ -641,8 +657,10 @@ malloc_elem_resize(struct malloc_elem *elem, size_t size) > const size_t new_size = size + elem->pad + MALLOC_ELEM_OVERHEAD; > > /* if we request a smaller size, then always return ok */ > - if (elem->size >= new_size) > + if (elem->size >= new_size) { > + asan_clear_alloczone(elem); > return 0; > + } > > /* check if there is a next element, it's free and adjacent */ > if (!elem->next || elem->next->state != ELEM_FREE || > @@ -661,9 +679,15 @@ malloc_elem_resize(struct malloc_elem *elem, size_t size) > /* now we have a big block together. Lets cut it down a bit, by splitting */ > struct malloc_elem *split_pt = RTE_PTR_ADD(elem, new_size); > split_pt = RTE_PTR_ALIGN_CEIL(split_pt, RTE_CACHE_LINE_SIZE); > + > + asan_clear_split_alloczone(split_pt); > + > split_elem(elem, split_pt); > malloc_elem_free_list_insert(split_pt); > } > + > + asan_clear_alloczone(elem); > + > return 0; > } > > diff --git a/lib/eal/common/malloc_elem.h b/lib/eal/common/malloc_elem.h > index a1e5f7f02c..01a739f2ea 100644 > --- a/lib/eal/common/malloc_elem.h > +++ b/lib/eal/common/malloc_elem.h > @@ -7,6 +7,14 @@ > > #include <stdbool.h> > > +#ifdef __SANITIZE_ADDRESS__ > +#define RTE_MALLOC_ASAN > +#elif defined(__has_feature) > +# if __has_feature(address_sanitizer) > +#define RTE_MALLOC_ASAN > +# endif > +#endif > + > #define MIN_DATA_SIZE (RTE_CACHE_LINE_SIZE) > > /* dummy definition of struct so we can use pointers to it in malloc_elem struct */ > @@ -36,10 +44,20 @@ struct malloc_elem { > uint64_t header_cookie; /* Cookie marking start of data */ > /* trailer cookie at start + size */ > #endif > +#ifdef RTE_MALLOC_ASAN > + size_t user_size; > + uint64_t asan_cookie[2]; /*must be next to header_cookie*/ Fix coding style for comment please. > +#endif > } __rte_cache_aligned; > > +static const unsigned int MALLOC_ELEM_HEADER_LEN = sizeof(struct malloc_elem); > + > #ifndef RTE_MALLOC_DEBUG > -static const unsigned MALLOC_ELEM_TRAILER_LEN = 0; > +#ifdef RTE_MALLOC_ASAN > +static const unsigned int MALLOC_ELEM_TRAILER_LEN = RTE_CACHE_LINE_SIZE; > +#else > +static const unsigned int MALLOC_ELEM_TRAILER_LEN; > +#endif > > /* dummy function - just check if pointer is non-null */ > static inline int > @@ -55,7 +73,7 @@ set_trailer(struct malloc_elem *elem __rte_unused){ } > > > #else > -static const unsigned MALLOC_ELEM_TRAILER_LEN = RTE_CACHE_LINE_SIZE; > +static const unsigned int MALLOC_ELEM_TRAILER_LEN = RTE_CACHE_LINE_SIZE; > > #define MALLOC_HEADER_COOKIE 0xbadbadbadadd2e55ULL /**< Header cookie. */ > #define MALLOC_TRAILER_COOKIE 0xadd2e55badbadbadULL /**< Trailer cookie.*/ > @@ -90,9 +108,169 @@ malloc_elem_cookies_ok(const struct malloc_elem *elem) > > #endif > > -static const unsigned MALLOC_ELEM_HEADER_LEN = sizeof(struct malloc_elem); > #define MALLOC_ELEM_OVERHEAD (MALLOC_ELEM_HEADER_LEN + MALLOC_ELEM_TRAILER_LEN) > > +#ifdef RTE_MALLOC_ASAN > + > +#define ASAN_SHADOW_GRAIN_SIZE 8 > +#define ASAN_MEM_FREE_FLAG 0xfd > +#define ASAN_MEM_REDZONE_FLAG 0xfa > +#define ASAN_MEM_TO_SHADOW(mem) (((mem) >> 3) + 0x00007fff8000) > + > +#if defined(__clang__) > +__attribute__((no_sanitize("address", "hwaddress"))) > +#else > +__attribute__((no_sanitize_address)) > +#endif This attribute is only used here, I am ok with leaving this as is. If later it is needed elsewhere, we'll have to define a new attribute wrapper in rte_common.h. > +static inline void > +asan_set_shadow(void *addr, char val) > +{ > + *(char *)addr = val; > +} > + > +static inline void > +asan_set_zone(void *ptr, size_t len, uint32_t val) > +{ > + size_t offset; > + char *shadow; > + size_t zone_len = len / ASAN_SHADOW_GRAIN_SIZE; + size_t i; + <empty line> to separate declarations from code. > + if (len % ASAN_SHADOW_GRAIN_SIZE != 0) > + zone_len += 1; > + > + for (size_t i = 0; i < zone_len; i++) { + for (i = 0; i < zone_len; i++) { That's to fix build issue: In file included from ../lib/eal/common/malloc_mp.c:16:0: ../lib/eal/common/malloc_elem.h: In function ‘asan_set_zone’: ../lib/eal/common/malloc_elem.h:140:2: error: ‘for’ loop initial declarations are only allowed in C99 mode for (size_t i = 0; i < zone_len; i++) { ^ ../lib/eal/common/malloc_elem.h:140:2: note: use option -std=c99 or -std=gnu99 to compile your code > + offset = i * ASAN_SHADOW_GRAIN_SIZE; > + shadow = (char *)ASAN_MEM_TO_SHADOW(((int64_t)ptr + offset)); > + asan_set_shadow(shadow, val); > + } > +} > + > +/* > + * When the memory is released, the release mark is > + * set in the corresponding range of the shadow area. > + */ > +static inline void > +asan_set_freezone(void *ptr, size_t size) > +{ > + asan_set_zone(ptr, size, ASAN_MEM_FREE_FLAG); > +} > + > +/* > + * When the memory is allocated, memory state must set as accessible. > + */ > +static inline void > +asan_clear_alloczone(struct malloc_elem *elem) > +{ > + asan_set_zone((void *)elem, elem->size, 0x0); > +} > + > +static inline void > +asan_clear_split_alloczone(struct malloc_elem *elem) > +{ > + void *ptr = RTE_PTR_SUB(elem, MALLOC_ELEM_TRAILER_LEN); > + asan_set_zone(ptr, MALLOC_ELEM_OVERHEAD, 0x0); > +} > + > +/* > + * When the memory is allocated, the memory boundary is > + * marked in the corresponding range of the shadow area. > + */ > +static inline void > +asan_set_redzone(struct malloc_elem *elem, size_t user_size) > +{ > + uint64_t ptr; > + char *shadow; > + if (elem != NULL) { > + if (elem->state != ELEM_PAD) > + elem = RTE_PTR_ADD(elem, elem->pad); > + > + elem->user_size = user_size; > + > + /* Set mark before the start of the allocated memory */ > + ptr = (uint64_t)RTE_PTR_ADD(elem, MALLOC_ELEM_HEADER_LEN) > + - ASAN_SHADOW_GRAIN_SIZE; > + shadow = (char *)ASAN_MEM_TO_SHADOW(ptr); > + asan_set_shadow(shadow, ASAN_MEM_REDZONE_FLAG); > + shadow = (char *)ASAN_MEM_TO_SHADOW(ptr > + - ASAN_SHADOW_GRAIN_SIZE); > + asan_set_shadow(shadow, ASAN_MEM_REDZONE_FLAG); > + > + /* Set mark after the end of the allocated memory */ > + ptr = (uint64_t)RTE_PTR_ADD(elem, MALLOC_ELEM_HEADER_LEN > + + elem->user_size); > + shadow = (char *)ASAN_MEM_TO_SHADOW(ptr); > + uint32_t val = (ptr % ASAN_SHADOW_GRAIN_SIZE); > + val = (val == 0) ? ASAN_MEM_REDZONE_FLAG : val; > + asan_set_shadow(shadow, val); > + shadow = (char *)ASAN_MEM_TO_SHADOW(ptr > + + ASAN_SHADOW_GRAIN_SIZE); > + asan_set_shadow(shadow, ASAN_MEM_REDZONE_FLAG); > + } > +} > + > +/* > + * When the memory is released, the mark of the memory boundary > + * in the corresponding range of the shadow area is cleared. > + */ > +static inline void > +asan_clear_redzone(struct malloc_elem *elem) > +{ > + uint64_t ptr; > + char *shadow; > + if (elem != NULL) { > + elem = RTE_PTR_ADD(elem, elem->pad); > + > + /* Clear mark before the start of the allocated memory */ > + ptr = (uint64_t)RTE_PTR_ADD(elem, MALLOC_ELEM_HEADER_LEN) > + - ASAN_SHADOW_GRAIN_SIZE; > + shadow = (char *)ASAN_MEM_TO_SHADOW(ptr); > + asan_set_shadow(shadow, 0x00); > + shadow = (char *)ASAN_MEM_TO_SHADOW(ptr > + - ASAN_SHADOW_GRAIN_SIZE); > + asan_set_shadow(shadow, 0x00); > + > + /* Clear mark after the end of the allocated memory */ > + ptr = (uint64_t)RTE_PTR_ADD(elem, MALLOC_ELEM_HEADER_LEN > + + elem->user_size); > + shadow = (char *)ASAN_MEM_TO_SHADOW(ptr); > + asan_set_shadow(shadow, 0x00); > + shadow = (char *)ASAN_MEM_TO_SHADOW(ptr > + + ASAN_SHADOW_GRAIN_SIZE); > + asan_set_shadow(shadow, 0x00); > + } > +} > + > +static inline size_t > +old_malloc_size(struct malloc_elem *elem) > +{ > + if (elem->state != ELEM_PAD) > + elem = RTE_PTR_ADD(elem, elem->pad); > + > + return elem->user_size; > +} > +#else > +static inline void > +asan_set_freezone(void *ptr __rte_unused, size_t size __rte_unused) { } > + > +static inline void > +asan_clear_alloczone(struct malloc_elem *elem __rte_unused) { } > + > +static inline void > +asan_clear_split_alloczone(struct malloc_elem *elem __rte_unused) { } > + > +static inline void > +asan_set_redzone(struct malloc_elem *elem __rte_unused, > + size_t user_size __rte_unused) { } > + > +static inline void > +asan_clear_redzone(struct malloc_elem *elem __rte_unused) { } > + > +static inline size_t > +old_malloc_size(struct malloc_elem *elem) > +{ > + return elem->size - elem->pad - MALLOC_ELEM_OVERHEAD; > +} > +#endif > + > /* > * Given a pointer to the start of a memory block returned by malloc, get > * the actual malloc_elem header for that block. > diff --git a/lib/eal/common/malloc_heap.c b/lib/eal/common/malloc_heap.c > index ee400f38ec..775d6789df 100644 > --- a/lib/eal/common/malloc_heap.c > +++ b/lib/eal/common/malloc_heap.c > @@ -237,6 +237,7 @@ heap_alloc(struct malloc_heap *heap, const char *type __rte_unused, size_t size, > unsigned int flags, size_t align, size_t bound, bool contig) > { > struct malloc_elem *elem; > + size_t user_size = size; > > size = RTE_CACHE_LINE_ROUNDUP(size); > align = RTE_CACHE_LINE_ROUNDUP(align); > @@ -250,6 +251,8 @@ heap_alloc(struct malloc_heap *heap, const char *type __rte_unused, size_t size, > > /* increase heap's count of allocated elements */ > heap->alloc_count++; > + > + asan_set_redzone(elem, user_size); > } > > return elem == NULL ? NULL : (void *)(&elem[1]); > @@ -270,6 +273,8 @@ heap_alloc_biggest(struct malloc_heap *heap, const char *type __rte_unused, > > /* increase heap's count of allocated elements */ > heap->alloc_count++; > + > + asan_set_redzone(elem, size); > } > > return elem == NULL ? NULL : (void *)(&elem[1]); > @@ -841,6 +846,8 @@ malloc_heap_free(struct malloc_elem *elem) > if (!malloc_elem_cookies_ok(elem) || elem->state != ELEM_BUSY) > return -1; > > + asan_clear_redzone(elem); > + > /* elem may be merged with previous element, so keep heap address */ > heap = elem->heap; > msl = elem->msl; > @@ -848,6 +855,9 @@ malloc_heap_free(struct malloc_elem *elem) > > rte_spinlock_lock(&(heap->lock)); > > + void *asan_ptr = RTE_PTR_ADD(elem, MALLOC_ELEM_HEADER_LEN + elem->pad); > + size_t asan_data_len = elem->size - MALLOC_ELEM_OVERHEAD - elem->pad; > + > /* mark element as free */ > elem->state = ELEM_FREE; > > @@ -1001,6 +1011,8 @@ malloc_heap_free(struct malloc_elem *elem) > > rte_mcfg_mem_write_unlock(); > free_unlock: > + asan_set_freezone(asan_ptr, asan_data_len); > + > rte_spinlock_unlock(&(heap->lock)); > return ret; > } > diff --git a/lib/eal/common/rte_malloc.c b/lib/eal/common/rte_malloc.c > index 9d39e58c08..d0bec26920 100644 > --- a/lib/eal/common/rte_malloc.c > +++ b/lib/eal/common/rte_malloc.c > @@ -162,6 +162,8 @@ rte_calloc(const char *type, size_t num, size_t size, unsigned align) > void * > rte_realloc_socket(void *ptr, size_t size, unsigned int align, int socket) > { > + size_t user_size; > + > if (ptr == NULL) > return rte_malloc_socket(NULL, size, align, socket); > > @@ -171,6 +173,8 @@ rte_realloc_socket(void *ptr, size_t size, unsigned int align, int socket) > return NULL; > } > > + user_size = size; > + > size = RTE_CACHE_LINE_ROUNDUP(size), align = RTE_CACHE_LINE_ROUNDUP(align); > > /* check requested socket id and alignment matches first, and if ok, > @@ -181,6 +185,9 @@ rte_realloc_socket(void *ptr, size_t size, unsigned int align, int socket) > RTE_PTR_ALIGN(ptr, align) == ptr && > malloc_heap_resize(elem, size) == 0) { > rte_eal_trace_mem_realloc(size, align, socket, ptr); > + > + asan_set_redzone(elem, user_size); > + > return ptr; > } > > @@ -192,7 +199,7 @@ rte_realloc_socket(void *ptr, size_t size, unsigned int align, int socket) > if (new_ptr == NULL) > return NULL; > /* elem: |pad|data_elem|data|trailer| */ > - const size_t old_size = elem->size - elem->pad - MALLOC_ELEM_OVERHEAD; > + const size_t old_size = old_malloc_size(elem); > rte_memcpy(new_ptr, ptr, old_size < size ? old_size : size); > rte_free(ptr); > > diff --git a/lib/pipeline/rte_swx_pipeline.c b/lib/pipeline/rte_swx_pipeline.c > index 8eb978a30c..aaa0107d02 100644 > --- a/lib/pipeline/rte_swx_pipeline.c > +++ b/lib/pipeline/rte_swx_pipeline.c > @@ -6340,7 +6340,7 @@ instr_meter_translate(struct rte_swx_pipeline *p, > return 0; > } > > - CHECK(0, EINVAL); > + return -EINVAL; > } > > static inline struct meter * > @@ -8025,7 +8025,7 @@ instr_translate(struct rte_swx_pipeline *p, > instr, > data); > > - CHECK(0, EINVAL); > + return -EINVAL; > } > > static struct instruction_data * > -- > 2.25.1 > -- David Marchand ^ permalink raw reply [flat|nested] 117+ messages in thread
* Re: [dpdk-dev] [PATCH v3] Enable AddressSanitizer feature on DPDK 2021-09-20 10:03 ` David Marchand @ 2021-09-22 7:26 ` Peng, ZhihongX 0 siblings, 0 replies; 117+ messages in thread From: Peng, ZhihongX @ 2021-09-22 7:26 UTC (permalink / raw) To: David Marchand Cc: Burakov, Anatoly, Ananyev, Konstantin, Stephen Hemminger, dev, Lin, Xueqin > -----Original Message----- > From: David Marchand <david.marchand@redhat.com> > Sent: Monday, September 20, 2021 6:04 PM > To: Peng, ZhihongX <zhihongx.peng@intel.com> > Cc: Burakov, Anatoly <anatoly.burakov@intel.com>; Ananyev, Konstantin > <konstantin.ananyev@intel.com>; Stephen Hemminger > <stephen@networkplumber.org>; dev <dev@dpdk.org>; Lin, Xueqin > <xueqin.lin@intel.com> > Subject: Re: [dpdk-dev] [PATCH v3] Enable AddressSanitizer feature on DPDK > > On Sat, Sep 18, 2021 at 9:51 AM <zhihongx.peng@intel.com> wrote: > > > > From: Zhihong Peng <zhihongx.peng@intel.com> > > - The title is too vague. > I am not sure what the best title is, but my current idea is: > mem: instrument allocator with Asan Do you think this name is accurate: Enable AddressSanitizer for memory detector on DPDK > > > - This is a nice feature that must be announced in the release notes. > > > - How should we spell it? > Asan ? > ASAN ? > ASan ? Use ASan, google's spelling: https://github.com/google/sanitizers/wiki/AddressSanitizer > > Please update devtools/words-case.txt and fix inconsistencies in this patch. V4 will update this document. > > > > AddressSanitizer (ASan) is a google memory error detect standard tool. > > It could help to detect use-after-free and {heap,stack,global}-buffer > > overflow bugs in C/C++ programs, print detailed error information when > > error happens, large improve debug efficiency. > > > > By referring to its implementation algorithm > > (https://github.com/google/sanitizers/wiki/AddressSanitizerAlgorithm), > > enable heap-buffer-overflow and use-after-free functions on dpdk. > > DPDK ASAN function currently only supports on Linux x86_64. > > If you don't intend to update other arches, at least explain in the commitlog > what should be done: so that other arches know what to do to add support. V4 will add instructions. > > > > > Here is an example of heap-buffer-overflow bug: > > ...... > > char *p = rte_zmalloc(NULL, 7, 0); > > p[7] = 'a'; > > ...... > > > > Here is an example of use-after-free bug: > > ...... > > char *p = rte_zmalloc(NULL, 7, 0); > > rte_free(p); > > *p = 'a'; > > ...... > > > > If you want to use this feature, > > you need to add below compilation options when compiling code: > > -Dbuildtype=debug -Db_lundef=false -Db_sanitize=address > > ASAN is triggered by -Db_sanitize=address, it is the only *needed* option > afaiu. > > > > "-Dbuildtype=debug": Display code information when coredump occurs in > > the program. > > In ASan context, there is no coredump. > ASan displays a backtrace which is easier to read when debug symbols are > available. > You can suggest building with debug, but this is *not needed*. > > > > "-Db_lundef=false": It is enabled by default, and needs to be disabled > > when using asan. Dbuildtype and Db_lundef will update the instructions. > This is an issue with meson and clang. > Tweaking b_lundef is needed with clang, gcc looks fine. > But still, on RHEL with gcc, I need to install libasan. > > Maybe we can add libasan at a requirement at project level, did you try it? libasan is installed by default on the ubuntu system. Because libasan and the compiler are matched, so it is best to manually install the matching library. > > > > > Signed-off-by: Xueqin Lin <xueqin.lin@intel.com> > > Signed-off-by: Zhihong Peng <zhihongx.peng@intel.com> > > --- > > doc/guides/prog_guide/asan.rst | 130 ++++++++++++++++++++++ > > doc/guides/prog_guide/index.rst | 1 + > > lib/eal/common/malloc_elem.c | 26 ++++- > > lib/eal/common/malloc_elem.h | 184 > +++++++++++++++++++++++++++++++- > > lib/eal/common/malloc_heap.c | 12 +++ > > lib/eal/common/rte_malloc.c | 9 +- > > lib/pipeline/rte_swx_pipeline.c | 4 +- > > This change on pipeline has no explanation, and looks out of place wrt to > current change. This is to solve the gcc compilation problem, I will separate this patch. > > > > > 7 files changed, 359 insertions(+), 7 deletions(-) create mode > > 100644 doc/guides/prog_guide/asan.rst > > > > diff --git a/doc/guides/prog_guide/asan.rst > > b/doc/guides/prog_guide/asan.rst new file mode 100644 index > > 0000000000..a0589d9b8a > > --- /dev/null > > +++ b/doc/guides/prog_guide/asan.rst > > @@ -0,0 +1,130 @@ > > +.. Copyright (c) <2021>, Intel Corporation > > + All rights reserved. > > + > > +Memory error detect standard tool - AddressSanitizer(Asan) > > > +========================================================= > = > > + > > +AddressSanitizer (ASan) is a google memory error detect standard > > +tool. It could help to detect use-after-free and > > +{heap,stack,global}-buffer overflow bugs in C/C++ programs, print > > +detailed error information when error happens, large improve debug > > +efficiency. > > + > > +By referring to its implementation algorithm > > +(https://github.com/google/sanitizers/wiki/AddressSanitizerAlgorithm) > > +, enabled heap-buffer-overflow and use-after-free functions on dpdk. > > +DPDK ASAN function currently only supports on Linux x86_64. > > + > > +AddressSanitizer is a part of LLVM(3.1+)and GCC(4.8+). > > + > > +Example heap-buffer-overflow error > > +---------------------------------- > > + > > +Following error was reported when Asan was enabled:: > > + > > + Applied 9 bytes of memory, but accessed the 10th byte of memory, > > + so heap-buffer-overflow appeared. > > + > > +Below code results in this error:: > > + > > + char *p = rte_zmalloc(NULL, 9, 0); > > + if (!p) { > > + printf("rte_zmalloc error."); > > + return -1; > > + } > > + p[9] = 'a'; > > + > > +The error log:: > > + > > + ==49433==ERROR: AddressSanitizer: heap-buffer-overflow on address > 0x7f773fafa249 at pc 0x5556b13bdae4 bp 0x7ffeb4965e40 sp 0x7ffeb4965e30 > WRITE of size 1 at 0x7f773fafa249 thread T0 > > + #0 0x5556b13bdae3 in > asan_heap_buffer_overflow ../app/test/test_asan_heap_buffer_overflow. > c:25 > > + #1 0x5556b043e9d4 in > cmd_autotest_parsed ../app/test/commands.c:71 > > + #2 0x5556b1cdd4b0 in > cmdline_parse ../lib/cmdline/cmdline_parse.c:290 > > + #3 0x5556b1cd8987 in cmdline_valid_buffer ../lib/cmdline/cmdline.c:26 > > + #4 0x5556b1ce477a in rdline_char_in ../lib/cmdline/cmdline_rdline.c:421 > > + #5 0x5556b1cd923e in cmdline_in ../lib/cmdline/cmdline.c:149 > > + #6 0x5556b1cd9769 in cmdline_interact ../lib/cmdline/cmdline.c:223 > > + #7 0x5556b045f53b in main ../app/test/test.c:234 > > + #8 0x7f7f1eba90b2 in __libc_start_main (/lib/x86_64-linux- > gnu/libc.so.6+0x270b2) > > + #9 0x5556b043e70d in _start > > + (/home/pzh/yyy/x86_64-native-linuxapp-gcc/app/test/dpdk- > test+0x7ce70 > > + d) > > + > > + Address 0x7f773fafa249 is a wild pointer. > > + SUMMARY: AddressSanitizer: heap-buffer- > overflow ../app/test/test_asan_heap_buffer_overflow.c:25 in > asan_heap_buffer_overflow > > + Shadow bytes around the buggy address: > > + 0x0fef67f573f0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 > > + 0x0fef67f57400: fa fa 00 00 00 00 00 00 00 00 00 00 00 00 00 00 > > + 0x0fef67f57410: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 > > + 0x0fef67f57420: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 > > + 0x0fef67f57430: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 > > + =>0x0fef67f57440: 00 00 00 00 00 00 fa fa 00[01]fa 00 00 00 00 00 > > + 0x0fef67f57450: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 > > + 0x0fef67f57460: 00 00 00 00 00 00 fa fa 00 00 00 00 00 00 00 00 > > + 0x0fef67f57470: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 > > + 0x0fef67f57480: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 > > + > > +Example use-after-free error > > +---------------------------- > > + > > +Following error was reported when Asan was enabled:: > > + > > + Applied for 9 bytes of memory, and accessed the first byte after > > + released, so heap-use-after-free appeared. > > + > > +Below code results in this error:: > > + > > + char *p = rte_zmalloc(NULL, 9, 0); > > + if (!p) { > > + printf("rte_zmalloc error."); > > + return -1; > > + } > > + rte_free(p); > > + *p = 'a'; > > + > > +The error log:: > > + > > + ==49478==ERROR: AddressSanitizer: heap-use-after-free on address > 0x7fe2ffafa240 at pc 0x56409b084bc8 bp 0x7ffef62c57d0 sp 0x7ffef62c57c0 > WRITE of size 1 at 0x7fe2ffafa240 thread T0 > > + #0 0x56409b084bc7 in > asan_use_after_free ../app/test/test_asan_use_after_free.c:26 > > + #1 0x56409a1059d4 in > cmd_autotest_parsed ../app/test/commands.c:71 > > + #2 0x56409b9a44b0 in > cmdline_parse ../lib/cmdline/cmdline_parse.c:290 > > + #3 0x56409b99f987 in cmdline_valid_buffer ../lib/cmdline/cmdline.c:26 > > + #4 0x56409b9ab77a in > rdline_char_in ../lib/cmdline/cmdline_rdline.c:421 > > + #5 0x56409b9a023e in cmdline_in ../lib/cmdline/cmdline.c:149 > > + #6 0x56409b9a0769 in cmdline_interact ../lib/cmdline/cmdline.c:223 > > + #7 0x56409a12653b in main ../app/test/test.c:234 > > + #8 0x7feafafc20b2 in __libc_start_main (/lib/x86_64-linux- > gnu/libc.so.6+0x270b2) > > + #9 0x56409a10570d in _start > > + (/home/pzh/yyy/x86_64-native-linuxapp-gcc/app/test/dpdk- > test+0x7ce70 > > + d) > > + > > + Address 0x7fe2ffafa240 is a wild pointer. > > + SUMMARY: AddressSanitizer: heap-use-after- > free ../app/test/test_asan_use_after_free.c:26 in asan_use_after_free > > + Shadow bytes around the buggy address: > > + 0x0ffcdff573f0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 > > + 0x0ffcdff57400: fa fa 00 00 00 00 00 00 00 00 00 00 00 00 00 00 > > + 0x0ffcdff57410: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 > > + 0x0ffcdff57420: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 > > + 0x0ffcdff57430: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 > > + =>0x0ffcdff57440: 00 00 00 00 00 00 00 00[fd]fd fd fd fd fd fd fd > > + 0x0ffcdff57450: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 > > + 0x0ffcdff57460: 00 00 00 00 00 00 fa fa 00 00 00 00 00 00 00 00 > > + 0x0ffcdff57470: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 > > + 0x0ffcdff57480: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 > > + 0x0ffcdff57490: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 > > + > > +Usage > > +----- > > + > > +meson build > > +^^^^^^^^^^^ > > + > > +To enable Asan in meson build system, use following meson build > command: > > + > > +Example usage:: > > + > > + meson build -Dbuildtype=debug -Db_lundef=false -Db_sanitize=address > > + ninja -C build > > + > > +.. Note:: > > + > > + a) The issue of asan wild pointer is that dpdk asan tool is not fully > adapted to google asan. > > + For example: Address 0x7fe2ffafa240 is a wild pointer. > > I can't understand what the "wild pointer" means in this context. > This comment belongs to the traces in the section before. This is because we only set up the red zone and did not construct the complete data structure required by libasan. As a result, libasan could not find the location of the memory request, so "wild pointer" was reported. The current information has been able to troubleshoot errors, so this issue is a lower priority. > > > + b) Centos8 needs to install libasan separately. > > See my previous comment on b_lundef. > > > > + c) If the program uses cmdline, when a memory bug occurs, need to > execute the "stty echo" command. > > Yes, this is annoying when executing failing unit tests. > That is something to handle better in the cmdline library, maybe in the future. > > Like "wild pointer", I don't think this comment belongs here. cmdline cannot solve this problem temporarily, so this problem must be explained, but I don’t know which position is better. > > > > diff --git a/doc/guides/prog_guide/index.rst > > b/doc/guides/prog_guide/index.rst index 2dce507f46..df8a4b93e1 100644 > > --- a/doc/guides/prog_guide/index.rst > > +++ b/doc/guides/prog_guide/index.rst > > @@ -71,3 +71,4 @@ Programmer's Guide > > lto > > profile_app > > glossary > > + asan > > diff --git a/lib/eal/common/malloc_elem.c > > b/lib/eal/common/malloc_elem.c index c2c9461f1d..bdd20a162e 100644 > > --- a/lib/eal/common/malloc_elem.c > > +++ b/lib/eal/common/malloc_elem.c > > @@ -446,6 +446,8 @@ malloc_elem_alloc(struct malloc_elem *elem, > size_t size, unsigned align, > > struct malloc_elem *new_free_elem = > > RTE_PTR_ADD(new_elem, size + > > MALLOC_ELEM_OVERHEAD); > > > > + asan_clear_split_alloczone(new_free_elem); > > + > > split_elem(elem, new_free_elem); > > malloc_elem_free_list_insert(new_free_elem); > > > > @@ -458,6 +460,8 @@ malloc_elem_alloc(struct malloc_elem *elem, > size_t size, unsigned align, > > elem->state = ELEM_BUSY; > > elem->pad = old_elem_size; > > > > + asan_clear_alloczone(elem); > > + > > /* put a dummy header in padding, to point to real element header > */ > > if (elem->pad > 0) { /* pad will be at least 64-bytes, as everything > > * is cache-line aligned */ @@ > > -470,12 +474,18 @@ malloc_elem_alloc(struct malloc_elem *elem, size_t > size, unsigned align, > > return new_elem; > > } > > > > + asan_clear_split_alloczone(new_elem); > > + > > /* we are going to split the element in two. The original element > > * remains free, and the new element is the one allocated. > > * Re-insert original element, in case its new size makes it > > * belong on a different list. > > */ > > + > > split_elem(elem, new_elem); > > + > > + asan_clear_alloczone(new_elem); > > + > > new_elem->state = ELEM_BUSY; > > malloc_elem_free_list_insert(elem); > > > > @@ -601,6 +611,8 @@ malloc_elem_hide_region(struct malloc_elem > *elem, void *start, size_t len) > > if (next && next_elem_is_adjacent(elem)) { > > len_after = RTE_PTR_DIFF(next, hide_end); > > if (len_after >= MALLOC_ELEM_OVERHEAD + MIN_DATA_SIZE) > > { > > + asan_clear_split_alloczone(hide_end); > > + > > /* split after */ > > split_elem(elem, hide_end); > > > > @@ -615,6 +627,8 @@ malloc_elem_hide_region(struct malloc_elem > *elem, void *start, size_t len) > > if (prev && prev_elem_is_adjacent(elem)) { > > len_before = RTE_PTR_DIFF(hide_start, elem); > > if (len_before >= MALLOC_ELEM_OVERHEAD + > > MIN_DATA_SIZE) { > > + asan_clear_split_alloczone(hide_start); > > + > > /* split before */ > > split_elem(elem, hide_start); > > > > @@ -628,6 +642,8 @@ malloc_elem_hide_region(struct malloc_elem > *elem, void *start, size_t len) > > } > > } > > > > + asan_clear_alloczone(elem); > > + > > remove_elem(elem); > > } > > > > @@ -641,8 +657,10 @@ malloc_elem_resize(struct malloc_elem *elem, > size_t size) > > const size_t new_size = size + elem->pad + > > MALLOC_ELEM_OVERHEAD; > > > > /* if we request a smaller size, then always return ok */ > > - if (elem->size >= new_size) > > + if (elem->size >= new_size) { > > + asan_clear_alloczone(elem); > > return 0; > > + } > > > > /* check if there is a next element, it's free and adjacent */ > > if (!elem->next || elem->next->state != ELEM_FREE || @@ -661,9 > > +679,15 @@ malloc_elem_resize(struct malloc_elem *elem, size_t size) > > /* now we have a big block together. Lets cut it down a bit, by > splitting */ > > struct malloc_elem *split_pt = RTE_PTR_ADD(elem, new_size); > > split_pt = RTE_PTR_ALIGN_CEIL(split_pt, > > RTE_CACHE_LINE_SIZE); > > + > > + asan_clear_split_alloczone(split_pt); > > + > > split_elem(elem, split_pt); > > malloc_elem_free_list_insert(split_pt); > > } > > + > > + asan_clear_alloczone(elem); > > + > > return 0; > > } > > > > diff --git a/lib/eal/common/malloc_elem.h > > b/lib/eal/common/malloc_elem.h index a1e5f7f02c..01a739f2ea 100644 > > --- a/lib/eal/common/malloc_elem.h > > +++ b/lib/eal/common/malloc_elem.h > > @@ -7,6 +7,14 @@ > > > > #include <stdbool.h> > > > > +#ifdef __SANITIZE_ADDRESS__ > > +#define RTE_MALLOC_ASAN > > +#elif defined(__has_feature) > > +# if __has_feature(address_sanitizer) #define RTE_MALLOC_ASAN # > endif > > +#endif > > + > > #define MIN_DATA_SIZE (RTE_CACHE_LINE_SIZE) > > > > /* dummy definition of struct so we can use pointers to it in > > malloc_elem struct */ @@ -36,10 +44,20 @@ struct malloc_elem { > > uint64_t header_cookie; /* Cookie marking start of data */ > > /* trailer cookie at start + > > size */ #endif > > +#ifdef RTE_MALLOC_ASAN > > + size_t user_size; > > + uint64_t asan_cookie[2]; /*must be next to header_cookie*/ > > Fix coding style for comment please. V4 will be fixed. > > > +#endif > > } __rte_cache_aligned; > > > > +static const unsigned int MALLOC_ELEM_HEADER_LEN = sizeof(struct > > +malloc_elem); > > + > > #ifndef RTE_MALLOC_DEBUG > > -static const unsigned MALLOC_ELEM_TRAILER_LEN = 0; > > +#ifdef RTE_MALLOC_ASAN > > +static const unsigned int MALLOC_ELEM_TRAILER_LEN = > > +RTE_CACHE_LINE_SIZE; #else static const unsigned int > > +MALLOC_ELEM_TRAILER_LEN; #endif > > > > /* dummy function - just check if pointer is non-null */ static > > inline int @@ -55,7 +73,7 @@ set_trailer(struct malloc_elem *elem > > __rte_unused){ } > > > > > > #else > > -static const unsigned MALLOC_ELEM_TRAILER_LEN = > RTE_CACHE_LINE_SIZE; > > +static const unsigned int MALLOC_ELEM_TRAILER_LEN = > > +RTE_CACHE_LINE_SIZE; > > > > #define MALLOC_HEADER_COOKIE 0xbadbadbadadd2e55ULL /**< > Header cookie. */ > > #define MALLOC_TRAILER_COOKIE 0xadd2e55badbadbadULL /**< Trailer > > cookie.*/ @@ -90,9 +108,169 @@ malloc_elem_cookies_ok(const struct > > malloc_elem *elem) > > > > #endif > > > > -static const unsigned MALLOC_ELEM_HEADER_LEN = sizeof(struct > > malloc_elem); #define MALLOC_ELEM_OVERHEAD > (MALLOC_ELEM_HEADER_LEN + > > MALLOC_ELEM_TRAILER_LEN) > > > > +#ifdef RTE_MALLOC_ASAN > > + > > +#define ASAN_SHADOW_GRAIN_SIZE 8 > > +#define ASAN_MEM_FREE_FLAG 0xfd > > +#define ASAN_MEM_REDZONE_FLAG 0xfa > > +#define ASAN_MEM_TO_SHADOW(mem) (((mem) >> 3) + > 0x00007fff8000) > > + > > +#if defined(__clang__) > > +__attribute__((no_sanitize("address", "hwaddress"))) #else > > +__attribute__((no_sanitize_address)) > > +#endif > > This attribute is only used here, I am ok with leaving this as is. > If later it is needed elsewhere, we'll have to define a new attribute wrapper > in rte_common.h. > > > > +static inline void > > +asan_set_shadow(void *addr, char val) { > > + *(char *)addr = val; > > +} > > + > > +static inline void > > +asan_set_zone(void *ptr, size_t len, uint32_t val) { > > + size_t offset; > > + char *shadow; > > + size_t zone_len = len / ASAN_SHADOW_GRAIN_SIZE; > > + size_t i; > + <empty line> to separate declarations from code. > > > > + if (len % ASAN_SHADOW_GRAIN_SIZE != 0) > > + zone_len += 1; > > + > > + for (size_t i = 0; i < zone_len; i++) { > > + for (i = 0; i < zone_len; i++) { > > That's to fix build issue: > V4 will be fixed. > In file included from ../lib/eal/common/malloc_mp.c:16:0: > ../lib/eal/common/malloc_elem.h: In function ‘asan_set_zone’: > ../lib/eal/common/malloc_elem.h:140:2: error: ‘for’ loop initial declarations > are only allowed in C99 mode > for (size_t i = 0; i < zone_len; i++) { > ^ > ../lib/eal/common/malloc_elem.h:140:2: note: use option -std=c99 or > -std=gnu99 to compile your code > > > > > > > > > + offset = i * ASAN_SHADOW_GRAIN_SIZE; > > + shadow = (char *)ASAN_MEM_TO_SHADOW(((int64_t)ptr + > offset)); > > + asan_set_shadow(shadow, val); > > + } > > +} > > + > > +/* > > + * When the memory is released, the release mark is > > + * set in the corresponding range of the shadow area. > > + */ > > +static inline void > > +asan_set_freezone(void *ptr, size_t size) { > > + asan_set_zone(ptr, size, ASAN_MEM_FREE_FLAG); } > > + > > +/* > > + * When the memory is allocated, memory state must set as accessible. > > + */ > > +static inline void > > +asan_clear_alloczone(struct malloc_elem *elem) > > +{ > > + asan_set_zone((void *)elem, elem->size, 0x0); > > +} > > + > > +static inline void > > +asan_clear_split_alloczone(struct malloc_elem *elem) > > +{ > > + void *ptr = RTE_PTR_SUB(elem, MALLOC_ELEM_TRAILER_LEN); > > + asan_set_zone(ptr, MALLOC_ELEM_OVERHEAD, 0x0); > > +} > > + > > +/* > > + * When the memory is allocated, the memory boundary is > > + * marked in the corresponding range of the shadow area. > > + */ > > +static inline void > > +asan_set_redzone(struct malloc_elem *elem, size_t user_size) > > +{ > > + uint64_t ptr; > > + char *shadow; > > + if (elem != NULL) { > > + if (elem->state != ELEM_PAD) > > + elem = RTE_PTR_ADD(elem, elem->pad); > > + > > + elem->user_size = user_size; > > + > > + /* Set mark before the start of the allocated memory */ > > + ptr = (uint64_t)RTE_PTR_ADD(elem, MALLOC_ELEM_HEADER_LEN) > > + - ASAN_SHADOW_GRAIN_SIZE; > > + shadow = (char *)ASAN_MEM_TO_SHADOW(ptr); > > + asan_set_shadow(shadow, ASAN_MEM_REDZONE_FLAG); > > + shadow = (char *)ASAN_MEM_TO_SHADOW(ptr > > + - ASAN_SHADOW_GRAIN_SIZE); > > + asan_set_shadow(shadow, ASAN_MEM_REDZONE_FLAG); > > + > > + /* Set mark after the end of the allocated memory */ > > + ptr = (uint64_t)RTE_PTR_ADD(elem, MALLOC_ELEM_HEADER_LEN > > + + elem->user_size); > > + shadow = (char *)ASAN_MEM_TO_SHADOW(ptr); > > + uint32_t val = (ptr % ASAN_SHADOW_GRAIN_SIZE); > > + val = (val == 0) ? ASAN_MEM_REDZONE_FLAG : val; > > + asan_set_shadow(shadow, val); > > + shadow = (char *)ASAN_MEM_TO_SHADOW(ptr > > + + ASAN_SHADOW_GRAIN_SIZE); > > + asan_set_shadow(shadow, ASAN_MEM_REDZONE_FLAG); > > + } > > +} > > + > > +/* > > + * When the memory is released, the mark of the memory boundary > > + * in the corresponding range of the shadow area is cleared. > > + */ > > +static inline void > > +asan_clear_redzone(struct malloc_elem *elem) > > +{ > > + uint64_t ptr; > > + char *shadow; > > + if (elem != NULL) { > > + elem = RTE_PTR_ADD(elem, elem->pad); > > + > > + /* Clear mark before the start of the allocated memory */ > > + ptr = (uint64_t)RTE_PTR_ADD(elem, MALLOC_ELEM_HEADER_LEN) > > + - ASAN_SHADOW_GRAIN_SIZE; > > + shadow = (char *)ASAN_MEM_TO_SHADOW(ptr); > > + asan_set_shadow(shadow, 0x00); > > + shadow = (char *)ASAN_MEM_TO_SHADOW(ptr > > + - ASAN_SHADOW_GRAIN_SIZE); > > + asan_set_shadow(shadow, 0x00); > > + > > + /* Clear mark after the end of the allocated memory */ > > + ptr = (uint64_t)RTE_PTR_ADD(elem, MALLOC_ELEM_HEADER_LEN > > + + elem->user_size); > > + shadow = (char *)ASAN_MEM_TO_SHADOW(ptr); > > + asan_set_shadow(shadow, 0x00); > > + shadow = (char *)ASAN_MEM_TO_SHADOW(ptr > > + + ASAN_SHADOW_GRAIN_SIZE); > > + asan_set_shadow(shadow, 0x00); > > + } > > +} > > + > > +static inline size_t > > +old_malloc_size(struct malloc_elem *elem) > > +{ > > + if (elem->state != ELEM_PAD) > > + elem = RTE_PTR_ADD(elem, elem->pad); > > + > > + return elem->user_size; > > +} > > +#else > > +static inline void > > +asan_set_freezone(void *ptr __rte_unused, size_t size __rte_unused) { } > > + > > +static inline void > > +asan_clear_alloczone(struct malloc_elem *elem __rte_unused) { } > > + > > +static inline void > > +asan_clear_split_alloczone(struct malloc_elem *elem __rte_unused) { } > > + > > +static inline void > > +asan_set_redzone(struct malloc_elem *elem __rte_unused, > > + size_t user_size __rte_unused) { } > > + > > +static inline void > > +asan_clear_redzone(struct malloc_elem *elem __rte_unused) { } > > + > > +static inline size_t > > +old_malloc_size(struct malloc_elem *elem) > > +{ > > + return elem->size - elem->pad - MALLOC_ELEM_OVERHEAD; > > +} > > +#endif > > + > > /* > > * Given a pointer to the start of a memory block returned by malloc, get > > * the actual malloc_elem header for that block. > > diff --git a/lib/eal/common/malloc_heap.c > b/lib/eal/common/malloc_heap.c > > index ee400f38ec..775d6789df 100644 > > --- a/lib/eal/common/malloc_heap.c > > +++ b/lib/eal/common/malloc_heap.c > > @@ -237,6 +237,7 @@ heap_alloc(struct malloc_heap *heap, const char > *type __rte_unused, size_t size, > > unsigned int flags, size_t align, size_t bound, bool contig) > > { > > struct malloc_elem *elem; > > + size_t user_size = size; > > > > size = RTE_CACHE_LINE_ROUNDUP(size); > > align = RTE_CACHE_LINE_ROUNDUP(align); > > @@ -250,6 +251,8 @@ heap_alloc(struct malloc_heap *heap, const char > *type __rte_unused, size_t size, > > > > /* increase heap's count of allocated elements */ > > heap->alloc_count++; > > + > > + asan_set_redzone(elem, user_size); > > } > > > > return elem == NULL ? NULL : (void *)(&elem[1]); > > @@ -270,6 +273,8 @@ heap_alloc_biggest(struct malloc_heap *heap, > const char *type __rte_unused, > > > > /* increase heap's count of allocated elements */ > > heap->alloc_count++; > > + > > + asan_set_redzone(elem, size); > > } > > > > return elem == NULL ? NULL : (void *)(&elem[1]); > > @@ -841,6 +846,8 @@ malloc_heap_free(struct malloc_elem *elem) > > if (!malloc_elem_cookies_ok(elem) || elem->state != ELEM_BUSY) > > return -1; > > > > + asan_clear_redzone(elem); > > + > > /* elem may be merged with previous element, so keep heap address > */ > > heap = elem->heap; > > msl = elem->msl; > > @@ -848,6 +855,9 @@ malloc_heap_free(struct malloc_elem *elem) > > > > rte_spinlock_lock(&(heap->lock)); > > > > + void *asan_ptr = RTE_PTR_ADD(elem, MALLOC_ELEM_HEADER_LEN + > elem->pad); > > + size_t asan_data_len = elem->size - MALLOC_ELEM_OVERHEAD - > elem->pad; > > + > > /* mark element as free */ > > elem->state = ELEM_FREE; > > > > @@ -1001,6 +1011,8 @@ malloc_heap_free(struct malloc_elem *elem) > > > > rte_mcfg_mem_write_unlock(); > > free_unlock: > > + asan_set_freezone(asan_ptr, asan_data_len); > > + > > rte_spinlock_unlock(&(heap->lock)); > > return ret; > > } > > diff --git a/lib/eal/common/rte_malloc.c b/lib/eal/common/rte_malloc.c > > index 9d39e58c08..d0bec26920 100644 > > --- a/lib/eal/common/rte_malloc.c > > +++ b/lib/eal/common/rte_malloc.c > > @@ -162,6 +162,8 @@ rte_calloc(const char *type, size_t num, size_t size, > unsigned align) > > void * > > rte_realloc_socket(void *ptr, size_t size, unsigned int align, int socket) > > { > > + size_t user_size; > > + > > if (ptr == NULL) > > return rte_malloc_socket(NULL, size, align, socket); > > > > @@ -171,6 +173,8 @@ rte_realloc_socket(void *ptr, size_t size, unsigned > int align, int socket) > > return NULL; > > } > > > > + user_size = size; > > + > > size = RTE_CACHE_LINE_ROUNDUP(size), align = > RTE_CACHE_LINE_ROUNDUP(align); > > > > /* check requested socket id and alignment matches first, and if ok, > > @@ -181,6 +185,9 @@ rte_realloc_socket(void *ptr, size_t size, unsigned > int align, int socket) > > RTE_PTR_ALIGN(ptr, align) == ptr && > > malloc_heap_resize(elem, size) == 0) { > > rte_eal_trace_mem_realloc(size, align, socket, ptr); > > + > > + asan_set_redzone(elem, user_size); > > + > > return ptr; > > } > > > > @@ -192,7 +199,7 @@ rte_realloc_socket(void *ptr, size_t size, unsigned > int align, int socket) > > if (new_ptr == NULL) > > return NULL; > > /* elem: |pad|data_elem|data|trailer| */ > > - const size_t old_size = elem->size - elem->pad - > MALLOC_ELEM_OVERHEAD; > > + const size_t old_size = old_malloc_size(elem); > > rte_memcpy(new_ptr, ptr, old_size < size ? old_size : size); > > rte_free(ptr); > > > > diff --git a/lib/pipeline/rte_swx_pipeline.c > b/lib/pipeline/rte_swx_pipeline.c > > index 8eb978a30c..aaa0107d02 100644 > > --- a/lib/pipeline/rte_swx_pipeline.c > > +++ b/lib/pipeline/rte_swx_pipeline.c > > @@ -6340,7 +6340,7 @@ instr_meter_translate(struct rte_swx_pipeline *p, > > return 0; > > } > > > > - CHECK(0, EINVAL); > > + return -EINVAL; > > } > > > > static inline struct meter * > > @@ -8025,7 +8025,7 @@ instr_translate(struct rte_swx_pipeline *p, > > instr, > > data); > > > > - CHECK(0, EINVAL); > > + return -EINVAL; > > } > > > > static struct instruction_data * > > -- > > 2.25.1 > > > > > -- > David Marchand ^ permalink raw reply [flat|nested] 117+ messages in thread
* [dpdk-dev] [PATCH v4 1/2] Enable ASan for memory detector on DPDK 2021-09-18 7:41 ` [dpdk-dev] [PATCH v3] " zhihongx.peng 2021-09-20 10:03 ` David Marchand @ 2021-09-24 2:20 ` zhihongx.peng 2021-09-24 2:20 ` [dpdk-dev] [PATCH v4 2/2] lib/pipeline: Fix gcc compilation error using ASan zhihongx.peng ` (2 more replies) 2021-09-27 12:02 ` [dpdk-dev] [PATCH v3] Enable AddressSanitizer feature " Burakov, Anatoly 2 siblings, 3 replies; 117+ messages in thread From: zhihongx.peng @ 2021-09-24 2:20 UTC (permalink / raw) To: anatoly.burakov, konstantin.ananyev, stephen Cc: dev, xueqin.lin, Zhihong Peng From: Zhihong Peng <zhihongx.peng@intel.com> AddressSanitizer (ASan) is a google memory error detect standard tool. It could help to detect use-after-free and {heap,stack,global}-buffer overflow bugs in C/C++ programs, print detailed error information when error happens, large improve debug efficiency. By referring to its implementation algorithm (https://github.com/google/sanitizers/wiki/AddressSanitizerAlgorithm), enable heap-buffer-overflow and use-after-free functions on dpdk. DPDK ASan function currently only supports on Linux x86_64. Support other platforms, need to define ASAN_SHADOW_OFFSET value according to google ASan document. Here is an example of heap-buffer-overflow bug: ...... char *p = rte_zmalloc(NULL, 7, 0); p[7] = 'a'; ...... Here is an example of use-after-free bug: ...... char *p = rte_zmalloc(NULL, 7, 0); rte_free(p); *p = 'a'; ...... If you want to use this feature, you need to add below compilation options when compiling code: -Dbuildtype=debug -Db_lundef=false -Db_sanitize=address "-Dbuildtype=debug": This is a non-essential option. When this option is added, if a memory error occurs, ASan can clearly show where the code is wrong. "-Db_lundef=false": When use clang to compile DPDK, this option must be added. Note: a) The issue of ASan wild pointer is that dpdk ASan tool is not fully adapted to google ASan. For example: Address 0x7fe2ffafa240 is a wild pointer. b) Centos needs to install libasan separately. c) If the program uses DPDK cmdline, when a memory bug occurs, need to execute the "stty echo" command. Signed-off-by: Xueqin Lin <xueqin.lin@intel.com> Signed-off-by: Zhihong Peng <zhihongx.peng@intel.com> --- devtools/words-case.txt | 1 + doc/guides/prog_guide/ASan.rst | 130 ++++++++++++++++++++++ doc/guides/prog_guide/index.rst | 1 + lib/eal/common/malloc_elem.c | 26 ++++- lib/eal/common/malloc_elem.h | 190 +++++++++++++++++++++++++++++++- lib/eal/common/malloc_heap.c | 12 ++ lib/eal/common/rte_malloc.c | 9 +- 7 files changed, 364 insertions(+), 5 deletions(-) create mode 100644 doc/guides/prog_guide/ASan.rst diff --git a/devtools/words-case.txt b/devtools/words-case.txt index 0bbad48626..3655596d47 100644 --- a/devtools/words-case.txt +++ b/devtools/words-case.txt @@ -86,3 +86,4 @@ VXLAN Windows XDP XOR +ASan diff --git a/doc/guides/prog_guide/ASan.rst b/doc/guides/prog_guide/ASan.rst new file mode 100644 index 0000000000..6d6f49f938 --- /dev/null +++ b/doc/guides/prog_guide/ASan.rst @@ -0,0 +1,130 @@ +.. Copyright (c) <2021>, Intel Corporation + All rights reserved. + +Memory error detect standard tool - AddressSanitizer(ASan) +========================================================== + +AddressSanitizer (ASan) is a google memory error detect +standard tool. It could help to detect use-after-free and +{heap,stack,global}-buffer overflow bugs in C/C++ programs, +print detailed error information when error happens, large +improve debug efficiency. + +By referring to its implementation algorithm +(https://github.com/google/sanitizers/wiki/AddressSanitizerAlgorithm), +enabled heap-buffer-overflow and use-after-free functions on dpdk. +DPDK ASan function currently only supports on Linux x86_64. + +AddressSanitizer is a part of LLVM(3.1+)and GCC(4.8+). + +Example heap-buffer-overflow error +---------------------------------- + +Following error was reported when ASan was enabled:: + + Applied 9 bytes of memory, but accessed the 10th byte of memory, + so heap-buffer-overflow appeared. + +Below code results in this error:: + + char *p = rte_zmalloc(NULL, 9, 0); + if (!p) { + printf("rte_zmalloc error."); + return -1; + } + p[9] = 'a'; + +The error log:: + + ==49433==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x7f773fafa249 at pc 0x5556b13bdae4 bp 0x7ffeb4965e40 sp 0x7ffeb4965e30 WRITE of size 1 at 0x7f773fafa249 thread T0 + #0 0x5556b13bdae3 in asan_heap_buffer_overflow ../app/test/test_asan_heap_buffer_overflow.c:25 + #1 0x5556b043e9d4 in cmd_autotest_parsed ../app/test/commands.c:71 + #2 0x5556b1cdd4b0 in cmdline_parse ../lib/cmdline/cmdline_parse.c:290 + #3 0x5556b1cd8987 in cmdline_valid_buffer ../lib/cmdline/cmdline.c:26 + #4 0x5556b1ce477a in rdline_char_in ../lib/cmdline/cmdline_rdline.c:421 + #5 0x5556b1cd923e in cmdline_in ../lib/cmdline/cmdline.c:149 + #6 0x5556b1cd9769 in cmdline_interact ../lib/cmdline/cmdline.c:223 + #7 0x5556b045f53b in main ../app/test/test.c:234 + #8 0x7f7f1eba90b2 in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x270b2) + #9 0x5556b043e70d in _start (/home/pzh/yyy/x86_64-native-linuxapp-gcc/app/test/dpdk-test+0x7ce70d) + + Address 0x7f773fafa249 is a wild pointer. + SUMMARY: AddressSanitizer: heap-buffer-overflow ../app/test/test_asan_heap_buffer_overflow.c:25 in asan_heap_buffer_overflow + Shadow bytes around the buggy address: + 0x0fef67f573f0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 0x0fef67f57400: fa fa 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 0x0fef67f57410: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 0x0fef67f57420: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 0x0fef67f57430: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + =>0x0fef67f57440: 00 00 00 00 00 00 fa fa 00[01]fa 00 00 00 00 00 + 0x0fef67f57450: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 0x0fef67f57460: 00 00 00 00 00 00 fa fa 00 00 00 00 00 00 00 00 + 0x0fef67f57470: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 0x0fef67f57480: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + +Example use-after-free error +---------------------------- + +Following error was reported when ASan was enabled:: + + Applied for 9 bytes of memory, and accessed the first byte after + released, so heap-use-after-free appeared. + +Below code results in this error:: + + char *p = rte_zmalloc(NULL, 9, 0); + if (!p) { + printf("rte_zmalloc error."); + return -1; + } + rte_free(p); + *p = 'a'; + +The error log:: + + ==49478==ERROR: AddressSanitizer: heap-use-after-free on address 0x7fe2ffafa240 at pc 0x56409b084bc8 bp 0x7ffef62c57d0 sp 0x7ffef62c57c0 WRITE of size 1 at 0x7fe2ffafa240 thread T0 + #0 0x56409b084bc7 in asan_use_after_free ../app/test/test_asan_use_after_free.c:26 + #1 0x56409a1059d4 in cmd_autotest_parsed ../app/test/commands.c:71 + #2 0x56409b9a44b0 in cmdline_parse ../lib/cmdline/cmdline_parse.c:290 + #3 0x56409b99f987 in cmdline_valid_buffer ../lib/cmdline/cmdline.c:26 + #4 0x56409b9ab77a in rdline_char_in ../lib/cmdline/cmdline_rdline.c:421 + #5 0x56409b9a023e in cmdline_in ../lib/cmdline/cmdline.c:149 + #6 0x56409b9a0769 in cmdline_interact ../lib/cmdline/cmdline.c:223 + #7 0x56409a12653b in main ../app/test/test.c:234 + #8 0x7feafafc20b2 in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x270b2) + #9 0x56409a10570d in _start (/home/pzh/yyy/x86_64-native-linuxapp-gcc/app/test/dpdk-test+0x7ce70d) + + Address 0x7fe2ffafa240 is a wild pointer. + SUMMARY: AddressSanitizer: heap-use-after-free ../app/test/test_asan_use_after_free.c:26 in asan_use_after_free + Shadow bytes around the buggy address: + 0x0ffcdff573f0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 0x0ffcdff57400: fa fa 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 0x0ffcdff57410: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 0x0ffcdff57420: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 0x0ffcdff57430: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + =>0x0ffcdff57440: 00 00 00 00 00 00 00 00[fd]fd fd fd fd fd fd fd + 0x0ffcdff57450: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 0x0ffcdff57460: 00 00 00 00 00 00 fa fa 00 00 00 00 00 00 00 00 + 0x0ffcdff57470: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 0x0ffcdff57480: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 0x0ffcdff57490: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + +Usage +----- + +meson build +^^^^^^^^^^^ + +To enable ASan in meson build system, use following meson build command: + +Example usage:: + + meson build -Dbuildtype=debug -Db_lundef=false -Db_sanitize=address + ninja -C build + +.. Note:: + + a) The issue of ASan wild pointer is that dpdk ASan tool is not fully adapted to google ASan. + For example: Address 0x7fe2ffafa240 is a wild pointer. + b) Centos needs to install libasan separately. + c) If the program uses cmdline, when a memory bug occurs, need to execute the "stty echo" command. diff --git a/doc/guides/prog_guide/index.rst b/doc/guides/prog_guide/index.rst index 2dce507f46..abaf687045 100644 --- a/doc/guides/prog_guide/index.rst +++ b/doc/guides/prog_guide/index.rst @@ -71,3 +71,4 @@ Programmer's Guide lto profile_app glossary + ASan diff --git a/lib/eal/common/malloc_elem.c b/lib/eal/common/malloc_elem.c index c2c9461f1d..bdd20a162e 100644 --- a/lib/eal/common/malloc_elem.c +++ b/lib/eal/common/malloc_elem.c @@ -446,6 +446,8 @@ malloc_elem_alloc(struct malloc_elem *elem, size_t size, unsigned align, struct malloc_elem *new_free_elem = RTE_PTR_ADD(new_elem, size + MALLOC_ELEM_OVERHEAD); + asan_clear_split_alloczone(new_free_elem); + split_elem(elem, new_free_elem); malloc_elem_free_list_insert(new_free_elem); @@ -458,6 +460,8 @@ malloc_elem_alloc(struct malloc_elem *elem, size_t size, unsigned align, elem->state = ELEM_BUSY; elem->pad = old_elem_size; + asan_clear_alloczone(elem); + /* put a dummy header in padding, to point to real element header */ if (elem->pad > 0) { /* pad will be at least 64-bytes, as everything * is cache-line aligned */ @@ -470,12 +474,18 @@ malloc_elem_alloc(struct malloc_elem *elem, size_t size, unsigned align, return new_elem; } + asan_clear_split_alloczone(new_elem); + /* we are going to split the element in two. The original element * remains free, and the new element is the one allocated. * Re-insert original element, in case its new size makes it * belong on a different list. */ + split_elem(elem, new_elem); + + asan_clear_alloczone(new_elem); + new_elem->state = ELEM_BUSY; malloc_elem_free_list_insert(elem); @@ -601,6 +611,8 @@ malloc_elem_hide_region(struct malloc_elem *elem, void *start, size_t len) if (next && next_elem_is_adjacent(elem)) { len_after = RTE_PTR_DIFF(next, hide_end); if (len_after >= MALLOC_ELEM_OVERHEAD + MIN_DATA_SIZE) { + asan_clear_split_alloczone(hide_end); + /* split after */ split_elem(elem, hide_end); @@ -615,6 +627,8 @@ malloc_elem_hide_region(struct malloc_elem *elem, void *start, size_t len) if (prev && prev_elem_is_adjacent(elem)) { len_before = RTE_PTR_DIFF(hide_start, elem); if (len_before >= MALLOC_ELEM_OVERHEAD + MIN_DATA_SIZE) { + asan_clear_split_alloczone(hide_start); + /* split before */ split_elem(elem, hide_start); @@ -628,6 +642,8 @@ malloc_elem_hide_region(struct malloc_elem *elem, void *start, size_t len) } } + asan_clear_alloczone(elem); + remove_elem(elem); } @@ -641,8 +657,10 @@ malloc_elem_resize(struct malloc_elem *elem, size_t size) const size_t new_size = size + elem->pad + MALLOC_ELEM_OVERHEAD; /* if we request a smaller size, then always return ok */ - if (elem->size >= new_size) + if (elem->size >= new_size) { + asan_clear_alloczone(elem); return 0; + } /* check if there is a next element, it's free and adjacent */ if (!elem->next || elem->next->state != ELEM_FREE || @@ -661,9 +679,15 @@ malloc_elem_resize(struct malloc_elem *elem, size_t size) /* now we have a big block together. Lets cut it down a bit, by splitting */ struct malloc_elem *split_pt = RTE_PTR_ADD(elem, new_size); split_pt = RTE_PTR_ALIGN_CEIL(split_pt, RTE_CACHE_LINE_SIZE); + + asan_clear_split_alloczone(split_pt); + split_elem(elem, split_pt); malloc_elem_free_list_insert(split_pt); } + + asan_clear_alloczone(elem); + return 0; } diff --git a/lib/eal/common/malloc_elem.h b/lib/eal/common/malloc_elem.h index a1e5f7f02c..08b25c6b1f 100644 --- a/lib/eal/common/malloc_elem.h +++ b/lib/eal/common/malloc_elem.h @@ -7,6 +7,16 @@ #include <stdbool.h> +#ifdef RTE_ARCH_X86_64 +#ifdef __SANITIZE_ADDRESS__ +#define RTE_MALLOC_ASAN +#elif defined(__has_feature) +# if __has_feature(address_sanitizer) +#define RTE_MALLOC_ASAN +# endif +#endif +#endif + #define MIN_DATA_SIZE (RTE_CACHE_LINE_SIZE) /* dummy definition of struct so we can use pointers to it in malloc_elem struct */ @@ -36,10 +46,20 @@ struct malloc_elem { uint64_t header_cookie; /* Cookie marking start of data */ /* trailer cookie at start + size */ #endif +#ifdef RTE_MALLOC_ASAN + size_t user_size; + uint64_t asan_cookie[2]; /* must be next to header_cookie */ +#endif } __rte_cache_aligned; +static const unsigned int MALLOC_ELEM_HEADER_LEN = sizeof(struct malloc_elem); + #ifndef RTE_MALLOC_DEBUG -static const unsigned MALLOC_ELEM_TRAILER_LEN = 0; +#ifdef RTE_MALLOC_ASAN +static const unsigned int MALLOC_ELEM_TRAILER_LEN = RTE_CACHE_LINE_SIZE; +#else +static const unsigned int MALLOC_ELEM_TRAILER_LEN; +#endif /* dummy function - just check if pointer is non-null */ static inline int @@ -55,7 +75,7 @@ set_trailer(struct malloc_elem *elem __rte_unused){ } #else -static const unsigned MALLOC_ELEM_TRAILER_LEN = RTE_CACHE_LINE_SIZE; +static const unsigned int MALLOC_ELEM_TRAILER_LEN = RTE_CACHE_LINE_SIZE; #define MALLOC_HEADER_COOKIE 0xbadbadbadadd2e55ULL /**< Header cookie. */ #define MALLOC_TRAILER_COOKIE 0xadd2e55badbadbadULL /**< Trailer cookie.*/ @@ -90,9 +110,173 @@ malloc_elem_cookies_ok(const struct malloc_elem *elem) #endif -static const unsigned MALLOC_ELEM_HEADER_LEN = sizeof(struct malloc_elem); #define MALLOC_ELEM_OVERHEAD (MALLOC_ELEM_HEADER_LEN + MALLOC_ELEM_TRAILER_LEN) +#ifdef RTE_MALLOC_ASAN + +#ifdef RTE_ARCH_X86_64 +#define ASAN_SHADOW_OFFSET 0x00007fff8000 +#endif + +#define ASAN_SHADOW_GRAIN_SIZE 8 +#define ASAN_MEM_FREE_FLAG 0xfd +#define ASAN_MEM_REDZONE_FLAG 0xfa +#define ASAN_MEM_TO_SHADOW(mem) (((mem) >> 3) + ASAN_SHADOW_OFFSET) + +#if defined(__clang__) +__attribute__((no_sanitize("address", "hwaddress"))) +#else +__attribute__((no_sanitize_address)) +#endif +static inline void +asan_set_shadow(void *addr, char val) +{ + *(char *)addr = val; +} + +static inline void +asan_set_zone(void *ptr, size_t len, uint32_t val) +{ + size_t offset, i; + char *shadow; + size_t zone_len = len / ASAN_SHADOW_GRAIN_SIZE; + if (len % ASAN_SHADOW_GRAIN_SIZE != 0) + zone_len += 1; + + for (i = 0; i < zone_len; i++) { + offset = i * ASAN_SHADOW_GRAIN_SIZE; + shadow = (char *)ASAN_MEM_TO_SHADOW(((int64_t)ptr + offset)); + asan_set_shadow(shadow, val); + } +} + +/* + * When the memory is released, the release mark is + * set in the corresponding range of the shadow area. + */ +static inline void +asan_set_freezone(void *ptr, size_t size) +{ + asan_set_zone(ptr, size, ASAN_MEM_FREE_FLAG); +} + +/* + * When the memory is allocated, memory state must set as accessible. + */ +static inline void +asan_clear_alloczone(struct malloc_elem *elem) +{ + asan_set_zone((void *)elem, elem->size, 0x0); +} + +static inline void +asan_clear_split_alloczone(struct malloc_elem *elem) +{ + void *ptr = RTE_PTR_SUB(elem, MALLOC_ELEM_TRAILER_LEN); + asan_set_zone(ptr, MALLOC_ELEM_OVERHEAD, 0x0); +} + +/* + * When the memory is allocated, the memory boundary is + * marked in the corresponding range of the shadow area. + */ +static inline void +asan_set_redzone(struct malloc_elem *elem, size_t user_size) +{ + uint64_t ptr; + char *shadow; + if (elem != NULL) { + if (elem->state != ELEM_PAD) + elem = RTE_PTR_ADD(elem, elem->pad); + + elem->user_size = user_size; + + /* Set mark before the start of the allocated memory */ + ptr = (uint64_t)RTE_PTR_ADD(elem, MALLOC_ELEM_HEADER_LEN) + - ASAN_SHADOW_GRAIN_SIZE; + shadow = (char *)ASAN_MEM_TO_SHADOW(ptr); + asan_set_shadow(shadow, ASAN_MEM_REDZONE_FLAG); + shadow = (char *)ASAN_MEM_TO_SHADOW(ptr + - ASAN_SHADOW_GRAIN_SIZE); + asan_set_shadow(shadow, ASAN_MEM_REDZONE_FLAG); + + /* Set mark after the end of the allocated memory */ + ptr = (uint64_t)RTE_PTR_ADD(elem, MALLOC_ELEM_HEADER_LEN + + elem->user_size); + shadow = (char *)ASAN_MEM_TO_SHADOW(ptr); + uint32_t val = (ptr % ASAN_SHADOW_GRAIN_SIZE); + val = (val == 0) ? ASAN_MEM_REDZONE_FLAG : val; + asan_set_shadow(shadow, val); + shadow = (char *)ASAN_MEM_TO_SHADOW(ptr + + ASAN_SHADOW_GRAIN_SIZE); + asan_set_shadow(shadow, ASAN_MEM_REDZONE_FLAG); + } +} + +/* + * When the memory is released, the mark of the memory boundary + * in the corresponding range of the shadow area is cleared. + */ +static inline void +asan_clear_redzone(struct malloc_elem *elem) +{ + uint64_t ptr; + char *shadow; + if (elem != NULL) { + elem = RTE_PTR_ADD(elem, elem->pad); + + /* Clear mark before the start of the allocated memory */ + ptr = (uint64_t)RTE_PTR_ADD(elem, MALLOC_ELEM_HEADER_LEN) + - ASAN_SHADOW_GRAIN_SIZE; + shadow = (char *)ASAN_MEM_TO_SHADOW(ptr); + asan_set_shadow(shadow, 0x00); + shadow = (char *)ASAN_MEM_TO_SHADOW(ptr + - ASAN_SHADOW_GRAIN_SIZE); + asan_set_shadow(shadow, 0x00); + + /* Clear mark after the end of the allocated memory */ + ptr = (uint64_t)RTE_PTR_ADD(elem, MALLOC_ELEM_HEADER_LEN + + elem->user_size); + shadow = (char *)ASAN_MEM_TO_SHADOW(ptr); + asan_set_shadow(shadow, 0x00); + shadow = (char *)ASAN_MEM_TO_SHADOW(ptr + + ASAN_SHADOW_GRAIN_SIZE); + asan_set_shadow(shadow, 0x00); + } +} + +static inline size_t +old_malloc_size(struct malloc_elem *elem) +{ + if (elem->state != ELEM_PAD) + elem = RTE_PTR_ADD(elem, elem->pad); + + return elem->user_size; +} +#else +static inline void +asan_set_freezone(void *ptr __rte_unused, size_t size __rte_unused) { } + +static inline void +asan_clear_alloczone(struct malloc_elem *elem __rte_unused) { } + +static inline void +asan_clear_split_alloczone(struct malloc_elem *elem __rte_unused) { } + +static inline void +asan_set_redzone(struct malloc_elem *elem __rte_unused, + size_t user_size __rte_unused) { } + +static inline void +asan_clear_redzone(struct malloc_elem *elem __rte_unused) { } + +static inline size_t +old_malloc_size(struct malloc_elem *elem) +{ + return elem->size - elem->pad - MALLOC_ELEM_OVERHEAD; +} +#endif + /* * Given a pointer to the start of a memory block returned by malloc, get * the actual malloc_elem header for that block. diff --git a/lib/eal/common/malloc_heap.c b/lib/eal/common/malloc_heap.c index ee400f38ec..775d6789df 100644 --- a/lib/eal/common/malloc_heap.c +++ b/lib/eal/common/malloc_heap.c @@ -237,6 +237,7 @@ heap_alloc(struct malloc_heap *heap, const char *type __rte_unused, size_t size, unsigned int flags, size_t align, size_t bound, bool contig) { struct malloc_elem *elem; + size_t user_size = size; size = RTE_CACHE_LINE_ROUNDUP(size); align = RTE_CACHE_LINE_ROUNDUP(align); @@ -250,6 +251,8 @@ heap_alloc(struct malloc_heap *heap, const char *type __rte_unused, size_t size, /* increase heap's count of allocated elements */ heap->alloc_count++; + + asan_set_redzone(elem, user_size); } return elem == NULL ? NULL : (void *)(&elem[1]); @@ -270,6 +273,8 @@ heap_alloc_biggest(struct malloc_heap *heap, const char *type __rte_unused, /* increase heap's count of allocated elements */ heap->alloc_count++; + + asan_set_redzone(elem, size); } return elem == NULL ? NULL : (void *)(&elem[1]); @@ -841,6 +846,8 @@ malloc_heap_free(struct malloc_elem *elem) if (!malloc_elem_cookies_ok(elem) || elem->state != ELEM_BUSY) return -1; + asan_clear_redzone(elem); + /* elem may be merged with previous element, so keep heap address */ heap = elem->heap; msl = elem->msl; @@ -848,6 +855,9 @@ malloc_heap_free(struct malloc_elem *elem) rte_spinlock_lock(&(heap->lock)); + void *asan_ptr = RTE_PTR_ADD(elem, MALLOC_ELEM_HEADER_LEN + elem->pad); + size_t asan_data_len = elem->size - MALLOC_ELEM_OVERHEAD - elem->pad; + /* mark element as free */ elem->state = ELEM_FREE; @@ -1001,6 +1011,8 @@ malloc_heap_free(struct malloc_elem *elem) rte_mcfg_mem_write_unlock(); free_unlock: + asan_set_freezone(asan_ptr, asan_data_len); + rte_spinlock_unlock(&(heap->lock)); return ret; } diff --git a/lib/eal/common/rte_malloc.c b/lib/eal/common/rte_malloc.c index 9d39e58c08..d0bec26920 100644 --- a/lib/eal/common/rte_malloc.c +++ b/lib/eal/common/rte_malloc.c @@ -162,6 +162,8 @@ rte_calloc(const char *type, size_t num, size_t size, unsigned align) void * rte_realloc_socket(void *ptr, size_t size, unsigned int align, int socket) { + size_t user_size; + if (ptr == NULL) return rte_malloc_socket(NULL, size, align, socket); @@ -171,6 +173,8 @@ rte_realloc_socket(void *ptr, size_t size, unsigned int align, int socket) return NULL; } + user_size = size; + size = RTE_CACHE_LINE_ROUNDUP(size), align = RTE_CACHE_LINE_ROUNDUP(align); /* check requested socket id and alignment matches first, and if ok, @@ -181,6 +185,9 @@ rte_realloc_socket(void *ptr, size_t size, unsigned int align, int socket) RTE_PTR_ALIGN(ptr, align) == ptr && malloc_heap_resize(elem, size) == 0) { rte_eal_trace_mem_realloc(size, align, socket, ptr); + + asan_set_redzone(elem, user_size); + return ptr; } @@ -192,7 +199,7 @@ rte_realloc_socket(void *ptr, size_t size, unsigned int align, int socket) if (new_ptr == NULL) return NULL; /* elem: |pad|data_elem|data|trailer| */ - const size_t old_size = elem->size - elem->pad - MALLOC_ELEM_OVERHEAD; + const size_t old_size = old_malloc_size(elem); rte_memcpy(new_ptr, ptr, old_size < size ? old_size : size); rte_free(ptr); -- 2.25.1 ^ permalink raw reply [flat|nested] 117+ messages in thread
* [dpdk-dev] [PATCH v4 2/2] lib/pipeline: Fix gcc compilation error using ASan 2021-09-24 2:20 ` [dpdk-dev] [PATCH v4 1/2] Enable ASan for memory detector " zhihongx.peng @ 2021-09-24 2:20 ` zhihongx.peng 2021-09-24 9:33 ` [dpdk-dev] [PATCH v5 1/2] Enable ASan for memory detector on DPDK zhihongx.peng 2021-09-24 10:03 ` [dpdk-dev] [PATCH v5 1/2] Enable ASan for memory detector on DPDK zhihongx.peng 2 siblings, 0 replies; 117+ messages in thread From: zhihongx.peng @ 2021-09-24 2:20 UTC (permalink / raw) To: anatoly.burakov, konstantin.ananyev, stephen Cc: dev, xueqin.lin, Zhihong Peng, stable From: Zhihong Peng <zhihongx.peng@intel.com> After adding ASan, the gcc compilation check will be stricter. "Control reaches end of non-void function" error occurs here. Fixes: f38913b7fb8e (pipeline: add meter array to SWX) Cc: stable@dpdk.org Signed-off-by: Xueqin Lin <xueqin.lin@intel.com> Signed-off-by: Zhihong Peng <zhihongx.peng@intel.com> --- lib/pipeline/rte_swx_pipeline.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/pipeline/rte_swx_pipeline.c b/lib/pipeline/rte_swx_pipeline.c index 8eb978a30c..aaa0107d02 100644 --- a/lib/pipeline/rte_swx_pipeline.c +++ b/lib/pipeline/rte_swx_pipeline.c @@ -6340,7 +6340,7 @@ instr_meter_translate(struct rte_swx_pipeline *p, return 0; } - CHECK(0, EINVAL); + return -EINVAL; } static inline struct meter * @@ -8025,7 +8025,7 @@ instr_translate(struct rte_swx_pipeline *p, instr, data); - CHECK(0, EINVAL); + return -EINVAL; } static struct instruction_data * -- 2.25.1 ^ permalink raw reply [flat|nested] 117+ messages in thread
* [dpdk-dev] [PATCH v5 1/2] Enable ASan for memory detector on DPDK 2021-09-24 2:20 ` [dpdk-dev] [PATCH v4 1/2] Enable ASan for memory detector " zhihongx.peng 2021-09-24 2:20 ` [dpdk-dev] [PATCH v4 2/2] lib/pipeline: Fix gcc compilation error using ASan zhihongx.peng @ 2021-09-24 9:33 ` zhihongx.peng 2021-09-24 9:33 ` [dpdk-dev] [PATCH v5 2/2] Fix gcc compilation error using ASan zhihongx.peng 2021-09-24 10:03 ` [dpdk-dev] [PATCH v5 1/2] Enable ASan for memory detector on DPDK zhihongx.peng 2 siblings, 1 reply; 117+ messages in thread From: zhihongx.peng @ 2021-09-24 9:33 UTC (permalink / raw) To: anatoly.burakov, konstantin.ananyev, stephen Cc: dev, xueqin.lin, Zhihong Peng From: Zhihong Peng <zhihongx.peng@intel.com> AddressSanitizer (ASan) is a google memory error detect standard tool. It could help to detect use-after-free and {heap,stack,global}-buffer overflow bugs in C/C++ programs, print detailed error information when error happens, large improve debug efficiency. By referring to its implementation algorithm (https://github.com/google/sanitizers/wiki/AddressSanitizerAlgorithm), enable heap-buffer-overflow and use-after-free functions on dpdk. DPDK ASan function currently only supports on Linux x86_64. Support other platforms, need to define ASAN_SHADOW_OFFSET value according to google ASan document. Here is an example of heap-buffer-overflow bug: ...... char *p = rte_zmalloc(NULL, 7, 0); p[7] = 'a'; ...... Here is an example of use-after-free bug: ...... char *p = rte_zmalloc(NULL, 7, 0); rte_free(p); *p = 'a'; ...... If you want to use this feature, you need to add below compilation options when compiling code: -Dbuildtype=debug -Db_lundef=false -Db_sanitize=address "-Dbuildtype=debug": This is a non-essential option. When this option is added, if a memory error occurs, ASan can clearly show where the code is wrong. "-Db_lundef=false": When use clang to compile DPDK, this option must be added. Note: a) The issue of ASan wild pointer is that dpdk ASan tool is not fully adapted to google ASan. For example: Address 0x7fe2ffafa240 is a wild pointer. b) Centos needs to install libasan separately. c) If the program uses DPDK cmdline, when a memory bug occurs, need to execute the "stty echo" command. Signed-off-by: Xueqin Lin <xueqin.lin@intel.com> Signed-off-by: Zhihong Peng <zhihongx.peng@intel.com> --- devtools/words-case.txt | 1 + doc/guides/prog_guide/ASan.rst | 109 ++++++++++++++++++ doc/guides/prog_guide/index.rst | 1 + lib/eal/common/malloc_elem.c | 26 ++++- lib/eal/common/malloc_elem.h | 190 +++++++++++++++++++++++++++++++- lib/eal/common/malloc_heap.c | 12 ++ lib/eal/common/rte_malloc.c | 9 +- 7 files changed, 343 insertions(+), 5 deletions(-) create mode 100644 doc/guides/prog_guide/ASan.rst diff --git a/devtools/words-case.txt b/devtools/words-case.txt index 0bbad48626..3655596d47 100644 --- a/devtools/words-case.txt +++ b/devtools/words-case.txt @@ -86,3 +86,4 @@ VXLAN Windows XDP XOR +ASan diff --git a/doc/guides/prog_guide/ASan.rst b/doc/guides/prog_guide/ASan.rst new file mode 100644 index 0000000000..f18380469d --- /dev/null +++ b/doc/guides/prog_guide/ASan.rst @@ -0,0 +1,109 @@ +.. Copyright (c) <2021>, Intel Corporation + All rights reserved. + +Memory error detect standard tool - AddressSanitizer(ASan) +========================================================== + +AddressSanitizer (ASan) is a google memory error detect +standard tool. It could help to detect use-after-free and +{heap,stack,global}-buffer overflow bugs in C/C++ programs, +print detailed error information when error happens, large +improve debug efficiency. + +By referring to its implementation algorithm +(https://github.com/google/sanitizers/wiki/AddressSanitizerAlgorithm), +enabled heap-buffer-overflow and use-after-free functions on dpdk. +DPDK ASan function currently only supports on Linux x86_64. + +AddressSanitizer is a part of LLVM(3.1+)and GCC(4.8+). + +Example heap-buffer-overflow error +---------------------------------- + +Following error was reported when ASan was enabled:: + + Applied 9 bytes of memory, but accessed the 10th byte of memory, + so heap-buffer-overflow appeared. + +Below code results in this error:: + + char *p = rte_zmalloc(NULL, 9, 0); + if (!p) { + printf("rte_zmalloc error."); + return -1; + } + p[9] = 'a'; + +The error log:: + + ==49433==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x7f773fafa249 at pc 0x5556b13bdae4 bp 0x7ffeb4965e40 sp 0x7ffeb4965e30 WRITE of size 1 at 0x7f773fafa249 thread T0 + #0 0x5556b13bdae3 in asan_heap_buffer_overflow ../app/test/test_asan_heap_buffer_overflow.c:25 + #1 0x5556b043e9d4 in cmd_autotest_parsed ../app/test/commands.c:71 + #2 0x5556b1cdd4b0 in cmdline_parse ../lib/cmdline/cmdline_parse.c:290 + #3 0x5556b1cd8987 in cmdline_valid_buffer ../lib/cmdline/cmdline.c:26 + #4 0x5556b1ce477a in rdline_char_in ../lib/cmdline/cmdline_rdline.c:421 + #5 0x5556b1cd923e in cmdline_in ../lib/cmdline/cmdline.c:149 + #6 0x5556b1cd9769 in cmdline_interact ../lib/cmdline/cmdline.c:223 + #7 0x5556b045f53b in main ../app/test/test.c:234 + #8 0x7f7f1eba90b2 in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x270b2) + #9 0x5556b043e70d in _start (/home/pzh/yyy/x86_64-native-linuxapp-gcc/app/test/dpdk-test+0x7ce70d) + + Address 0x7f773fafa249 is a wild pointer. + SUMMARY: AddressSanitizer: heap-buffer-overflow ../app/test/test_asan_heap_buffer_overflow.c:25 in asan_heap_buffer_overflow + Shadow bytes around the buggy address: + +Example use-after-free error +---------------------------- + +Following error was reported when ASan was enabled:: + + Applied for 9 bytes of memory, and accessed the first byte after + released, so heap-use-after-free appeared. + +Below code results in this error:: + + char *p = rte_zmalloc(NULL, 9, 0); + if (!p) { + printf("rte_zmalloc error."); + return -1; + } + rte_free(p); + *p = 'a'; + +The error log:: + + ==49478==ERROR: AddressSanitizer: heap-use-after-free on address 0x7fe2ffafa240 at pc 0x56409b084bc8 bp 0x7ffef62c57d0 sp 0x7ffef62c57c0 WRITE of size 1 at 0x7fe2ffafa240 thread T0 + #0 0x56409b084bc7 in asan_use_after_free ../app/test/test_asan_use_after_free.c:26 + #1 0x56409a1059d4 in cmd_autotest_parsed ../app/test/commands.c:71 + #2 0x56409b9a44b0 in cmdline_parse ../lib/cmdline/cmdline_parse.c:290 + #3 0x56409b99f987 in cmdline_valid_buffer ../lib/cmdline/cmdline.c:26 + #4 0x56409b9ab77a in rdline_char_in ../lib/cmdline/cmdline_rdline.c:421 + #5 0x56409b9a023e in cmdline_in ../lib/cmdline/cmdline.c:149 + #6 0x56409b9a0769 in cmdline_interact ../lib/cmdline/cmdline.c:223 + #7 0x56409a12653b in main ../app/test/test.c:234 + #8 0x7feafafc20b2 in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x270b2) + #9 0x56409a10570d in _start (/home/pzh/yyy/x86_64-native-linuxapp-gcc/app/test/dpdk-test+0x7ce70d) + + Address 0x7fe2ffafa240 is a wild pointer. + SUMMARY: AddressSanitizer: heap-use-after-free ../app/test/test_asan_use_after_free.c:26 in asan_use_after_free + Shadow bytes around the buggy address: + +Usage +----- + +meson build +^^^^^^^^^^^ + +To enable ASan in meson build system, use following meson build command: + +Example usage:: + + meson build -Dbuildtype=debug -Db_lundef=false -Db_sanitize=address + ninja -C build + +.. Note:: + + a) The issue of ASan wild pointer is that dpdk ASan tool is not fully adapted to google ASan. + For example: Address 0x7fe2ffafa240 is a wild pointer. + b) Centos needs to install libasan separately. + c) If the program uses cmdline, when a memory bug occurs, need to execute the "stty echo" command. diff --git a/doc/guides/prog_guide/index.rst b/doc/guides/prog_guide/index.rst index 2dce507f46..abaf687045 100644 --- a/doc/guides/prog_guide/index.rst +++ b/doc/guides/prog_guide/index.rst @@ -71,3 +71,4 @@ Programmer's Guide lto profile_app glossary + ASan diff --git a/lib/eal/common/malloc_elem.c b/lib/eal/common/malloc_elem.c index c2c9461f1d..bdd20a162e 100644 --- a/lib/eal/common/malloc_elem.c +++ b/lib/eal/common/malloc_elem.c @@ -446,6 +446,8 @@ malloc_elem_alloc(struct malloc_elem *elem, size_t size, unsigned align, struct malloc_elem *new_free_elem = RTE_PTR_ADD(new_elem, size + MALLOC_ELEM_OVERHEAD); + asan_clear_split_alloczone(new_free_elem); + split_elem(elem, new_free_elem); malloc_elem_free_list_insert(new_free_elem); @@ -458,6 +460,8 @@ malloc_elem_alloc(struct malloc_elem *elem, size_t size, unsigned align, elem->state = ELEM_BUSY; elem->pad = old_elem_size; + asan_clear_alloczone(elem); + /* put a dummy header in padding, to point to real element header */ if (elem->pad > 0) { /* pad will be at least 64-bytes, as everything * is cache-line aligned */ @@ -470,12 +474,18 @@ malloc_elem_alloc(struct malloc_elem *elem, size_t size, unsigned align, return new_elem; } + asan_clear_split_alloczone(new_elem); + /* we are going to split the element in two. The original element * remains free, and the new element is the one allocated. * Re-insert original element, in case its new size makes it * belong on a different list. */ + split_elem(elem, new_elem); + + asan_clear_alloczone(new_elem); + new_elem->state = ELEM_BUSY; malloc_elem_free_list_insert(elem); @@ -601,6 +611,8 @@ malloc_elem_hide_region(struct malloc_elem *elem, void *start, size_t len) if (next && next_elem_is_adjacent(elem)) { len_after = RTE_PTR_DIFF(next, hide_end); if (len_after >= MALLOC_ELEM_OVERHEAD + MIN_DATA_SIZE) { + asan_clear_split_alloczone(hide_end); + /* split after */ split_elem(elem, hide_end); @@ -615,6 +627,8 @@ malloc_elem_hide_region(struct malloc_elem *elem, void *start, size_t len) if (prev && prev_elem_is_adjacent(elem)) { len_before = RTE_PTR_DIFF(hide_start, elem); if (len_before >= MALLOC_ELEM_OVERHEAD + MIN_DATA_SIZE) { + asan_clear_split_alloczone(hide_start); + /* split before */ split_elem(elem, hide_start); @@ -628,6 +642,8 @@ malloc_elem_hide_region(struct malloc_elem *elem, void *start, size_t len) } } + asan_clear_alloczone(elem); + remove_elem(elem); } @@ -641,8 +657,10 @@ malloc_elem_resize(struct malloc_elem *elem, size_t size) const size_t new_size = size + elem->pad + MALLOC_ELEM_OVERHEAD; /* if we request a smaller size, then always return ok */ - if (elem->size >= new_size) + if (elem->size >= new_size) { + asan_clear_alloczone(elem); return 0; + } /* check if there is a next element, it's free and adjacent */ if (!elem->next || elem->next->state != ELEM_FREE || @@ -661,9 +679,15 @@ malloc_elem_resize(struct malloc_elem *elem, size_t size) /* now we have a big block together. Lets cut it down a bit, by splitting */ struct malloc_elem *split_pt = RTE_PTR_ADD(elem, new_size); split_pt = RTE_PTR_ALIGN_CEIL(split_pt, RTE_CACHE_LINE_SIZE); + + asan_clear_split_alloczone(split_pt); + split_elem(elem, split_pt); malloc_elem_free_list_insert(split_pt); } + + asan_clear_alloczone(elem); + return 0; } diff --git a/lib/eal/common/malloc_elem.h b/lib/eal/common/malloc_elem.h index a1e5f7f02c..08b25c6b1f 100644 --- a/lib/eal/common/malloc_elem.h +++ b/lib/eal/common/malloc_elem.h @@ -7,6 +7,16 @@ #include <stdbool.h> +#ifdef RTE_ARCH_X86_64 +#ifdef __SANITIZE_ADDRESS__ +#define RTE_MALLOC_ASAN +#elif defined(__has_feature) +# if __has_feature(address_sanitizer) +#define RTE_MALLOC_ASAN +# endif +#endif +#endif + #define MIN_DATA_SIZE (RTE_CACHE_LINE_SIZE) /* dummy definition of struct so we can use pointers to it in malloc_elem struct */ @@ -36,10 +46,20 @@ struct malloc_elem { uint64_t header_cookie; /* Cookie marking start of data */ /* trailer cookie at start + size */ #endif +#ifdef RTE_MALLOC_ASAN + size_t user_size; + uint64_t asan_cookie[2]; /* must be next to header_cookie */ +#endif } __rte_cache_aligned; +static const unsigned int MALLOC_ELEM_HEADER_LEN = sizeof(struct malloc_elem); + #ifndef RTE_MALLOC_DEBUG -static const unsigned MALLOC_ELEM_TRAILER_LEN = 0; +#ifdef RTE_MALLOC_ASAN +static const unsigned int MALLOC_ELEM_TRAILER_LEN = RTE_CACHE_LINE_SIZE; +#else +static const unsigned int MALLOC_ELEM_TRAILER_LEN; +#endif /* dummy function - just check if pointer is non-null */ static inline int @@ -55,7 +75,7 @@ set_trailer(struct malloc_elem *elem __rte_unused){ } #else -static const unsigned MALLOC_ELEM_TRAILER_LEN = RTE_CACHE_LINE_SIZE; +static const unsigned int MALLOC_ELEM_TRAILER_LEN = RTE_CACHE_LINE_SIZE; #define MALLOC_HEADER_COOKIE 0xbadbadbadadd2e55ULL /**< Header cookie. */ #define MALLOC_TRAILER_COOKIE 0xadd2e55badbadbadULL /**< Trailer cookie.*/ @@ -90,9 +110,173 @@ malloc_elem_cookies_ok(const struct malloc_elem *elem) #endif -static const unsigned MALLOC_ELEM_HEADER_LEN = sizeof(struct malloc_elem); #define MALLOC_ELEM_OVERHEAD (MALLOC_ELEM_HEADER_LEN + MALLOC_ELEM_TRAILER_LEN) +#ifdef RTE_MALLOC_ASAN + +#ifdef RTE_ARCH_X86_64 +#define ASAN_SHADOW_OFFSET 0x00007fff8000 +#endif + +#define ASAN_SHADOW_GRAIN_SIZE 8 +#define ASAN_MEM_FREE_FLAG 0xfd +#define ASAN_MEM_REDZONE_FLAG 0xfa +#define ASAN_MEM_TO_SHADOW(mem) (((mem) >> 3) + ASAN_SHADOW_OFFSET) + +#if defined(__clang__) +__attribute__((no_sanitize("address", "hwaddress"))) +#else +__attribute__((no_sanitize_address)) +#endif +static inline void +asan_set_shadow(void *addr, char val) +{ + *(char *)addr = val; +} + +static inline void +asan_set_zone(void *ptr, size_t len, uint32_t val) +{ + size_t offset, i; + char *shadow; + size_t zone_len = len / ASAN_SHADOW_GRAIN_SIZE; + if (len % ASAN_SHADOW_GRAIN_SIZE != 0) + zone_len += 1; + + for (i = 0; i < zone_len; i++) { + offset = i * ASAN_SHADOW_GRAIN_SIZE; + shadow = (char *)ASAN_MEM_TO_SHADOW(((int64_t)ptr + offset)); + asan_set_shadow(shadow, val); + } +} + +/* + * When the memory is released, the release mark is + * set in the corresponding range of the shadow area. + */ +static inline void +asan_set_freezone(void *ptr, size_t size) +{ + asan_set_zone(ptr, size, ASAN_MEM_FREE_FLAG); +} + +/* + * When the memory is allocated, memory state must set as accessible. + */ +static inline void +asan_clear_alloczone(struct malloc_elem *elem) +{ + asan_set_zone((void *)elem, elem->size, 0x0); +} + +static inline void +asan_clear_split_alloczone(struct malloc_elem *elem) +{ + void *ptr = RTE_PTR_SUB(elem, MALLOC_ELEM_TRAILER_LEN); + asan_set_zone(ptr, MALLOC_ELEM_OVERHEAD, 0x0); +} + +/* + * When the memory is allocated, the memory boundary is + * marked in the corresponding range of the shadow area. + */ +static inline void +asan_set_redzone(struct malloc_elem *elem, size_t user_size) +{ + uint64_t ptr; + char *shadow; + if (elem != NULL) { + if (elem->state != ELEM_PAD) + elem = RTE_PTR_ADD(elem, elem->pad); + + elem->user_size = user_size; + + /* Set mark before the start of the allocated memory */ + ptr = (uint64_t)RTE_PTR_ADD(elem, MALLOC_ELEM_HEADER_LEN) + - ASAN_SHADOW_GRAIN_SIZE; + shadow = (char *)ASAN_MEM_TO_SHADOW(ptr); + asan_set_shadow(shadow, ASAN_MEM_REDZONE_FLAG); + shadow = (char *)ASAN_MEM_TO_SHADOW(ptr + - ASAN_SHADOW_GRAIN_SIZE); + asan_set_shadow(shadow, ASAN_MEM_REDZONE_FLAG); + + /* Set mark after the end of the allocated memory */ + ptr = (uint64_t)RTE_PTR_ADD(elem, MALLOC_ELEM_HEADER_LEN + + elem->user_size); + shadow = (char *)ASAN_MEM_TO_SHADOW(ptr); + uint32_t val = (ptr % ASAN_SHADOW_GRAIN_SIZE); + val = (val == 0) ? ASAN_MEM_REDZONE_FLAG : val; + asan_set_shadow(shadow, val); + shadow = (char *)ASAN_MEM_TO_SHADOW(ptr + + ASAN_SHADOW_GRAIN_SIZE); + asan_set_shadow(shadow, ASAN_MEM_REDZONE_FLAG); + } +} + +/* + * When the memory is released, the mark of the memory boundary + * in the corresponding range of the shadow area is cleared. + */ +static inline void +asan_clear_redzone(struct malloc_elem *elem) +{ + uint64_t ptr; + char *shadow; + if (elem != NULL) { + elem = RTE_PTR_ADD(elem, elem->pad); + + /* Clear mark before the start of the allocated memory */ + ptr = (uint64_t)RTE_PTR_ADD(elem, MALLOC_ELEM_HEADER_LEN) + - ASAN_SHADOW_GRAIN_SIZE; + shadow = (char *)ASAN_MEM_TO_SHADOW(ptr); + asan_set_shadow(shadow, 0x00); + shadow = (char *)ASAN_MEM_TO_SHADOW(ptr + - ASAN_SHADOW_GRAIN_SIZE); + asan_set_shadow(shadow, 0x00); + + /* Clear mark after the end of the allocated memory */ + ptr = (uint64_t)RTE_PTR_ADD(elem, MALLOC_ELEM_HEADER_LEN + + elem->user_size); + shadow = (char *)ASAN_MEM_TO_SHADOW(ptr); + asan_set_shadow(shadow, 0x00); + shadow = (char *)ASAN_MEM_TO_SHADOW(ptr + + ASAN_SHADOW_GRAIN_SIZE); + asan_set_shadow(shadow, 0x00); + } +} + +static inline size_t +old_malloc_size(struct malloc_elem *elem) +{ + if (elem->state != ELEM_PAD) + elem = RTE_PTR_ADD(elem, elem->pad); + + return elem->user_size; +} +#else +static inline void +asan_set_freezone(void *ptr __rte_unused, size_t size __rte_unused) { } + +static inline void +asan_clear_alloczone(struct malloc_elem *elem __rte_unused) { } + +static inline void +asan_clear_split_alloczone(struct malloc_elem *elem __rte_unused) { } + +static inline void +asan_set_redzone(struct malloc_elem *elem __rte_unused, + size_t user_size __rte_unused) { } + +static inline void +asan_clear_redzone(struct malloc_elem *elem __rte_unused) { } + +static inline size_t +old_malloc_size(struct malloc_elem *elem) +{ + return elem->size - elem->pad - MALLOC_ELEM_OVERHEAD; +} +#endif + /* * Given a pointer to the start of a memory block returned by malloc, get * the actual malloc_elem header for that block. diff --git a/lib/eal/common/malloc_heap.c b/lib/eal/common/malloc_heap.c index ee400f38ec..775d6789df 100644 --- a/lib/eal/common/malloc_heap.c +++ b/lib/eal/common/malloc_heap.c @@ -237,6 +237,7 @@ heap_alloc(struct malloc_heap *heap, const char *type __rte_unused, size_t size, unsigned int flags, size_t align, size_t bound, bool contig) { struct malloc_elem *elem; + size_t user_size = size; size = RTE_CACHE_LINE_ROUNDUP(size); align = RTE_CACHE_LINE_ROUNDUP(align); @@ -250,6 +251,8 @@ heap_alloc(struct malloc_heap *heap, const char *type __rte_unused, size_t size, /* increase heap's count of allocated elements */ heap->alloc_count++; + + asan_set_redzone(elem, user_size); } return elem == NULL ? NULL : (void *)(&elem[1]); @@ -270,6 +273,8 @@ heap_alloc_biggest(struct malloc_heap *heap, const char *type __rte_unused, /* increase heap's count of allocated elements */ heap->alloc_count++; + + asan_set_redzone(elem, size); } return elem == NULL ? NULL : (void *)(&elem[1]); @@ -841,6 +846,8 @@ malloc_heap_free(struct malloc_elem *elem) if (!malloc_elem_cookies_ok(elem) || elem->state != ELEM_BUSY) return -1; + asan_clear_redzone(elem); + /* elem may be merged with previous element, so keep heap address */ heap = elem->heap; msl = elem->msl; @@ -848,6 +855,9 @@ malloc_heap_free(struct malloc_elem *elem) rte_spinlock_lock(&(heap->lock)); + void *asan_ptr = RTE_PTR_ADD(elem, MALLOC_ELEM_HEADER_LEN + elem->pad); + size_t asan_data_len = elem->size - MALLOC_ELEM_OVERHEAD - elem->pad; + /* mark element as free */ elem->state = ELEM_FREE; @@ -1001,6 +1011,8 @@ malloc_heap_free(struct malloc_elem *elem) rte_mcfg_mem_write_unlock(); free_unlock: + asan_set_freezone(asan_ptr, asan_data_len); + rte_spinlock_unlock(&(heap->lock)); return ret; } diff --git a/lib/eal/common/rte_malloc.c b/lib/eal/common/rte_malloc.c index 9d39e58c08..d0bec26920 100644 --- a/lib/eal/common/rte_malloc.c +++ b/lib/eal/common/rte_malloc.c @@ -162,6 +162,8 @@ rte_calloc(const char *type, size_t num, size_t size, unsigned align) void * rte_realloc_socket(void *ptr, size_t size, unsigned int align, int socket) { + size_t user_size; + if (ptr == NULL) return rte_malloc_socket(NULL, size, align, socket); @@ -171,6 +173,8 @@ rte_realloc_socket(void *ptr, size_t size, unsigned int align, int socket) return NULL; } + user_size = size; + size = RTE_CACHE_LINE_ROUNDUP(size), align = RTE_CACHE_LINE_ROUNDUP(align); /* check requested socket id and alignment matches first, and if ok, @@ -181,6 +185,9 @@ rte_realloc_socket(void *ptr, size_t size, unsigned int align, int socket) RTE_PTR_ALIGN(ptr, align) == ptr && malloc_heap_resize(elem, size) == 0) { rte_eal_trace_mem_realloc(size, align, socket, ptr); + + asan_set_redzone(elem, user_size); + return ptr; } @@ -192,7 +199,7 @@ rte_realloc_socket(void *ptr, size_t size, unsigned int align, int socket) if (new_ptr == NULL) return NULL; /* elem: |pad|data_elem|data|trailer| */ - const size_t old_size = elem->size - elem->pad - MALLOC_ELEM_OVERHEAD; + const size_t old_size = old_malloc_size(elem); rte_memcpy(new_ptr, ptr, old_size < size ? old_size : size); rte_free(ptr); -- 2.25.1 ^ permalink raw reply [flat|nested] 117+ messages in thread
* [dpdk-dev] [PATCH v5 2/2] Fix gcc compilation error using ASan 2021-09-24 9:33 ` [dpdk-dev] [PATCH v5 1/2] Enable ASan for memory detector on DPDK zhihongx.peng @ 2021-09-24 9:33 ` zhihongx.peng 0 siblings, 0 replies; 117+ messages in thread From: zhihongx.peng @ 2021-09-24 9:33 UTC (permalink / raw) To: anatoly.burakov, konstantin.ananyev, stephen Cc: dev, xueqin.lin, Zhihong Peng, stable From: Zhihong Peng <zhihongx.peng@intel.com> After adding ASan, the gcc compilation check will be stricter. "Control reaches end of non-void function" error occurs here. Fixes: f38913b7fb8e (pipeline: add meter array to SWX) Cc: stable@dpdk.org Signed-off-by: Xueqin Lin <xueqin.lin@intel.com> Signed-off-by: Zhihong Peng <zhihongx.peng@intel.com> --- lib/pipeline/rte_swx_pipeline.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/pipeline/rte_swx_pipeline.c b/lib/pipeline/rte_swx_pipeline.c index 8eb978a30c..aaa0107d02 100644 --- a/lib/pipeline/rte_swx_pipeline.c +++ b/lib/pipeline/rte_swx_pipeline.c @@ -6340,7 +6340,7 @@ instr_meter_translate(struct rte_swx_pipeline *p, return 0; } - CHECK(0, EINVAL); + return -EINVAL; } static inline struct meter * @@ -8025,7 +8025,7 @@ instr_translate(struct rte_swx_pipeline *p, instr, data); - CHECK(0, EINVAL); + return -EINVAL; } static struct instruction_data * -- 2.25.1 ^ permalink raw reply [flat|nested] 117+ messages in thread
* [dpdk-dev] [PATCH v5 1/2] Enable ASan for memory detector on DPDK 2021-09-24 2:20 ` [dpdk-dev] [PATCH v4 1/2] Enable ASan for memory detector " zhihongx.peng 2021-09-24 2:20 ` [dpdk-dev] [PATCH v4 2/2] lib/pipeline: Fix gcc compilation error using ASan zhihongx.peng 2021-09-24 9:33 ` [dpdk-dev] [PATCH v5 1/2] Enable ASan for memory detector on DPDK zhihongx.peng @ 2021-09-24 10:03 ` zhihongx.peng 2021-09-24 10:03 ` [dpdk-dev] [PATCH v5 2/2] lib/pipeline: Fix gcc compilation error using ASan zhihongx.peng ` (2 more replies) 2 siblings, 3 replies; 117+ messages in thread From: zhihongx.peng @ 2021-09-24 10:03 UTC (permalink / raw) To: anatoly.burakov, konstantin.ananyev, stephen Cc: dev, xueqin.lin, Zhihong Peng From: Zhihong Peng <zhihongx.peng@intel.com> AddressSanitizer (ASan) is a google memory error detect standard tool. It could help to detect use-after-free and {heap,stack,global}-buffer overflow bugs in C/C++ programs, print detailed error information when error happens, large improve debug efficiency. By referring to its implementation algorithm (https://github.com/google/sanitizers/wiki/AddressSanitizerAlgorithm), enable heap-buffer-overflow and use-after-free functions on dpdk. DPDK ASan function currently only supports on Linux x86_64. Support other platforms, need to define ASAN_SHADOW_OFFSET value according to google ASan document. Here is an example of heap-buffer-overflow bug: ...... char *p = rte_zmalloc(NULL, 7, 0); p[7] = 'a'; ...... Here is an example of use-after-free bug: ...... char *p = rte_zmalloc(NULL, 7, 0); rte_free(p); *p = 'a'; ...... If you want to use this feature, you need to add below compilation options when compiling code: -Dbuildtype=debug -Db_lundef=false -Db_sanitize=address "-Dbuildtype=debug": This is a non-essential option. When this option is added, if a memory error occurs, ASan can clearly show where the code is wrong. "-Db_lundef=false": When use clang to compile DPDK, this option must be added. Signed-off-by: Xueqin Lin <xueqin.lin@intel.com> Signed-off-by: Zhihong Peng <zhihongx.peng@intel.com> --- devtools/words-case.txt | 1 + doc/guides/prog_guide/ASan.rst | 107 ++++++++++++++++++ doc/guides/prog_guide/index.rst | 1 + lib/eal/common/malloc_elem.c | 26 ++++- lib/eal/common/malloc_elem.h | 190 +++++++++++++++++++++++++++++++- lib/eal/common/malloc_heap.c | 12 ++ lib/eal/common/rte_malloc.c | 9 +- 7 files changed, 341 insertions(+), 5 deletions(-) create mode 100644 doc/guides/prog_guide/ASan.rst diff --git a/devtools/words-case.txt b/devtools/words-case.txt index 0bbad48626..3655596d47 100644 --- a/devtools/words-case.txt +++ b/devtools/words-case.txt @@ -86,3 +86,4 @@ VXLAN Windows XDP XOR +ASan diff --git a/doc/guides/prog_guide/ASan.rst b/doc/guides/prog_guide/ASan.rst new file mode 100644 index 0000000000..d9278d475e --- /dev/null +++ b/doc/guides/prog_guide/ASan.rst @@ -0,0 +1,107 @@ +.. Copyright (c) <2021>, Intel Corporation + All rights reserved. + +Memory error detect standard tool - AddressSanitizer(ASan) +========================================================== + +AddressSanitizer (ASan) is a google memory error detect +standard tool. It could help to detect use-after-free and +{heap,stack,global}-buffer overflow bugs in C/C++ programs, +print detailed error information when error happens, large +improve debug efficiency. + +By referring to its implementation algorithm +(https://github.com/google/sanitizers/wiki/AddressSanitizerAlgorithm), +enabled heap-buffer-overflow and use-after-free functions on dpdk. +DPDK ASan function currently only supports on Linux x86_64. + +AddressSanitizer is a part of LLVM(3.1+)and GCC(4.8+). + +Example heap-buffer-overflow error +---------------------------------- + +Following error was reported when ASan was enabled:: + + Applied 9 bytes of memory, but accessed the 10th byte of memory, + so heap-buffer-overflow appeared. + +Below code results in this error:: + + char *p = rte_zmalloc(NULL, 9, 0); + if (!p) { + printf("rte_zmalloc error."); + return -1; + } + p[9] = 'a'; + +The error log:: + + ==49433==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x7f773fafa249 at pc 0x5556b13bdae4 bp 0x7ffeb4965e40 sp 0x7ffeb4965e30 WRITE of size 1 at 0x7f773fafa249 thread T0 + #0 0x5556b13bdae3 in asan_heap_buffer_overflow ../app/test/test_asan_heap_buffer_overflow.c:25 + #1 0x5556b043e9d4 in cmd_autotest_parsed ../app/test/commands.c:71 + #2 0x5556b1cdd4b0 in cmdline_parse ../lib/cmdline/cmdline_parse.c:290 + #3 0x5556b1cd8987 in cmdline_valid_buffer ../lib/cmdline/cmdline.c:26 + #4 0x5556b1ce477a in rdline_char_in ../lib/cmdline/cmdline_rdline.c:421 + #5 0x5556b1cd923e in cmdline_in ../lib/cmdline/cmdline.c:149 + #6 0x5556b1cd9769 in cmdline_interact ../lib/cmdline/cmdline.c:223 + #7 0x5556b045f53b in main ../app/test/test.c:234 + #8 0x7f7f1eba90b2 in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x270b2) + #9 0x5556b043e70d in _start (/home/pzh/yyy/x86_64-native-linuxapp-gcc/app/test/dpdk-test+0x7ce70d) + + Address 0x7f773fafa249 is a wild pointer. + SUMMARY: AddressSanitizer: heap-buffer-overflow ../app/test/test_asan_heap_buffer_overflow.c:25 in asan_heap_buffer_overflow + +Example use-after-free error +---------------------------- + +Following error was reported when ASan was enabled:: + + Applied for 9 bytes of memory, and accessed the first byte after + released, so heap-use-after-free appeared. + +Below code results in this error:: + + char *p = rte_zmalloc(NULL, 9, 0); + if (!p) { + printf("rte_zmalloc error."); + return -1; + } + rte_free(p); + *p = 'a'; + +The error log:: + + ==49478==ERROR: AddressSanitizer: heap-use-after-free on address 0x7fe2ffafa240 at pc 0x56409b084bc8 bp 0x7ffef62c57d0 sp 0x7ffef62c57c0 WRITE of size 1 at 0x7fe2ffafa240 thread T0 + #0 0x56409b084bc7 in asan_use_after_free ../app/test/test_asan_use_after_free.c:26 + #1 0x56409a1059d4 in cmd_autotest_parsed ../app/test/commands.c:71 + #2 0x56409b9a44b0 in cmdline_parse ../lib/cmdline/cmdline_parse.c:290 + #3 0x56409b99f987 in cmdline_valid_buffer ../lib/cmdline/cmdline.c:26 + #4 0x56409b9ab77a in rdline_char_in ../lib/cmdline/cmdline_rdline.c:421 + #5 0x56409b9a023e in cmdline_in ../lib/cmdline/cmdline.c:149 + #6 0x56409b9a0769 in cmdline_interact ../lib/cmdline/cmdline.c:223 + #7 0x56409a12653b in main ../app/test/test.c:234 + #8 0x7feafafc20b2 in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x270b2) + #9 0x56409a10570d in _start (/home/pzh/yyy/x86_64-native-linuxapp-gcc/app/test/dpdk-test+0x7ce70d) + + Address 0x7fe2ffafa240 is a wild pointer. + SUMMARY: AddressSanitizer: heap-use-after-free ../app/test/test_asan_use_after_free.c:26 in asan_use_after_free + +Usage +----- + +meson build +^^^^^^^^^^^ + +To enable ASan in meson build system, use following meson build command: + +Example usage:: + + meson build -Dbuildtype=debug -Db_lundef=false -Db_sanitize=address + ninja -C build + +.. Note:: + + a) The issue of ASan wild pointer is that dpdk ASan tool is not fully adapted to google ASan. + For example: Address 0x7fe2ffafa240 is a wild pointer. + b) Centos needs to install libasan separately. + c) If the program uses cmdline, when a memory bug occurs, need to execute the "stty echo" command. diff --git a/doc/guides/prog_guide/index.rst b/doc/guides/prog_guide/index.rst index 2dce507f46..abaf687045 100644 --- a/doc/guides/prog_guide/index.rst +++ b/doc/guides/prog_guide/index.rst @@ -71,3 +71,4 @@ Programmer's Guide lto profile_app glossary + ASan diff --git a/lib/eal/common/malloc_elem.c b/lib/eal/common/malloc_elem.c index c2c9461f1d..bdd20a162e 100644 --- a/lib/eal/common/malloc_elem.c +++ b/lib/eal/common/malloc_elem.c @@ -446,6 +446,8 @@ malloc_elem_alloc(struct malloc_elem *elem, size_t size, unsigned align, struct malloc_elem *new_free_elem = RTE_PTR_ADD(new_elem, size + MALLOC_ELEM_OVERHEAD); + asan_clear_split_alloczone(new_free_elem); + split_elem(elem, new_free_elem); malloc_elem_free_list_insert(new_free_elem); @@ -458,6 +460,8 @@ malloc_elem_alloc(struct malloc_elem *elem, size_t size, unsigned align, elem->state = ELEM_BUSY; elem->pad = old_elem_size; + asan_clear_alloczone(elem); + /* put a dummy header in padding, to point to real element header */ if (elem->pad > 0) { /* pad will be at least 64-bytes, as everything * is cache-line aligned */ @@ -470,12 +474,18 @@ malloc_elem_alloc(struct malloc_elem *elem, size_t size, unsigned align, return new_elem; } + asan_clear_split_alloczone(new_elem); + /* we are going to split the element in two. The original element * remains free, and the new element is the one allocated. * Re-insert original element, in case its new size makes it * belong on a different list. */ + split_elem(elem, new_elem); + + asan_clear_alloczone(new_elem); + new_elem->state = ELEM_BUSY; malloc_elem_free_list_insert(elem); @@ -601,6 +611,8 @@ malloc_elem_hide_region(struct malloc_elem *elem, void *start, size_t len) if (next && next_elem_is_adjacent(elem)) { len_after = RTE_PTR_DIFF(next, hide_end); if (len_after >= MALLOC_ELEM_OVERHEAD + MIN_DATA_SIZE) { + asan_clear_split_alloczone(hide_end); + /* split after */ split_elem(elem, hide_end); @@ -615,6 +627,8 @@ malloc_elem_hide_region(struct malloc_elem *elem, void *start, size_t len) if (prev && prev_elem_is_adjacent(elem)) { len_before = RTE_PTR_DIFF(hide_start, elem); if (len_before >= MALLOC_ELEM_OVERHEAD + MIN_DATA_SIZE) { + asan_clear_split_alloczone(hide_start); + /* split before */ split_elem(elem, hide_start); @@ -628,6 +642,8 @@ malloc_elem_hide_region(struct malloc_elem *elem, void *start, size_t len) } } + asan_clear_alloczone(elem); + remove_elem(elem); } @@ -641,8 +657,10 @@ malloc_elem_resize(struct malloc_elem *elem, size_t size) const size_t new_size = size + elem->pad + MALLOC_ELEM_OVERHEAD; /* if we request a smaller size, then always return ok */ - if (elem->size >= new_size) + if (elem->size >= new_size) { + asan_clear_alloczone(elem); return 0; + } /* check if there is a next element, it's free and adjacent */ if (!elem->next || elem->next->state != ELEM_FREE || @@ -661,9 +679,15 @@ malloc_elem_resize(struct malloc_elem *elem, size_t size) /* now we have a big block together. Lets cut it down a bit, by splitting */ struct malloc_elem *split_pt = RTE_PTR_ADD(elem, new_size); split_pt = RTE_PTR_ALIGN_CEIL(split_pt, RTE_CACHE_LINE_SIZE); + + asan_clear_split_alloczone(split_pt); + split_elem(elem, split_pt); malloc_elem_free_list_insert(split_pt); } + + asan_clear_alloczone(elem); + return 0; } diff --git a/lib/eal/common/malloc_elem.h b/lib/eal/common/malloc_elem.h index a1e5f7f02c..08b25c6b1f 100644 --- a/lib/eal/common/malloc_elem.h +++ b/lib/eal/common/malloc_elem.h @@ -7,6 +7,16 @@ #include <stdbool.h> +#ifdef RTE_ARCH_X86_64 +#ifdef __SANITIZE_ADDRESS__ +#define RTE_MALLOC_ASAN +#elif defined(__has_feature) +# if __has_feature(address_sanitizer) +#define RTE_MALLOC_ASAN +# endif +#endif +#endif + #define MIN_DATA_SIZE (RTE_CACHE_LINE_SIZE) /* dummy definition of struct so we can use pointers to it in malloc_elem struct */ @@ -36,10 +46,20 @@ struct malloc_elem { uint64_t header_cookie; /* Cookie marking start of data */ /* trailer cookie at start + size */ #endif +#ifdef RTE_MALLOC_ASAN + size_t user_size; + uint64_t asan_cookie[2]; /* must be next to header_cookie */ +#endif } __rte_cache_aligned; +static const unsigned int MALLOC_ELEM_HEADER_LEN = sizeof(struct malloc_elem); + #ifndef RTE_MALLOC_DEBUG -static const unsigned MALLOC_ELEM_TRAILER_LEN = 0; +#ifdef RTE_MALLOC_ASAN +static const unsigned int MALLOC_ELEM_TRAILER_LEN = RTE_CACHE_LINE_SIZE; +#else +static const unsigned int MALLOC_ELEM_TRAILER_LEN; +#endif /* dummy function - just check if pointer is non-null */ static inline int @@ -55,7 +75,7 @@ set_trailer(struct malloc_elem *elem __rte_unused){ } #else -static const unsigned MALLOC_ELEM_TRAILER_LEN = RTE_CACHE_LINE_SIZE; +static const unsigned int MALLOC_ELEM_TRAILER_LEN = RTE_CACHE_LINE_SIZE; #define MALLOC_HEADER_COOKIE 0xbadbadbadadd2e55ULL /**< Header cookie. */ #define MALLOC_TRAILER_COOKIE 0xadd2e55badbadbadULL /**< Trailer cookie.*/ @@ -90,9 +110,173 @@ malloc_elem_cookies_ok(const struct malloc_elem *elem) #endif -static const unsigned MALLOC_ELEM_HEADER_LEN = sizeof(struct malloc_elem); #define MALLOC_ELEM_OVERHEAD (MALLOC_ELEM_HEADER_LEN + MALLOC_ELEM_TRAILER_LEN) +#ifdef RTE_MALLOC_ASAN + +#ifdef RTE_ARCH_X86_64 +#define ASAN_SHADOW_OFFSET 0x00007fff8000 +#endif + +#define ASAN_SHADOW_GRAIN_SIZE 8 +#define ASAN_MEM_FREE_FLAG 0xfd +#define ASAN_MEM_REDZONE_FLAG 0xfa +#define ASAN_MEM_TO_SHADOW(mem) (((mem) >> 3) + ASAN_SHADOW_OFFSET) + +#if defined(__clang__) +__attribute__((no_sanitize("address", "hwaddress"))) +#else +__attribute__((no_sanitize_address)) +#endif +static inline void +asan_set_shadow(void *addr, char val) +{ + *(char *)addr = val; +} + +static inline void +asan_set_zone(void *ptr, size_t len, uint32_t val) +{ + size_t offset, i; + char *shadow; + size_t zone_len = len / ASAN_SHADOW_GRAIN_SIZE; + if (len % ASAN_SHADOW_GRAIN_SIZE != 0) + zone_len += 1; + + for (i = 0; i < zone_len; i++) { + offset = i * ASAN_SHADOW_GRAIN_SIZE; + shadow = (char *)ASAN_MEM_TO_SHADOW(((int64_t)ptr + offset)); + asan_set_shadow(shadow, val); + } +} + +/* + * When the memory is released, the release mark is + * set in the corresponding range of the shadow area. + */ +static inline void +asan_set_freezone(void *ptr, size_t size) +{ + asan_set_zone(ptr, size, ASAN_MEM_FREE_FLAG); +} + +/* + * When the memory is allocated, memory state must set as accessible. + */ +static inline void +asan_clear_alloczone(struct malloc_elem *elem) +{ + asan_set_zone((void *)elem, elem->size, 0x0); +} + +static inline void +asan_clear_split_alloczone(struct malloc_elem *elem) +{ + void *ptr = RTE_PTR_SUB(elem, MALLOC_ELEM_TRAILER_LEN); + asan_set_zone(ptr, MALLOC_ELEM_OVERHEAD, 0x0); +} + +/* + * When the memory is allocated, the memory boundary is + * marked in the corresponding range of the shadow area. + */ +static inline void +asan_set_redzone(struct malloc_elem *elem, size_t user_size) +{ + uint64_t ptr; + char *shadow; + if (elem != NULL) { + if (elem->state != ELEM_PAD) + elem = RTE_PTR_ADD(elem, elem->pad); + + elem->user_size = user_size; + + /* Set mark before the start of the allocated memory */ + ptr = (uint64_t)RTE_PTR_ADD(elem, MALLOC_ELEM_HEADER_LEN) + - ASAN_SHADOW_GRAIN_SIZE; + shadow = (char *)ASAN_MEM_TO_SHADOW(ptr); + asan_set_shadow(shadow, ASAN_MEM_REDZONE_FLAG); + shadow = (char *)ASAN_MEM_TO_SHADOW(ptr + - ASAN_SHADOW_GRAIN_SIZE); + asan_set_shadow(shadow, ASAN_MEM_REDZONE_FLAG); + + /* Set mark after the end of the allocated memory */ + ptr = (uint64_t)RTE_PTR_ADD(elem, MALLOC_ELEM_HEADER_LEN + + elem->user_size); + shadow = (char *)ASAN_MEM_TO_SHADOW(ptr); + uint32_t val = (ptr % ASAN_SHADOW_GRAIN_SIZE); + val = (val == 0) ? ASAN_MEM_REDZONE_FLAG : val; + asan_set_shadow(shadow, val); + shadow = (char *)ASAN_MEM_TO_SHADOW(ptr + + ASAN_SHADOW_GRAIN_SIZE); + asan_set_shadow(shadow, ASAN_MEM_REDZONE_FLAG); + } +} + +/* + * When the memory is released, the mark of the memory boundary + * in the corresponding range of the shadow area is cleared. + */ +static inline void +asan_clear_redzone(struct malloc_elem *elem) +{ + uint64_t ptr; + char *shadow; + if (elem != NULL) { + elem = RTE_PTR_ADD(elem, elem->pad); + + /* Clear mark before the start of the allocated memory */ + ptr = (uint64_t)RTE_PTR_ADD(elem, MALLOC_ELEM_HEADER_LEN) + - ASAN_SHADOW_GRAIN_SIZE; + shadow = (char *)ASAN_MEM_TO_SHADOW(ptr); + asan_set_shadow(shadow, 0x00); + shadow = (char *)ASAN_MEM_TO_SHADOW(ptr + - ASAN_SHADOW_GRAIN_SIZE); + asan_set_shadow(shadow, 0x00); + + /* Clear mark after the end of the allocated memory */ + ptr = (uint64_t)RTE_PTR_ADD(elem, MALLOC_ELEM_HEADER_LEN + + elem->user_size); + shadow = (char *)ASAN_MEM_TO_SHADOW(ptr); + asan_set_shadow(shadow, 0x00); + shadow = (char *)ASAN_MEM_TO_SHADOW(ptr + + ASAN_SHADOW_GRAIN_SIZE); + asan_set_shadow(shadow, 0x00); + } +} + +static inline size_t +old_malloc_size(struct malloc_elem *elem) +{ + if (elem->state != ELEM_PAD) + elem = RTE_PTR_ADD(elem, elem->pad); + + return elem->user_size; +} +#else +static inline void +asan_set_freezone(void *ptr __rte_unused, size_t size __rte_unused) { } + +static inline void +asan_clear_alloczone(struct malloc_elem *elem __rte_unused) { } + +static inline void +asan_clear_split_alloczone(struct malloc_elem *elem __rte_unused) { } + +static inline void +asan_set_redzone(struct malloc_elem *elem __rte_unused, + size_t user_size __rte_unused) { } + +static inline void +asan_clear_redzone(struct malloc_elem *elem __rte_unused) { } + +static inline size_t +old_malloc_size(struct malloc_elem *elem) +{ + return elem->size - elem->pad - MALLOC_ELEM_OVERHEAD; +} +#endif + /* * Given a pointer to the start of a memory block returned by malloc, get * the actual malloc_elem header for that block. diff --git a/lib/eal/common/malloc_heap.c b/lib/eal/common/malloc_heap.c index ee400f38ec..775d6789df 100644 --- a/lib/eal/common/malloc_heap.c +++ b/lib/eal/common/malloc_heap.c @@ -237,6 +237,7 @@ heap_alloc(struct malloc_heap *heap, const char *type __rte_unused, size_t size, unsigned int flags, size_t align, size_t bound, bool contig) { struct malloc_elem *elem; + size_t user_size = size; size = RTE_CACHE_LINE_ROUNDUP(size); align = RTE_CACHE_LINE_ROUNDUP(align); @@ -250,6 +251,8 @@ heap_alloc(struct malloc_heap *heap, const char *type __rte_unused, size_t size, /* increase heap's count of allocated elements */ heap->alloc_count++; + + asan_set_redzone(elem, user_size); } return elem == NULL ? NULL : (void *)(&elem[1]); @@ -270,6 +273,8 @@ heap_alloc_biggest(struct malloc_heap *heap, const char *type __rte_unused, /* increase heap's count of allocated elements */ heap->alloc_count++; + + asan_set_redzone(elem, size); } return elem == NULL ? NULL : (void *)(&elem[1]); @@ -841,6 +846,8 @@ malloc_heap_free(struct malloc_elem *elem) if (!malloc_elem_cookies_ok(elem) || elem->state != ELEM_BUSY) return -1; + asan_clear_redzone(elem); + /* elem may be merged with previous element, so keep heap address */ heap = elem->heap; msl = elem->msl; @@ -848,6 +855,9 @@ malloc_heap_free(struct malloc_elem *elem) rte_spinlock_lock(&(heap->lock)); + void *asan_ptr = RTE_PTR_ADD(elem, MALLOC_ELEM_HEADER_LEN + elem->pad); + size_t asan_data_len = elem->size - MALLOC_ELEM_OVERHEAD - elem->pad; + /* mark element as free */ elem->state = ELEM_FREE; @@ -1001,6 +1011,8 @@ malloc_heap_free(struct malloc_elem *elem) rte_mcfg_mem_write_unlock(); free_unlock: + asan_set_freezone(asan_ptr, asan_data_len); + rte_spinlock_unlock(&(heap->lock)); return ret; } diff --git a/lib/eal/common/rte_malloc.c b/lib/eal/common/rte_malloc.c index 9d39e58c08..d0bec26920 100644 --- a/lib/eal/common/rte_malloc.c +++ b/lib/eal/common/rte_malloc.c @@ -162,6 +162,8 @@ rte_calloc(const char *type, size_t num, size_t size, unsigned align) void * rte_realloc_socket(void *ptr, size_t size, unsigned int align, int socket) { + size_t user_size; + if (ptr == NULL) return rte_malloc_socket(NULL, size, align, socket); @@ -171,6 +173,8 @@ rte_realloc_socket(void *ptr, size_t size, unsigned int align, int socket) return NULL; } + user_size = size; + size = RTE_CACHE_LINE_ROUNDUP(size), align = RTE_CACHE_LINE_ROUNDUP(align); /* check requested socket id and alignment matches first, and if ok, @@ -181,6 +185,9 @@ rte_realloc_socket(void *ptr, size_t size, unsigned int align, int socket) RTE_PTR_ALIGN(ptr, align) == ptr && malloc_heap_resize(elem, size) == 0) { rte_eal_trace_mem_realloc(size, align, socket, ptr); + + asan_set_redzone(elem, user_size); + return ptr; } @@ -192,7 +199,7 @@ rte_realloc_socket(void *ptr, size_t size, unsigned int align, int socket) if (new_ptr == NULL) return NULL; /* elem: |pad|data_elem|data|trailer| */ - const size_t old_size = elem->size - elem->pad - MALLOC_ELEM_OVERHEAD; + const size_t old_size = old_malloc_size(elem); rte_memcpy(new_ptr, ptr, old_size < size ? old_size : size); rte_free(ptr); -- 2.25.1 ^ permalink raw reply [flat|nested] 117+ messages in thread
* [dpdk-dev] [PATCH v5 2/2] lib/pipeline: Fix gcc compilation error using ASan 2021-09-24 10:03 ` [dpdk-dev] [PATCH v5 1/2] Enable ASan for memory detector on DPDK zhihongx.peng @ 2021-09-24 10:03 ` zhihongx.peng 2021-09-30 5:27 ` [dpdk-dev] [PATCH v6 1/2] Enable ASan for memory detector on DPDK zhihongx.peng 2021-09-30 12:59 ` zhihongx.peng 2 siblings, 0 replies; 117+ messages in thread From: zhihongx.peng @ 2021-09-24 10:03 UTC (permalink / raw) To: anatoly.burakov, konstantin.ananyev, stephen Cc: dev, xueqin.lin, Zhihong Peng, stable From: Zhihong Peng <zhihongx.peng@intel.com> After adding ASan, the gcc compilation check will be stricter. "Control reaches end of non-void function" error occurs here. Fixes: f38913b7fb8e (pipeline: add meter array to SWX) Cc: stable@dpdk.org Signed-off-by: Xueqin Lin <xueqin.lin@intel.com> Signed-off-by: Zhihong Peng <zhihongx.peng@intel.com> --- lib/pipeline/rte_swx_pipeline.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/pipeline/rte_swx_pipeline.c b/lib/pipeline/rte_swx_pipeline.c index 8eb978a30c..aaa0107d02 100644 --- a/lib/pipeline/rte_swx_pipeline.c +++ b/lib/pipeline/rte_swx_pipeline.c @@ -6340,7 +6340,7 @@ instr_meter_translate(struct rte_swx_pipeline *p, return 0; } - CHECK(0, EINVAL); + return -EINVAL; } static inline struct meter * @@ -8025,7 +8025,7 @@ instr_translate(struct rte_swx_pipeline *p, instr, data); - CHECK(0, EINVAL); + return -EINVAL; } static struct instruction_data * -- 2.25.1 ^ permalink raw reply [flat|nested] 117+ messages in thread
* [dpdk-dev] [PATCH v6 1/2] Enable ASan for memory detector on DPDK 2021-09-24 10:03 ` [dpdk-dev] [PATCH v5 1/2] Enable ASan for memory detector on DPDK zhihongx.peng 2021-09-24 10:03 ` [dpdk-dev] [PATCH v5 2/2] lib/pipeline: Fix gcc compilation error using ASan zhihongx.peng @ 2021-09-30 5:27 ` zhihongx.peng 2021-09-30 5:27 ` [dpdk-dev] [PATCH v6 2/2] lib/pipeline: Fix gcc compilation error using ASan zhihongx.peng 2021-09-30 8:20 ` [dpdk-dev] [PATCH v6 1/2] Enable ASan for memory detector on DPDK David Marchand 2021-09-30 12:59 ` zhihongx.peng 2 siblings, 2 replies; 117+ messages in thread From: zhihongx.peng @ 2021-09-30 5:27 UTC (permalink / raw) To: anatoly.burakov, konstantin.ananyev, stephen Cc: dev, xueqin.lin, Zhihong Peng From: Zhihong Peng <zhihongx.peng@intel.com> AddressSanitizer (ASan) is a google memory error detect standard tool. It could help to detect use-after-free and {heap,stack,global}-buffer overflow bugs in C/C++ programs, print detailed error information when error happens, large improve debug efficiency. `AddressSanitizer <https://github.com/google/sanitizers/wiki/AddressSanitizer>` (ASan) is a widely-used debugging tool to detect memory access errors. It helps detect issues like use-after-free, various kinds of buffer overruns in C/C++ programs, and other similar errors, as well as printing out detailed debug information whenever an error is detected. DPDK ASan functionality is currently only supported Linux x86_64. Support other platforms, need to define ASAN_SHADOW_OFFSET value according to google ASan document. Here is an example of heap-buffer-overflow bug: ...... char *p = rte_zmalloc(NULL, 7, 0); p[7] = 'a'; ...... Here is an example of use-after-free bug: ...... char *p = rte_zmalloc(NULL, 7, 0); rte_free(p); *p = 'a'; ...... If you want to use this feature, you need to add below compilation options when compiling code: -Dbuildtype=debug -Db_lundef=false -Db_sanitize=address "-Dbuildtype=debug": This is a non-essential option. When this option is added, if a memory error occurs, ASan can clearly show where the code is wrong. "-Db_lundef=false": When use clang to compile DPDK, this option must be added. Signed-off-by: Xueqin Lin <xueqin.lin@intel.com> Signed-off-by: Zhihong Peng <zhihongx.peng@intel.com> --- devtools/words-case.txt | 1 + doc/guides/prog_guide/ASan.rst | 108 +++++++++++++++++ doc/guides/prog_guide/index.rst | 1 + examples/helloworld/main.c | 5 + lib/eal/common/malloc_elem.c | 26 +++- lib/eal/common/malloc_elem.h | 204 +++++++++++++++++++++++++++++++- lib/eal/common/malloc_heap.c | 12 ++ lib/eal/common/rte_malloc.c | 9 +- 8 files changed, 361 insertions(+), 5 deletions(-) create mode 100644 doc/guides/prog_guide/ASan.rst diff --git a/devtools/words-case.txt b/devtools/words-case.txt index 0bbad48626..3655596d47 100644 --- a/devtools/words-case.txt +++ b/devtools/words-case.txt @@ -86,3 +86,4 @@ VXLAN Windows XDP XOR +ASan diff --git a/doc/guides/prog_guide/ASan.rst b/doc/guides/prog_guide/ASan.rst new file mode 100644 index 0000000000..7145a3b1a1 --- /dev/null +++ b/doc/guides/prog_guide/ASan.rst @@ -0,0 +1,108 @@ +.. Copyright (c) <2021>, Intel Corporation + All rights reserved. + +Memory error detect standard tool - AddressSanitizer(ASan) +========================================================== + +AddressSanitizer (ASan) is a google memory error detect +standard tool. It could help to detect use-after-free and +{heap,stack,global}-buffer overflow bugs in C/C++ programs, +print detailed error information when error happens, large +improve debug efficiency. + +By referring to its implementation algorithm +(https://github.com/google/sanitizers/wiki/AddressSanitizerAlgorithm), +enabled heap-buffer-overflow and use-after-free functions on DPDK. +DPDK ASan function currently only supports on Linux x86_64. + +AddressSanitizer is a part of LLVM(3.1+)and GCC(4.8+). + +Example heap-buffer-overflow error +---------------------------------- + +Following error was reported when ASan was enabled:: + + Applied 9 bytes of memory, but accessed the 10th byte of memory, + so heap-buffer-overflow appeared. + +Below code results in this error:: + + char *p = rte_zmalloc(NULL, 9, 0); + if (!p) { + printf("rte_zmalloc error."); + return -1; + } + p[9] = 'a'; + +The error log:: + + ==49433==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x7f773fafa249 at pc 0x5556b13bdae4 bp 0x7ffeb4965e40 sp 0x7ffeb4965e30 WRITE of size 1 at 0x7f773fafa249 thread T0 + #0 0x5556b13bdae3 in asan_heap_buffer_overflow ../app/test/test_asan_heap_buffer_overflow.c:25 + #1 0x5556b043e9d4 in cmd_autotest_parsed ../app/test/commands.c:71 + #2 0x5556b1cdd4b0 in cmdline_parse ../lib/cmdline/cmdline_parse.c:290 + #3 0x5556b1cd8987 in cmdline_valid_buffer ../lib/cmdline/cmdline.c:26 + #4 0x5556b1ce477a in rdline_char_in ../lib/cmdline/cmdline_rdline.c:421 + #5 0x5556b1cd923e in cmdline_in ../lib/cmdline/cmdline.c:149 + #6 0x5556b1cd9769 in cmdline_interact ../lib/cmdline/cmdline.c:223 + #7 0x5556b045f53b in main ../app/test/test.c:234 + #8 0x7f7f1eba90b2 in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x270b2) + #9 0x5556b043e70d in _start (/home/pzh/yyy/x86_64-native-linuxapp-gcc/app/test/dpdk-test+0x7ce70d) + + Address 0x7f773fafa249 is a wild pointer. + SUMMARY: AddressSanitizer: heap-buffer-overflow ../app/test/test_asan_heap_buffer_overflow.c:25 in asan_heap_buffer_overflow + +Example use-after-free error +---------------------------- + +Following error was reported when ASan was enabled:: + + Applied for 9 bytes of memory, and accessed the first byte after + released, so heap-use-after-free appeared. + +Below code results in this error:: + + char *p = rte_zmalloc(NULL, 9, 0); + if (!p) { + printf("rte_zmalloc error."); + return -1; + } + rte_free(p); + *p = 'a'; + +The error log:: + + ==49478==ERROR: AddressSanitizer: heap-use-after-free on address 0x7fe2ffafa240 at pc 0x56409b084bc8 bp 0x7ffef62c57d0 sp 0x7ffef62c57c0 WRITE of size 1 at 0x7fe2ffafa240 thread T0 + #0 0x56409b084bc7 in asan_use_after_free ../app/test/test_asan_use_after_free.c:26 + #1 0x56409a1059d4 in cmd_autotest_parsed ../app/test/commands.c:71 + #2 0x56409b9a44b0 in cmdline_parse ../lib/cmdline/cmdline_parse.c:290 + #3 0x56409b99f987 in cmdline_valid_buffer ../lib/cmdline/cmdline.c:26 + #4 0x56409b9ab77a in rdline_char_in ../lib/cmdline/cmdline_rdline.c:421 + #5 0x56409b9a023e in cmdline_in ../lib/cmdline/cmdline.c:149 + #6 0x56409b9a0769 in cmdline_interact ../lib/cmdline/cmdline.c:223 + #7 0x56409a12653b in main ../app/test/test.c:234 + #8 0x7feafafc20b2 in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x270b2) + #9 0x56409a10570d in _start (/home/pzh/yyy/x86_64-native-linuxapp-gcc/app/test/dpdk-test+0x7ce70d) + + Address 0x7fe2ffafa240 is a wild pointer. + SUMMARY: AddressSanitizer: heap-use-after-free ../app/test/test_asan_use_after_free.c:26 in asan_use_after_free + +Usage +----- + +meson build +^^^^^^^^^^^ + +To enable ASan in meson build system, use following meson build command: + +Example usage:: + + meson build -Dbuildtype=debug -Db_lundef=false -Db_sanitize=address + ninja -C build + +.. Note:: + + a) Some of the features of ASan (for example, 'Display memory application location,currently + displayed as a wild pointer') are not currently supported by DPDK's implementation. + b) DPDK test has been completed in ubuntu18.04/ubuntu20.04/redhat8.3. Centos needs to install + libasan separately. + c) If the program uses cmdline, when a memory bug occurs, need to execute the "stty echo" command. diff --git a/doc/guides/prog_guide/index.rst b/doc/guides/prog_guide/index.rst index 2dce507f46..abaf687045 100644 --- a/doc/guides/prog_guide/index.rst +++ b/doc/guides/prog_guide/index.rst @@ -71,3 +71,4 @@ Programmer's Guide lto profile_app glossary + ASan diff --git a/examples/helloworld/main.c b/examples/helloworld/main.c index af509138da..f46a6c347e 100644 --- a/examples/helloworld/main.c +++ b/examples/helloworld/main.c @@ -14,6 +14,7 @@ #include <rte_per_lcore.h> #include <rte_lcore.h> #include <rte_debug.h> +#include <rte_malloc.h> /* Launch a function on lcore. 8< */ static int @@ -38,6 +39,10 @@ main(int argc, char **argv) rte_panic("Cannot init EAL\n"); /* >8 End of initialization of Environment Abstraction Layer */ + char *p = (char *)rte_malloc(NULL, 9, 0); + rte_free(p); + p[5] = 'a'; + /* Launches the function on each lcore. 8< */ RTE_LCORE_FOREACH_WORKER(lcore_id) { /* Simpler equivalent. 8< */ diff --git a/lib/eal/common/malloc_elem.c b/lib/eal/common/malloc_elem.c index c2c9461f1d..bdd20a162e 100644 --- a/lib/eal/common/malloc_elem.c +++ b/lib/eal/common/malloc_elem.c @@ -446,6 +446,8 @@ malloc_elem_alloc(struct malloc_elem *elem, size_t size, unsigned align, struct malloc_elem *new_free_elem = RTE_PTR_ADD(new_elem, size + MALLOC_ELEM_OVERHEAD); + asan_clear_split_alloczone(new_free_elem); + split_elem(elem, new_free_elem); malloc_elem_free_list_insert(new_free_elem); @@ -458,6 +460,8 @@ malloc_elem_alloc(struct malloc_elem *elem, size_t size, unsigned align, elem->state = ELEM_BUSY; elem->pad = old_elem_size; + asan_clear_alloczone(elem); + /* put a dummy header in padding, to point to real element header */ if (elem->pad > 0) { /* pad will be at least 64-bytes, as everything * is cache-line aligned */ @@ -470,12 +474,18 @@ malloc_elem_alloc(struct malloc_elem *elem, size_t size, unsigned align, return new_elem; } + asan_clear_split_alloczone(new_elem); + /* we are going to split the element in two. The original element * remains free, and the new element is the one allocated. * Re-insert original element, in case its new size makes it * belong on a different list. */ + split_elem(elem, new_elem); + + asan_clear_alloczone(new_elem); + new_elem->state = ELEM_BUSY; malloc_elem_free_list_insert(elem); @@ -601,6 +611,8 @@ malloc_elem_hide_region(struct malloc_elem *elem, void *start, size_t len) if (next && next_elem_is_adjacent(elem)) { len_after = RTE_PTR_DIFF(next, hide_end); if (len_after >= MALLOC_ELEM_OVERHEAD + MIN_DATA_SIZE) { + asan_clear_split_alloczone(hide_end); + /* split after */ split_elem(elem, hide_end); @@ -615,6 +627,8 @@ malloc_elem_hide_region(struct malloc_elem *elem, void *start, size_t len) if (prev && prev_elem_is_adjacent(elem)) { len_before = RTE_PTR_DIFF(hide_start, elem); if (len_before >= MALLOC_ELEM_OVERHEAD + MIN_DATA_SIZE) { + asan_clear_split_alloczone(hide_start); + /* split before */ split_elem(elem, hide_start); @@ -628,6 +642,8 @@ malloc_elem_hide_region(struct malloc_elem *elem, void *start, size_t len) } } + asan_clear_alloczone(elem); + remove_elem(elem); } @@ -641,8 +657,10 @@ malloc_elem_resize(struct malloc_elem *elem, size_t size) const size_t new_size = size + elem->pad + MALLOC_ELEM_OVERHEAD; /* if we request a smaller size, then always return ok */ - if (elem->size >= new_size) + if (elem->size >= new_size) { + asan_clear_alloczone(elem); return 0; + } /* check if there is a next element, it's free and adjacent */ if (!elem->next || elem->next->state != ELEM_FREE || @@ -661,9 +679,15 @@ malloc_elem_resize(struct malloc_elem *elem, size_t size) /* now we have a big block together. Lets cut it down a bit, by splitting */ struct malloc_elem *split_pt = RTE_PTR_ADD(elem, new_size); split_pt = RTE_PTR_ALIGN_CEIL(split_pt, RTE_CACHE_LINE_SIZE); + + asan_clear_split_alloczone(split_pt); + split_elem(elem, split_pt); malloc_elem_free_list_insert(split_pt); } + + asan_clear_alloczone(elem); + return 0; } diff --git a/lib/eal/common/malloc_elem.h b/lib/eal/common/malloc_elem.h index a1e5f7f02c..637b404eea 100644 --- a/lib/eal/common/malloc_elem.h +++ b/lib/eal/common/malloc_elem.h @@ -7,6 +7,16 @@ #include <stdbool.h> +#ifdef RTE_ARCH_X86_64 +#ifdef __SANITIZE_ADDRESS__ +#define RTE_MALLOC_ASAN +#elif defined(__has_feature) +# if __has_feature(address_sanitizer) +#define RTE_MALLOC_ASAN +# endif +#endif +#endif + #define MIN_DATA_SIZE (RTE_CACHE_LINE_SIZE) /* dummy definition of struct so we can use pointers to it in malloc_elem struct */ @@ -36,10 +46,20 @@ struct malloc_elem { uint64_t header_cookie; /* Cookie marking start of data */ /* trailer cookie at start + size */ #endif +#ifdef RTE_MALLOC_ASAN + size_t user_size; + uint64_t asan_cookie[2]; /* must be next to header_cookie */ +#endif } __rte_cache_aligned; +static const unsigned int MALLOC_ELEM_HEADER_LEN = sizeof(struct malloc_elem); + #ifndef RTE_MALLOC_DEBUG -static const unsigned MALLOC_ELEM_TRAILER_LEN = 0; +#ifdef RTE_MALLOC_ASAN +static const unsigned int MALLOC_ELEM_TRAILER_LEN = RTE_CACHE_LINE_SIZE; +#else +static const unsigned int MALLOC_ELEM_TRAILER_LEN; +#endif /* dummy function - just check if pointer is non-null */ static inline int @@ -55,7 +75,7 @@ set_trailer(struct malloc_elem *elem __rte_unused){ } #else -static const unsigned MALLOC_ELEM_TRAILER_LEN = RTE_CACHE_LINE_SIZE; +static const unsigned int MALLOC_ELEM_TRAILER_LEN = RTE_CACHE_LINE_SIZE; #define MALLOC_HEADER_COOKIE 0xbadbadbadadd2e55ULL /**< Header cookie. */ #define MALLOC_TRAILER_COOKIE 0xadd2e55badbadbadULL /**< Trailer cookie.*/ @@ -90,9 +110,187 @@ malloc_elem_cookies_ok(const struct malloc_elem *elem) #endif -static const unsigned MALLOC_ELEM_HEADER_LEN = sizeof(struct malloc_elem); #define MALLOC_ELEM_OVERHEAD (MALLOC_ELEM_HEADER_LEN + MALLOC_ELEM_TRAILER_LEN) +#ifdef RTE_MALLOC_ASAN + +#ifdef RTE_ARCH_X86_64 +#define ASAN_SHADOW_OFFSET 0x00007fff8000 +#endif + +#define ASAN_SHADOW_GRAIN_SIZE 8 +#define ASAN_MEM_FREE_FLAG 0xfd +#define ASAN_MEM_REDZONE_FLAG 0xfa +#define ASAN_SHADOW_SCALE 3 + +#define ASAN_MEM_SHIFT(mem) ((void *)((uintptr_t)(mem) >> ASAN_SHADOW_SCALE)) +#define ASAN_MEM_TO_SHADOW(mem) \ + RTE_PTR_ADD(ASAN_MEM_SHIFT(mem), ASAN_SHADOW_OFFSET) + +#if defined(__clang__) +__attribute__((no_sanitize("address", "hwaddress"))) +#else +__attribute__((no_sanitize_address)) +#endif +static inline void +asan_set_shadow(void *addr, char val) +{ + *(char *)addr = val; +} + +static inline void +asan_set_zone(void *ptr, size_t len, uint32_t val) +{ + size_t offset, i; + void *shadow; + size_t zone_len = len / ASAN_SHADOW_GRAIN_SIZE; + if (len % ASAN_SHADOW_GRAIN_SIZE != 0) + zone_len += 1; + + for (i = 0; i < zone_len; i++) { + offset = i * ASAN_SHADOW_GRAIN_SIZE; + shadow = ASAN_MEM_TO_SHADOW(((uintptr_t)ptr + offset)); + asan_set_shadow(shadow, val); + } +} + +/* + * When the memory is released, the release mark is + * set in the corresponding range of the shadow area. + */ +static inline void +asan_set_freezone(void *ptr, size_t size) +{ + asan_set_zone(ptr, size, ASAN_MEM_FREE_FLAG); +} + +/* + * When the memory is allocated, memory state must set as accessible. + */ +static inline void +asan_clear_alloczone(struct malloc_elem *elem) +{ + asan_set_zone((void *)elem, elem->size, 0x0); +} + +static inline void +asan_clear_split_alloczone(struct malloc_elem *elem) +{ + void *ptr = RTE_PTR_SUB(elem, MALLOC_ELEM_TRAILER_LEN); + asan_set_zone(ptr, MALLOC_ELEM_OVERHEAD, 0x0); +} + +/* + * When the memory is allocated, the memory boundary is + * marked in the corresponding range of the shadow area. + * Requirement: redzone >= 16, is a power of two. + */ +static inline void +asan_set_redzone(struct malloc_elem *elem, size_t user_size) +{ + uintptr_t head_redzone; + uintptr_t tail_redzone; + void *front_shadow; + void *tail_shadow; + uint32_t val; + + if (elem != NULL) { + if (elem->state != ELEM_PAD) + elem = RTE_PTR_ADD(elem, elem->pad); + + elem->user_size = user_size; + + /* Set mark before the start of the allocated memory */ + head_redzone = (uintptr_t)RTE_PTR_ADD(elem, + MALLOC_ELEM_HEADER_LEN - ASAN_SHADOW_GRAIN_SIZE); + front_shadow = ASAN_MEM_TO_SHADOW(head_redzone); + asan_set_shadow(front_shadow, ASAN_MEM_REDZONE_FLAG); + front_shadow = ASAN_MEM_TO_SHADOW(head_redzone + - ASAN_SHADOW_GRAIN_SIZE); + asan_set_shadow(front_shadow, ASAN_MEM_REDZONE_FLAG); + + /* Set mark after the end of the allocated memory */ + tail_redzone = (uintptr_t)RTE_PTR_ADD(elem, + MALLOC_ELEM_HEADER_LEN + + elem->user_size); + tail_shadow = ASAN_MEM_TO_SHADOW(tail_redzone); + val = (tail_redzone % ASAN_SHADOW_GRAIN_SIZE); + val = (val == 0) ? ASAN_MEM_REDZONE_FLAG : val; + asan_set_shadow(tail_shadow, val); + tail_shadow = ASAN_MEM_TO_SHADOW(tail_redzone + + ASAN_SHADOW_GRAIN_SIZE); + asan_set_shadow(tail_shadow, ASAN_MEM_REDZONE_FLAG); + } +} + +/* + * When the memory is released, the mark of the memory boundary + * in the corresponding range of the shadow area is cleared. + * Requirement: redzone >= 16, is a power of two. + */ +static inline void +asan_clear_redzone(struct malloc_elem *elem) +{ + uintptr_t head_redzone; + uintptr_t tail_redzone; + void *head_shadow; + void *tail_shadow; + + if (elem != NULL) { + elem = RTE_PTR_ADD(elem, elem->pad); + + /* Clear mark before the start of the allocated memory */ + head_redzone = (uintptr_t)RTE_PTR_ADD(elem, + MALLOC_ELEM_HEADER_LEN - ASAN_SHADOW_GRAIN_SIZE); + head_shadow = ASAN_MEM_TO_SHADOW(head_redzone); + asan_set_shadow(head_shadow, 0x00); + head_shadow = ASAN_MEM_TO_SHADOW(head_redzone + - ASAN_SHADOW_GRAIN_SIZE); + asan_set_shadow(head_shadow, 0x00); + + /* Clear mark after the end of the allocated memory */ + tail_redzone = (uintptr_t)RTE_PTR_ADD(elem, + MALLOC_ELEM_HEADER_LEN + elem->user_size); + tail_shadow = ASAN_MEM_TO_SHADOW(tail_redzone); + asan_set_shadow(tail_shadow, 0x00); + tail_shadow = ASAN_MEM_TO_SHADOW(tail_redzone + + ASAN_SHADOW_GRAIN_SIZE); + asan_set_shadow(tail_shadow, 0x00); + } +} + +static inline size_t +old_malloc_size(struct malloc_elem *elem) +{ + if (elem->state != ELEM_PAD) + elem = RTE_PTR_ADD(elem, elem->pad); + + return elem->user_size; +} +#else +static inline void +asan_set_freezone(void *ptr __rte_unused, size_t size __rte_unused) { } + +static inline void +asan_clear_alloczone(struct malloc_elem *elem __rte_unused) { } + +static inline void +asan_clear_split_alloczone(struct malloc_elem *elem __rte_unused) { } + +static inline void +asan_set_redzone(struct malloc_elem *elem __rte_unused, + size_t user_size __rte_unused) { } + +static inline void +asan_clear_redzone(struct malloc_elem *elem __rte_unused) { } + +static inline size_t +old_malloc_size(struct malloc_elem *elem) +{ + return elem->size - elem->pad - MALLOC_ELEM_OVERHEAD; +} +#endif + /* * Given a pointer to the start of a memory block returned by malloc, get * the actual malloc_elem header for that block. diff --git a/lib/eal/common/malloc_heap.c b/lib/eal/common/malloc_heap.c index ee400f38ec..775d6789df 100644 --- a/lib/eal/common/malloc_heap.c +++ b/lib/eal/common/malloc_heap.c @@ -237,6 +237,7 @@ heap_alloc(struct malloc_heap *heap, const char *type __rte_unused, size_t size, unsigned int flags, size_t align, size_t bound, bool contig) { struct malloc_elem *elem; + size_t user_size = size; size = RTE_CACHE_LINE_ROUNDUP(size); align = RTE_CACHE_LINE_ROUNDUP(align); @@ -250,6 +251,8 @@ heap_alloc(struct malloc_heap *heap, const char *type __rte_unused, size_t size, /* increase heap's count of allocated elements */ heap->alloc_count++; + + asan_set_redzone(elem, user_size); } return elem == NULL ? NULL : (void *)(&elem[1]); @@ -270,6 +273,8 @@ heap_alloc_biggest(struct malloc_heap *heap, const char *type __rte_unused, /* increase heap's count of allocated elements */ heap->alloc_count++; + + asan_set_redzone(elem, size); } return elem == NULL ? NULL : (void *)(&elem[1]); @@ -841,6 +846,8 @@ malloc_heap_free(struct malloc_elem *elem) if (!malloc_elem_cookies_ok(elem) || elem->state != ELEM_BUSY) return -1; + asan_clear_redzone(elem); + /* elem may be merged with previous element, so keep heap address */ heap = elem->heap; msl = elem->msl; @@ -848,6 +855,9 @@ malloc_heap_free(struct malloc_elem *elem) rte_spinlock_lock(&(heap->lock)); + void *asan_ptr = RTE_PTR_ADD(elem, MALLOC_ELEM_HEADER_LEN + elem->pad); + size_t asan_data_len = elem->size - MALLOC_ELEM_OVERHEAD - elem->pad; + /* mark element as free */ elem->state = ELEM_FREE; @@ -1001,6 +1011,8 @@ malloc_heap_free(struct malloc_elem *elem) rte_mcfg_mem_write_unlock(); free_unlock: + asan_set_freezone(asan_ptr, asan_data_len); + rte_spinlock_unlock(&(heap->lock)); return ret; } diff --git a/lib/eal/common/rte_malloc.c b/lib/eal/common/rte_malloc.c index 9d39e58c08..d0bec26920 100644 --- a/lib/eal/common/rte_malloc.c +++ b/lib/eal/common/rte_malloc.c @@ -162,6 +162,8 @@ rte_calloc(const char *type, size_t num, size_t size, unsigned align) void * rte_realloc_socket(void *ptr, size_t size, unsigned int align, int socket) { + size_t user_size; + if (ptr == NULL) return rte_malloc_socket(NULL, size, align, socket); @@ -171,6 +173,8 @@ rte_realloc_socket(void *ptr, size_t size, unsigned int align, int socket) return NULL; } + user_size = size; + size = RTE_CACHE_LINE_ROUNDUP(size), align = RTE_CACHE_LINE_ROUNDUP(align); /* check requested socket id and alignment matches first, and if ok, @@ -181,6 +185,9 @@ rte_realloc_socket(void *ptr, size_t size, unsigned int align, int socket) RTE_PTR_ALIGN(ptr, align) == ptr && malloc_heap_resize(elem, size) == 0) { rte_eal_trace_mem_realloc(size, align, socket, ptr); + + asan_set_redzone(elem, user_size); + return ptr; } @@ -192,7 +199,7 @@ rte_realloc_socket(void *ptr, size_t size, unsigned int align, int socket) if (new_ptr == NULL) return NULL; /* elem: |pad|data_elem|data|trailer| */ - const size_t old_size = elem->size - elem->pad - MALLOC_ELEM_OVERHEAD; + const size_t old_size = old_malloc_size(elem); rte_memcpy(new_ptr, ptr, old_size < size ? old_size : size); rte_free(ptr); -- 2.25.1 ^ permalink raw reply [flat|nested] 117+ messages in thread
* [dpdk-dev] [PATCH v6 2/2] lib/pipeline: Fix gcc compilation error using ASan 2021-09-30 5:27 ` [dpdk-dev] [PATCH v6 1/2] Enable ASan for memory detector on DPDK zhihongx.peng @ 2021-09-30 5:27 ` zhihongx.peng 2021-09-30 8:29 ` [dpdk-dev] [dpdk-stable] " David Marchand 2021-09-30 8:20 ` [dpdk-dev] [PATCH v6 1/2] Enable ASan for memory detector on DPDK David Marchand 1 sibling, 1 reply; 117+ messages in thread From: zhihongx.peng @ 2021-09-30 5:27 UTC (permalink / raw) To: anatoly.burakov, konstantin.ananyev, stephen Cc: dev, xueqin.lin, Zhihong Peng, stable From: Zhihong Peng <zhihongx.peng@intel.com> After adding ASan, the gcc compilation check will be stricter. "Control reaches end of non-void function" error occurs here. Fixes: f38913b7fb8e (pipeline: add meter array to SWX) Cc: stable@dpdk.org Signed-off-by: Xueqin Lin <xueqin.lin@intel.com> Signed-off-by: Zhihong Peng <zhihongx.peng@intel.com> --- lib/pipeline/rte_swx_pipeline.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/pipeline/rte_swx_pipeline.c b/lib/pipeline/rte_swx_pipeline.c index 1cd09a4b44..0acd6c6752 100644 --- a/lib/pipeline/rte_swx_pipeline.c +++ b/lib/pipeline/rte_swx_pipeline.c @@ -4642,7 +4642,7 @@ instr_meter_translate(struct rte_swx_pipeline *p, return 0; } - CHECK(0, EINVAL); + return -EINVAL; } static inline void @@ -5937,7 +5937,7 @@ instr_translate(struct rte_swx_pipeline *p, instr, data); - CHECK(0, EINVAL); + return -EINVAL; } static struct instruction_data * -- 2.25.1 ^ permalink raw reply [flat|nested] 117+ messages in thread
* Re: [dpdk-dev] [dpdk-stable] [PATCH v6 2/2] lib/pipeline: Fix gcc compilation error using ASan 2021-09-30 5:27 ` [dpdk-dev] [PATCH v6 2/2] lib/pipeline: Fix gcc compilation error using ASan zhihongx.peng @ 2021-09-30 8:29 ` David Marchand 2021-10-12 2:41 ` Peng, ZhihongX 0 siblings, 1 reply; 117+ messages in thread From: David Marchand @ 2021-09-30 8:29 UTC (permalink / raw) To: Zhihong Peng Cc: Burakov, Anatoly, Ananyev, Konstantin, Stephen Hemminger, dev, Xueqin Lin, dpdk stable On Thu, Sep 30, 2021 at 7:37 AM <zhihongx.peng@intel.com> wrote: > > From: Zhihong Peng <zhihongx.peng@intel.com> Commit titles don't start with lib/. > > After adding ASan, the gcc compilation check will be stricter. > "Control reaches end of non-void function" error occurs here. Fwiw, I could not pinpoint the right version where this warning appears. I can't see it with gcc v4.8.5 (rhel7), but I get it with gcc 11.2.1 (fc34). Do you know which versions are affected? Just asking for info. > > Fixes: f38913b7fb8e (pipeline: add meter array to SWX) Should be formatted as: Fixes: f38913b7fb8e ("pipeline: add meter array to SWX") Please use a git alias as suggested in the contribution guide. > Cc: stable@dpdk.org > > Signed-off-by: Xueqin Lin <xueqin.lin@intel.com> > Signed-off-by: Zhihong Peng <zhihongx.peng@intel.com> > --- > lib/pipeline/rte_swx_pipeline.c | 4 ++-- > 1 file changed, 2 insertions(+), 2 deletions(-) > > diff --git a/lib/pipeline/rte_swx_pipeline.c b/lib/pipeline/rte_swx_pipeline.c > index 1cd09a4b44..0acd6c6752 100644 > --- a/lib/pipeline/rte_swx_pipeline.c > +++ b/lib/pipeline/rte_swx_pipeline.c > @@ -4642,7 +4642,7 @@ instr_meter_translate(struct rte_swx_pipeline *p, > return 0; > } > > - CHECK(0, EINVAL); > + return -EINVAL; > } > > static inline void > @@ -5937,7 +5937,7 @@ instr_translate(struct rte_swx_pipeline *p, > instr, > data); > > - CHECK(0, EINVAL); > + return -EINVAL; > } > > static struct instruction_data * There are two other functions (instr_table_translate, and instr_extern_translate) which have the same pattern in this file. Odd that the compiler is not reporting them. -- David Marchand ^ permalink raw reply [flat|nested] 117+ messages in thread
* Re: [dpdk-dev] [dpdk-stable] [PATCH v6 2/2] lib/pipeline: Fix gcc compilation error using ASan 2021-09-30 8:29 ` [dpdk-dev] [dpdk-stable] " David Marchand @ 2021-10-12 2:41 ` Peng, ZhihongX 0 siblings, 0 replies; 117+ messages in thread From: Peng, ZhihongX @ 2021-10-12 2:41 UTC (permalink / raw) To: David Marchand Cc: Burakov, Anatoly, Ananyev, Konstantin, Stephen Hemminger, dev, Lin, Xueqin, dpdk stable > -----Original Message----- > From: David Marchand <david.marchand@redhat.com> > Sent: Thursday, September 30, 2021 4:30 PM > To: Peng, ZhihongX <zhihongx.peng@intel.com> > Cc: Burakov, Anatoly <anatoly.burakov@intel.com>; Ananyev, Konstantin > <konstantin.ananyev@intel.com>; Stephen Hemminger > <stephen@networkplumber.org>; dev <dev@dpdk.org>; Lin, Xueqin > <xueqin.lin@intel.com>; dpdk stable <stable@dpdk.org> > Subject: Re: [dpdk-stable] [PATCH v6 2/2] lib/pipeline: Fix gcc compilation > error using ASan > > On Thu, Sep 30, 2021 at 7:37 AM <zhihongx.peng@intel.com> wrote: > > > > From: Zhihong Peng <zhihongx.peng@intel.com> > > Commit titles don't start with lib/. The v9 version will be fixed. > > > > > After adding ASan, the gcc compilation check will be stricter. > > "Control reaches end of non-void function" error occurs here. > > Fwiw, I could not pinpoint the right version where this warning appears. > I can't see it with gcc v4.8.5 (rhel7), but I get it with gcc 11.2.1 (fc34). > Do you know which versions are affected? Just asking for info. > > > > > > Fixes: f38913b7fb8e (pipeline: add meter array to SWX) > > Should be formatted as: > Fixes: f38913b7fb8e ("pipeline: add meter array to SWX") > > Please use a git alias as suggested in the contribution guide. The v9 version will be fixed. > > > Cc: stable@dpdk.org > > > > Signed-off-by: Xueqin Lin <xueqin.lin@intel.com> > > Signed-off-by: Zhihong Peng <zhihongx.peng@intel.com> > > --- > > lib/pipeline/rte_swx_pipeline.c | 4 ++-- > > 1 file changed, 2 insertions(+), 2 deletions(-) > > > > diff --git a/lib/pipeline/rte_swx_pipeline.c > > b/lib/pipeline/rte_swx_pipeline.c index 1cd09a4b44..0acd6c6752 100644 > > --- a/lib/pipeline/rte_swx_pipeline.c > > +++ b/lib/pipeline/rte_swx_pipeline.c > > @@ -4642,7 +4642,7 @@ instr_meter_translate(struct rte_swx_pipeline *p, > > return 0; > > } > > > > - CHECK(0, EINVAL); > > + return -EINVAL; > > } > > > > static inline void > > @@ -5937,7 +5937,7 @@ instr_translate(struct rte_swx_pipeline *p, > > instr, > > data); > > > > - CHECK(0, EINVAL); > > + return -EINVAL; > > } > > > > static struct instruction_data * > > There are two other functions (instr_table_translate, and > instr_extern_translate) which have the same pattern in this file. > Odd that the compiler is not reporting them. The lowest version we tried is gcc version 7.4.0, the highest gcc version 9.3.0, these versions will report errors. As for why some do not report errors, it may be gcc's own problem, we just let the compilation pass. > -- > David Marchand ^ permalink raw reply [flat|nested] 117+ messages in thread
* Re: [dpdk-dev] [PATCH v6 1/2] Enable ASan for memory detector on DPDK 2021-09-30 5:27 ` [dpdk-dev] [PATCH v6 1/2] Enable ASan for memory detector on DPDK zhihongx.peng 2021-09-30 5:27 ` [dpdk-dev] [PATCH v6 2/2] lib/pipeline: Fix gcc compilation error using ASan zhihongx.peng @ 2021-09-30 8:20 ` David Marchand 2021-10-08 8:07 ` Peng, ZhihongX ` (2 more replies) 1 sibling, 3 replies; 117+ messages in thread From: David Marchand @ 2021-09-30 8:20 UTC (permalink / raw) To: Zhihong Peng, Bruce Richardson Cc: Burakov, Anatoly, Ananyev, Konstantin, Stephen Hemminger, dev, Xueqin Lin, Thomas Monjalon Hello, I see v6 is superseded in pw, I have been cleaning my queue... maybe my fault. On Thu, Sep 30, 2021 at 7:37 AM <zhihongx.peng@intel.com> wrote: > > From: Zhihong Peng <zhihongx.peng@intel.com> > > AddressSanitizer (ASan) is a google memory error detect > standard tool. It could help to detect use-after-free and > {heap,stack,global}-buffer overflow bugs in C/C++ programs, > print detailed error information when error happens, large > improve debug efficiency. > > `AddressSanitizer > <https://github.com/google/sanitizers/wiki/AddressSanitizer>` (ASan) > is a widely-used debugging tool to detect memory access errors. > It helps detect issues like use-after-free, various kinds of buffer > overruns in C/C++ programs, and other similar errors, as well as > printing out detailed debug information whenever an error is detected. This patch mixes how to use ASan and instrumenting the DPDK mem allocator. I would split this patch in two. The first patch can add the documentation on enabling/using ASan and describe the known issues on enabling it. I'd find it better (from a user pov) if we hide all those details about b_lundef and installation of libasan on Centos. Something like (only quickly tested): diff --git a/config/meson.build b/config/meson.build index 4cdf589e20..7d8b71da79 100644 --- a/config/meson.build +++ b/config/meson.build @@ -411,6 +411,33 @@ if get_option('b_lto') endif endif +if get_option('b_sanitize') == 'address' + asan_dep = cc.find_library('asan', required: true) + if (not cc.links('int main(int argc, char *argv[]) { return 0; }', + dependencies: asan_dep)) + error('broken dependency, "libasan"') + endif + add_project_link_arguments('-lasan', language: 'c') + dpdk_extra_ldflags += '-lasan' +endif + if get_option('default_library') == 'both' error( ''' Unsupported value "both" for "default_library" option. Bruce, do you see an issue with this approach? Then a second patch adds the rte_malloc instrumentation, with a check at configuration time. endif add_project_link_arguments('-lasan', language: 'c') dpdk_extra_ldflags += '-lasan' + if arch_subdir == 'x86' + asan_check_code = ''' +#ifdef __SANITIZE_ADDRESS__ +#define RTE_MALLOC_ASAN +#elif defined(__has_feature) +# if __has_feature(address_sanitizer) +#define RTE_MALLOC_ASAN +# endif +#endif + +#ifndef RTE_MALLOC_ASAN +#error ASan not available. +#endif +''' + if cc.compiles(asan_check_code) + dpdk_conf.set10('RTE_MALLOC_ASAN', true) + endif + endif endif if get_option('default_library') == 'both' Few more comments: > > DPDK ASan functionality is currently only supported Linux x86_64. > Support other platforms, need to define ASAN_SHADOW_OFFSET value > according to google ASan document. > > Here is an example of heap-buffer-overflow bug: > ...... > char *p = rte_zmalloc(NULL, 7, 0); > p[7] = 'a'; > ...... > > Here is an example of use-after-free bug: > ...... > char *p = rte_zmalloc(NULL, 7, 0); > rte_free(p); > *p = 'a'; > ...... > > If you want to use this feature, > you need to add below compilation options when compiling code: > -Dbuildtype=debug -Db_lundef=false -Db_sanitize=address > "-Dbuildtype=debug": This is a non-essential option. When this option > is added, if a memory error occurs, ASan can clearly show where the > code is wrong. > "-Db_lundef=false": When use clang to compile DPDK, this option must > be added. > > Signed-off-by: Xueqin Lin <xueqin.lin@intel.com> > Signed-off-by: Zhihong Peng <zhihongx.peng@intel.com> > --- > devtools/words-case.txt | 1 + > doc/guides/prog_guide/ASan.rst | 108 +++++++++++++++++ > doc/guides/prog_guide/index.rst | 1 + > examples/helloworld/main.c | 5 + > lib/eal/common/malloc_elem.c | 26 +++- > lib/eal/common/malloc_elem.h | 204 +++++++++++++++++++++++++++++++- > lib/eal/common/malloc_heap.c | 12 ++ > lib/eal/common/rte_malloc.c | 9 +- > 8 files changed, 361 insertions(+), 5 deletions(-) > create mode 100644 doc/guides/prog_guide/ASan.rst > > diff --git a/devtools/words-case.txt b/devtools/words-case.txt > index 0bbad48626..3655596d47 100644 > --- a/devtools/words-case.txt > +++ b/devtools/words-case.txt > @@ -86,3 +86,4 @@ VXLAN > Windows > XDP > XOR > +ASan Alphabetical order please. > diff --git a/doc/guides/prog_guide/ASan.rst b/doc/guides/prog_guide/ASan.rst Filenames are lowercase in the doc. > new file mode 100644 > index 0000000000..7145a3b1a1 > --- /dev/null > +++ b/doc/guides/prog_guide/ASan.rst > @@ -0,0 +1,108 @@ > +.. Copyright (c) <2021>, Intel Corporation > + All rights reserved. > + > +Memory error detect standard tool - AddressSanitizer(ASan) > +========================================================== > + > +AddressSanitizer (ASan) is a google memory error detect > +standard tool. It could help to detect use-after-free and > +{heap,stack,global}-buffer overflow bugs in C/C++ programs, > +print detailed error information when error happens, large > +improve debug efficiency. > + > +By referring to its implementation algorithm > +(https://github.com/google/sanitizers/wiki/AddressSanitizerAlgorithm), > +enabled heap-buffer-overflow and use-after-free functions on DPDK. > +DPDK ASan function currently only supports on Linux x86_64. > + > +AddressSanitizer is a part of LLVM(3.1+)and GCC(4.8+). missing spaces around (). > + > +Example heap-buffer-overflow error > +---------------------------------- > + > +Following error was reported when ASan was enabled:: > + > + Applied 9 bytes of memory, but accessed the 10th byte of memory, > + so heap-buffer-overflow appeared. > + > +Below code results in this error:: > + > + char *p = rte_zmalloc(NULL, 9, 0); > + if (!p) { > + printf("rte_zmalloc error."); > + return -1; > + } > + p[9] = 'a'; > + > +The error log:: > + > + ==49433==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x7f773fafa249 at pc 0x5556b13bdae4 bp 0x7ffeb4965e40 sp 0x7ffeb4965e30 WRITE of size 1 at 0x7f773fafa249 thread T0 > + #0 0x5556b13bdae3 in asan_heap_buffer_overflow ../app/test/test_asan_heap_buffer_overflow.c:25 Please update this example since the unit test has been removed. > + #1 0x5556b043e9d4 in cmd_autotest_parsed ../app/test/commands.c:71 > + #2 0x5556b1cdd4b0 in cmdline_parse ../lib/cmdline/cmdline_parse.c:290 > + #3 0x5556b1cd8987 in cmdline_valid_buffer ../lib/cmdline/cmdline.c:26 > + #4 0x5556b1ce477a in rdline_char_in ../lib/cmdline/cmdline_rdline.c:421 > + #5 0x5556b1cd923e in cmdline_in ../lib/cmdline/cmdline.c:149 > + #6 0x5556b1cd9769 in cmdline_interact ../lib/cmdline/cmdline.c:223 > + #7 0x5556b045f53b in main ../app/test/test.c:234 > + #8 0x7f7f1eba90b2 in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x270b2) > + #9 0x5556b043e70d in _start (/home/pzh/yyy/x86_64-native-linuxapp-gcc/app/test/dpdk-test+0x7ce70d) > + > + Address 0x7f773fafa249 is a wild pointer. > + SUMMARY: AddressSanitizer: heap-buffer-overflow ../app/test/test_asan_heap_buffer_overflow.c:25 in asan_heap_buffer_overflow > + > +Example use-after-free error > +---------------------------- > + > +Following error was reported when ASan was enabled:: > + > + Applied for 9 bytes of memory, and accessed the first byte after > + released, so heap-use-after-free appeared. > + > +Below code results in this error:: > + > + char *p = rte_zmalloc(NULL, 9, 0); > + if (!p) { > + printf("rte_zmalloc error."); > + return -1; > + } > + rte_free(p); > + *p = 'a'; > + > +The error log:: > + > + ==49478==ERROR: AddressSanitizer: heap-use-after-free on address 0x7fe2ffafa240 at pc 0x56409b084bc8 bp 0x7ffef62c57d0 sp 0x7ffef62c57c0 WRITE of size 1 at 0x7fe2ffafa240 thread T0 > + #0 0x56409b084bc7 in asan_use_after_free ../app/test/test_asan_use_after_free.c:26 Idem. > + #1 0x56409a1059d4 in cmd_autotest_parsed ../app/test/commands.c:71 > + #2 0x56409b9a44b0 in cmdline_parse ../lib/cmdline/cmdline_parse.c:290 > + #3 0x56409b99f987 in cmdline_valid_buffer ../lib/cmdline/cmdline.c:26 > + #4 0x56409b9ab77a in rdline_char_in ../lib/cmdline/cmdline_rdline.c:421 > + #5 0x56409b9a023e in cmdline_in ../lib/cmdline/cmdline.c:149 > + #6 0x56409b9a0769 in cmdline_interact ../lib/cmdline/cmdline.c:223 > + #7 0x56409a12653b in main ../app/test/test.c:234 > + #8 0x7feafafc20b2 in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x270b2) > + #9 0x56409a10570d in _start (/home/pzh/yyy/x86_64-native-linuxapp-gcc/app/test/dpdk-test+0x7ce70d) > + > + Address 0x7fe2ffafa240 is a wild pointer. > + SUMMARY: AddressSanitizer: heap-use-after-free ../app/test/test_asan_use_after_free.c:26 in asan_use_after_free -- David Marchand ^ permalink raw reply [flat|nested] 117+ messages in thread
* Re: [dpdk-dev] [PATCH v6 1/2] Enable ASan for memory detector on DPDK 2021-09-30 8:20 ` [dpdk-dev] [PATCH v6 1/2] Enable ASan for memory detector on DPDK David Marchand @ 2021-10-08 8:07 ` Peng, ZhihongX 2021-10-08 8:30 ` David Marchand 2021-10-12 7:17 ` Peng, ZhihongX 2021-10-13 7:59 ` Bruce Richardson 2 siblings, 1 reply; 117+ messages in thread From: Peng, ZhihongX @ 2021-10-08 8:07 UTC (permalink / raw) To: David Marchand, Richardson, Bruce Cc: Burakov, Anatoly, Ananyev, Konstantin, Stephen Hemminger, dev, Lin, Xueqin, Thomas Monjalon > -----Original Message----- > From: David Marchand <david.marchand@redhat.com> > Sent: Thursday, September 30, 2021 4:20 PM > To: Peng, ZhihongX <ZhihongX.Peng@intel.com>; Richardson, Bruce > <bruce.richardson@intel.com> > Cc: Burakov, Anatoly <anatoly.burakov@intel.com>; Ananyev, Konstantin > <konstantin.ananyev@intel.com>; Stephen Hemminger > <stephen@networkplumber.org>; dev <dev@dpdk.org>; Lin, Xueqin > <xueqin.lin@intel.com>; Thomas Monjalon <thomas@monjalon.net> > Subject: Re: [dpdk-dev] [PATCH v6 1/2] Enable ASan for memory detector on > DPDK > > Hello, > > I see v6 is superseded in pw, I have been cleaning my queue... maybe my > fault. > > > On Thu, Sep 30, 2021 at 7:37 AM <zhihongx.peng@intel.com> wrote: > > > > From: Zhihong Peng <zhihongx.peng@intel.com> > > > > AddressSanitizer (ASan) is a google memory error detect standard tool. > > It could help to detect use-after-free and {heap,stack,global}-buffer > > overflow bugs in C/C++ programs, print detailed error information when > > error happens, large improve debug efficiency. > > > > `AddressSanitizer > > <https://github.com/google/sanitizers/wiki/AddressSanitizer>` (ASan) > > is a widely-used debugging tool to detect memory access errors. > > It helps detect issues like use-after-free, various kinds of buffer > > overruns in C/C++ programs, and other similar errors, as well as > > printing out detailed debug information whenever an error is detected. > > This patch mixes how to use ASan and instrumenting the DPDK mem > allocator. > > I would split this patch in two. > > The first patch can add the documentation on enabling/using ASan and > describe the known issues on enabling it. > I'd find it better (from a user pov) if we hide all those details about b_lundef > and installation of libasan on Centos. > V7 version will be modified. > Something like (only quickly tested): > > diff --git a/config/meson.build b/config/meson.build index > 4cdf589e20..7d8b71da79 100644 > --- a/config/meson.build > +++ b/config/meson.build > @@ -411,6 +411,33 @@ if get_option('b_lto') > endif > endif > > +if get_option('b_sanitize') == 'address' > + asan_dep = cc.find_library('asan', required: true) > + if (not cc.links('int main(int argc, char *argv[]) { return 0; }', > + dependencies: asan_dep)) > + error('broken dependency, "libasan"') > + endif > + add_project_link_arguments('-lasan', language: 'c') > + dpdk_extra_ldflags += '-lasan' > +endif > + > if get_option('default_library') == 'both' > error( ''' > Unsupported value "both" for "default_library" option. > > > Bruce, do you see an issue with this approach? It seems clearer to get the ASan switch in the c code. > > Then a second patch adds the rte_malloc instrumentation, with a check at > configuration time. > > endif > add_project_link_arguments('-lasan', language: 'c') > dpdk_extra_ldflags += '-lasan' > + if arch_subdir == 'x86' > + asan_check_code = ''' > +#ifdef __SANITIZE_ADDRESS__ > +#define RTE_MALLOC_ASAN > +#elif defined(__has_feature) > +# if __has_feature(address_sanitizer) > +#define RTE_MALLOC_ASAN > +# endif > +#endif > + > +#ifndef RTE_MALLOC_ASAN > +#error ASan not available. > +#endif > +''' > + if cc.compiles(asan_check_code) > + dpdk_conf.set10('RTE_MALLOC_ASAN', true) > + endif > + endif > endif > > if get_option('default_library') == 'both' > > > Few more comments: > > > > > > DPDK ASan functionality is currently only supported Linux x86_64. > > Support other platforms, need to define ASAN_SHADOW_OFFSET value > > according to google ASan document. > > > > Here is an example of heap-buffer-overflow bug: > > ...... > > char *p = rte_zmalloc(NULL, 7, 0); > > p[7] = 'a'; > > ...... > > > > Here is an example of use-after-free bug: > > ...... > > char *p = rte_zmalloc(NULL, 7, 0); > > rte_free(p); > > *p = 'a'; > > ...... > > > > If you want to use this feature, > > you need to add below compilation options when compiling code: > > -Dbuildtype=debug -Db_lundef=false -Db_sanitize=address > > "-Dbuildtype=debug": This is a non-essential option. When this option > > is added, if a memory error occurs, ASan can clearly show where the > > code is wrong. > > "-Db_lundef=false": When use clang to compile DPDK, this option must > > be added. > > > > Signed-off-by: Xueqin Lin <xueqin.lin@intel.com> > > Signed-off-by: Zhihong Peng <zhihongx.peng@intel.com> > > --- > > devtools/words-case.txt | 1 + > > doc/guides/prog_guide/ASan.rst | 108 +++++++++++++++++ > > doc/guides/prog_guide/index.rst | 1 + > > examples/helloworld/main.c | 5 + > > lib/eal/common/malloc_elem.c | 26 +++- > > lib/eal/common/malloc_elem.h | 204 > +++++++++++++++++++++++++++++++- > > lib/eal/common/malloc_heap.c | 12 ++ > > lib/eal/common/rte_malloc.c | 9 +- > > 8 files changed, 361 insertions(+), 5 deletions(-) create mode > > 100644 doc/guides/prog_guide/ASan.rst > > > > diff --git a/devtools/words-case.txt b/devtools/words-case.txt index > > 0bbad48626..3655596d47 100644 > > --- a/devtools/words-case.txt > > +++ b/devtools/words-case.txt > > @@ -86,3 +86,4 @@ VXLAN > > Windows > > XDP > > XOR > > +ASan > > Alphabetical order please. > V7 version will be modified. > > diff --git a/doc/guides/prog_guide/ASan.rst > > b/doc/guides/prog_guide/ASan.rst > > Filenames are lowercase in the doc. V7 version will be modified. > > > new file mode 100644 > > index 0000000000..7145a3b1a1 > > --- /dev/null > > +++ b/doc/guides/prog_guide/ASan.rst > > @@ -0,0 +1,108 @@ > > +.. Copyright (c) <2021>, Intel Corporation > > + All rights reserved. > > + > > +Memory error detect standard tool - AddressSanitizer(ASan) > > > +========================================================= > = > > + > > +AddressSanitizer (ASan) is a google memory error detect standard > > +tool. It could help to detect use-after-free and > > +{heap,stack,global}-buffer overflow bugs in C/C++ programs, print > > +detailed error information when error happens, large improve debug > > +efficiency. > > + > > +By referring to its implementation algorithm > > +(https://github.com/google/sanitizers/wiki/AddressSanitizerAlgorithm) > > +, enabled heap-buffer-overflow and use-after-free functions on DPDK. > > +DPDK ASan function currently only supports on Linux x86_64. > > + > > +AddressSanitizer is a part of LLVM(3.1+)and GCC(4.8+). > > missing spaces around (). V7 version will be modified. > > > + > > +Example heap-buffer-overflow error > > +---------------------------------- > > + > > +Following error was reported when ASan was enabled:: > > + > > + Applied 9 bytes of memory, but accessed the 10th byte of memory, > > + so heap-buffer-overflow appeared. > > + > > +Below code results in this error:: > > + > > + char *p = rte_zmalloc(NULL, 9, 0); > > + if (!p) { > > + printf("rte_zmalloc error."); > > + return -1; > > + } > > + p[9] = 'a'; > > + > > +The error log:: > > + > > + ==49433==ERROR: AddressSanitizer: heap-buffer-overflow on address > 0x7f773fafa249 at pc 0x5556b13bdae4 bp 0x7ffeb4965e40 sp 0x7ffeb4965e30 > WRITE of size 1 at 0x7f773fafa249 thread T0 > > + #0 0x5556b13bdae3 in asan_heap_buffer_overflow > > + ../app/test/test_asan_heap_buffer_overflow.c:25 > > Please update this example since the unit test has been removed. V7 version will be modified. > > > + #1 0x5556b043e9d4 in > cmd_autotest_parsed ../app/test/commands.c:71 > > + #2 0x5556b1cdd4b0 in > cmdline_parse ../lib/cmdline/cmdline_parse.c:290 > > + #3 0x5556b1cd8987 in cmdline_valid_buffer ../lib/cmdline/cmdline.c:26 > > + #4 0x5556b1ce477a in rdline_char_in ../lib/cmdline/cmdline_rdline.c:421 > > + #5 0x5556b1cd923e in cmdline_in ../lib/cmdline/cmdline.c:149 > > + #6 0x5556b1cd9769 in cmdline_interact ../lib/cmdline/cmdline.c:223 > > + #7 0x5556b045f53b in main ../app/test/test.c:234 > > + #8 0x7f7f1eba90b2 in __libc_start_main (/lib/x86_64-linux- > gnu/libc.so.6+0x270b2) > > + #9 0x5556b043e70d in _start > > + (/home/pzh/yyy/x86_64-native-linuxapp-gcc/app/test/dpdk- > test+0x7ce70 > > + d) > > + > > + Address 0x7f773fafa249 is a wild pointer. > > + SUMMARY: AddressSanitizer: heap-buffer-overflow > > + ../app/test/test_asan_heap_buffer_overflow.c:25 in > > + asan_heap_buffer_overflow > > + > > +Example use-after-free error > > +---------------------------- > > + > > +Following error was reported when ASan was enabled:: > > + > > + Applied for 9 bytes of memory, and accessed the first byte after > > + released, so heap-use-after-free appeared. > > + > > +Below code results in this error:: > > + > > + char *p = rte_zmalloc(NULL, 9, 0); > > + if (!p) { > > + printf("rte_zmalloc error."); > > + return -1; > > + } > > + rte_free(p); > > + *p = 'a'; > > + > > +The error log:: > > + > > + ==49478==ERROR: AddressSanitizer: heap-use-after-free on address > 0x7fe2ffafa240 at pc 0x56409b084bc8 bp 0x7ffef62c57d0 sp 0x7ffef62c57c0 > WRITE of size 1 at 0x7fe2ffafa240 thread T0 > > + #0 0x56409b084bc7 in asan_use_after_free > > + ../app/test/test_asan_use_after_free.c:26 > > Idem. > > > > + #1 0x56409a1059d4 in > cmd_autotest_parsed ../app/test/commands.c:71 > > + #2 0x56409b9a44b0 in > cmdline_parse ../lib/cmdline/cmdline_parse.c:290 > > + #3 0x56409b99f987 in cmdline_valid_buffer ../lib/cmdline/cmdline.c:26 > > + #4 0x56409b9ab77a in > rdline_char_in ../lib/cmdline/cmdline_rdline.c:421 > > + #5 0x56409b9a023e in cmdline_in ../lib/cmdline/cmdline.c:149 > > + #6 0x56409b9a0769 in cmdline_interact ../lib/cmdline/cmdline.c:223 > > + #7 0x56409a12653b in main ../app/test/test.c:234 > > + #8 0x7feafafc20b2 in __libc_start_main (/lib/x86_64-linux- > gnu/libc.so.6+0x270b2) > > + #9 0x56409a10570d in _start > > + (/home/pzh/yyy/x86_64-native-linuxapp-gcc/app/test/dpdk- > test+0x7ce70 > > + d) > > + > > + Address 0x7fe2ffafa240 is a wild pointer. > > + SUMMARY: AddressSanitizer: heap-use-after-free > > + ../app/test/test_asan_use_after_free.c:26 in asan_use_after_free > > > -- > David Marchand ^ permalink raw reply [flat|nested] 117+ messages in thread
* Re: [dpdk-dev] [PATCH v6 1/2] Enable ASan for memory detector on DPDK 2021-10-08 8:07 ` Peng, ZhihongX @ 2021-10-08 8:30 ` David Marchand 2021-10-12 5:41 ` Peng, ZhihongX 0 siblings, 1 reply; 117+ messages in thread From: David Marchand @ 2021-10-08 8:30 UTC (permalink / raw) To: Peng, ZhihongX, Richardson, Bruce Cc: Burakov, Anatoly, Ananyev, Konstantin, Stephen Hemminger, dev, Lin, Xueqin, Thomas Monjalon On Fri, Oct 8, 2021 at 10:07 AM Peng, ZhihongX <zhihongx.peng@intel.com> wrote: > > > -----Original Message----- > > From: David Marchand <david.marchand@redhat.com> > > Sent: Thursday, September 30, 2021 4:20 PM > > To: Peng, ZhihongX <ZhihongX.Peng@intel.com>; Richardson, Bruce > > <bruce.richardson@intel.com> > > Cc: Burakov, Anatoly <anatoly.burakov@intel.com>; Ananyev, Konstantin > > <konstantin.ananyev@intel.com>; Stephen Hemminger > > <stephen@networkplumber.org>; dev <dev@dpdk.org>; Lin, Xueqin > > <xueqin.lin@intel.com>; Thomas Monjalon <thomas@monjalon.net> > > Subject: Re: [dpdk-dev] [PATCH v6 1/2] Enable ASan for memory detector on > > DPDK > > > > Hello, > > > > I see v6 is superseded in pw, I have been cleaning my queue... maybe my > > fault. > > [snip] > > > V7 version will be modified. Ok, let's forget about *v6 bis* and go with v7... > > > Something like (only quickly tested): > > > > diff --git a/config/meson.build b/config/meson.build index > > 4cdf589e20..7d8b71da79 100644 > > --- a/config/meson.build > > +++ b/config/meson.build > > @@ -411,6 +411,33 @@ if get_option('b_lto') > > endif > > endif > > > > +if get_option('b_sanitize') == 'address' > > + asan_dep = cc.find_library('asan', required: true) > > + if (not cc.links('int main(int argc, char *argv[]) { return 0; }', > > + dependencies: asan_dep)) > > + error('broken dependency, "libasan"') > > + endif > > + add_project_link_arguments('-lasan', language: 'c') > > + dpdk_extra_ldflags += '-lasan' > > +endif > > + > > if get_option('default_library') == 'both' > > error( ''' > > Unsupported value "both" for "default_library" option. > > > > > > Bruce, do you see an issue with this approach? > > It seems clearer to get the ASan switch in the c code. This is a feature the developper asked for at configuration time. We have other condition to fulfill to get ASan linking correctly (wrt lundef workaround and presence of libasan on Centos/RHEL 7). It's not a matter of being clearer (which I fail to see how it is), it's a matter of putting the check at the right place. -- David Marchand ^ permalink raw reply [flat|nested] 117+ messages in thread
* Re: [dpdk-dev] [PATCH v6 1/2] Enable ASan for memory detector on DPDK 2021-10-08 8:30 ` David Marchand @ 2021-10-12 5:41 ` Peng, ZhihongX 0 siblings, 0 replies; 117+ messages in thread From: Peng, ZhihongX @ 2021-10-12 5:41 UTC (permalink / raw) To: David Marchand, Richardson, Bruce Cc: Burakov, Anatoly, Ananyev, Konstantin, Stephen Hemminger, dev, Lin, Xueqin, Thomas Monjalon > -----Original Message----- > From: David Marchand <david.marchand@redhat.com> > Sent: Friday, October 8, 2021 4:30 PM > To: Peng, ZhihongX <zhihongx.peng@intel.com>; Richardson, Bruce > <bruce.richardson@intel.com> > Cc: Burakov, Anatoly <anatoly.burakov@intel.com>; Ananyev, Konstantin > <konstantin.ananyev@intel.com>; Stephen Hemminger > <stephen@networkplumber.org>; dev <dev@dpdk.org>; Lin, Xueqin > <xueqin.lin@intel.com>; Thomas Monjalon <thomas@monjalon.net> > Subject: Re: [dpdk-dev] [PATCH v6 1/2] Enable ASan for memory detector on > DPDK > > On Fri, Oct 8, 2021 at 10:07 AM Peng, ZhihongX <zhihongx.peng@intel.com> > wrote: > > > > > -----Original Message----- > > > From: David Marchand <david.marchand@redhat.com> > > > Sent: Thursday, September 30, 2021 4:20 PM > > > To: Peng, ZhihongX <ZhihongX.Peng@intel.com>; Richardson, Bruce > > > <bruce.richardson@intel.com> > > > Cc: Burakov, Anatoly <anatoly.burakov@intel.com>; Ananyev, > > > Konstantin <konstantin.ananyev@intel.com>; Stephen Hemminger > > > <stephen@networkplumber.org>; dev <dev@dpdk.org>; Lin, Xueqin > > > <xueqin.lin@intel.com>; Thomas Monjalon <thomas@monjalon.net> > > > Subject: Re: [dpdk-dev] [PATCH v6 1/2] Enable ASan for memory > > > detector on DPDK > > > > > > Hello, > > > > > > I see v6 is superseded in pw, I have been cleaning my queue... maybe > > > my fault. > > > > > [snip] > > > > > > V7 version will be modified. > > Ok, let's forget about *v6 bis* and go with v7... > > > > > > > Something like (only quickly tested): > > > > > > diff --git a/config/meson.build b/config/meson.build index > > > 4cdf589e20..7d8b71da79 100644 > > > --- a/config/meson.build > > > +++ b/config/meson.build > > > @@ -411,6 +411,33 @@ if get_option('b_lto') > > > endif > > > endif > > > > > > +if get_option('b_sanitize') == 'address' > > > + asan_dep = cc.find_library('asan', required: true) > > > + if (not cc.links('int main(int argc, char *argv[]) { return 0; }', > > > + dependencies: asan_dep)) > > > + error('broken dependency, "libasan"') > > > + endif > > > + add_project_link_arguments('-lasan', language: 'c') > > > + dpdk_extra_ldflags += '-lasan' > > > +endif > > > + > > > if get_option('default_library') == 'both' > > > error( ''' > > > Unsupported value "both" for "default_library" option. > > > > > > > > > Bruce, do you see an issue with this approach? > > > > It seems clearer to get the ASan switch in the c code. > > This is a feature the developper asked for at configuration time. > We have other condition to fulfill to get ASan linking correctly (wrt lundef > workaround and presence of libasan on Centos/RHEL 7). > > It's not a matter of being clearer (which I fail to see how it is), it's a matter of > putting the check at the right place. The v9 version will be fixed. > > -- > David Marchand ^ permalink raw reply [flat|nested] 117+ messages in thread
* Re: [dpdk-dev] [PATCH v6 1/2] Enable ASan for memory detector on DPDK 2021-09-30 8:20 ` [dpdk-dev] [PATCH v6 1/2] Enable ASan for memory detector on DPDK David Marchand 2021-10-08 8:07 ` Peng, ZhihongX @ 2021-10-12 7:17 ` Peng, ZhihongX 2021-10-13 7:59 ` Bruce Richardson 2 siblings, 0 replies; 117+ messages in thread From: Peng, ZhihongX @ 2021-10-12 7:17 UTC (permalink / raw) To: David Marchand, Richardson, Bruce Cc: Burakov, Anatoly, Ananyev, Konstantin, Stephen Hemminger, dev, Lin, Xueqin, Thomas Monjalon > -----Original Message----- > From: David Marchand <david.marchand@redhat.com> > Sent: Thursday, September 30, 2021 4:20 PM > To: Peng, ZhihongX <ZhihongX.Peng@intel.com>; Richardson, Bruce > <bruce.richardson@intel.com> > Cc: Burakov, Anatoly <anatoly.burakov@intel.com>; Ananyev, Konstantin > <konstantin.ananyev@intel.com>; Stephen Hemminger > <stephen@networkplumber.org>; dev <dev@dpdk.org>; Lin, Xueqin > <xueqin.lin@intel.com>; Thomas Monjalon <thomas@monjalon.net> > Subject: Re: [dpdk-dev] [PATCH v6 1/2] Enable ASan for memory detector on > DPDK > > Hello, > > I see v6 is superseded in pw, I have been cleaning my queue... maybe my > fault. > > > On Thu, Sep 30, 2021 at 7:37 AM <zhihongx.peng@intel.com> wrote: > > > > From: Zhihong Peng <zhihongx.peng@intel.com> > > > > AddressSanitizer (ASan) is a google memory error detect standard tool. > > It could help to detect use-after-free and {heap,stack,global}-buffer > > overflow bugs in C/C++ programs, print detailed error information when > > error happens, large improve debug efficiency. > > > > `AddressSanitizer > > <https://github.com/google/sanitizers/wiki/AddressSanitizer>` (ASan) > > is a widely-used debugging tool to detect memory access errors. > > It helps detect issues like use-after-free, various kinds of buffer > > overruns in C/C++ programs, and other similar errors, as well as > > printing out detailed debug information whenever an error is detected. > > This patch mixes how to use ASan and instrumenting the DPDK mem > allocator. > > I would split this patch in two. > > The first patch can add the documentation on enabling/using ASan and > describe the known issues on enabling it. > I'd find it better (from a user pov) if we hide all those details about b_lundef > and installation of libasan on Centos. > > Something like (only quickly tested): > > diff --git a/config/meson.build b/config/meson.build index > 4cdf589e20..7d8b71da79 100644 > --- a/config/meson.build > +++ b/config/meson.build > @@ -411,6 +411,33 @@ if get_option('b_lto') > endif > endif > > +if get_option('b_sanitize') == 'address' > + asan_dep = cc.find_library('asan', required: true) > + if (not cc.links('int main(int argc, char *argv[]) { return 0; }', > + dependencies: asan_dep)) > + error('broken dependency, "libasan"') > + endif > + add_project_link_arguments('-lasan', language: 'c') > + dpdk_extra_ldflags += '-lasan' No need to add code: add_project_link_arguments('-lasan', language: 'c') dpdk_extra_ldflags += '-lasan' The app compiled by clang will fail to run > +endif > + > if get_option('default_library') == 'both' > error( ''' > Unsupported value "both" for "default_library" option. > > > Bruce, do you see an issue with this approach? > > > Then a second patch adds the rte_malloc instrumentation, with a check at > configuration time. > > endif > add_project_link_arguments('-lasan', language: 'c') > dpdk_extra_ldflags += '-lasan' > + if arch_subdir == 'x86' > + asan_check_code = ''' > +#ifdef __SANITIZE_ADDRESS__ > +#define RTE_MALLOC_ASAN > +#elif defined(__has_feature) > +# if __has_feature(address_sanitizer) > +#define RTE_MALLOC_ASAN > +# endif > +#endif > + > +#ifndef RTE_MALLOC_ASAN > +#error ASan not available. > +#endif > +''' > + if cc.compiles(asan_check_code) > + dpdk_conf.set10('RTE_MALLOC_ASAN', true) dpdk_conf.set10('RTE_MALLOC_ASAN', true) is not executed > + endif > + endif > endif Set the macro directly: dpdk_conf.set10('RTE_MALLOC_ASAN', true) All code: if get_option('b_sanitize') == 'address' asan_dep = cc.find_library('asan', required: true) if (not cc.links('int main(int argc, char *argv[]) { return 0; }', dependencies: asan_dep)) error('broken dependency, "libasan"') endif if arch_subdir == 'x86' dpdk_conf.set10('RTE_MALLOC_ASAN', true) endif endif > if get_option('default_library') == 'both' > > > Few more comments: > > > > > > DPDK ASan functionality is currently only supported Linux x86_64. > > Support other platforms, need to define ASAN_SHADOW_OFFSET value > > according to google ASan document. > > > > Here is an example of heap-buffer-overflow bug: > > ...... > > char *p = rte_zmalloc(NULL, 7, 0); > > p[7] = 'a'; > > ...... > > > > Here is an example of use-after-free bug: > > ...... > > char *p = rte_zmalloc(NULL, 7, 0); > > rte_free(p); > > *p = 'a'; > > ...... > > > > If you want to use this feature, > > you need to add below compilation options when compiling code: > > -Dbuildtype=debug -Db_lundef=false -Db_sanitize=address > > "-Dbuildtype=debug": This is a non-essential option. When this option > > is added, if a memory error occurs, ASan can clearly show where the > > code is wrong. > > "-Db_lundef=false": When use clang to compile DPDK, this option must > > be added. > > > > Signed-off-by: Xueqin Lin <xueqin.lin@intel.com> > > Signed-off-by: Zhihong Peng <zhihongx.peng@intel.com> > > --- > > devtools/words-case.txt | 1 + > > doc/guides/prog_guide/ASan.rst | 108 +++++++++++++++++ > > doc/guides/prog_guide/index.rst | 1 + > > examples/helloworld/main.c | 5 + > > lib/eal/common/malloc_elem.c | 26 +++- > > lib/eal/common/malloc_elem.h | 204 > +++++++++++++++++++++++++++++++- > > lib/eal/common/malloc_heap.c | 12 ++ > > lib/eal/common/rte_malloc.c | 9 +- > > 8 files changed, 361 insertions(+), 5 deletions(-) create mode > > 100644 doc/guides/prog_guide/ASan.rst > > > > diff --git a/devtools/words-case.txt b/devtools/words-case.txt index > > 0bbad48626..3655596d47 100644 > > --- a/devtools/words-case.txt > > +++ b/devtools/words-case.txt > > @@ -86,3 +86,4 @@ VXLAN > > Windows > > XDP > > XOR > > +ASan > > Alphabetical order please. > > > > diff --git a/doc/guides/prog_guide/ASan.rst > > b/doc/guides/prog_guide/ASan.rst > > Filenames are lowercase in the doc. > > > > new file mode 100644 > > index 0000000000..7145a3b1a1 > > --- /dev/null > > +++ b/doc/guides/prog_guide/ASan.rst > > @@ -0,0 +1,108 @@ > > +.. Copyright (c) <2021>, Intel Corporation > > + All rights reserved. > > + > > +Memory error detect standard tool - AddressSanitizer(ASan) > > > +========================================================= > = > > + > > +AddressSanitizer (ASan) is a google memory error detect standard > > +tool. It could help to detect use-after-free and > > +{heap,stack,global}-buffer overflow bugs in C/C++ programs, print > > +detailed error information when error happens, large improve debug > > +efficiency. > > + > > +By referring to its implementation algorithm > > +(https://github.com/google/sanitizers/wiki/AddressSanitizerAlgorithm) > > +, enabled heap-buffer-overflow and use-after-free functions on DPDK. > > +DPDK ASan function currently only supports on Linux x86_64. > > + > > +AddressSanitizer is a part of LLVM(3.1+)and GCC(4.8+). > > missing spaces around (). > > > > + > > +Example heap-buffer-overflow error > > +---------------------------------- > > + > > +Following error was reported when ASan was enabled:: > > + > > + Applied 9 bytes of memory, but accessed the 10th byte of memory, > > + so heap-buffer-overflow appeared. > > + > > +Below code results in this error:: > > + > > + char *p = rte_zmalloc(NULL, 9, 0); > > + if (!p) { > > + printf("rte_zmalloc error."); > > + return -1; > > + } > > + p[9] = 'a'; > > + > > +The error log:: > > + > > + ==49433==ERROR: AddressSanitizer: heap-buffer-overflow on address > 0x7f773fafa249 at pc 0x5556b13bdae4 bp 0x7ffeb4965e40 sp 0x7ffeb4965e30 > WRITE of size 1 at 0x7f773fafa249 thread T0 > > + #0 0x5556b13bdae3 in asan_heap_buffer_overflow > > + ../app/test/test_asan_heap_buffer_overflow.c:25 > > Please update this example since the unit test has been removed. > > > > + #1 0x5556b043e9d4 in > cmd_autotest_parsed ../app/test/commands.c:71 > > + #2 0x5556b1cdd4b0 in > cmdline_parse ../lib/cmdline/cmdline_parse.c:290 > > + #3 0x5556b1cd8987 in cmdline_valid_buffer ../lib/cmdline/cmdline.c:26 > > + #4 0x5556b1ce477a in rdline_char_in ../lib/cmdline/cmdline_rdline.c:421 > > + #5 0x5556b1cd923e in cmdline_in ../lib/cmdline/cmdline.c:149 > > + #6 0x5556b1cd9769 in cmdline_interact ../lib/cmdline/cmdline.c:223 > > + #7 0x5556b045f53b in main ../app/test/test.c:234 > > + #8 0x7f7f1eba90b2 in __libc_start_main (/lib/x86_64-linux- > gnu/libc.so.6+0x270b2) > > + #9 0x5556b043e70d in _start > > + (/home/pzh/yyy/x86_64-native-linuxapp-gcc/app/test/dpdk- > test+0x7ce70 > > + d) > > + > > + Address 0x7f773fafa249 is a wild pointer. > > + SUMMARY: AddressSanitizer: heap-buffer-overflow > > + ../app/test/test_asan_heap_buffer_overflow.c:25 in > > + asan_heap_buffer_overflow > > + > > +Example use-after-free error > > +---------------------------- > > + > > +Following error was reported when ASan was enabled:: > > + > > + Applied for 9 bytes of memory, and accessed the first byte after > > + released, so heap-use-after-free appeared. > > + > > +Below code results in this error:: > > + > > + char *p = rte_zmalloc(NULL, 9, 0); > > + if (!p) { > > + printf("rte_zmalloc error."); > > + return -1; > > + } > > + rte_free(p); > > + *p = 'a'; > > + > > +The error log:: > > + > > + ==49478==ERROR: AddressSanitizer: heap-use-after-free on address > 0x7fe2ffafa240 at pc 0x56409b084bc8 bp 0x7ffef62c57d0 sp 0x7ffef62c57c0 > WRITE of size 1 at 0x7fe2ffafa240 thread T0 > > + #0 0x56409b084bc7 in asan_use_after_free > > + ../app/test/test_asan_use_after_free.c:26 > > Idem. > > > > + #1 0x56409a1059d4 in > cmd_autotest_parsed ../app/test/commands.c:71 > > + #2 0x56409b9a44b0 in > cmdline_parse ../lib/cmdline/cmdline_parse.c:290 > > + #3 0x56409b99f987 in cmdline_valid_buffer ../lib/cmdline/cmdline.c:26 > > + #4 0x56409b9ab77a in > rdline_char_in ../lib/cmdline/cmdline_rdline.c:421 > > + #5 0x56409b9a023e in cmdline_in ../lib/cmdline/cmdline.c:149 > > + #6 0x56409b9a0769 in cmdline_interact ../lib/cmdline/cmdline.c:223 > > + #7 0x56409a12653b in main ../app/test/test.c:234 > > + #8 0x7feafafc20b2 in __libc_start_main (/lib/x86_64-linux- > gnu/libc.so.6+0x270b2) > > + #9 0x56409a10570d in _start > > + (/home/pzh/yyy/x86_64-native-linuxapp-gcc/app/test/dpdk- > test+0x7ce70 > > + d) > > + > > + Address 0x7fe2ffafa240 is a wild pointer. > > + SUMMARY: AddressSanitizer: heap-use-after-free > > + ../app/test/test_asan_use_after_free.c:26 in asan_use_after_free > > > -- > David Marchand ^ permalink raw reply [flat|nested] 117+ messages in thread
* Re: [dpdk-dev] [PATCH v6 1/2] Enable ASan for memory detector on DPDK 2021-09-30 8:20 ` [dpdk-dev] [PATCH v6 1/2] Enable ASan for memory detector on DPDK David Marchand 2021-10-08 8:07 ` Peng, ZhihongX 2021-10-12 7:17 ` Peng, ZhihongX @ 2021-10-13 7:59 ` Bruce Richardson 2021-10-14 6:33 ` Peng, ZhihongX 2 siblings, 1 reply; 117+ messages in thread From: Bruce Richardson @ 2021-10-13 7:59 UTC (permalink / raw) To: David Marchand Cc: Zhihong Peng, Burakov, Anatoly, Ananyev, Konstantin, Stephen Hemminger, dev, Xueqin Lin, Thomas Monjalon On Thu, Sep 30, 2021 at 10:20:00AM +0200, David Marchand wrote: > Hello, > > I see v6 is superseded in pw, I have been cleaning my queue... maybe my fault. > > > On Thu, Sep 30, 2021 at 7:37 AM <zhihongx.peng@intel.com> wrote: > > > > From: Zhihong Peng <zhihongx.peng@intel.com> > > > > AddressSanitizer (ASan) is a google memory error detect > > standard tool. It could help to detect use-after-free and > > {heap,stack,global}-buffer overflow bugs in C/C++ programs, > > print detailed error information when error happens, large > > improve debug efficiency. > > > > `AddressSanitizer > > <https://github.com/google/sanitizers/wiki/AddressSanitizer>` (ASan) > > is a widely-used debugging tool to detect memory access errors. > > It helps detect issues like use-after-free, various kinds of buffer > > overruns in C/C++ programs, and other similar errors, as well as > > printing out detailed debug information whenever an error is detected. > > This patch mixes how to use ASan and instrumenting the DPDK mem allocator. > > I would split this patch in two. > > The first patch can add the documentation on enabling/using ASan and > describe the known issues on enabling it. > I'd find it better (from a user pov) if we hide all those details > about b_lundef and installation of libasan on Centos. > > Something like (only quickly tested): > > diff --git a/config/meson.build b/config/meson.build > index 4cdf589e20..7d8b71da79 100644 > --- a/config/meson.build > +++ b/config/meson.build > @@ -411,6 +411,33 @@ if get_option('b_lto') > endif > endif > > +if get_option('b_sanitize') == 'address' > + asan_dep = cc.find_library('asan', required: true) > + if (not cc.links('int main(int argc, char *argv[]) { return 0; }', > + dependencies: asan_dep)) > + error('broken dependency, "libasan"') > + endif > + add_project_link_arguments('-lasan', language: 'c') > + dpdk_extra_ldflags += '-lasan' > +endif > + > if get_option('default_library') == 'both' > error( ''' > Unsupported value "both" for "default_library" option. > > > Bruce, do you see an issue with this approach? > Apologies for delayed reply on this. No issue with this approach on my end, seems reasonable. Just watch out that b_sanitize can have "address,undefined" as a possible value, so if we want to support that, we can't just check directly for the literal string "address" > > Then a second patch adds the rte_malloc instrumentation, with a check > at configuration time. > > endif > add_project_link_arguments('-lasan', language: 'c') > dpdk_extra_ldflags += '-lasan' > + if arch_subdir == 'x86' > + asan_check_code = ''' > +#ifdef __SANITIZE_ADDRESS__ > +#define RTE_MALLOC_ASAN > +#elif defined(__has_feature) > +# if __has_feature(address_sanitizer) > +#define RTE_MALLOC_ASAN > +# endif > +#endif > + > +#ifndef RTE_MALLOC_ASAN > +#error ASan not available. > +#endif > +''' > + if cc.compiles(asan_check_code) > + dpdk_conf.set10('RTE_MALLOC_ASAN', true) > + endif > + endif > endif > Apologies, but I haven't been tracking this set in much detail. Do we really need this second configuration check? Should it, or could it, be merged into the check above for the asan library presence? /Bruce ^ permalink raw reply [flat|nested] 117+ messages in thread
* Re: [dpdk-dev] [PATCH v6 1/2] Enable ASan for memory detector on DPDK 2021-10-13 7:59 ` Bruce Richardson @ 2021-10-14 6:33 ` Peng, ZhihongX 2021-10-14 6:53 ` Peng, ZhihongX 0 siblings, 1 reply; 117+ messages in thread From: Peng, ZhihongX @ 2021-10-14 6:33 UTC (permalink / raw) To: Richardson, Bruce, David Marchand Cc: Burakov, Anatoly, Ananyev, Konstantin, Stephen Hemminger, dev, Lin, Xueqin, Thomas Monjalon > -----Original Message----- > From: Richardson, Bruce <bruce.richardson@intel.com> > Sent: Wednesday, October 13, 2021 4:00 PM > To: David Marchand <david.marchand@redhat.com> > Cc: Peng, ZhihongX <zhihongx.peng@intel.com>; Burakov, Anatoly > <anatoly.burakov@intel.com>; Ananyev, Konstantin > <konstantin.ananyev@intel.com>; Stephen Hemminger > <stephen@networkplumber.org>; dev <dev@dpdk.org>; Lin, Xueqin > <xueqin.lin@intel.com>; Thomas Monjalon <thomas@monjalon.net> > Subject: Re: [dpdk-dev] [PATCH v6 1/2] Enable ASan for memory detector on > DPDK > > On Thu, Sep 30, 2021 at 10:20:00AM +0200, David Marchand wrote: > > Hello, > > > > I see v6 is superseded in pw, I have been cleaning my queue... maybe my > fault. > > > > > > On Thu, Sep 30, 2021 at 7:37 AM <zhihongx.peng@intel.com> wrote: > > > > > > From: Zhihong Peng <zhihongx.peng@intel.com> > > > > > > AddressSanitizer (ASan) is a google memory error detect standard > > > tool. It could help to detect use-after-free and > > > {heap,stack,global}-buffer overflow bugs in C/C++ programs, print > > > detailed error information when error happens, large improve debug > > > efficiency. > > > > > > `AddressSanitizer > > > <https://github.com/google/sanitizers/wiki/AddressSanitizer>` (ASan) > > > is a widely-used debugging tool to detect memory access errors. > > > It helps detect issues like use-after-free, various kinds of buffer > > > overruns in C/C++ programs, and other similar errors, as well as > > > printing out detailed debug information whenever an error is detected. > > > > This patch mixes how to use ASan and instrumenting the DPDK mem > allocator. > > > > I would split this patch in two. > > > > The first patch can add the documentation on enabling/using ASan and > > describe the known issues on enabling it. > > I'd find it better (from a user pov) if we hide all those details > > about b_lundef and installation of libasan on Centos. > > > > Something like (only quickly tested): > > > > diff --git a/config/meson.build b/config/meson.build index > > 4cdf589e20..7d8b71da79 100644 > > --- a/config/meson.build > > +++ b/config/meson.build > > @@ -411,6 +411,33 @@ if get_option('b_lto') > > endif > > endif > > > > +if get_option('b_sanitize') == 'address' > > + asan_dep = cc.find_library('asan', required: true) > > + if (not cc.links('int main(int argc, char *argv[]) { return 0; }', > > + dependencies: asan_dep)) > > + error('broken dependency, "libasan"') > > + endif > > + add_project_link_arguments('-lasan', language: 'c') > > + dpdk_extra_ldflags += '-lasan' > > +endif > > + > > if get_option('default_library') == 'both' > > error( ''' > > Unsupported value "both" for "default_library" option. > > > > > > Bruce, do you see an issue with this approach? > > > > Apologies for delayed reply on this. > > No issue with this approach on my end, seems reasonable. Just watch out > that b_sanitize can have "address,undefined" as a possible value, so if we > want to support that, we can't just check directly for the literal string > "address" > > > > > Then a second patch adds the rte_malloc instrumentation, with a check > > at configuration time. > > > > endif > > add_project_link_arguments('-lasan', language: 'c') > > dpdk_extra_ldflags += '-lasan' > > + if arch_subdir == 'x86' > > + asan_check_code = ''' > > +#ifdef __SANITIZE_ADDRESS__ > > +#define RTE_MALLOC_ASAN > > +#elif defined(__has_feature) > > +# if __has_feature(address_sanitizer) #define RTE_MALLOC_ASAN # > endif > > +#endif > > + > > +#ifndef RTE_MALLOC_ASAN > > +#error ASan not available. > > +#endif > > +''' > > + if cc.compiles(asan_check_code) > > + dpdk_conf.set10('RTE_MALLOC_ASAN', true) > > + endif > > + endif > > endif > > > > Apologies, but I haven't been tracking this set in much detail. > Do we really need this second configuration check? Should it, or could it, be > merged into the check above for the asan library presence? > All code: if get_option('b_sanitize') == 'address' or get_option('b_sanitize') == 'undefined' if cc.get_id() == 'gcc' asan_dep = cc.find_library('asan', required: true) if (not cc.links('int main(int argc, char *argv[]) { return 0; }', dependencies: asan_dep)) error('broken dependency, "libasan"') endif endif if exec_env == 'linux' and arch_subdir == 'x86' dpdk_conf.set10('RTE_MALLOC_ASAN', true) endif endif Bruce, is this code correct? Thanks! > /Bruce ^ permalink raw reply [flat|nested] 117+ messages in thread
* Re: [dpdk-dev] [PATCH v6 1/2] Enable ASan for memory detector on DPDK 2021-10-14 6:33 ` Peng, ZhihongX @ 2021-10-14 6:53 ` Peng, ZhihongX 0 siblings, 0 replies; 117+ messages in thread From: Peng, ZhihongX @ 2021-10-14 6:53 UTC (permalink / raw) To: Peng, ZhihongX, Richardson, Bruce, David Marchand Cc: Burakov, Anatoly, Ananyev, Konstantin, Stephen Hemminger, dev, Lin, Xueqin, Thomas Monjalon > -----Original Message----- > From: dev <dev-bounces@dpdk.org> On Behalf Of Peng, ZhihongX > Sent: Thursday, October 14, 2021 2:34 PM > To: Richardson, Bruce <bruce.richardson@intel.com>; David Marchand > <david.marchand@redhat.com> > Cc: Burakov, Anatoly <anatoly.burakov@intel.com>; Ananyev, Konstantin > <konstantin.ananyev@intel.com>; Stephen Hemminger > <stephen@networkplumber.org>; dev <dev@dpdk.org>; Lin, Xueqin > <xueqin.lin@intel.com>; Thomas Monjalon <thomas@monjalon.net> > Subject: Re: [dpdk-dev] [PATCH v6 1/2] Enable ASan for memory detector on > DPDK > > > -----Original Message----- > > From: Richardson, Bruce <bruce.richardson@intel.com> > > Sent: Wednesday, October 13, 2021 4:00 PM > > To: David Marchand <david.marchand@redhat.com> > > Cc: Peng, ZhihongX <zhihongx.peng@intel.com>; Burakov, Anatoly > > <anatoly.burakov@intel.com>; Ananyev, Konstantin > > <konstantin.ananyev@intel.com>; Stephen Hemminger > > <stephen@networkplumber.org>; dev <dev@dpdk.org>; Lin, Xueqin > > <xueqin.lin@intel.com>; Thomas Monjalon <thomas@monjalon.net> > > Subject: Re: [dpdk-dev] [PATCH v6 1/2] Enable ASan for memory detector > > on DPDK > > > > On Thu, Sep 30, 2021 at 10:20:00AM +0200, David Marchand wrote: > > > Hello, > > > > > > I see v6 is superseded in pw, I have been cleaning my queue... maybe > > > my > > fault. > > > > > > > > > On Thu, Sep 30, 2021 at 7:37 AM <zhihongx.peng@intel.com> wrote: > > > > > > > > From: Zhihong Peng <zhihongx.peng@intel.com> > > > > > > > > AddressSanitizer (ASan) is a google memory error detect standard > > > > tool. It could help to detect use-after-free and > > > > {heap,stack,global}-buffer overflow bugs in C/C++ programs, print > > > > detailed error information when error happens, large improve debug > > > > efficiency. > > > > > > > > `AddressSanitizer > > > > <https://github.com/google/sanitizers/wiki/AddressSanitizer>` > > > > (ASan) is a widely-used debugging tool to detect memory access errors. > > > > It helps detect issues like use-after-free, various kinds of > > > > buffer overruns in C/C++ programs, and other similar errors, as > > > > well as printing out detailed debug information whenever an error is > detected. > > > > > > This patch mixes how to use ASan and instrumenting the DPDK mem > > allocator. > > > > > > I would split this patch in two. > > > > > > The first patch can add the documentation on enabling/using ASan and > > > describe the known issues on enabling it. > > > I'd find it better (from a user pov) if we hide all those details > > > about b_lundef and installation of libasan on Centos. > > > > > > Something like (only quickly tested): > > > > > > diff --git a/config/meson.build b/config/meson.build index > > > 4cdf589e20..7d8b71da79 100644 > > > --- a/config/meson.build > > > +++ b/config/meson.build > > > @@ -411,6 +411,33 @@ if get_option('b_lto') > > > endif > > > endif > > > > > > +if get_option('b_sanitize') == 'address' > > > + asan_dep = cc.find_library('asan', required: true) > > > + if (not cc.links('int main(int argc, char *argv[]) { return 0; }', > > > + dependencies: asan_dep)) > > > + error('broken dependency, "libasan"') > > > + endif > > > + add_project_link_arguments('-lasan', language: 'c') > > > + dpdk_extra_ldflags += '-lasan' > > > +endif > > > + > > > if get_option('default_library') == 'both' > > > error( ''' > > > Unsupported value "both" for "default_library" option. > > > > > > > > > Bruce, do you see an issue with this approach? > > > > > > > Apologies for delayed reply on this. > > > > No issue with this approach on my end, seems reasonable. Just watch > > out that b_sanitize can have "address,undefined" as a possible value, > > so if we want to support that, we can't just check directly for the > > literal string "address" > > > > > > > > Then a second patch adds the rte_malloc instrumentation, with a > > > check at configuration time. > > > > > > endif > > > add_project_link_arguments('-lasan', language: 'c') > > > dpdk_extra_ldflags += '-lasan' > > > + if arch_subdir == 'x86' > > > + asan_check_code = ''' > > > +#ifdef __SANITIZE_ADDRESS__ > > > +#define RTE_MALLOC_ASAN > > > +#elif defined(__has_feature) > > > +# if __has_feature(address_sanitizer) #define RTE_MALLOC_ASAN # > > endif > > > +#endif > > > + > > > +#ifndef RTE_MALLOC_ASAN > > > +#error ASan not available. > > > +#endif > > > +''' > > > + if cc.compiles(asan_check_code) > > > + dpdk_conf.set10('RTE_MALLOC_ASAN', true) > > > + endif > > > + endif > > > endif > > > > > > > Apologies, but I haven't been tracking this set in much detail. > > Do we really need this second configuration check? Should it, or could > > it, be merged into the check above for the asan library presence? > > > > All code: > if get_option('b_sanitize') == 'address' or get_option('b_sanitize') == > 'undefined' > if cc.get_id() == 'gcc' > asan_dep = cc.find_library('asan', required: true) > if (not cc.links('int main(int argc, char *argv[]) { return 0; }', > dependencies: asan_dep)) > error('broken dependency, "libasan"') > endif > endif > > if exec_env == 'linux' and arch_subdir == 'x86' > dpdk_conf.set10('RTE_MALLOC_ASAN', true) > endif > endif > Modify: if get_option('b_sanitize') == 'address' or get_option('b_sanitize') == 'undefined' if cc.get_id() == 'gcc' asan_dep = cc.find_library('asan', required: true) if (not cc.links('int main(int argc, char *argv[]) { return 0; }', dependencies: asan_dep)) error('broken dependency, "libasan"') endif endif if get_option('b_sanitize') == 'address' if exec_env == 'linux' and arch_subdir == 'x86' dpdk_conf.set10('RTE_MALLOC_ASAN', true) endif endif endif > Bruce, is this code correct? > Thanks! > > /Bruce ^ permalink raw reply [flat|nested] 117+ messages in thread
* [dpdk-dev] [PATCH v6 1/2] Enable ASan for memory detector on DPDK 2021-09-24 10:03 ` [dpdk-dev] [PATCH v5 1/2] Enable ASan for memory detector on DPDK zhihongx.peng 2021-09-24 10:03 ` [dpdk-dev] [PATCH v5 2/2] lib/pipeline: Fix gcc compilation error using ASan zhihongx.peng 2021-09-30 5:27 ` [dpdk-dev] [PATCH v6 1/2] Enable ASan for memory detector on DPDK zhihongx.peng @ 2021-09-30 12:59 ` zhihongx.peng 2021-09-30 12:59 ` [dpdk-dev] [PATCH v6 2/2] lib/pipeline: Fix compilation error with gcc ASan zhihongx.peng ` (3 more replies) 2 siblings, 4 replies; 117+ messages in thread From: zhihongx.peng @ 2021-09-30 12:59 UTC (permalink / raw) To: anatoly.burakov, konstantin.ananyev, stephen Cc: dev, xueqin.lin, Zhihong Peng From: Zhihong Peng <zhihongx.peng@intel.com> AddressSanitizer (ASan) is a google memory error detect standard tool. It could help to detect use-after-free and {heap,stack,global}-buffer overflow bugs in C/C++ programs, print detailed error information when error happens, large improve debug efficiency. `AddressSanitizer <https://github.com/google/sanitizers/wiki/AddressSanitizer>` (ASan) is a widely-used debugging tool to detect memory access errors. It helps detect issues like use-after-free, various kinds of buffer overruns in C/C++ programs, and other similar errors, as well as printing out detailed debug information whenever an error is detected. DPDK ASan functionality is currently only supported Linux x86_64. Support other platforms, need to define ASAN_SHADOW_OFFSET value according to google ASan document. Here is an example of heap-buffer-overflow bug: ...... char *p = rte_zmalloc(NULL, 7, 0); p[7] = 'a'; ...... Here is an example of use-after-free bug: ...... char *p = rte_zmalloc(NULL, 7, 0); rte_free(p); *p = 'a'; ...... If you want to use this feature, you need to add below compilation options when compiling code: -Dbuildtype=debug -Db_lundef=false -Db_sanitize=address "-Dbuildtype=debug": This is a non-essential option. When this option is added, if a memory error occurs, ASan can clearly show where the code is wrong. "-Db_lundef=false": When use clang to compile DPDK, this option must be added. Signed-off-by: Xueqin Lin <xueqin.lin@intel.com> Signed-off-by: Zhihong Peng <zhihongx.peng@intel.com> --- devtools/words-case.txt | 1 + doc/guides/prog_guide/ASan.rst | 112 ++++++++++++++++++ doc/guides/prog_guide/index.rst | 1 + lib/eal/common/malloc_elem.c | 26 +++- lib/eal/common/malloc_elem.h | 204 +++++++++++++++++++++++++++++++- lib/eal/common/malloc_heap.c | 12 ++ lib/eal/common/rte_malloc.c | 9 +- 7 files changed, 360 insertions(+), 5 deletions(-) create mode 100644 doc/guides/prog_guide/ASan.rst diff --git a/devtools/words-case.txt b/devtools/words-case.txt index 0bbad48626..3655596d47 100644 --- a/devtools/words-case.txt +++ b/devtools/words-case.txt @@ -86,3 +86,4 @@ VXLAN Windows XDP XOR +ASan diff --git a/doc/guides/prog_guide/ASan.rst b/doc/guides/prog_guide/ASan.rst new file mode 100644 index 0000000000..1fd495150b --- /dev/null +++ b/doc/guides/prog_guide/ASan.rst @@ -0,0 +1,112 @@ +.. Copyright (c) <2021>, Intel Corporation + All rights reserved. + +Memory error detect standard tool - AddressSanitizer(ASan) +========================================================== + +AddressSanitizer (ASan) is a google memory error detect +standard tool. It could help to detect use-after-free and +{heap,stack,global}-buffer overflow bugs in C/C++ programs, +print detailed error information when error happens, large +improve debug efficiency. + +By referring to its implementation algorithm +(https://github.com/google/sanitizers/wiki/AddressSanitizerAlgorithm), +enabled heap-buffer-overflow and use-after-free functions on DPDK. +DPDK ASan function currently only supports on Linux x86_64. + +AddressSanitizer is a part of LLVM(3.1+)and GCC(4.8+). + +DPDK ASan functionality is currently only supported Linux x86_64. +Support other platforms, need to define ASAN_SHADOW_OFFSET value +according to google ASan document. + +Example heap-buffer-overflow error +---------------------------------- + +Following error was reported when ASan was enabled:: + + Applied 9 bytes of memory, but accessed the 10th byte of memory, + so heap-buffer-overflow appeared. + +Below code results in this error:: + + char *p = rte_zmalloc(NULL, 9, 0); + if (!p) { + printf("rte_zmalloc error."); + return -1; + } + p[9] = 'a'; + +The error log:: + + ==49433==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x7f773fafa249 at pc 0x5556b13bdae4 bp 0x7ffeb4965e40 sp 0x7ffeb4965e30 WRITE of size 1 at 0x7f773fafa249 thread T0 + #0 0x5556b13bdae3 in asan_heap_buffer_overflow ../app/test/test_asan_heap_buffer_overflow.c:25 + #1 0x5556b043e9d4 in cmd_autotest_parsed ../app/test/commands.c:71 + #2 0x5556b1cdd4b0 in cmdline_parse ../lib/cmdline/cmdline_parse.c:290 + #3 0x5556b1cd8987 in cmdline_valid_buffer ../lib/cmdline/cmdline.c:26 + #4 0x5556b1ce477a in rdline_char_in ../lib/cmdline/cmdline_rdline.c:421 + #5 0x5556b1cd923e in cmdline_in ../lib/cmdline/cmdline.c:149 + #6 0x5556b1cd9769 in cmdline_interact ../lib/cmdline/cmdline.c:223 + #7 0x5556b045f53b in main ../app/test/test.c:234 + #8 0x7f7f1eba90b2 in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x270b2) + #9 0x5556b043e70d in _start (/home/pzh/yyy/x86_64-native-linuxapp-gcc/app/test/dpdk-test+0x7ce70d) + + Address 0x7f773fafa249 is a wild pointer. + SUMMARY: AddressSanitizer: heap-buffer-overflow ../app/test/test_asan_heap_buffer_overflow.c:25 in asan_heap_buffer_overflow + +Example use-after-free error +---------------------------- + +Following error was reported when ASan was enabled:: + + Applied for 9 bytes of memory, and accessed the first byte after + released, so heap-use-after-free appeared. + +Below code results in this error:: + + char *p = rte_zmalloc(NULL, 9, 0); + if (!p) { + printf("rte_zmalloc error."); + return -1; + } + rte_free(p); + *p = 'a'; + +The error log:: + + ==49478==ERROR: AddressSanitizer: heap-use-after-free on address 0x7fe2ffafa240 at pc 0x56409b084bc8 bp 0x7ffef62c57d0 sp 0x7ffef62c57c0 WRITE of size 1 at 0x7fe2ffafa240 thread T0 + #0 0x56409b084bc7 in asan_use_after_free ../app/test/test_asan_use_after_free.c:26 + #1 0x56409a1059d4 in cmd_autotest_parsed ../app/test/commands.c:71 + #2 0x56409b9a44b0 in cmdline_parse ../lib/cmdline/cmdline_parse.c:290 + #3 0x56409b99f987 in cmdline_valid_buffer ../lib/cmdline/cmdline.c:26 + #4 0x56409b9ab77a in rdline_char_in ../lib/cmdline/cmdline_rdline.c:421 + #5 0x56409b9a023e in cmdline_in ../lib/cmdline/cmdline.c:149 + #6 0x56409b9a0769 in cmdline_interact ../lib/cmdline/cmdline.c:223 + #7 0x56409a12653b in main ../app/test/test.c:234 + #8 0x7feafafc20b2 in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x270b2) + #9 0x56409a10570d in _start (/home/pzh/yyy/x86_64-native-linuxapp-gcc/app/test/dpdk-test+0x7ce70d) + + Address 0x7fe2ffafa240 is a wild pointer. + SUMMARY: AddressSanitizer: heap-use-after-free ../app/test/test_asan_use_after_free.c:26 in asan_use_after_free + +Usage +----- + +meson build +^^^^^^^^^^^ + +To enable ASan in meson build system, use following meson build command: + +Example usage:: + + meson build -Dbuildtype=debug -Db_lundef=false -Db_sanitize=address + ninja -C build + +.. Note:: + + a) Some of the features of ASan (for example, 'Display memory application location,currently + displayed as a wild pointer') are not currently supported by DPDK's implementation. + b) DPDK test has been completed in ubuntu18.04/ubuntu20.04/redhat8.3. Centos needs to install + libasan separately. + c) If the program uses cmdline, when a memory bug occurs, need to execute the "stty echo" command. diff --git a/doc/guides/prog_guide/index.rst b/doc/guides/prog_guide/index.rst index 2dce507f46..abaf687045 100644 --- a/doc/guides/prog_guide/index.rst +++ b/doc/guides/prog_guide/index.rst @@ -71,3 +71,4 @@ Programmer's Guide lto profile_app glossary + ASan diff --git a/lib/eal/common/malloc_elem.c b/lib/eal/common/malloc_elem.c index c2c9461f1d..bdd20a162e 100644 --- a/lib/eal/common/malloc_elem.c +++ b/lib/eal/common/malloc_elem.c @@ -446,6 +446,8 @@ malloc_elem_alloc(struct malloc_elem *elem, size_t size, unsigned align, struct malloc_elem *new_free_elem = RTE_PTR_ADD(new_elem, size + MALLOC_ELEM_OVERHEAD); + asan_clear_split_alloczone(new_free_elem); + split_elem(elem, new_free_elem); malloc_elem_free_list_insert(new_free_elem); @@ -458,6 +460,8 @@ malloc_elem_alloc(struct malloc_elem *elem, size_t size, unsigned align, elem->state = ELEM_BUSY; elem->pad = old_elem_size; + asan_clear_alloczone(elem); + /* put a dummy header in padding, to point to real element header */ if (elem->pad > 0) { /* pad will be at least 64-bytes, as everything * is cache-line aligned */ @@ -470,12 +474,18 @@ malloc_elem_alloc(struct malloc_elem *elem, size_t size, unsigned align, return new_elem; } + asan_clear_split_alloczone(new_elem); + /* we are going to split the element in two. The original element * remains free, and the new element is the one allocated. * Re-insert original element, in case its new size makes it * belong on a different list. */ + split_elem(elem, new_elem); + + asan_clear_alloczone(new_elem); + new_elem->state = ELEM_BUSY; malloc_elem_free_list_insert(elem); @@ -601,6 +611,8 @@ malloc_elem_hide_region(struct malloc_elem *elem, void *start, size_t len) if (next && next_elem_is_adjacent(elem)) { len_after = RTE_PTR_DIFF(next, hide_end); if (len_after >= MALLOC_ELEM_OVERHEAD + MIN_DATA_SIZE) { + asan_clear_split_alloczone(hide_end); + /* split after */ split_elem(elem, hide_end); @@ -615,6 +627,8 @@ malloc_elem_hide_region(struct malloc_elem *elem, void *start, size_t len) if (prev && prev_elem_is_adjacent(elem)) { len_before = RTE_PTR_DIFF(hide_start, elem); if (len_before >= MALLOC_ELEM_OVERHEAD + MIN_DATA_SIZE) { + asan_clear_split_alloczone(hide_start); + /* split before */ split_elem(elem, hide_start); @@ -628,6 +642,8 @@ malloc_elem_hide_region(struct malloc_elem *elem, void *start, size_t len) } } + asan_clear_alloczone(elem); + remove_elem(elem); } @@ -641,8 +657,10 @@ malloc_elem_resize(struct malloc_elem *elem, size_t size) const size_t new_size = size + elem->pad + MALLOC_ELEM_OVERHEAD; /* if we request a smaller size, then always return ok */ - if (elem->size >= new_size) + if (elem->size >= new_size) { + asan_clear_alloczone(elem); return 0; + } /* check if there is a next element, it's free and adjacent */ if (!elem->next || elem->next->state != ELEM_FREE || @@ -661,9 +679,15 @@ malloc_elem_resize(struct malloc_elem *elem, size_t size) /* now we have a big block together. Lets cut it down a bit, by splitting */ struct malloc_elem *split_pt = RTE_PTR_ADD(elem, new_size); split_pt = RTE_PTR_ALIGN_CEIL(split_pt, RTE_CACHE_LINE_SIZE); + + asan_clear_split_alloczone(split_pt); + split_elem(elem, split_pt); malloc_elem_free_list_insert(split_pt); } + + asan_clear_alloczone(elem); + return 0; } diff --git a/lib/eal/common/malloc_elem.h b/lib/eal/common/malloc_elem.h index a1e5f7f02c..6007a38fbc 100644 --- a/lib/eal/common/malloc_elem.h +++ b/lib/eal/common/malloc_elem.h @@ -7,6 +7,16 @@ #include <stdbool.h> +#ifdef RTE_ARCH_X86_64 +#ifdef __SANITIZE_ADDRESS__ +#define RTE_MALLOC_ASAN +#elif defined(__has_feature) +# if __has_feature(address_sanitizer) +#define RTE_MALLOC_ASAN +# endif +#endif +#endif + #define MIN_DATA_SIZE (RTE_CACHE_LINE_SIZE) /* dummy definition of struct so we can use pointers to it in malloc_elem struct */ @@ -36,10 +46,20 @@ struct malloc_elem { uint64_t header_cookie; /* Cookie marking start of data */ /* trailer cookie at start + size */ #endif +#ifdef RTE_MALLOC_ASAN + size_t user_size; + uint64_t asan_cookie[2]; /* must be next to header_cookie */ +#endif } __rte_cache_aligned; +static const unsigned int MALLOC_ELEM_HEADER_LEN = sizeof(struct malloc_elem); + #ifndef RTE_MALLOC_DEBUG -static const unsigned MALLOC_ELEM_TRAILER_LEN = 0; +#ifdef RTE_MALLOC_ASAN +static const unsigned int MALLOC_ELEM_TRAILER_LEN = RTE_CACHE_LINE_SIZE; +#else +static const unsigned int MALLOC_ELEM_TRAILER_LEN; +#endif /* dummy function - just check if pointer is non-null */ static inline int @@ -55,7 +75,7 @@ set_trailer(struct malloc_elem *elem __rte_unused){ } #else -static const unsigned MALLOC_ELEM_TRAILER_LEN = RTE_CACHE_LINE_SIZE; +static const unsigned int MALLOC_ELEM_TRAILER_LEN = RTE_CACHE_LINE_SIZE; #define MALLOC_HEADER_COOKIE 0xbadbadbadadd2e55ULL /**< Header cookie. */ #define MALLOC_TRAILER_COOKIE 0xadd2e55badbadbadULL /**< Trailer cookie.*/ @@ -90,9 +110,187 @@ malloc_elem_cookies_ok(const struct malloc_elem *elem) #endif -static const unsigned MALLOC_ELEM_HEADER_LEN = sizeof(struct malloc_elem); #define MALLOC_ELEM_OVERHEAD (MALLOC_ELEM_HEADER_LEN + MALLOC_ELEM_TRAILER_LEN) +#ifdef RTE_MALLOC_ASAN + +#ifdef RTE_ARCH_X86_64 +#define ASAN_SHADOW_OFFSET 0x00007fff8000 +#endif + +#define ASAN_SHADOW_GRAIN_SIZE 8 +#define ASAN_MEM_FREE_FLAG 0xfd +#define ASAN_MEM_REDZONE_FLAG 0xfa +#define ASAN_SHADOW_SCALE 3 + +#define ASAN_MEM_SHIFT(mem) ((void *)((uintptr_t)(mem) >> ASAN_SHADOW_SCALE)) +#define ASAN_MEM_TO_SHADOW(mem) \ + RTE_PTR_ADD(ASAN_MEM_SHIFT(mem), ASAN_SHADOW_OFFSET) + +#if defined(__clang__) +__attribute__((no_sanitize("address", "hwaddress"))) +#else +__attribute__((no_sanitize_address)) +#endif +static inline void +asan_set_shadow(void *addr, char val) +{ + *(char *)addr = val; +} + +static inline void +asan_set_zone(void *ptr, size_t len, uint32_t val) +{ + size_t offset, i; + void *shadow; + size_t zone_len = len / ASAN_SHADOW_GRAIN_SIZE; + if (len % ASAN_SHADOW_GRAIN_SIZE != 0) + zone_len += 1; + + for (i = 0; i < zone_len; i++) { + offset = i * ASAN_SHADOW_GRAIN_SIZE; + shadow = ASAN_MEM_TO_SHADOW((uintptr_t)ptr + offset); + asan_set_shadow(shadow, val); + } +} + +/* + * When the memory is released, the release mark is + * set in the corresponding range of the shadow area. + */ +static inline void +asan_set_freezone(void *ptr, size_t size) +{ + asan_set_zone(ptr, size, ASAN_MEM_FREE_FLAG); +} + +/* + * When the memory is allocated, memory state must set as accessible. + */ +static inline void +asan_clear_alloczone(struct malloc_elem *elem) +{ + asan_set_zone((void *)elem, elem->size, 0x0); +} + +static inline void +asan_clear_split_alloczone(struct malloc_elem *elem) +{ + void *ptr = RTE_PTR_SUB(elem, MALLOC_ELEM_TRAILER_LEN); + asan_set_zone(ptr, MALLOC_ELEM_OVERHEAD, 0x0); +} + +/* + * When the memory is allocated, the memory boundary is + * marked in the corresponding range of the shadow area. + * Requirement: redzone >= 16, is a power of two. + */ +static inline void +asan_set_redzone(struct malloc_elem *elem, size_t user_size) +{ + uintptr_t head_redzone; + uintptr_t tail_redzone; + void *front_shadow; + void *tail_shadow; + uint32_t val; + + if (elem != NULL) { + if (elem->state != ELEM_PAD) + elem = RTE_PTR_ADD(elem, elem->pad); + + elem->user_size = user_size; + + /* Set mark before the start of the allocated memory */ + head_redzone = (uintptr_t)RTE_PTR_ADD(elem, + MALLOC_ELEM_HEADER_LEN - ASAN_SHADOW_GRAIN_SIZE); + front_shadow = ASAN_MEM_TO_SHADOW(head_redzone); + asan_set_shadow(front_shadow, ASAN_MEM_REDZONE_FLAG); + front_shadow = ASAN_MEM_TO_SHADOW(head_redzone + - ASAN_SHADOW_GRAIN_SIZE); + asan_set_shadow(front_shadow, ASAN_MEM_REDZONE_FLAG); + + /* Set mark after the end of the allocated memory */ + tail_redzone = (uintptr_t)RTE_PTR_ADD(elem, + MALLOC_ELEM_HEADER_LEN + + elem->user_size); + tail_shadow = ASAN_MEM_TO_SHADOW(tail_redzone); + val = (tail_redzone % ASAN_SHADOW_GRAIN_SIZE); + val = (val == 0) ? ASAN_MEM_REDZONE_FLAG : val; + asan_set_shadow(tail_shadow, val); + tail_shadow = ASAN_MEM_TO_SHADOW(tail_redzone + + ASAN_SHADOW_GRAIN_SIZE); + asan_set_shadow(tail_shadow, ASAN_MEM_REDZONE_FLAG); + } +} + +/* + * When the memory is released, the mark of the memory boundary + * in the corresponding range of the shadow area is cleared. + * Requirement: redzone >= 16, is a power of two. + */ +static inline void +asan_clear_redzone(struct malloc_elem *elem) +{ + uintptr_t head_redzone; + uintptr_t tail_redzone; + void *head_shadow; + void *tail_shadow; + + if (elem != NULL) { + elem = RTE_PTR_ADD(elem, elem->pad); + + /* Clear mark before the start of the allocated memory */ + head_redzone = (uintptr_t)RTE_PTR_ADD(elem, + MALLOC_ELEM_HEADER_LEN - ASAN_SHADOW_GRAIN_SIZE); + head_shadow = ASAN_MEM_TO_SHADOW(head_redzone); + asan_set_shadow(head_shadow, 0x00); + head_shadow = ASAN_MEM_TO_SHADOW(head_redzone + - ASAN_SHADOW_GRAIN_SIZE); + asan_set_shadow(head_shadow, 0x00); + + /* Clear mark after the end of the allocated memory */ + tail_redzone = (uintptr_t)RTE_PTR_ADD(elem, + MALLOC_ELEM_HEADER_LEN + elem->user_size); + tail_shadow = ASAN_MEM_TO_SHADOW(tail_redzone); + asan_set_shadow(tail_shadow, 0x00); + tail_shadow = ASAN_MEM_TO_SHADOW(tail_redzone + + ASAN_SHADOW_GRAIN_SIZE); + asan_set_shadow(tail_shadow, 0x00); + } +} + +static inline size_t +old_malloc_size(struct malloc_elem *elem) +{ + if (elem->state != ELEM_PAD) + elem = RTE_PTR_ADD(elem, elem->pad); + + return elem->user_size; +} +#else +static inline void +asan_set_freezone(void *ptr __rte_unused, size_t size __rte_unused) { } + +static inline void +asan_clear_alloczone(struct malloc_elem *elem __rte_unused) { } + +static inline void +asan_clear_split_alloczone(struct malloc_elem *elem __rte_unused) { } + +static inline void +asan_set_redzone(struct malloc_elem *elem __rte_unused, + size_t user_size __rte_unused) { } + +static inline void +asan_clear_redzone(struct malloc_elem *elem __rte_unused) { } + +static inline size_t +old_malloc_size(struct malloc_elem *elem) +{ + return elem->size - elem->pad - MALLOC_ELEM_OVERHEAD; +} +#endif + /* * Given a pointer to the start of a memory block returned by malloc, get * the actual malloc_elem header for that block. diff --git a/lib/eal/common/malloc_heap.c b/lib/eal/common/malloc_heap.c index ee400f38ec..775d6789df 100644 --- a/lib/eal/common/malloc_heap.c +++ b/lib/eal/common/malloc_heap.c @@ -237,6 +237,7 @@ heap_alloc(struct malloc_heap *heap, const char *type __rte_unused, size_t size, unsigned int flags, size_t align, size_t bound, bool contig) { struct malloc_elem *elem; + size_t user_size = size; size = RTE_CACHE_LINE_ROUNDUP(size); align = RTE_CACHE_LINE_ROUNDUP(align); @@ -250,6 +251,8 @@ heap_alloc(struct malloc_heap *heap, const char *type __rte_unused, size_t size, /* increase heap's count of allocated elements */ heap->alloc_count++; + + asan_set_redzone(elem, user_size); } return elem == NULL ? NULL : (void *)(&elem[1]); @@ -270,6 +273,8 @@ heap_alloc_biggest(struct malloc_heap *heap, const char *type __rte_unused, /* increase heap's count of allocated elements */ heap->alloc_count++; + + asan_set_redzone(elem, size); } return elem == NULL ? NULL : (void *)(&elem[1]); @@ -841,6 +846,8 @@ malloc_heap_free(struct malloc_elem *elem) if (!malloc_elem_cookies_ok(elem) || elem->state != ELEM_BUSY) return -1; + asan_clear_redzone(elem); + /* elem may be merged with previous element, so keep heap address */ heap = elem->heap; msl = elem->msl; @@ -848,6 +855,9 @@ malloc_heap_free(struct malloc_elem *elem) rte_spinlock_lock(&(heap->lock)); + void *asan_ptr = RTE_PTR_ADD(elem, MALLOC_ELEM_HEADER_LEN + elem->pad); + size_t asan_data_len = elem->size - MALLOC_ELEM_OVERHEAD - elem->pad; + /* mark element as free */ elem->state = ELEM_FREE; @@ -1001,6 +1011,8 @@ malloc_heap_free(struct malloc_elem *elem) rte_mcfg_mem_write_unlock(); free_unlock: + asan_set_freezone(asan_ptr, asan_data_len); + rte_spinlock_unlock(&(heap->lock)); return ret; } diff --git a/lib/eal/common/rte_malloc.c b/lib/eal/common/rte_malloc.c index 9d39e58c08..d0bec26920 100644 --- a/lib/eal/common/rte_malloc.c +++ b/lib/eal/common/rte_malloc.c @@ -162,6 +162,8 @@ rte_calloc(const char *type, size_t num, size_t size, unsigned align) void * rte_realloc_socket(void *ptr, size_t size, unsigned int align, int socket) { + size_t user_size; + if (ptr == NULL) return rte_malloc_socket(NULL, size, align, socket); @@ -171,6 +173,8 @@ rte_realloc_socket(void *ptr, size_t size, unsigned int align, int socket) return NULL; } + user_size = size; + size = RTE_CACHE_LINE_ROUNDUP(size), align = RTE_CACHE_LINE_ROUNDUP(align); /* check requested socket id and alignment matches first, and if ok, @@ -181,6 +185,9 @@ rte_realloc_socket(void *ptr, size_t size, unsigned int align, int socket) RTE_PTR_ALIGN(ptr, align) == ptr && malloc_heap_resize(elem, size) == 0) { rte_eal_trace_mem_realloc(size, align, socket, ptr); + + asan_set_redzone(elem, user_size); + return ptr; } @@ -192,7 +199,7 @@ rte_realloc_socket(void *ptr, size_t size, unsigned int align, int socket) if (new_ptr == NULL) return NULL; /* elem: |pad|data_elem|data|trailer| */ - const size_t old_size = elem->size - elem->pad - MALLOC_ELEM_OVERHEAD; + const size_t old_size = old_malloc_size(elem); rte_memcpy(new_ptr, ptr, old_size < size ? old_size : size); rte_free(ptr); -- 2.25.1 ^ permalink raw reply [flat|nested] 117+ messages in thread
* [dpdk-dev] [PATCH v6 2/2] lib/pipeline: Fix compilation error with gcc ASan 2021-09-30 12:59 ` zhihongx.peng @ 2021-09-30 12:59 ` zhihongx.peng 2021-09-30 13:59 ` [dpdk-dev] [PATCH v6 1/2] Enable ASan for memory detector on DPDK Burakov, Anatoly ` (2 subsequent siblings) 3 siblings, 0 replies; 117+ messages in thread From: zhihongx.peng @ 2021-09-30 12:59 UTC (permalink / raw) To: anatoly.burakov, konstantin.ananyev, stephen Cc: dev, xueqin.lin, Zhihong Peng, stable From: Zhihong Peng <zhihongx.peng@intel.com> After adding ASan, the gcc compilation check will be stricter. "Control reaches end of non-void function" error occurs here. Fixes: f38913b7fb8e (pipeline: add meter array to SWX) Cc: stable@dpdk.org Signed-off-by: Xueqin Lin <xueqin.lin@intel.com> Signed-off-by: Zhihong Peng <zhihongx.peng@intel.com> --- lib/pipeline/rte_swx_pipeline.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/pipeline/rte_swx_pipeline.c b/lib/pipeline/rte_swx_pipeline.c index 1cd09a4b44..0acd6c6752 100644 --- a/lib/pipeline/rte_swx_pipeline.c +++ b/lib/pipeline/rte_swx_pipeline.c @@ -4642,7 +4642,7 @@ instr_meter_translate(struct rte_swx_pipeline *p, return 0; } - CHECK(0, EINVAL); + return -EINVAL; } static inline void @@ -5937,7 +5937,7 @@ instr_translate(struct rte_swx_pipeline *p, instr, data); - CHECK(0, EINVAL); + return -EINVAL; } static struct instruction_data * -- 2.25.1 ^ permalink raw reply [flat|nested] 117+ messages in thread
* Re: [dpdk-dev] [PATCH v6 1/2] Enable ASan for memory detector on DPDK 2021-09-30 12:59 ` zhihongx.peng 2021-09-30 12:59 ` [dpdk-dev] [PATCH v6 2/2] lib/pipeline: Fix compilation error with gcc ASan zhihongx.peng @ 2021-09-30 13:59 ` Burakov, Anatoly 2021-09-30 18:49 ` David Marchand 2021-10-08 9:17 ` [dpdk-dev] [PATCH v7 1/3] " zhihongx.peng 3 siblings, 0 replies; 117+ messages in thread From: Burakov, Anatoly @ 2021-09-30 13:59 UTC (permalink / raw) To: zhihongx.peng, konstantin.ananyev, stephen; +Cc: dev, xueqin.lin On 30-Sep-21 1:59 PM, zhihongx.peng@intel.com wrote: > From: Zhihong Peng <zhihongx.peng@intel.com> > > AddressSanitizer (ASan) is a google memory error detect > standard tool. It could help to detect use-after-free and > {heap,stack,global}-buffer overflow bugs in C/C++ programs, > print detailed error information when error happens, large > improve debug efficiency. > > `AddressSanitizer > <https://github.com/google/sanitizers/wiki/AddressSanitizer>` (ASan) > is a widely-used debugging tool to detect memory access errors. > It helps detect issues like use-after-free, various kinds of buffer > overruns in C/C++ programs, and other similar errors, as well as > printing out detailed debug information whenever an error is detected. > > DPDK ASan functionality is currently only supported Linux x86_64. > Support other platforms, need to define ASAN_SHADOW_OFFSET value > according to google ASan document. > > Here is an example of heap-buffer-overflow bug: > ...... > char *p = rte_zmalloc(NULL, 7, 0); > p[7] = 'a'; > ...... > > Here is an example of use-after-free bug: > ...... > char *p = rte_zmalloc(NULL, 7, 0); > rte_free(p); > *p = 'a'; > ...... > > If you want to use this feature, > you need to add below compilation options when compiling code: > -Dbuildtype=debug -Db_lundef=false -Db_sanitize=address > "-Dbuildtype=debug": This is a non-essential option. When this option > is added, if a memory error occurs, ASan can clearly show where the > code is wrong. > "-Db_lundef=false": When use clang to compile DPDK, this option must > be added. > > Signed-off-by: Xueqin Lin <xueqin.lin@intel.com> > Signed-off-by: Zhihong Peng <zhihongx.peng@intel.com> > --- Acked-by: Anatoly Burakov <anatoly.burakov@intel.com> -- Thanks, Anatoly ^ permalink raw reply [flat|nested] 117+ messages in thread
* Re: [dpdk-dev] [PATCH v6 1/2] Enable ASan for memory detector on DPDK 2021-09-30 12:59 ` zhihongx.peng 2021-09-30 12:59 ` [dpdk-dev] [PATCH v6 2/2] lib/pipeline: Fix compilation error with gcc ASan zhihongx.peng 2021-09-30 13:59 ` [dpdk-dev] [PATCH v6 1/2] Enable ASan for memory detector on DPDK Burakov, Anatoly @ 2021-09-30 18:49 ` David Marchand 2021-10-08 9:17 ` [dpdk-dev] [PATCH v7 1/3] " zhihongx.peng 3 siblings, 0 replies; 117+ messages in thread From: David Marchand @ 2021-09-30 18:49 UTC (permalink / raw) To: Zhihong Peng Cc: Burakov, Anatoly, Ananyev, Konstantin, Stephen Hemminger, dev, Xueqin Lin On Thu, Sep 30, 2021 at 3:09 PM <zhihongx.peng@intel.com> wrote: > > From: Zhihong Peng <zhihongx.peng@intel.com> > > AddressSanitizer (ASan) is a google memory error detect > standard tool. It could help to detect use-after-free and > {heap,stack,global}-buffer overflow bugs in C/C++ programs, > print detailed error information when error happens, large > improve debug efficiency. > > `AddressSanitizer > <https://github.com/google/sanitizers/wiki/AddressSanitizer>` (ASan) > is a widely-used debugging tool to detect memory access errors. > It helps detect issues like use-after-free, various kinds of buffer > overruns in C/C++ programs, and other similar errors, as well as > printing out detailed debug information whenever an error is detected. > > DPDK ASan functionality is currently only supported Linux x86_64. > Support other platforms, need to define ASAN_SHADOW_OFFSET value > according to google ASan document. > > Here is an example of heap-buffer-overflow bug: > ...... > char *p = rte_zmalloc(NULL, 7, 0); > p[7] = 'a'; > ...... > > Here is an example of use-after-free bug: > ...... > char *p = rte_zmalloc(NULL, 7, 0); > rte_free(p); > *p = 'a'; > ...... > > If you want to use this feature, > you need to add below compilation options when compiling code: > -Dbuildtype=debug -Db_lundef=false -Db_sanitize=address > "-Dbuildtype=debug": This is a non-essential option. When this option > is added, if a memory error occurs, ASan can clearly show where the > code is wrong. > "-Db_lundef=false": When use clang to compile DPDK, this option must > be added. > > Signed-off-by: Xueqin Lin <xueqin.lin@intel.com> > Signed-off-by: Zhihong Peng <zhihongx.peng@intel.com> > --- > devtools/words-case.txt | 1 + > doc/guides/prog_guide/ASan.rst | 112 ++++++++++++++++++ > doc/guides/prog_guide/index.rst | 1 + > lib/eal/common/malloc_elem.c | 26 +++- > lib/eal/common/malloc_elem.h | 204 +++++++++++++++++++++++++++++++- > lib/eal/common/malloc_heap.c | 12 ++ > lib/eal/common/rte_malloc.c | 9 +- > 7 files changed, 360 insertions(+), 5 deletions(-) > create mode 100644 doc/guides/prog_guide/ASan.rst > I suppose this makes it a v7 since I can see differences when comparing with "previous" v6 series. Please pay attention to versioning and add a changelog when submitting a new revision. Plus, I had comments on v6: https://inbox.dpdk.org/dev/CAJFAV8yzYJtwpnx+jsaB+X7q7POT86uKC3RS-FB9t7p=kTyGDw@mail.gmail.com/ https://inbox.dpdk.org/dev/CAJFAV8w8Zj5xP+giZtYCUz=4ekuFRDW5Niys9uM6xvAW0kteQg@mail.gmail.com/ Thanks. -- David Marchand ^ permalink raw reply [flat|nested] 117+ messages in thread
* [dpdk-dev] [PATCH v7 1/3] Enable ASan for memory detector on DPDK 2021-09-30 12:59 ` zhihongx.peng ` (2 preceding siblings ...) 2021-09-30 18:49 ` David Marchand @ 2021-10-08 9:17 ` zhihongx.peng 2021-10-08 9:17 ` [dpdk-dev] [PATCH v7 2/3] DPDK code adapts to ASan zhihongx.peng ` (2 more replies) 3 siblings, 3 replies; 117+ messages in thread From: zhihongx.peng @ 2021-10-08 9:17 UTC (permalink / raw) To: anatoly.burakov, konstantin.ananyev, stephen, david.marchand Cc: dev, xueqin.lin, Zhihong Peng From: Zhihong Peng <zhihongx.peng@intel.com> AddressSanitizer (ASan) is a google memory error detect standard tool. It could help to detect use-after-free and {heap,stack,global}-buffer overflow bugs in C/C++ programs, print detailed error information when error happens, large improve debug efficiency. `AddressSanitizer <https://github.com/google/sanitizers/wiki/AddressSanitizer>` (ASan) is a widely-used debugging tool to detect memory access errors. It helps detect issues like use-after-free, various kinds of buffer overruns in C/C++ programs, and other similar errors, as well as printing out detailed debug information whenever an error is detected. DPDK ASan functionality is currently only supported Linux x86_64. Support other platforms, need to define ASAN_SHADOW_OFFSET value according to google ASan document. Here is an example of heap-buffer-overflow bug: ...... char *p = rte_zmalloc(NULL, 7, 0); p[7] = 'a'; ...... Here is an example of use-after-free bug: ...... char *p = rte_zmalloc(NULL, 7, 0); rte_free(p); *p = 'a'; ...... We can enable ASan by adding below compilation options: -Dbuildtype=debug -Db_lundef=false -Db_sanitize=address "-Dbuildtype=debug": This is a non-essential option. When this option is added, if a memory error occurs, ASan can clearly show where the code is wrong. "-Db_lundef=false": When use clang to compile DPDK, this option must be added. Signed-off-by: Xueqin Lin <xueqin.lin@intel.com> Signed-off-by: Zhihong Peng <zhihongx.peng@intel.com> --- devtools/words-case.txt | 1 + doc/guides/prog_guide/asan.rst | 100 ++++++++++++++++++++++++++++++++ doc/guides/prog_guide/index.rst | 1 + 3 files changed, 102 insertions(+) create mode 100644 doc/guides/prog_guide/asan.rst diff --git a/devtools/words-case.txt b/devtools/words-case.txt index 0bbad48626..ada6910fa0 100644 --- a/devtools/words-case.txt +++ b/devtools/words-case.txt @@ -5,6 +5,7 @@ API Arm armv7 armv8 +ASan BAR CRC DCB diff --git a/doc/guides/prog_guide/asan.rst b/doc/guides/prog_guide/asan.rst new file mode 100644 index 0000000000..fb48b9f10b --- /dev/null +++ b/doc/guides/prog_guide/asan.rst @@ -0,0 +1,100 @@ +.. Copyright (c) <2021>, Intel Corporation + All rights reserved. + +Memory error detect standard tool - AddressSanitizer(ASan) +========================================================== + +AddressSanitizer (ASan) is a google memory error detect +standard tool. It could help to detect use-after-free and +{heap,stack,global}-buffer overflow bugs in C/C++ programs, +print detailed error information when error happens, large +improve debug efficiency. + +By referring to its implementation algorithm +(https://github.com/google/sanitizers/wiki/AddressSanitizerAlgorithm), +enabled heap-buffer-overflow and use-after-free functions on DPDK. +DPDK ASan function currently only supports on Linux x86_64. + +AddressSanitizer is a part of LLVM (3.1+) and GCC (4.8+). + +DPDK ASan functionality is currently only supported Linux x86_64. +Support other platforms, need to define ASAN_SHADOW_OFFSET value +according to google ASan document. + +Example heap-buffer-overflow error +---------------------------------- + +Following error was reported when ASan was enabled:: + + Applied 9 bytes of memory, but accessed the 10th byte of memory, + so heap-buffer-overflow appeared. + +Below code results in this error:: + + Add code to helloworld: + char *p = rte_zmalloc(NULL, 9, 0); + if (!p) { + printf("rte_zmalloc error."); + return -1; + } + p[9] = 'a'; + +The error log:: + + ==369953==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x7fb17f465809 at pc 0x5652e6707b84 bp 0x7ffea70eea20 sp 0x7ffea70eea10 WRITE of size 1 at 0x7fb17f465809 thread T0 + #0 0x5652e6707b83 in main ../examples/helloworld/main.c:47 + #1 0x7fb94953c0b2 in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x270b2) + #2 0x5652e67079bd in _start (/home/pzh/asan_test/x86_64-native-linuxapp-gcc/examples/dpdk-helloworld+0x8329bd) + + Address 0x7fb17f465809 is a wild pointer. + SUMMARY: AddressSanitizer: heap-buffer-overflow ../examples/helloworld/main.c:47 in main + +Example use-after-free error +---------------------------- + +Following error was reported when ASan was enabled:: + + Applied for 9 bytes of memory, and accessed the first byte after + released, so heap-use-after-free appeared. + +Below code results in this error:: + + Add code to helloworld: + char *p = rte_zmalloc(NULL, 9, 0); + if (!p) { + printf("rte_zmalloc error."); + return -1; + } + rte_free(p); + *p = 'a'; + +The error log:: + + ==417048==ERROR: AddressSanitizer: heap-use-after-free on address 0x7fc83f465800 at pc 0x564308a39b89 bp 0x7ffc8c85bf50 sp 0x7ffc8c85bf40 WRITE of size 1 at 0x7fc83f465800 thread T0 + #0 0x564308a39b88 in main ../examples/helloworld/main.c:48 + #1 0x7fd0079c60b2 in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x270b2) + #2 0x564308a399bd in _start (/home/pzh/asan_test/x86_64-native-linuxapp-gcc/examples/dpdk-helloworld+0x8329bd) + + Address 0x7fc83f465800 is a wild pointer. + SUMMARY: AddressSanitizer: heap-use-after-free ../examples/helloworld/main.c:48 in main + +Usage +----- + +meson build +^^^^^^^^^^^ + +To enable ASan in meson build system, use following meson build command: + +Example usage:: + + meson build -Dbuildtype=debug -Db_lundef=false -Db_sanitize=address + ninja -C build + +.. Note:: + + a) Some of the features of ASan (for example, 'Display memory application location, currently + displayed as a wild pointer') are not currently supported by DPDK's implementation. + b) DPDK test has been completed in ubuntu18.04/ubuntu20.04/redhat8.3. Centos needs to install + libasan separately. + c) If the program uses cmdline, when a memory bug occurs, need to execute the "stty echo" command. diff --git a/doc/guides/prog_guide/index.rst b/doc/guides/prog_guide/index.rst index 2dce507f46..df8a4b93e1 100644 --- a/doc/guides/prog_guide/index.rst +++ b/doc/guides/prog_guide/index.rst @@ -71,3 +71,4 @@ Programmer's Guide lto profile_app glossary + asan -- 2.25.1 ^ permalink raw reply [flat|nested] 117+ messages in thread
* [dpdk-dev] [PATCH v7 2/3] DPDK code adapts to ASan 2021-10-08 9:17 ` [dpdk-dev] [PATCH v7 1/3] " zhihongx.peng @ 2021-10-08 9:17 ` zhihongx.peng 2021-10-08 9:17 ` [dpdk-dev] [PATCH v7 3/3] lib/pipeline: Fix compilation error with gcc ASan zhihongx.peng 2021-10-11 6:28 ` [dpdk-dev] [PATCH v8 1/3] Enable ASan for memory detector on DPDK zhihongx.peng 2 siblings, 0 replies; 117+ messages in thread From: zhihongx.peng @ 2021-10-08 9:17 UTC (permalink / raw) To: anatoly.burakov, konstantin.ananyev, stephen, david.marchand Cc: dev, xueqin.lin, Zhihong Peng From: Zhihong Peng <zhihongx.peng@intel.com> DPDK ASan functionality is currently only supported Linux x86_64. Support other platforms, need to define ASAN_SHADOW_OFFSET value according to google ASan document. Signed-off-by: Xueqin Lin <xueqin.lin@intel.com> Signed-off-by: Zhihong Peng <zhihongx.peng@intel.com> --- lib/eal/common/malloc_elem.c | 26 ++++- lib/eal/common/malloc_elem.h | 204 ++++++++++++++++++++++++++++++++++- lib/eal/common/malloc_heap.c | 12 +++ lib/eal/common/rte_malloc.c | 9 +- 4 files changed, 246 insertions(+), 5 deletions(-) diff --git a/lib/eal/common/malloc_elem.c b/lib/eal/common/malloc_elem.c index c2c9461f1d..bdd20a162e 100644 --- a/lib/eal/common/malloc_elem.c +++ b/lib/eal/common/malloc_elem.c @@ -446,6 +446,8 @@ malloc_elem_alloc(struct malloc_elem *elem, size_t size, unsigned align, struct malloc_elem *new_free_elem = RTE_PTR_ADD(new_elem, size + MALLOC_ELEM_OVERHEAD); + asan_clear_split_alloczone(new_free_elem); + split_elem(elem, new_free_elem); malloc_elem_free_list_insert(new_free_elem); @@ -458,6 +460,8 @@ malloc_elem_alloc(struct malloc_elem *elem, size_t size, unsigned align, elem->state = ELEM_BUSY; elem->pad = old_elem_size; + asan_clear_alloczone(elem); + /* put a dummy header in padding, to point to real element header */ if (elem->pad > 0) { /* pad will be at least 64-bytes, as everything * is cache-line aligned */ @@ -470,12 +474,18 @@ malloc_elem_alloc(struct malloc_elem *elem, size_t size, unsigned align, return new_elem; } + asan_clear_split_alloczone(new_elem); + /* we are going to split the element in two. The original element * remains free, and the new element is the one allocated. * Re-insert original element, in case its new size makes it * belong on a different list. */ + split_elem(elem, new_elem); + + asan_clear_alloczone(new_elem); + new_elem->state = ELEM_BUSY; malloc_elem_free_list_insert(elem); @@ -601,6 +611,8 @@ malloc_elem_hide_region(struct malloc_elem *elem, void *start, size_t len) if (next && next_elem_is_adjacent(elem)) { len_after = RTE_PTR_DIFF(next, hide_end); if (len_after >= MALLOC_ELEM_OVERHEAD + MIN_DATA_SIZE) { + asan_clear_split_alloczone(hide_end); + /* split after */ split_elem(elem, hide_end); @@ -615,6 +627,8 @@ malloc_elem_hide_region(struct malloc_elem *elem, void *start, size_t len) if (prev && prev_elem_is_adjacent(elem)) { len_before = RTE_PTR_DIFF(hide_start, elem); if (len_before >= MALLOC_ELEM_OVERHEAD + MIN_DATA_SIZE) { + asan_clear_split_alloczone(hide_start); + /* split before */ split_elem(elem, hide_start); @@ -628,6 +642,8 @@ malloc_elem_hide_region(struct malloc_elem *elem, void *start, size_t len) } } + asan_clear_alloczone(elem); + remove_elem(elem); } @@ -641,8 +657,10 @@ malloc_elem_resize(struct malloc_elem *elem, size_t size) const size_t new_size = size + elem->pad + MALLOC_ELEM_OVERHEAD; /* if we request a smaller size, then always return ok */ - if (elem->size >= new_size) + if (elem->size >= new_size) { + asan_clear_alloczone(elem); return 0; + } /* check if there is a next element, it's free and adjacent */ if (!elem->next || elem->next->state != ELEM_FREE || @@ -661,9 +679,15 @@ malloc_elem_resize(struct malloc_elem *elem, size_t size) /* now we have a big block together. Lets cut it down a bit, by splitting */ struct malloc_elem *split_pt = RTE_PTR_ADD(elem, new_size); split_pt = RTE_PTR_ALIGN_CEIL(split_pt, RTE_CACHE_LINE_SIZE); + + asan_clear_split_alloczone(split_pt); + split_elem(elem, split_pt); malloc_elem_free_list_insert(split_pt); } + + asan_clear_alloczone(elem); + return 0; } diff --git a/lib/eal/common/malloc_elem.h b/lib/eal/common/malloc_elem.h index a1e5f7f02c..6007a38fbc 100644 --- a/lib/eal/common/malloc_elem.h +++ b/lib/eal/common/malloc_elem.h @@ -7,6 +7,16 @@ #include <stdbool.h> +#ifdef RTE_ARCH_X86_64 +#ifdef __SANITIZE_ADDRESS__ +#define RTE_MALLOC_ASAN +#elif defined(__has_feature) +# if __has_feature(address_sanitizer) +#define RTE_MALLOC_ASAN +# endif +#endif +#endif + #define MIN_DATA_SIZE (RTE_CACHE_LINE_SIZE) /* dummy definition of struct so we can use pointers to it in malloc_elem struct */ @@ -36,10 +46,20 @@ struct malloc_elem { uint64_t header_cookie; /* Cookie marking start of data */ /* trailer cookie at start + size */ #endif +#ifdef RTE_MALLOC_ASAN + size_t user_size; + uint64_t asan_cookie[2]; /* must be next to header_cookie */ +#endif } __rte_cache_aligned; +static const unsigned int MALLOC_ELEM_HEADER_LEN = sizeof(struct malloc_elem); + #ifndef RTE_MALLOC_DEBUG -static const unsigned MALLOC_ELEM_TRAILER_LEN = 0; +#ifdef RTE_MALLOC_ASAN +static const unsigned int MALLOC_ELEM_TRAILER_LEN = RTE_CACHE_LINE_SIZE; +#else +static const unsigned int MALLOC_ELEM_TRAILER_LEN; +#endif /* dummy function - just check if pointer is non-null */ static inline int @@ -55,7 +75,7 @@ set_trailer(struct malloc_elem *elem __rte_unused){ } #else -static const unsigned MALLOC_ELEM_TRAILER_LEN = RTE_CACHE_LINE_SIZE; +static const unsigned int MALLOC_ELEM_TRAILER_LEN = RTE_CACHE_LINE_SIZE; #define MALLOC_HEADER_COOKIE 0xbadbadbadadd2e55ULL /**< Header cookie. */ #define MALLOC_TRAILER_COOKIE 0xadd2e55badbadbadULL /**< Trailer cookie.*/ @@ -90,9 +110,187 @@ malloc_elem_cookies_ok(const struct malloc_elem *elem) #endif -static const unsigned MALLOC_ELEM_HEADER_LEN = sizeof(struct malloc_elem); #define MALLOC_ELEM_OVERHEAD (MALLOC_ELEM_HEADER_LEN + MALLOC_ELEM_TRAILER_LEN) +#ifdef RTE_MALLOC_ASAN + +#ifdef RTE_ARCH_X86_64 +#define ASAN_SHADOW_OFFSET 0x00007fff8000 +#endif + +#define ASAN_SHADOW_GRAIN_SIZE 8 +#define ASAN_MEM_FREE_FLAG 0xfd +#define ASAN_MEM_REDZONE_FLAG 0xfa +#define ASAN_SHADOW_SCALE 3 + +#define ASAN_MEM_SHIFT(mem) ((void *)((uintptr_t)(mem) >> ASAN_SHADOW_SCALE)) +#define ASAN_MEM_TO_SHADOW(mem) \ + RTE_PTR_ADD(ASAN_MEM_SHIFT(mem), ASAN_SHADOW_OFFSET) + +#if defined(__clang__) +__attribute__((no_sanitize("address", "hwaddress"))) +#else +__attribute__((no_sanitize_address)) +#endif +static inline void +asan_set_shadow(void *addr, char val) +{ + *(char *)addr = val; +} + +static inline void +asan_set_zone(void *ptr, size_t len, uint32_t val) +{ + size_t offset, i; + void *shadow; + size_t zone_len = len / ASAN_SHADOW_GRAIN_SIZE; + if (len % ASAN_SHADOW_GRAIN_SIZE != 0) + zone_len += 1; + + for (i = 0; i < zone_len; i++) { + offset = i * ASAN_SHADOW_GRAIN_SIZE; + shadow = ASAN_MEM_TO_SHADOW((uintptr_t)ptr + offset); + asan_set_shadow(shadow, val); + } +} + +/* + * When the memory is released, the release mark is + * set in the corresponding range of the shadow area. + */ +static inline void +asan_set_freezone(void *ptr, size_t size) +{ + asan_set_zone(ptr, size, ASAN_MEM_FREE_FLAG); +} + +/* + * When the memory is allocated, memory state must set as accessible. + */ +static inline void +asan_clear_alloczone(struct malloc_elem *elem) +{ + asan_set_zone((void *)elem, elem->size, 0x0); +} + +static inline void +asan_clear_split_alloczone(struct malloc_elem *elem) +{ + void *ptr = RTE_PTR_SUB(elem, MALLOC_ELEM_TRAILER_LEN); + asan_set_zone(ptr, MALLOC_ELEM_OVERHEAD, 0x0); +} + +/* + * When the memory is allocated, the memory boundary is + * marked in the corresponding range of the shadow area. + * Requirement: redzone >= 16, is a power of two. + */ +static inline void +asan_set_redzone(struct malloc_elem *elem, size_t user_size) +{ + uintptr_t head_redzone; + uintptr_t tail_redzone; + void *front_shadow; + void *tail_shadow; + uint32_t val; + + if (elem != NULL) { + if (elem->state != ELEM_PAD) + elem = RTE_PTR_ADD(elem, elem->pad); + + elem->user_size = user_size; + + /* Set mark before the start of the allocated memory */ + head_redzone = (uintptr_t)RTE_PTR_ADD(elem, + MALLOC_ELEM_HEADER_LEN - ASAN_SHADOW_GRAIN_SIZE); + front_shadow = ASAN_MEM_TO_SHADOW(head_redzone); + asan_set_shadow(front_shadow, ASAN_MEM_REDZONE_FLAG); + front_shadow = ASAN_MEM_TO_SHADOW(head_redzone + - ASAN_SHADOW_GRAIN_SIZE); + asan_set_shadow(front_shadow, ASAN_MEM_REDZONE_FLAG); + + /* Set mark after the end of the allocated memory */ + tail_redzone = (uintptr_t)RTE_PTR_ADD(elem, + MALLOC_ELEM_HEADER_LEN + + elem->user_size); + tail_shadow = ASAN_MEM_TO_SHADOW(tail_redzone); + val = (tail_redzone % ASAN_SHADOW_GRAIN_SIZE); + val = (val == 0) ? ASAN_MEM_REDZONE_FLAG : val; + asan_set_shadow(tail_shadow, val); + tail_shadow = ASAN_MEM_TO_SHADOW(tail_redzone + + ASAN_SHADOW_GRAIN_SIZE); + asan_set_shadow(tail_shadow, ASAN_MEM_REDZONE_FLAG); + } +} + +/* + * When the memory is released, the mark of the memory boundary + * in the corresponding range of the shadow area is cleared. + * Requirement: redzone >= 16, is a power of two. + */ +static inline void +asan_clear_redzone(struct malloc_elem *elem) +{ + uintptr_t head_redzone; + uintptr_t tail_redzone; + void *head_shadow; + void *tail_shadow; + + if (elem != NULL) { + elem = RTE_PTR_ADD(elem, elem->pad); + + /* Clear mark before the start of the allocated memory */ + head_redzone = (uintptr_t)RTE_PTR_ADD(elem, + MALLOC_ELEM_HEADER_LEN - ASAN_SHADOW_GRAIN_SIZE); + head_shadow = ASAN_MEM_TO_SHADOW(head_redzone); + asan_set_shadow(head_shadow, 0x00); + head_shadow = ASAN_MEM_TO_SHADOW(head_redzone + - ASAN_SHADOW_GRAIN_SIZE); + asan_set_shadow(head_shadow, 0x00); + + /* Clear mark after the end of the allocated memory */ + tail_redzone = (uintptr_t)RTE_PTR_ADD(elem, + MALLOC_ELEM_HEADER_LEN + elem->user_size); + tail_shadow = ASAN_MEM_TO_SHADOW(tail_redzone); + asan_set_shadow(tail_shadow, 0x00); + tail_shadow = ASAN_MEM_TO_SHADOW(tail_redzone + + ASAN_SHADOW_GRAIN_SIZE); + asan_set_shadow(tail_shadow, 0x00); + } +} + +static inline size_t +old_malloc_size(struct malloc_elem *elem) +{ + if (elem->state != ELEM_PAD) + elem = RTE_PTR_ADD(elem, elem->pad); + + return elem->user_size; +} +#else +static inline void +asan_set_freezone(void *ptr __rte_unused, size_t size __rte_unused) { } + +static inline void +asan_clear_alloczone(struct malloc_elem *elem __rte_unused) { } + +static inline void +asan_clear_split_alloczone(struct malloc_elem *elem __rte_unused) { } + +static inline void +asan_set_redzone(struct malloc_elem *elem __rte_unused, + size_t user_size __rte_unused) { } + +static inline void +asan_clear_redzone(struct malloc_elem *elem __rte_unused) { } + +static inline size_t +old_malloc_size(struct malloc_elem *elem) +{ + return elem->size - elem->pad - MALLOC_ELEM_OVERHEAD; +} +#endif + /* * Given a pointer to the start of a memory block returned by malloc, get * the actual malloc_elem header for that block. diff --git a/lib/eal/common/malloc_heap.c b/lib/eal/common/malloc_heap.c index ee400f38ec..775d6789df 100644 --- a/lib/eal/common/malloc_heap.c +++ b/lib/eal/common/malloc_heap.c @@ -237,6 +237,7 @@ heap_alloc(struct malloc_heap *heap, const char *type __rte_unused, size_t size, unsigned int flags, size_t align, size_t bound, bool contig) { struct malloc_elem *elem; + size_t user_size = size; size = RTE_CACHE_LINE_ROUNDUP(size); align = RTE_CACHE_LINE_ROUNDUP(align); @@ -250,6 +251,8 @@ heap_alloc(struct malloc_heap *heap, const char *type __rte_unused, size_t size, /* increase heap's count of allocated elements */ heap->alloc_count++; + + asan_set_redzone(elem, user_size); } return elem == NULL ? NULL : (void *)(&elem[1]); @@ -270,6 +273,8 @@ heap_alloc_biggest(struct malloc_heap *heap, const char *type __rte_unused, /* increase heap's count of allocated elements */ heap->alloc_count++; + + asan_set_redzone(elem, size); } return elem == NULL ? NULL : (void *)(&elem[1]); @@ -841,6 +846,8 @@ malloc_heap_free(struct malloc_elem *elem) if (!malloc_elem_cookies_ok(elem) || elem->state != ELEM_BUSY) return -1; + asan_clear_redzone(elem); + /* elem may be merged with previous element, so keep heap address */ heap = elem->heap; msl = elem->msl; @@ -848,6 +855,9 @@ malloc_heap_free(struct malloc_elem *elem) rte_spinlock_lock(&(heap->lock)); + void *asan_ptr = RTE_PTR_ADD(elem, MALLOC_ELEM_HEADER_LEN + elem->pad); + size_t asan_data_len = elem->size - MALLOC_ELEM_OVERHEAD - elem->pad; + /* mark element as free */ elem->state = ELEM_FREE; @@ -1001,6 +1011,8 @@ malloc_heap_free(struct malloc_elem *elem) rte_mcfg_mem_write_unlock(); free_unlock: + asan_set_freezone(asan_ptr, asan_data_len); + rte_spinlock_unlock(&(heap->lock)); return ret; } diff --git a/lib/eal/common/rte_malloc.c b/lib/eal/common/rte_malloc.c index 9d39e58c08..d0bec26920 100644 --- a/lib/eal/common/rte_malloc.c +++ b/lib/eal/common/rte_malloc.c @@ -162,6 +162,8 @@ rte_calloc(const char *type, size_t num, size_t size, unsigned align) void * rte_realloc_socket(void *ptr, size_t size, unsigned int align, int socket) { + size_t user_size; + if (ptr == NULL) return rte_malloc_socket(NULL, size, align, socket); @@ -171,6 +173,8 @@ rte_realloc_socket(void *ptr, size_t size, unsigned int align, int socket) return NULL; } + user_size = size; + size = RTE_CACHE_LINE_ROUNDUP(size), align = RTE_CACHE_LINE_ROUNDUP(align); /* check requested socket id and alignment matches first, and if ok, @@ -181,6 +185,9 @@ rte_realloc_socket(void *ptr, size_t size, unsigned int align, int socket) RTE_PTR_ALIGN(ptr, align) == ptr && malloc_heap_resize(elem, size) == 0) { rte_eal_trace_mem_realloc(size, align, socket, ptr); + + asan_set_redzone(elem, user_size); + return ptr; } @@ -192,7 +199,7 @@ rte_realloc_socket(void *ptr, size_t size, unsigned int align, int socket) if (new_ptr == NULL) return NULL; /* elem: |pad|data_elem|data|trailer| */ - const size_t old_size = elem->size - elem->pad - MALLOC_ELEM_OVERHEAD; + const size_t old_size = old_malloc_size(elem); rte_memcpy(new_ptr, ptr, old_size < size ? old_size : size); rte_free(ptr); -- 2.25.1 ^ permalink raw reply [flat|nested] 117+ messages in thread
* [dpdk-dev] [PATCH v7 3/3] lib/pipeline: Fix compilation error with gcc ASan 2021-10-08 9:17 ` [dpdk-dev] [PATCH v7 1/3] " zhihongx.peng 2021-10-08 9:17 ` [dpdk-dev] [PATCH v7 2/3] DPDK code adapts to ASan zhihongx.peng @ 2021-10-08 9:17 ` zhihongx.peng 2021-10-11 6:28 ` [dpdk-dev] [PATCH v8 1/3] Enable ASan for memory detector on DPDK zhihongx.peng 2 siblings, 0 replies; 117+ messages in thread From: zhihongx.peng @ 2021-10-08 9:17 UTC (permalink / raw) To: anatoly.burakov, konstantin.ananyev, stephen, david.marchand Cc: dev, xueqin.lin, Zhihong Peng, stable From: Zhihong Peng <zhihongx.peng@intel.com> The gcc will check code more stricter when ASan enabled. "Control reaches end of non-void function" error occurs here. Fixes: f38913b7fb8e (pipeline: add meter array to SWX) Cc: stable@dpdk.org Signed-off-by: Xueqin Lin <xueqin.lin@intel.com> Signed-off-by: Zhihong Peng <zhihongx.peng@intel.com> --- lib/pipeline/rte_swx_pipeline.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/pipeline/rte_swx_pipeline.c b/lib/pipeline/rte_swx_pipeline.c index 1cd09a4b44..0acd6c6752 100644 --- a/lib/pipeline/rte_swx_pipeline.c +++ b/lib/pipeline/rte_swx_pipeline.c @@ -4642,7 +4642,7 @@ instr_meter_translate(struct rte_swx_pipeline *p, return 0; } - CHECK(0, EINVAL); + return -EINVAL; } static inline void @@ -5937,7 +5937,7 @@ instr_translate(struct rte_swx_pipeline *p, instr, data); - CHECK(0, EINVAL); + return -EINVAL; } static struct instruction_data * -- 2.25.1 ^ permalink raw reply [flat|nested] 117+ messages in thread
* [dpdk-dev] [PATCH v8 1/3] Enable ASan for memory detector on DPDK 2021-10-08 9:17 ` [dpdk-dev] [PATCH v7 1/3] " zhihongx.peng 2021-10-08 9:17 ` [dpdk-dev] [PATCH v7 2/3] DPDK code adapts to ASan zhihongx.peng 2021-10-08 9:17 ` [dpdk-dev] [PATCH v7 3/3] lib/pipeline: Fix compilation error with gcc ASan zhihongx.peng @ 2021-10-11 6:28 ` zhihongx.peng 2021-10-11 6:28 ` [dpdk-dev] [PATCH v8 2/3] DPDK code adapts to ASan zhihongx.peng ` (2 more replies) 2 siblings, 3 replies; 117+ messages in thread From: zhihongx.peng @ 2021-10-11 6:28 UTC (permalink / raw) To: anatoly.burakov, konstantin.ananyev, stephen Cc: dev, xueqin.lin, Zhihong Peng From: Zhihong Peng <zhihongx.peng@intel.com> AddressSanitizer (ASan) is a google memory error detect standard tool. It could help to detect use-after-free and {heap,stack,global}-buffer overflow bugs in C/C++ programs, print detailed error information when error happens, large improve debug efficiency. `AddressSanitizer <https://github.com/google/sanitizers/wiki/AddressSanitizer>` (ASan) is a widely-used debugging tool to detect memory access errors. It helps detect issues like use-after-free, various kinds of buffer overruns in C/C++ programs, and other similar errors, as well as printing out detailed debug information whenever an error is detected. DPDK ASan functionality is currently only supported Linux x86_64. Support other platforms, need to define ASAN_SHADOW_OFFSET value according to google ASan document. Here is an example of heap-buffer-overflow bug: ...... char *p = rte_zmalloc(NULL, 7, 0); p[7] = 'a'; ...... Here is an example of use-after-free bug: ...... char *p = rte_zmalloc(NULL, 7, 0); rte_free(p); *p = 'a'; ...... We can enable ASan by adding below compilation options: -Dbuildtype=debug -Db_lundef=false -Db_sanitize=address "-Dbuildtype=debug": This is a non-essential option. When this option is added, if a memory error occurs, ASan can clearly show where the code is wrong. "-Db_lundef=false": When use clang to compile DPDK, this option must be added. Signed-off-by: Xueqin Lin <xueqin.lin@intel.com> Signed-off-by: Zhihong Peng <zhihongx.peng@intel.com> --- devtools/words-case.txt | 1 + doc/guides/prog_guide/asan.rst | 100 ++++++++++++++++++++++++++++++++ doc/guides/prog_guide/index.rst | 1 + 3 files changed, 102 insertions(+) create mode 100644 doc/guides/prog_guide/asan.rst diff --git a/devtools/words-case.txt b/devtools/words-case.txt index 0bbad48626..ada6910fa0 100644 --- a/devtools/words-case.txt +++ b/devtools/words-case.txt @@ -5,6 +5,7 @@ API Arm armv7 armv8 +ASan BAR CRC DCB diff --git a/doc/guides/prog_guide/asan.rst b/doc/guides/prog_guide/asan.rst new file mode 100644 index 0000000000..fb48b9f10b --- /dev/null +++ b/doc/guides/prog_guide/asan.rst @@ -0,0 +1,100 @@ +.. Copyright (c) <2021>, Intel Corporation + All rights reserved. + +Memory error detect standard tool - AddressSanitizer(ASan) +========================================================== + +AddressSanitizer (ASan) is a google memory error detect +standard tool. It could help to detect use-after-free and +{heap,stack,global}-buffer overflow bugs in C/C++ programs, +print detailed error information when error happens, large +improve debug efficiency. + +By referring to its implementation algorithm +(https://github.com/google/sanitizers/wiki/AddressSanitizerAlgorithm), +enabled heap-buffer-overflow and use-after-free functions on DPDK. +DPDK ASan function currently only supports on Linux x86_64. + +AddressSanitizer is a part of LLVM (3.1+) and GCC (4.8+). + +DPDK ASan functionality is currently only supported Linux x86_64. +Support other platforms, need to define ASAN_SHADOW_OFFSET value +according to google ASan document. + +Example heap-buffer-overflow error +---------------------------------- + +Following error was reported when ASan was enabled:: + + Applied 9 bytes of memory, but accessed the 10th byte of memory, + so heap-buffer-overflow appeared. + +Below code results in this error:: + + Add code to helloworld: + char *p = rte_zmalloc(NULL, 9, 0); + if (!p) { + printf("rte_zmalloc error."); + return -1; + } + p[9] = 'a'; + +The error log:: + + ==369953==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x7fb17f465809 at pc 0x5652e6707b84 bp 0x7ffea70eea20 sp 0x7ffea70eea10 WRITE of size 1 at 0x7fb17f465809 thread T0 + #0 0x5652e6707b83 in main ../examples/helloworld/main.c:47 + #1 0x7fb94953c0b2 in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x270b2) + #2 0x5652e67079bd in _start (/home/pzh/asan_test/x86_64-native-linuxapp-gcc/examples/dpdk-helloworld+0x8329bd) + + Address 0x7fb17f465809 is a wild pointer. + SUMMARY: AddressSanitizer: heap-buffer-overflow ../examples/helloworld/main.c:47 in main + +Example use-after-free error +---------------------------- + +Following error was reported when ASan was enabled:: + + Applied for 9 bytes of memory, and accessed the first byte after + released, so heap-use-after-free appeared. + +Below code results in this error:: + + Add code to helloworld: + char *p = rte_zmalloc(NULL, 9, 0); + if (!p) { + printf("rte_zmalloc error."); + return -1; + } + rte_free(p); + *p = 'a'; + +The error log:: + + ==417048==ERROR: AddressSanitizer: heap-use-after-free on address 0x7fc83f465800 at pc 0x564308a39b89 bp 0x7ffc8c85bf50 sp 0x7ffc8c85bf40 WRITE of size 1 at 0x7fc83f465800 thread T0 + #0 0x564308a39b88 in main ../examples/helloworld/main.c:48 + #1 0x7fd0079c60b2 in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x270b2) + #2 0x564308a399bd in _start (/home/pzh/asan_test/x86_64-native-linuxapp-gcc/examples/dpdk-helloworld+0x8329bd) + + Address 0x7fc83f465800 is a wild pointer. + SUMMARY: AddressSanitizer: heap-use-after-free ../examples/helloworld/main.c:48 in main + +Usage +----- + +meson build +^^^^^^^^^^^ + +To enable ASan in meson build system, use following meson build command: + +Example usage:: + + meson build -Dbuildtype=debug -Db_lundef=false -Db_sanitize=address + ninja -C build + +.. Note:: + + a) Some of the features of ASan (for example, 'Display memory application location, currently + displayed as a wild pointer') are not currently supported by DPDK's implementation. + b) DPDK test has been completed in ubuntu18.04/ubuntu20.04/redhat8.3. Centos needs to install + libasan separately. + c) If the program uses cmdline, when a memory bug occurs, need to execute the "stty echo" command. diff --git a/doc/guides/prog_guide/index.rst b/doc/guides/prog_guide/index.rst index 2dce507f46..df8a4b93e1 100644 --- a/doc/guides/prog_guide/index.rst +++ b/doc/guides/prog_guide/index.rst @@ -71,3 +71,4 @@ Programmer's Guide lto profile_app glossary + asan -- 2.25.1 ^ permalink raw reply [flat|nested] 117+ messages in thread
* [dpdk-dev] [PATCH v8 2/3] DPDK code adapts to ASan 2021-10-11 6:28 ` [dpdk-dev] [PATCH v8 1/3] Enable ASan for memory detector on DPDK zhihongx.peng @ 2021-10-11 6:28 ` zhihongx.peng 2021-10-11 6:28 ` [dpdk-dev] [PATCH v8 3/3] lib/pipeline: Fix compilation error with gcc ASan zhihongx.peng 2021-10-12 9:43 ` [dpdk-dev] [PATCH v9 1/3] Enable ASan for memory detector on DPDK zhihongx.peng 2 siblings, 0 replies; 117+ messages in thread From: zhihongx.peng @ 2021-10-11 6:28 UTC (permalink / raw) To: anatoly.burakov, konstantin.ananyev, stephen Cc: dev, xueqin.lin, Zhihong Peng From: Zhihong Peng <zhihongx.peng@intel.com> DPDK ASan functionality is currently only supported Linux x86_64. Support other platforms, need to define ASAN_SHADOW_OFFSET value according to google ASan document. Signed-off-by: Xueqin Lin <xueqin.lin@intel.com> Signed-off-by: Zhihong Peng <zhihongx.peng@intel.com> --- lib/eal/common/malloc_elem.c | 26 ++++- lib/eal/common/malloc_elem.h | 204 ++++++++++++++++++++++++++++++++++- lib/eal/common/malloc_heap.c | 12 +++ lib/eal/common/rte_malloc.c | 9 +- 4 files changed, 246 insertions(+), 5 deletions(-) diff --git a/lib/eal/common/malloc_elem.c b/lib/eal/common/malloc_elem.c index c2c9461f1d..bdd20a162e 100644 --- a/lib/eal/common/malloc_elem.c +++ b/lib/eal/common/malloc_elem.c @@ -446,6 +446,8 @@ malloc_elem_alloc(struct malloc_elem *elem, size_t size, unsigned align, struct malloc_elem *new_free_elem = RTE_PTR_ADD(new_elem, size + MALLOC_ELEM_OVERHEAD); + asan_clear_split_alloczone(new_free_elem); + split_elem(elem, new_free_elem); malloc_elem_free_list_insert(new_free_elem); @@ -458,6 +460,8 @@ malloc_elem_alloc(struct malloc_elem *elem, size_t size, unsigned align, elem->state = ELEM_BUSY; elem->pad = old_elem_size; + asan_clear_alloczone(elem); + /* put a dummy header in padding, to point to real element header */ if (elem->pad > 0) { /* pad will be at least 64-bytes, as everything * is cache-line aligned */ @@ -470,12 +474,18 @@ malloc_elem_alloc(struct malloc_elem *elem, size_t size, unsigned align, return new_elem; } + asan_clear_split_alloczone(new_elem); + /* we are going to split the element in two. The original element * remains free, and the new element is the one allocated. * Re-insert original element, in case its new size makes it * belong on a different list. */ + split_elem(elem, new_elem); + + asan_clear_alloczone(new_elem); + new_elem->state = ELEM_BUSY; malloc_elem_free_list_insert(elem); @@ -601,6 +611,8 @@ malloc_elem_hide_region(struct malloc_elem *elem, void *start, size_t len) if (next && next_elem_is_adjacent(elem)) { len_after = RTE_PTR_DIFF(next, hide_end); if (len_after >= MALLOC_ELEM_OVERHEAD + MIN_DATA_SIZE) { + asan_clear_split_alloczone(hide_end); + /* split after */ split_elem(elem, hide_end); @@ -615,6 +627,8 @@ malloc_elem_hide_region(struct malloc_elem *elem, void *start, size_t len) if (prev && prev_elem_is_adjacent(elem)) { len_before = RTE_PTR_DIFF(hide_start, elem); if (len_before >= MALLOC_ELEM_OVERHEAD + MIN_DATA_SIZE) { + asan_clear_split_alloczone(hide_start); + /* split before */ split_elem(elem, hide_start); @@ -628,6 +642,8 @@ malloc_elem_hide_region(struct malloc_elem *elem, void *start, size_t len) } } + asan_clear_alloczone(elem); + remove_elem(elem); } @@ -641,8 +657,10 @@ malloc_elem_resize(struct malloc_elem *elem, size_t size) const size_t new_size = size + elem->pad + MALLOC_ELEM_OVERHEAD; /* if we request a smaller size, then always return ok */ - if (elem->size >= new_size) + if (elem->size >= new_size) { + asan_clear_alloczone(elem); return 0; + } /* check if there is a next element, it's free and adjacent */ if (!elem->next || elem->next->state != ELEM_FREE || @@ -661,9 +679,15 @@ malloc_elem_resize(struct malloc_elem *elem, size_t size) /* now we have a big block together. Lets cut it down a bit, by splitting */ struct malloc_elem *split_pt = RTE_PTR_ADD(elem, new_size); split_pt = RTE_PTR_ALIGN_CEIL(split_pt, RTE_CACHE_LINE_SIZE); + + asan_clear_split_alloczone(split_pt); + split_elem(elem, split_pt); malloc_elem_free_list_insert(split_pt); } + + asan_clear_alloczone(elem); + return 0; } diff --git a/lib/eal/common/malloc_elem.h b/lib/eal/common/malloc_elem.h index a1e5f7f02c..6007a38fbc 100644 --- a/lib/eal/common/malloc_elem.h +++ b/lib/eal/common/malloc_elem.h @@ -7,6 +7,16 @@ #include <stdbool.h> +#ifdef RTE_ARCH_X86_64 +#ifdef __SANITIZE_ADDRESS__ +#define RTE_MALLOC_ASAN +#elif defined(__has_feature) +# if __has_feature(address_sanitizer) +#define RTE_MALLOC_ASAN +# endif +#endif +#endif + #define MIN_DATA_SIZE (RTE_CACHE_LINE_SIZE) /* dummy definition of struct so we can use pointers to it in malloc_elem struct */ @@ -36,10 +46,20 @@ struct malloc_elem { uint64_t header_cookie; /* Cookie marking start of data */ /* trailer cookie at start + size */ #endif +#ifdef RTE_MALLOC_ASAN + size_t user_size; + uint64_t asan_cookie[2]; /* must be next to header_cookie */ +#endif } __rte_cache_aligned; +static const unsigned int MALLOC_ELEM_HEADER_LEN = sizeof(struct malloc_elem); + #ifndef RTE_MALLOC_DEBUG -static const unsigned MALLOC_ELEM_TRAILER_LEN = 0; +#ifdef RTE_MALLOC_ASAN +static const unsigned int MALLOC_ELEM_TRAILER_LEN = RTE_CACHE_LINE_SIZE; +#else +static const unsigned int MALLOC_ELEM_TRAILER_LEN; +#endif /* dummy function - just check if pointer is non-null */ static inline int @@ -55,7 +75,7 @@ set_trailer(struct malloc_elem *elem __rte_unused){ } #else -static const unsigned MALLOC_ELEM_TRAILER_LEN = RTE_CACHE_LINE_SIZE; +static const unsigned int MALLOC_ELEM_TRAILER_LEN = RTE_CACHE_LINE_SIZE; #define MALLOC_HEADER_COOKIE 0xbadbadbadadd2e55ULL /**< Header cookie. */ #define MALLOC_TRAILER_COOKIE 0xadd2e55badbadbadULL /**< Trailer cookie.*/ @@ -90,9 +110,187 @@ malloc_elem_cookies_ok(const struct malloc_elem *elem) #endif -static const unsigned MALLOC_ELEM_HEADER_LEN = sizeof(struct malloc_elem); #define MALLOC_ELEM_OVERHEAD (MALLOC_ELEM_HEADER_LEN + MALLOC_ELEM_TRAILER_LEN) +#ifdef RTE_MALLOC_ASAN + +#ifdef RTE_ARCH_X86_64 +#define ASAN_SHADOW_OFFSET 0x00007fff8000 +#endif + +#define ASAN_SHADOW_GRAIN_SIZE 8 +#define ASAN_MEM_FREE_FLAG 0xfd +#define ASAN_MEM_REDZONE_FLAG 0xfa +#define ASAN_SHADOW_SCALE 3 + +#define ASAN_MEM_SHIFT(mem) ((void *)((uintptr_t)(mem) >> ASAN_SHADOW_SCALE)) +#define ASAN_MEM_TO_SHADOW(mem) \ + RTE_PTR_ADD(ASAN_MEM_SHIFT(mem), ASAN_SHADOW_OFFSET) + +#if defined(__clang__) +__attribute__((no_sanitize("address", "hwaddress"))) +#else +__attribute__((no_sanitize_address)) +#endif +static inline void +asan_set_shadow(void *addr, char val) +{ + *(char *)addr = val; +} + +static inline void +asan_set_zone(void *ptr, size_t len, uint32_t val) +{ + size_t offset, i; + void *shadow; + size_t zone_len = len / ASAN_SHADOW_GRAIN_SIZE; + if (len % ASAN_SHADOW_GRAIN_SIZE != 0) + zone_len += 1; + + for (i = 0; i < zone_len; i++) { + offset = i * ASAN_SHADOW_GRAIN_SIZE; + shadow = ASAN_MEM_TO_SHADOW((uintptr_t)ptr + offset); + asan_set_shadow(shadow, val); + } +} + +/* + * When the memory is released, the release mark is + * set in the corresponding range of the shadow area. + */ +static inline void +asan_set_freezone(void *ptr, size_t size) +{ + asan_set_zone(ptr, size, ASAN_MEM_FREE_FLAG); +} + +/* + * When the memory is allocated, memory state must set as accessible. + */ +static inline void +asan_clear_alloczone(struct malloc_elem *elem) +{ + asan_set_zone((void *)elem, elem->size, 0x0); +} + +static inline void +asan_clear_split_alloczone(struct malloc_elem *elem) +{ + void *ptr = RTE_PTR_SUB(elem, MALLOC_ELEM_TRAILER_LEN); + asan_set_zone(ptr, MALLOC_ELEM_OVERHEAD, 0x0); +} + +/* + * When the memory is allocated, the memory boundary is + * marked in the corresponding range of the shadow area. + * Requirement: redzone >= 16, is a power of two. + */ +static inline void +asan_set_redzone(struct malloc_elem *elem, size_t user_size) +{ + uintptr_t head_redzone; + uintptr_t tail_redzone; + void *front_shadow; + void *tail_shadow; + uint32_t val; + + if (elem != NULL) { + if (elem->state != ELEM_PAD) + elem = RTE_PTR_ADD(elem, elem->pad); + + elem->user_size = user_size; + + /* Set mark before the start of the allocated memory */ + head_redzone = (uintptr_t)RTE_PTR_ADD(elem, + MALLOC_ELEM_HEADER_LEN - ASAN_SHADOW_GRAIN_SIZE); + front_shadow = ASAN_MEM_TO_SHADOW(head_redzone); + asan_set_shadow(front_shadow, ASAN_MEM_REDZONE_FLAG); + front_shadow = ASAN_MEM_TO_SHADOW(head_redzone + - ASAN_SHADOW_GRAIN_SIZE); + asan_set_shadow(front_shadow, ASAN_MEM_REDZONE_FLAG); + + /* Set mark after the end of the allocated memory */ + tail_redzone = (uintptr_t)RTE_PTR_ADD(elem, + MALLOC_ELEM_HEADER_LEN + + elem->user_size); + tail_shadow = ASAN_MEM_TO_SHADOW(tail_redzone); + val = (tail_redzone % ASAN_SHADOW_GRAIN_SIZE); + val = (val == 0) ? ASAN_MEM_REDZONE_FLAG : val; + asan_set_shadow(tail_shadow, val); + tail_shadow = ASAN_MEM_TO_SHADOW(tail_redzone + + ASAN_SHADOW_GRAIN_SIZE); + asan_set_shadow(tail_shadow, ASAN_MEM_REDZONE_FLAG); + } +} + +/* + * When the memory is released, the mark of the memory boundary + * in the corresponding range of the shadow area is cleared. + * Requirement: redzone >= 16, is a power of two. + */ +static inline void +asan_clear_redzone(struct malloc_elem *elem) +{ + uintptr_t head_redzone; + uintptr_t tail_redzone; + void *head_shadow; + void *tail_shadow; + + if (elem != NULL) { + elem = RTE_PTR_ADD(elem, elem->pad); + + /* Clear mark before the start of the allocated memory */ + head_redzone = (uintptr_t)RTE_PTR_ADD(elem, + MALLOC_ELEM_HEADER_LEN - ASAN_SHADOW_GRAIN_SIZE); + head_shadow = ASAN_MEM_TO_SHADOW(head_redzone); + asan_set_shadow(head_shadow, 0x00); + head_shadow = ASAN_MEM_TO_SHADOW(head_redzone + - ASAN_SHADOW_GRAIN_SIZE); + asan_set_shadow(head_shadow, 0x00); + + /* Clear mark after the end of the allocated memory */ + tail_redzone = (uintptr_t)RTE_PTR_ADD(elem, + MALLOC_ELEM_HEADER_LEN + elem->user_size); + tail_shadow = ASAN_MEM_TO_SHADOW(tail_redzone); + asan_set_shadow(tail_shadow, 0x00); + tail_shadow = ASAN_MEM_TO_SHADOW(tail_redzone + + ASAN_SHADOW_GRAIN_SIZE); + asan_set_shadow(tail_shadow, 0x00); + } +} + +static inline size_t +old_malloc_size(struct malloc_elem *elem) +{ + if (elem->state != ELEM_PAD) + elem = RTE_PTR_ADD(elem, elem->pad); + + return elem->user_size; +} +#else +static inline void +asan_set_freezone(void *ptr __rte_unused, size_t size __rte_unused) { } + +static inline void +asan_clear_alloczone(struct malloc_elem *elem __rte_unused) { } + +static inline void +asan_clear_split_alloczone(struct malloc_elem *elem __rte_unused) { } + +static inline void +asan_set_redzone(struct malloc_elem *elem __rte_unused, + size_t user_size __rte_unused) { } + +static inline void +asan_clear_redzone(struct malloc_elem *elem __rte_unused) { } + +static inline size_t +old_malloc_size(struct malloc_elem *elem) +{ + return elem->size - elem->pad - MALLOC_ELEM_OVERHEAD; +} +#endif + /* * Given a pointer to the start of a memory block returned by malloc, get * the actual malloc_elem header for that block. diff --git a/lib/eal/common/malloc_heap.c b/lib/eal/common/malloc_heap.c index ee400f38ec..775d6789df 100644 --- a/lib/eal/common/malloc_heap.c +++ b/lib/eal/common/malloc_heap.c @@ -237,6 +237,7 @@ heap_alloc(struct malloc_heap *heap, const char *type __rte_unused, size_t size, unsigned int flags, size_t align, size_t bound, bool contig) { struct malloc_elem *elem; + size_t user_size = size; size = RTE_CACHE_LINE_ROUNDUP(size); align = RTE_CACHE_LINE_ROUNDUP(align); @@ -250,6 +251,8 @@ heap_alloc(struct malloc_heap *heap, const char *type __rte_unused, size_t size, /* increase heap's count of allocated elements */ heap->alloc_count++; + + asan_set_redzone(elem, user_size); } return elem == NULL ? NULL : (void *)(&elem[1]); @@ -270,6 +273,8 @@ heap_alloc_biggest(struct malloc_heap *heap, const char *type __rte_unused, /* increase heap's count of allocated elements */ heap->alloc_count++; + + asan_set_redzone(elem, size); } return elem == NULL ? NULL : (void *)(&elem[1]); @@ -841,6 +846,8 @@ malloc_heap_free(struct malloc_elem *elem) if (!malloc_elem_cookies_ok(elem) || elem->state != ELEM_BUSY) return -1; + asan_clear_redzone(elem); + /* elem may be merged with previous element, so keep heap address */ heap = elem->heap; msl = elem->msl; @@ -848,6 +855,9 @@ malloc_heap_free(struct malloc_elem *elem) rte_spinlock_lock(&(heap->lock)); + void *asan_ptr = RTE_PTR_ADD(elem, MALLOC_ELEM_HEADER_LEN + elem->pad); + size_t asan_data_len = elem->size - MALLOC_ELEM_OVERHEAD - elem->pad; + /* mark element as free */ elem->state = ELEM_FREE; @@ -1001,6 +1011,8 @@ malloc_heap_free(struct malloc_elem *elem) rte_mcfg_mem_write_unlock(); free_unlock: + asan_set_freezone(asan_ptr, asan_data_len); + rte_spinlock_unlock(&(heap->lock)); return ret; } diff --git a/lib/eal/common/rte_malloc.c b/lib/eal/common/rte_malloc.c index 9d39e58c08..d0bec26920 100644 --- a/lib/eal/common/rte_malloc.c +++ b/lib/eal/common/rte_malloc.c @@ -162,6 +162,8 @@ rte_calloc(const char *type, size_t num, size_t size, unsigned align) void * rte_realloc_socket(void *ptr, size_t size, unsigned int align, int socket) { + size_t user_size; + if (ptr == NULL) return rte_malloc_socket(NULL, size, align, socket); @@ -171,6 +173,8 @@ rte_realloc_socket(void *ptr, size_t size, unsigned int align, int socket) return NULL; } + user_size = size; + size = RTE_CACHE_LINE_ROUNDUP(size), align = RTE_CACHE_LINE_ROUNDUP(align); /* check requested socket id and alignment matches first, and if ok, @@ -181,6 +185,9 @@ rte_realloc_socket(void *ptr, size_t size, unsigned int align, int socket) RTE_PTR_ALIGN(ptr, align) == ptr && malloc_heap_resize(elem, size) == 0) { rte_eal_trace_mem_realloc(size, align, socket, ptr); + + asan_set_redzone(elem, user_size); + return ptr; } @@ -192,7 +199,7 @@ rte_realloc_socket(void *ptr, size_t size, unsigned int align, int socket) if (new_ptr == NULL) return NULL; /* elem: |pad|data_elem|data|trailer| */ - const size_t old_size = elem->size - elem->pad - MALLOC_ELEM_OVERHEAD; + const size_t old_size = old_malloc_size(elem); rte_memcpy(new_ptr, ptr, old_size < size ? old_size : size); rte_free(ptr); -- 2.25.1 ^ permalink raw reply [flat|nested] 117+ messages in thread
* [dpdk-dev] [PATCH v8 3/3] lib/pipeline: Fix compilation error with gcc ASan 2021-10-11 6:28 ` [dpdk-dev] [PATCH v8 1/3] Enable ASan for memory detector on DPDK zhihongx.peng 2021-10-11 6:28 ` [dpdk-dev] [PATCH v8 2/3] DPDK code adapts to ASan zhihongx.peng @ 2021-10-11 6:28 ` zhihongx.peng 2021-10-12 9:43 ` [dpdk-dev] [PATCH v9 1/3] Enable ASan for memory detector on DPDK zhihongx.peng 2 siblings, 0 replies; 117+ messages in thread From: zhihongx.peng @ 2021-10-11 6:28 UTC (permalink / raw) To: anatoly.burakov, konstantin.ananyev, stephen Cc: dev, xueqin.lin, Zhihong Peng, stable From: Zhihong Peng <zhihongx.peng@intel.com> The gcc will check code more stricter when ASan enabled. "Control reaches end of non-void function" error occurs here. Fixes: f38913b7fb8e (pipeline: add meter array to SWX) Cc: stable@dpdk.org Signed-off-by: Xueqin Lin <xueqin.lin@intel.com> Signed-off-by: Zhihong Peng <zhihongx.peng@intel.com> --- lib/pipeline/rte_swx_pipeline.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/pipeline/rte_swx_pipeline.c b/lib/pipeline/rte_swx_pipeline.c index 1cd09a4b44..0acd6c6752 100644 --- a/lib/pipeline/rte_swx_pipeline.c +++ b/lib/pipeline/rte_swx_pipeline.c @@ -4642,7 +4642,7 @@ instr_meter_translate(struct rte_swx_pipeline *p, return 0; } - CHECK(0, EINVAL); + return -EINVAL; } static inline void @@ -5937,7 +5937,7 @@ instr_translate(struct rte_swx_pipeline *p, instr, data); - CHECK(0, EINVAL); + return -EINVAL; } static struct instruction_data * -- 2.25.1 ^ permalink raw reply [flat|nested] 117+ messages in thread
* [dpdk-dev] [PATCH v9 1/3] Enable ASan for memory detector on DPDK 2021-10-11 6:28 ` [dpdk-dev] [PATCH v8 1/3] Enable ASan for memory detector on DPDK zhihongx.peng 2021-10-11 6:28 ` [dpdk-dev] [PATCH v8 2/3] DPDK code adapts to ASan zhihongx.peng 2021-10-11 6:28 ` [dpdk-dev] [PATCH v8 3/3] lib/pipeline: Fix compilation error with gcc ASan zhihongx.peng @ 2021-10-12 9:43 ` zhihongx.peng 2021-10-12 9:43 ` [dpdk-dev] [PATCH v9 2/3] DPDK code adapts to ASan zhihongx.peng ` (2 more replies) 2 siblings, 3 replies; 117+ messages in thread From: zhihongx.peng @ 2021-10-12 9:43 UTC (permalink / raw) To: david.marchand, anatoly.burakov, konstantin.ananyev, stephen Cc: dev, xueqin.lin, Zhihong Peng From: Zhihong Peng <zhihongx.peng@intel.com> `AddressSanitizer <https://github.com/google/sanitizers/wiki/AddressSanitizer>` (ASan) is a widely-used debugging tool to detect memory access errors. It helps detect issues like use-after-free, various kinds of buffer overruns in C/C++ programs, and other similar errors, as well as printing out detailed debug information whenever an error is detected. DPDK ASan functionality is currently only supported Linux x86_64. Support other platforms, need to define ASAN_SHADOW_OFFSET value according to google ASan document, and configure meson (config/meson.build). Here is an example of heap-buffer-overflow bug: ...... char *p = rte_zmalloc(NULL, 7, 0); p[7] = 'a'; ...... Here is an example of use-after-free bug: ...... char *p = rte_zmalloc(NULL, 7, 0); rte_free(p); *p = 'a'; ...... We can enable ASan by adding below compilation options: -Dbuildtype=debug -Db_lundef=false -Db_sanitize=address "-Dbuildtype=debug": This is a non-essential option. When this option is added, if a memory error occurs, ASan can clearly show where the code is wrong. "-Db_lundef=false": When use clang to compile DPDK, this option must be added. Signed-off-by: Xueqin Lin <xueqin.lin@intel.com> Signed-off-by: Zhihong Peng <zhihongx.peng@intel.com> --- v7: 1) Split doc and code into two. 2) Modify asan.rst doc v8: No change. v9: 1) Add the check of libasan library. 2) Add release notes. --- config/meson.build | 10 +++ devtools/words-case.txt | 1 + doc/guides/prog_guide/asan.rst | 97 ++++++++++++++++++++++++++ doc/guides/prog_guide/index.rst | 1 + doc/guides/rel_notes/release_21_11.rst | 9 +++ 5 files changed, 118 insertions(+) create mode 100644 doc/guides/prog_guide/asan.rst diff --git a/config/meson.build b/config/meson.build index 4cdf589e20..5170b79fed 100644 --- a/config/meson.build +++ b/config/meson.build @@ -411,6 +411,16 @@ if get_option('b_lto') endif endif +if get_option('b_sanitize') == 'address' + if cc.get_id() == 'gcc' + asan_dep = cc.find_library('asan', required: true) + if (not cc.links('int main(int argc, char *argv[]) { return 0; }', + dependencies: asan_dep)) + error('broken dependency, "libasan"') + endif + endif +endif + if get_option('default_library') == 'both' error( ''' Unsupported value "both" for "default_library" option. diff --git a/devtools/words-case.txt b/devtools/words-case.txt index 0bbad48626..ada6910fa0 100644 --- a/devtools/words-case.txt +++ b/devtools/words-case.txt @@ -5,6 +5,7 @@ API Arm armv7 armv8 +ASan BAR CRC DCB diff --git a/doc/guides/prog_guide/asan.rst b/doc/guides/prog_guide/asan.rst new file mode 100644 index 0000000000..627675ef46 --- /dev/null +++ b/doc/guides/prog_guide/asan.rst @@ -0,0 +1,97 @@ +.. Copyright (c) <2021>, Intel Corporation + All rights reserved. + +Memory error detect standard tool - AddressSanitizer(ASan) +========================================================== + +`AddressSanitizer +<https://github.com/google/sanitizers/wiki/AddressSanitizer>` (ASan) +is a widely-used debugging tool to detect memory access errors. +It helps detect issues like use-after-free, various kinds of buffer +overruns in C/C++ programs, and other similar errors, as well as +printing out detailed debug information whenever an error is detected. + +AddressSanitizer is a part of LLVM (3.1+) and GCC (4.8+). + +DPDK ASan functionality is currently only supported Linux x86_64. +Support other platforms, need to define ASAN_SHADOW_OFFSET value +according to google ASan document, and configure meson +(config/meson.build). + +Example heap-buffer-overflow error +---------------------------------- + +Following error was reported when ASan was enabled:: + + Applied 9 bytes of memory, but accessed the 10th byte of memory, + so heap-buffer-overflow appeared. + +Below code results in this error:: + + Add code to helloworld: + char *p = rte_zmalloc(NULL, 9, 0); + if (!p) { + printf("rte_zmalloc error."); + return -1; + } + p[9] = 'a'; + +The error log:: + + ==369953==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x7fb17f465809 at pc 0x5652e6707b84 bp 0x7ffea70eea20 sp 0x7ffea70eea10 WRITE of size 1 at 0x7fb17f465809 thread T0 + #0 0x5652e6707b83 in main ../examples/helloworld/main.c:47 + #1 0x7fb94953c0b2 in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x270b2) + #2 0x5652e67079bd in _start (/home/pzh/asan_test/x86_64-native-linuxapp-gcc/examples/dpdk-helloworld+0x8329bd) + + Address 0x7fb17f465809 is a wild pointer. + SUMMARY: AddressSanitizer: heap-buffer-overflow ../examples/helloworld/main.c:47 in main + +Example use-after-free error +---------------------------- + +Following error was reported when ASan was enabled:: + + Applied for 9 bytes of memory, and accessed the first byte after + released, so heap-use-after-free appeared. + +Below code results in this error:: + + Add code to helloworld: + char *p = rte_zmalloc(NULL, 9, 0); + if (!p) { + printf("rte_zmalloc error."); + return -1; + } + rte_free(p); + *p = 'a'; + +The error log:: + + ==417048==ERROR: AddressSanitizer: heap-use-after-free on address 0x7fc83f465800 at pc 0x564308a39b89 bp 0x7ffc8c85bf50 sp 0x7ffc8c85bf40 WRITE of size 1 at 0x7fc83f465800 thread T0 + #0 0x564308a39b88 in main ../examples/helloworld/main.c:48 + #1 0x7fd0079c60b2 in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x270b2) + #2 0x564308a399bd in _start (/home/pzh/asan_test/x86_64-native-linuxapp-gcc/examples/dpdk-helloworld+0x8329bd) + + Address 0x7fc83f465800 is a wild pointer. + SUMMARY: AddressSanitizer: heap-use-after-free ../examples/helloworld/main.c:48 in main + +Usage +----- + +meson build +^^^^^^^^^^^ + +To enable ASan in meson build system, use following meson build command: + +Example usage:: + + meson build -Dbuildtype=debug -Db_lundef=false -Db_sanitize=address + ninja -C build + +.. Note:: + + a) Some of the features of ASan (for example, 'Display memory application location, currently + displayed as a wild pointer') are not currently supported by DPDK's implementation. + b) DPDK test has been completed in ubuntu18.04/ubuntu20.04/redhat8.3. To compile with gcc in + centos, libasan needs to be installed separately. + c) If the program uses cmdline, when a memory bug occurs, need to execute the "stty echo" command. diff --git a/doc/guides/prog_guide/index.rst b/doc/guides/prog_guide/index.rst index 2dce507f46..df8a4b93e1 100644 --- a/doc/guides/prog_guide/index.rst +++ b/doc/guides/prog_guide/index.rst @@ -71,3 +71,4 @@ Programmer's Guide lto profile_app glossary + asan diff --git a/doc/guides/rel_notes/release_21_11.rst b/doc/guides/rel_notes/release_21_11.rst index 5036641842..58c6377148 100644 --- a/doc/guides/rel_notes/release_21_11.rst +++ b/doc/guides/rel_notes/release_21_11.rst @@ -141,6 +141,15 @@ New Features * Added tests to validate packets hard expiry. * Added tests to verify tunnel header verification in IPsec inbound. +* **Enable ASan for memory detector on DPDK.** + + `AddressSanitizer + <https://github.com/google/sanitizers/wiki/AddressSanitizer>` (ASan) + is a widely-used debugging tool to detect memory access errors. + It helps detect issues like use-after-free, various kinds of buffer + overruns in C/C++ programs, and other similar errors, as well as + printing out detailed debug information whenever an error is detected. + Removed Items ------------- -- 2.25.1 ^ permalink raw reply [flat|nested] 117+ messages in thread
* [dpdk-dev] [PATCH v9 2/3] DPDK code adapts to ASan 2021-10-12 9:43 ` [dpdk-dev] [PATCH v9 1/3] Enable ASan for memory detector on DPDK zhihongx.peng @ 2021-10-12 9:43 ` zhihongx.peng 2021-10-13 16:45 ` David Marchand 2021-10-12 9:43 ` [dpdk-dev] [PATCH v9 3/3] pipeline: Fix compilation error with gcc ASan zhihongx.peng 2021-10-13 16:44 ` [dpdk-dev] [PATCH v9 1/3] " David Marchand 2 siblings, 1 reply; 117+ messages in thread From: zhihongx.peng @ 2021-10-12 9:43 UTC (permalink / raw) To: david.marchand, anatoly.burakov, konstantin.ananyev, stephen Cc: dev, xueqin.lin, Zhihong Peng From: Zhihong Peng <zhihongx.peng@intel.com> DPDK ASan functionality is currently only supported Linux x86_64. Support other platforms, need to define ASAN_SHADOW_OFFSET value according to google ASan document, and configure meson (config/meson.build). Signed-off-by: Xueqin Lin <xueqin.lin@intel.com> Signed-off-by: Zhihong Peng <zhihongx.peng@intel.com> --- v7: Split doc and code into two. v8: No change. v9: Modify the definition of RTE_MALLOC_ASAN. --- config/meson.build | 2 + lib/eal/common/malloc_elem.c | 26 ++++- lib/eal/common/malloc_elem.h | 194 ++++++++++++++++++++++++++++++++++- lib/eal/common/malloc_heap.c | 12 +++ lib/eal/common/rte_malloc.c | 9 +- 5 files changed, 238 insertions(+), 5 deletions(-) diff --git a/config/meson.build b/config/meson.build index 5170b79fed..1ae9390e68 100644 --- a/config/meson.build +++ b/config/meson.build @@ -419,6 +419,8 @@ if get_option('b_sanitize') == 'address' error('broken dependency, "libasan"') endif endif + + dpdk_conf.set10('RTE_MALLOC_ASAN', true) endif if get_option('default_library') == 'both' diff --git a/lib/eal/common/malloc_elem.c b/lib/eal/common/malloc_elem.c index c2c9461f1d..bdd20a162e 100644 --- a/lib/eal/common/malloc_elem.c +++ b/lib/eal/common/malloc_elem.c @@ -446,6 +446,8 @@ malloc_elem_alloc(struct malloc_elem *elem, size_t size, unsigned align, struct malloc_elem *new_free_elem = RTE_PTR_ADD(new_elem, size + MALLOC_ELEM_OVERHEAD); + asan_clear_split_alloczone(new_free_elem); + split_elem(elem, new_free_elem); malloc_elem_free_list_insert(new_free_elem); @@ -458,6 +460,8 @@ malloc_elem_alloc(struct malloc_elem *elem, size_t size, unsigned align, elem->state = ELEM_BUSY; elem->pad = old_elem_size; + asan_clear_alloczone(elem); + /* put a dummy header in padding, to point to real element header */ if (elem->pad > 0) { /* pad will be at least 64-bytes, as everything * is cache-line aligned */ @@ -470,12 +474,18 @@ malloc_elem_alloc(struct malloc_elem *elem, size_t size, unsigned align, return new_elem; } + asan_clear_split_alloczone(new_elem); + /* we are going to split the element in two. The original element * remains free, and the new element is the one allocated. * Re-insert original element, in case its new size makes it * belong on a different list. */ + split_elem(elem, new_elem); + + asan_clear_alloczone(new_elem); + new_elem->state = ELEM_BUSY; malloc_elem_free_list_insert(elem); @@ -601,6 +611,8 @@ malloc_elem_hide_region(struct malloc_elem *elem, void *start, size_t len) if (next && next_elem_is_adjacent(elem)) { len_after = RTE_PTR_DIFF(next, hide_end); if (len_after >= MALLOC_ELEM_OVERHEAD + MIN_DATA_SIZE) { + asan_clear_split_alloczone(hide_end); + /* split after */ split_elem(elem, hide_end); @@ -615,6 +627,8 @@ malloc_elem_hide_region(struct malloc_elem *elem, void *start, size_t len) if (prev && prev_elem_is_adjacent(elem)) { len_before = RTE_PTR_DIFF(hide_start, elem); if (len_before >= MALLOC_ELEM_OVERHEAD + MIN_DATA_SIZE) { + asan_clear_split_alloczone(hide_start); + /* split before */ split_elem(elem, hide_start); @@ -628,6 +642,8 @@ malloc_elem_hide_region(struct malloc_elem *elem, void *start, size_t len) } } + asan_clear_alloczone(elem); + remove_elem(elem); } @@ -641,8 +657,10 @@ malloc_elem_resize(struct malloc_elem *elem, size_t size) const size_t new_size = size + elem->pad + MALLOC_ELEM_OVERHEAD; /* if we request a smaller size, then always return ok */ - if (elem->size >= new_size) + if (elem->size >= new_size) { + asan_clear_alloczone(elem); return 0; + } /* check if there is a next element, it's free and adjacent */ if (!elem->next || elem->next->state != ELEM_FREE || @@ -661,9 +679,15 @@ malloc_elem_resize(struct malloc_elem *elem, size_t size) /* now we have a big block together. Lets cut it down a bit, by splitting */ struct malloc_elem *split_pt = RTE_PTR_ADD(elem, new_size); split_pt = RTE_PTR_ALIGN_CEIL(split_pt, RTE_CACHE_LINE_SIZE); + + asan_clear_split_alloczone(split_pt); + split_elem(elem, split_pt); malloc_elem_free_list_insert(split_pt); } + + asan_clear_alloczone(elem); + return 0; } diff --git a/lib/eal/common/malloc_elem.h b/lib/eal/common/malloc_elem.h index a1e5f7f02c..a06c11ac8b 100644 --- a/lib/eal/common/malloc_elem.h +++ b/lib/eal/common/malloc_elem.h @@ -36,10 +36,20 @@ struct malloc_elem { uint64_t header_cookie; /* Cookie marking start of data */ /* trailer cookie at start + size */ #endif +#ifdef RTE_MALLOC_ASAN + size_t user_size; + uint64_t asan_cookie[2]; /* must be next to header_cookie */ +#endif } __rte_cache_aligned; +static const unsigned int MALLOC_ELEM_HEADER_LEN = sizeof(struct malloc_elem); + #ifndef RTE_MALLOC_DEBUG -static const unsigned MALLOC_ELEM_TRAILER_LEN = 0; +#ifdef RTE_MALLOC_ASAN +static const unsigned int MALLOC_ELEM_TRAILER_LEN = RTE_CACHE_LINE_SIZE; +#else +static const unsigned int MALLOC_ELEM_TRAILER_LEN; +#endif /* dummy function - just check if pointer is non-null */ static inline int @@ -55,7 +65,7 @@ set_trailer(struct malloc_elem *elem __rte_unused){ } #else -static const unsigned MALLOC_ELEM_TRAILER_LEN = RTE_CACHE_LINE_SIZE; +static const unsigned int MALLOC_ELEM_TRAILER_LEN = RTE_CACHE_LINE_SIZE; #define MALLOC_HEADER_COOKIE 0xbadbadbadadd2e55ULL /**< Header cookie. */ #define MALLOC_TRAILER_COOKIE 0xadd2e55badbadbadULL /**< Trailer cookie.*/ @@ -90,9 +100,187 @@ malloc_elem_cookies_ok(const struct malloc_elem *elem) #endif -static const unsigned MALLOC_ELEM_HEADER_LEN = sizeof(struct malloc_elem); #define MALLOC_ELEM_OVERHEAD (MALLOC_ELEM_HEADER_LEN + MALLOC_ELEM_TRAILER_LEN) +#ifdef RTE_MALLOC_ASAN + +#ifdef RTE_ARCH_X86_64 +#define ASAN_SHADOW_OFFSET 0x00007fff8000 +#endif + +#define ASAN_SHADOW_GRAIN_SIZE 8 +#define ASAN_MEM_FREE_FLAG 0xfd +#define ASAN_MEM_REDZONE_FLAG 0xfa +#define ASAN_SHADOW_SCALE 3 + +#define ASAN_MEM_SHIFT(mem) ((void *)((uintptr_t)(mem) >> ASAN_SHADOW_SCALE)) +#define ASAN_MEM_TO_SHADOW(mem) \ + RTE_PTR_ADD(ASAN_MEM_SHIFT(mem), ASAN_SHADOW_OFFSET) + +#if defined(__clang__) +__attribute__((no_sanitize("address", "hwaddress"))) +#else +__attribute__((no_sanitize_address)) +#endif +static inline void +asan_set_shadow(void *addr, char val) +{ + *(char *)addr = val; +} + +static inline void +asan_set_zone(void *ptr, size_t len, uint32_t val) +{ + size_t offset, i; + void *shadow; + size_t zone_len = len / ASAN_SHADOW_GRAIN_SIZE; + if (len % ASAN_SHADOW_GRAIN_SIZE != 0) + zone_len += 1; + + for (i = 0; i < zone_len; i++) { + offset = i * ASAN_SHADOW_GRAIN_SIZE; + shadow = ASAN_MEM_TO_SHADOW((uintptr_t)ptr + offset); + asan_set_shadow(shadow, val); + } +} + +/* + * When the memory is released, the release mark is + * set in the corresponding range of the shadow area. + */ +static inline void +asan_set_freezone(void *ptr, size_t size) +{ + asan_set_zone(ptr, size, ASAN_MEM_FREE_FLAG); +} + +/* + * When the memory is allocated, memory state must set as accessible. + */ +static inline void +asan_clear_alloczone(struct malloc_elem *elem) +{ + asan_set_zone((void *)elem, elem->size, 0x0); +} + +static inline void +asan_clear_split_alloczone(struct malloc_elem *elem) +{ + void *ptr = RTE_PTR_SUB(elem, MALLOC_ELEM_TRAILER_LEN); + asan_set_zone(ptr, MALLOC_ELEM_OVERHEAD, 0x0); +} + +/* + * When the memory is allocated, the memory boundary is + * marked in the corresponding range of the shadow area. + * Requirement: redzone >= 16, is a power of two. + */ +static inline void +asan_set_redzone(struct malloc_elem *elem, size_t user_size) +{ + uintptr_t head_redzone; + uintptr_t tail_redzone; + void *front_shadow; + void *tail_shadow; + uint32_t val; + + if (elem != NULL) { + if (elem->state != ELEM_PAD) + elem = RTE_PTR_ADD(elem, elem->pad); + + elem->user_size = user_size; + + /* Set mark before the start of the allocated memory */ + head_redzone = (uintptr_t)RTE_PTR_ADD(elem, + MALLOC_ELEM_HEADER_LEN - ASAN_SHADOW_GRAIN_SIZE); + front_shadow = ASAN_MEM_TO_SHADOW(head_redzone); + asan_set_shadow(front_shadow, ASAN_MEM_REDZONE_FLAG); + front_shadow = ASAN_MEM_TO_SHADOW(head_redzone + - ASAN_SHADOW_GRAIN_SIZE); + asan_set_shadow(front_shadow, ASAN_MEM_REDZONE_FLAG); + + /* Set mark after the end of the allocated memory */ + tail_redzone = (uintptr_t)RTE_PTR_ADD(elem, + MALLOC_ELEM_HEADER_LEN + + elem->user_size); + tail_shadow = ASAN_MEM_TO_SHADOW(tail_redzone); + val = (tail_redzone % ASAN_SHADOW_GRAIN_SIZE); + val = (val == 0) ? ASAN_MEM_REDZONE_FLAG : val; + asan_set_shadow(tail_shadow, val); + tail_shadow = ASAN_MEM_TO_SHADOW(tail_redzone + + ASAN_SHADOW_GRAIN_SIZE); + asan_set_shadow(tail_shadow, ASAN_MEM_REDZONE_FLAG); + } +} + +/* + * When the memory is released, the mark of the memory boundary + * in the corresponding range of the shadow area is cleared. + * Requirement: redzone >= 16, is a power of two. + */ +static inline void +asan_clear_redzone(struct malloc_elem *elem) +{ + uintptr_t head_redzone; + uintptr_t tail_redzone; + void *head_shadow; + void *tail_shadow; + + if (elem != NULL) { + elem = RTE_PTR_ADD(elem, elem->pad); + + /* Clear mark before the start of the allocated memory */ + head_redzone = (uintptr_t)RTE_PTR_ADD(elem, + MALLOC_ELEM_HEADER_LEN - ASAN_SHADOW_GRAIN_SIZE); + head_shadow = ASAN_MEM_TO_SHADOW(head_redzone); + asan_set_shadow(head_shadow, 0x00); + head_shadow = ASAN_MEM_TO_SHADOW(head_redzone + - ASAN_SHADOW_GRAIN_SIZE); + asan_set_shadow(head_shadow, 0x00); + + /* Clear mark after the end of the allocated memory */ + tail_redzone = (uintptr_t)RTE_PTR_ADD(elem, + MALLOC_ELEM_HEADER_LEN + elem->user_size); + tail_shadow = ASAN_MEM_TO_SHADOW(tail_redzone); + asan_set_shadow(tail_shadow, 0x00); + tail_shadow = ASAN_MEM_TO_SHADOW(tail_redzone + + ASAN_SHADOW_GRAIN_SIZE); + asan_set_shadow(tail_shadow, 0x00); + } +} + +static inline size_t +old_malloc_size(struct malloc_elem *elem) +{ + if (elem->state != ELEM_PAD) + elem = RTE_PTR_ADD(elem, elem->pad); + + return elem->user_size; +} +#else +static inline void +asan_set_freezone(void *ptr __rte_unused, size_t size __rte_unused) { } + +static inline void +asan_clear_alloczone(struct malloc_elem *elem __rte_unused) { } + +static inline void +asan_clear_split_alloczone(struct malloc_elem *elem __rte_unused) { } + +static inline void +asan_set_redzone(struct malloc_elem *elem __rte_unused, + size_t user_size __rte_unused) { } + +static inline void +asan_clear_redzone(struct malloc_elem *elem __rte_unused) { } + +static inline size_t +old_malloc_size(struct malloc_elem *elem) +{ + return elem->size - elem->pad - MALLOC_ELEM_OVERHEAD; +} +#endif + /* * Given a pointer to the start of a memory block returned by malloc, get * the actual malloc_elem header for that block. diff --git a/lib/eal/common/malloc_heap.c b/lib/eal/common/malloc_heap.c index ee400f38ec..775d6789df 100644 --- a/lib/eal/common/malloc_heap.c +++ b/lib/eal/common/malloc_heap.c @@ -237,6 +237,7 @@ heap_alloc(struct malloc_heap *heap, const char *type __rte_unused, size_t size, unsigned int flags, size_t align, size_t bound, bool contig) { struct malloc_elem *elem; + size_t user_size = size; size = RTE_CACHE_LINE_ROUNDUP(size); align = RTE_CACHE_LINE_ROUNDUP(align); @@ -250,6 +251,8 @@ heap_alloc(struct malloc_heap *heap, const char *type __rte_unused, size_t size, /* increase heap's count of allocated elements */ heap->alloc_count++; + + asan_set_redzone(elem, user_size); } return elem == NULL ? NULL : (void *)(&elem[1]); @@ -270,6 +273,8 @@ heap_alloc_biggest(struct malloc_heap *heap, const char *type __rte_unused, /* increase heap's count of allocated elements */ heap->alloc_count++; + + asan_set_redzone(elem, size); } return elem == NULL ? NULL : (void *)(&elem[1]); @@ -841,6 +846,8 @@ malloc_heap_free(struct malloc_elem *elem) if (!malloc_elem_cookies_ok(elem) || elem->state != ELEM_BUSY) return -1; + asan_clear_redzone(elem); + /* elem may be merged with previous element, so keep heap address */ heap = elem->heap; msl = elem->msl; @@ -848,6 +855,9 @@ malloc_heap_free(struct malloc_elem *elem) rte_spinlock_lock(&(heap->lock)); + void *asan_ptr = RTE_PTR_ADD(elem, MALLOC_ELEM_HEADER_LEN + elem->pad); + size_t asan_data_len = elem->size - MALLOC_ELEM_OVERHEAD - elem->pad; + /* mark element as free */ elem->state = ELEM_FREE; @@ -1001,6 +1011,8 @@ malloc_heap_free(struct malloc_elem *elem) rte_mcfg_mem_write_unlock(); free_unlock: + asan_set_freezone(asan_ptr, asan_data_len); + rte_spinlock_unlock(&(heap->lock)); return ret; } diff --git a/lib/eal/common/rte_malloc.c b/lib/eal/common/rte_malloc.c index 9d39e58c08..d0bec26920 100644 --- a/lib/eal/common/rte_malloc.c +++ b/lib/eal/common/rte_malloc.c @@ -162,6 +162,8 @@ rte_calloc(const char *type, size_t num, size_t size, unsigned align) void * rte_realloc_socket(void *ptr, size_t size, unsigned int align, int socket) { + size_t user_size; + if (ptr == NULL) return rte_malloc_socket(NULL, size, align, socket); @@ -171,6 +173,8 @@ rte_realloc_socket(void *ptr, size_t size, unsigned int align, int socket) return NULL; } + user_size = size; + size = RTE_CACHE_LINE_ROUNDUP(size), align = RTE_CACHE_LINE_ROUNDUP(align); /* check requested socket id and alignment matches first, and if ok, @@ -181,6 +185,9 @@ rte_realloc_socket(void *ptr, size_t size, unsigned int align, int socket) RTE_PTR_ALIGN(ptr, align) == ptr && malloc_heap_resize(elem, size) == 0) { rte_eal_trace_mem_realloc(size, align, socket, ptr); + + asan_set_redzone(elem, user_size); + return ptr; } @@ -192,7 +199,7 @@ rte_realloc_socket(void *ptr, size_t size, unsigned int align, int socket) if (new_ptr == NULL) return NULL; /* elem: |pad|data_elem|data|trailer| */ - const size_t old_size = elem->size - elem->pad - MALLOC_ELEM_OVERHEAD; + const size_t old_size = old_malloc_size(elem); rte_memcpy(new_ptr, ptr, old_size < size ? old_size : size); rte_free(ptr); -- 2.25.1 ^ permalink raw reply [flat|nested] 117+ messages in thread
* Re: [dpdk-dev] [PATCH v9 2/3] DPDK code adapts to ASan 2021-10-12 9:43 ` [dpdk-dev] [PATCH v9 2/3] DPDK code adapts to ASan zhihongx.peng @ 2021-10-13 16:45 ` David Marchand 2021-10-14 11:45 ` Peng, ZhihongX 0 siblings, 1 reply; 117+ messages in thread From: David Marchand @ 2021-10-13 16:45 UTC (permalink / raw) To: Zhihong Peng Cc: Burakov, Anatoly, Ananyev, Konstantin, Stephen Hemminger, dev, Xueqin Lin, Bruce Richardson, Thomas Monjalon On Tue, Oct 12, 2021 at 11:54 AM <zhihongx.peng@intel.com> wrote: > > From: Zhihong Peng <zhihongx.peng@intel.com> > > DPDK ASan functionality is currently only supported Linux x86_64. > Support other platforms, need to define ASAN_SHADOW_OFFSET value > according to google ASan document, and configure meson > (config/meson.build). > > Signed-off-by: Xueqin Lin <xueqin.lin@intel.com> > Signed-off-by: Zhihong Peng <zhihongx.peng@intel.com> > --- > v7: Split doc and code into two. > v8: No change. > v9: Modify the definition of RTE_MALLOC_ASAN. > --- > config/meson.build | 2 + > lib/eal/common/malloc_elem.c | 26 ++++- > lib/eal/common/malloc_elem.h | 194 ++++++++++++++++++++++++++++++++++- > lib/eal/common/malloc_heap.c | 12 +++ > lib/eal/common/rte_malloc.c | 9 +- > 5 files changed, 238 insertions(+), 5 deletions(-) > > diff --git a/config/meson.build b/config/meson.build > index 5170b79fed..1ae9390e68 100644 > --- a/config/meson.build > +++ b/config/meson.build > @@ -419,6 +419,8 @@ if get_option('b_sanitize') == 'address' > error('broken dependency, "libasan"') > endif > endif > + > + dpdk_conf.set10('RTE_MALLOC_ASAN', true) The test on architecture == x86_64 is missing. ARM compilation with ASan is broken. $ ./devtools/test-meson-builds.sh ... ... ninja: Entering directory `/home/dmarchan/builds/build-arm64-bluefield' [0/1] Regenerating build files. The Meson build system Version: 0.59.0 Source dir: /home/dmarchan/dpdk Build dir: /home/dmarchan/builds/build-arm64-bluefield ... [2/2784] Compiling C object lib/librte_eal.a.p/eal_common_eal_common_memzone.c.o FAILED: lib/librte_eal.a.p/eal_common_eal_common_memzone.c.o aarch64-linux-gnu-gcc -Ilib/librte_eal.a.p -Ilib -I../../dpdk/lib -I. -I../../dpdk -Iconfig -I../../dpdk/config -Ilib/eal/include -I../../dpdk/lib/eal/include -Ilib/eal/linux/include -I../../dpdk/lib/eal/linux/include -Ilib/eal/arm/include -I../../dpdk/lib/eal/arm/include -Ilib/eal/common -I../../dpdk/lib/eal/common -Ilib/eal -I../../dpdk/lib/eal -Ilib/kvargs -I../../dpdk/lib/kvargs -Ilib/metrics -I../../dpdk/lib/metrics -Ilib/telemetry -I../../dpdk/lib/telemetry -fdiagnostics-color=always -fsanitize=address -fno-omit-frame-pointer -D_FILE_OFFSET_BITS=64 -Wall -Winvalid-pch -Werror -O2 -g -include rte_config.h -Wextra -Wcast-qual -Wdeprecated -Wformat -Wformat-nonliteral -Wformat-security -Wmissing-declarations -Wmissing-prototypes -Wnested-externs -Wold-style-definition -Wpointer-arith -Wsign-compare -Wstrict-prototypes -Wundef -Wwrite-strings -Wno-address-of-packed-member -Wno-packed-not-aligned -Wno-missing-field-initializers -Wno-zero-length-bounds -D_GNU_SOURCE -fPIC -mcpu=cortex-a72 -DALLOW_EXPERIMENTAL_API -DALLOW_INTERNAL_API -Wno-format-truncation '-DABI_VERSION="22.0"' -DRTE_LIBEAL_USE_GETENTROPY -DRTE_LOG_DEFAULT_LOGTYPE=lib.eal -MD -MQ lib/librte_eal.a.p/eal_common_eal_common_memzone.c.o -MF lib/librte_eal.a.p/eal_common_eal_common_memzone.c.o.d -o lib/librte_eal.a.p/eal_common_eal_common_memzone.c.o -c ../../dpdk/lib/eal/common/eal_common_memzone.c In file included from ../../dpdk/lib/eal/include/rte_log.h:25, from ../../dpdk/lib/eal/common/eal_common_memzone.c:14: ../../dpdk/lib/eal/common/malloc_elem.h: In function ‘asan_set_zone’: ../../dpdk/lib/eal/common/malloc_elem.h:118:35: error: ‘ASAN_SHADOW_OFFSET’ undeclared (first use in this function); did you mean ‘ASAN_SHADOW_SCALE’? 118 | RTE_PTR_ADD(ASAN_MEM_SHIFT(mem), ASAN_SHADOW_OFFSET) | ^~~~~~~~~~~~~~~~~~ ../../dpdk/lib/eal/include/rte_common.h:250:58: note: in definition of macro ‘RTE_PTR_ADD’ 250 | #define RTE_PTR_ADD(ptr, x) ((void*)((uintptr_t)(ptr) + (x))) | ^ ../../dpdk/lib/eal/common/malloc_elem.h:142:12: note: in expansion of macro ‘ASAN_MEM_TO_SHADOW’ 142 | shadow = ASAN_MEM_TO_SHADOW((uintptr_t)ptr + offset); | ^~~~~~~~~~~~~~~~~~ ../../dpdk/lib/eal/common/malloc_elem.h:118:35: note: each undeclared identifier is reported only once for each function it appears in 118 | RTE_PTR_ADD(ASAN_MEM_SHIFT(mem), ASAN_SHADOW_OFFSET) | ^~~~~~~~~~~~~~~~~~ ../../dpdk/lib/eal/include/rte_common.h:250:58: note: in definition of macro ‘RTE_PTR_ADD’ 250 | #define RTE_PTR_ADD(ptr, x) ((void*)((uintptr_t)(ptr) + (x))) | ^ ../../dpdk/lib/eal/common/malloc_elem.h:142:12: note: in expansion of macro ‘ASAN_MEM_TO_SHADOW’ 142 | shadow = ASAN_MEM_TO_SHADOW((uintptr_t)ptr + offset); | ^~~~~~~~~~~~~~~~~~ ../../dpdk/lib/eal/common/malloc_elem.h: In function ‘asan_set_redzone’: ../../dpdk/lib/eal/common/malloc_elem.h:118:35: error: ‘ASAN_SHADOW_OFFSET’ undeclared (first use in this function); did you mean ‘ASAN_SHADOW_SCALE’? 118 | RTE_PTR_ADD(ASAN_MEM_SHIFT(mem), ASAN_SHADOW_OFFSET) | ^~~~~~~~~~~~~~~~~~ ../../dpdk/lib/eal/include/rte_common.h:250:58: note: in definition of macro ‘RTE_PTR_ADD’ 250 | #define RTE_PTR_ADD(ptr, x) ((void*)((uintptr_t)(ptr) + (x))) | ^ ../../dpdk/lib/eal/common/malloc_elem.h:196:18: note: in expansion of macro ‘ASAN_MEM_TO_SHADOW’ 196 | front_shadow = ASAN_MEM_TO_SHADOW(head_redzone); | ^~~~~~~~~~~~~~~~~~ ../../dpdk/lib/eal/common/malloc_elem.h: In function ‘asan_clear_redzone’: ../../dpdk/lib/eal/common/malloc_elem.h:118:35: error: ‘ASAN_SHADOW_OFFSET’ undeclared (first use in this function); did you mean ‘ASAN_SHADOW_SCALE’? 118 | RTE_PTR_ADD(ASAN_MEM_SHIFT(mem), ASAN_SHADOW_OFFSET) | ^~~~~~~~~~~~~~~~~~ ../../dpdk/lib/eal/include/rte_common.h:250:58: note: in definition of macro ‘RTE_PTR_ADD’ 250 | #define RTE_PTR_ADD(ptr, x) ((void*)((uintptr_t)(ptr) + (x))) | ^ ../../dpdk/lib/eal/common/malloc_elem.h:235:17: note: in expansion of macro ‘ASAN_MEM_TO_SHADOW’ 235 | head_shadow = ASAN_MEM_TO_SHADOW(head_redzone); | ^~~~~~~~~~~~~~~~~~ [31/2784] Compiling C object lib/librte_eal.a.p/eal_linux_eal_interrupts.c.o ninja: build stopped: subcommand failed. -- David Marchand -- David Marchand ^ permalink raw reply [flat|nested] 117+ messages in thread
* Re: [dpdk-dev] [PATCH v9 2/3] DPDK code adapts to ASan 2021-10-13 16:45 ` David Marchand @ 2021-10-14 11:45 ` Peng, ZhihongX 0 siblings, 0 replies; 117+ messages in thread From: Peng, ZhihongX @ 2021-10-14 11:45 UTC (permalink / raw) To: David Marchand Cc: Burakov, Anatoly, Ananyev, Konstantin, Stephen Hemminger, dev, Lin, Xueqin, Richardson, Bruce, Thomas Monjalon > -----Original Message----- > From: David Marchand <david.marchand@redhat.com> > Sent: Thursday, October 14, 2021 12:45 AM > To: Peng, ZhihongX <zhihongx.peng@intel.com> > Cc: Burakov, Anatoly <anatoly.burakov@intel.com>; Ananyev, Konstantin > <konstantin.ananyev@intel.com>; Stephen Hemminger > <stephen@networkplumber.org>; dev <dev@dpdk.org>; Lin, Xueqin > <xueqin.lin@intel.com>; Richardson, Bruce <bruce.richardson@intel.com>; > Thomas Monjalon <thomas@monjalon.net> > Subject: Re: [PATCH v9 2/3] DPDK code adapts to ASan > > On Tue, Oct 12, 2021 at 11:54 AM <zhihongx.peng@intel.com> wrote: > > > > From: Zhihong Peng <zhihongx.peng@intel.com> > > > > DPDK ASan functionality is currently only supported Linux x86_64. > > Support other platforms, need to define ASAN_SHADOW_OFFSET value > > according to google ASan document, and configure meson > > (config/meson.build). > > > > Signed-off-by: Xueqin Lin <xueqin.lin@intel.com> > > Signed-off-by: Zhihong Peng <zhihongx.peng@intel.com> > > --- > > v7: Split doc and code into two. > > v8: No change. > > v9: Modify the definition of RTE_MALLOC_ASAN. > > --- > > config/meson.build | 2 + > > lib/eal/common/malloc_elem.c | 26 ++++- > > lib/eal/common/malloc_elem.h | 194 > ++++++++++++++++++++++++++++++++++- > > lib/eal/common/malloc_heap.c | 12 +++ > > lib/eal/common/rte_malloc.c | 9 +- > > 5 files changed, 238 insertions(+), 5 deletions(-) > > > > diff --git a/config/meson.build b/config/meson.build index > > 5170b79fed..1ae9390e68 100644 > > --- a/config/meson.build > > +++ b/config/meson.build > > @@ -419,6 +419,8 @@ if get_option('b_sanitize') == 'address' > > error('broken dependency, "libasan"') > > endif > > endif > > + > > + dpdk_conf.set10('RTE_MALLOC_ASAN', true) > > The test on architecture == x86_64 is missing. > ARM compilation with ASan is broken. > > $ ./devtools/test-meson-builds.sh > ... > ... > ninja: Entering directory `/home/dmarchan/builds/build-arm64-bluefield' > [0/1] Regenerating build files. > The Meson build system > Version: 0.59.0 > Source dir: /home/dmarchan/dpdk > Build dir: /home/dmarchan/builds/build-arm64-bluefield > ... > [2/2784] Compiling C object > lib/librte_eal.a.p/eal_common_eal_common_memzone.c.o > FAILED: lib/librte_eal.a.p/eal_common_eal_common_memzone.c.o > aarch64-linux-gnu-gcc -Ilib/librte_eal.a.p -Ilib -I../../dpdk/lib -I. > -I../../dpdk -Iconfig -I../../dpdk/config -Ilib/eal/include - > I../../dpdk/lib/eal/include -Ilib/eal/linux/include - > I../../dpdk/lib/eal/linux/include -Ilib/eal/arm/include - > I../../dpdk/lib/eal/arm/include -Ilib/eal/common - > I../../dpdk/lib/eal/common -Ilib/eal -I../../dpdk/lib/eal -Ilib/kvargs - > I../../dpdk/lib/kvargs -Ilib/metrics -I../../dpdk/lib/metrics -Ilib/telemetry - > I../../dpdk/lib/telemetry -fdiagnostics-color=always -fsanitize=address -fno- > omit-frame-pointer > -D_FILE_OFFSET_BITS=64 -Wall -Winvalid-pch -Werror -O2 -g -include > rte_config.h -Wextra -Wcast-qual -Wdeprecated -Wformat -Wformat- > nonliteral -Wformat-security -Wmissing-declarations -Wmissing-prototypes - > Wnested-externs -Wold-style-definition -Wpointer-arith -Wsign-compare - > Wstrict-prototypes -Wundef -Wwrite-strings -Wno-address-of-packed- > member -Wno-packed-not-aligned -Wno-missing-field-initializers -Wno- > zero-length-bounds -D_GNU_SOURCE -fPIC -mcpu=cortex-a72 - > DALLOW_EXPERIMENTAL_API -DALLOW_INTERNAL_API -Wno-format- > truncation '-DABI_VERSION="22.0"' > -DRTE_LIBEAL_USE_GETENTROPY -DRTE_LOG_DEFAULT_LOGTYPE=lib.eal - > MD -MQ lib/librte_eal.a.p/eal_common_eal_common_memzone.c.o -MF > lib/librte_eal.a.p/eal_common_eal_common_memzone.c.o.d -o > lib/librte_eal.a.p/eal_common_eal_common_memzone.c.o - > c ../../dpdk/lib/eal/common/eal_common_memzone.c > In file included from ../../dpdk/lib/eal/include/rte_log.h:25, > from ../../dpdk/lib/eal/common/eal_common_memzone.c:14: > ../../dpdk/lib/eal/common/malloc_elem.h: In function ‘asan_set_zone’: > ../../dpdk/lib/eal/common/malloc_elem.h:118:35: error: > ‘ASAN_SHADOW_OFFSET’ undeclared (first use in this function); did you > mean ‘ASAN_SHADOW_SCALE’? > 118 | RTE_PTR_ADD(ASAN_MEM_SHIFT(mem), ASAN_SHADOW_OFFSET) > | ^~~~~~~~~~~~~~~~~~ > ../../dpdk/lib/eal/include/rte_common.h:250:58: note: in definition of macro > ‘RTE_PTR_ADD’ > 250 | #define RTE_PTR_ADD(ptr, x) ((void*)((uintptr_t)(ptr) + (x))) > | ^ > ../../dpdk/lib/eal/common/malloc_elem.h:142:12: note: in expansion of > macro ‘ASAN_MEM_TO_SHADOW’ > 142 | shadow = ASAN_MEM_TO_SHADOW((uintptr_t)ptr + offset); > | ^~~~~~~~~~~~~~~~~~ > ../../dpdk/lib/eal/common/malloc_elem.h:118:35: note: each undeclared > identifier is reported only once for each function it appears in > 118 | RTE_PTR_ADD(ASAN_MEM_SHIFT(mem), ASAN_SHADOW_OFFSET) > | ^~~~~~~~~~~~~~~~~~ > ../../dpdk/lib/eal/include/rte_common.h:250:58: note: in definition of macro > ‘RTE_PTR_ADD’ > 250 | #define RTE_PTR_ADD(ptr, x) ((void*)((uintptr_t)(ptr) + (x))) > | ^ > ../../dpdk/lib/eal/common/malloc_elem.h:142:12: note: in expansion of > macro ‘ASAN_MEM_TO_SHADOW’ > 142 | shadow = ASAN_MEM_TO_SHADOW((uintptr_t)ptr + offset); > | ^~~~~~~~~~~~~~~~~~ > ../../dpdk/lib/eal/common/malloc_elem.h: In function ‘asan_set_redzone’: > ../../dpdk/lib/eal/common/malloc_elem.h:118:35: error: > ‘ASAN_SHADOW_OFFSET’ undeclared (first use in this function); did you > mean ‘ASAN_SHADOW_SCALE’? > 118 | RTE_PTR_ADD(ASAN_MEM_SHIFT(mem), ASAN_SHADOW_OFFSET) > | ^~~~~~~~~~~~~~~~~~ > ../../dpdk/lib/eal/include/rte_common.h:250:58: note: in definition of macro > ‘RTE_PTR_ADD’ > 250 | #define RTE_PTR_ADD(ptr, x) ((void*)((uintptr_t)(ptr) + (x))) > | ^ > ../../dpdk/lib/eal/common/malloc_elem.h:196:18: note: in expansion of > macro ‘ASAN_MEM_TO_SHADOW’ > 196 | front_shadow = ASAN_MEM_TO_SHADOW(head_redzone); > | ^~~~~~~~~~~~~~~~~~ > ../../dpdk/lib/eal/common/malloc_elem.h: In function ‘asan_clear_redzone’: > ../../dpdk/lib/eal/common/malloc_elem.h:118:35: error: > ‘ASAN_SHADOW_OFFSET’ undeclared (first use in this function); did you > mean ‘ASAN_SHADOW_SCALE’? > 118 | RTE_PTR_ADD(ASAN_MEM_SHIFT(mem), ASAN_SHADOW_OFFSET) > | ^~~~~~~~~~~~~~~~~~ > ../../dpdk/lib/eal/include/rte_common.h:250:58: note: in definition of macro > ‘RTE_PTR_ADD’ > 250 | #define RTE_PTR_ADD(ptr, x) ((void*)((uintptr_t)(ptr) + (x))) > | ^ > ../../dpdk/lib/eal/common/malloc_elem.h:235:17: note: in expansion of > macro ‘ASAN_MEM_TO_SHADOW’ > 235 | head_shadow = ASAN_MEM_TO_SHADOW(head_redzone); > | ^~~~~~~~~~~~~~~~~~ > [31/2784] Compiling C object lib/librte_eal.a.p/eal_linux_eal_interrupts.c.o > ninja: build stopped: subcommand failed. The v10 version will be fixed. All meson code: if get_option('b_sanitize') == 'address' or get_option('b_sanitize') == 'address,undefined' if cc.get_id() == 'gcc' asan_dep = cc.find_library('asan', required: true) if (not cc.links('int main(int argc, char *argv[]) { return 0; }', dependencies: asan_dep)) error('broken dependency, "libasan"') endif add_project_link_arguments('-lasan', language: 'c') dpdk_extra_ldflags += '-lasan' endif if exec_env == 'linux' and arch_subdir == 'x86' dpdk_conf.set10('RTE_MALLOC_ASAN', true) endif endif > > > -- > David Marchand > > > > > -- > David Marchand ^ permalink raw reply [flat|nested] 117+ messages in thread
* [dpdk-dev] [PATCH v9 3/3] pipeline: Fix compilation error with gcc ASan 2021-10-12 9:43 ` [dpdk-dev] [PATCH v9 1/3] Enable ASan for memory detector on DPDK zhihongx.peng 2021-10-12 9:43 ` [dpdk-dev] [PATCH v9 2/3] DPDK code adapts to ASan zhihongx.peng @ 2021-10-12 9:43 ` zhihongx.peng 2021-10-15 14:27 ` [dpdk-dev] [PATCH v10 1/4] Enable ASan for memory detector on DPDK zhihongx.peng 2021-10-15 15:11 ` zhihongx.peng 2021-10-13 16:44 ` [dpdk-dev] [PATCH v9 1/3] " David Marchand 2 siblings, 2 replies; 117+ messages in thread From: zhihongx.peng @ 2021-10-12 9:43 UTC (permalink / raw) To: david.marchand, anatoly.burakov, konstantin.ananyev, stephen Cc: dev, xueqin.lin, Zhihong Peng, stable From: Zhihong Peng <zhihongx.peng@intel.com> The gcc will check code more stricter when ASan enabled. "Control reaches end of non-void function" error occurs here. Fixes: f38913b7fb8e ("pipeline: add meter array to SWX") Cc: stable@dpdk.org Signed-off-by: Xueqin Lin <xueqin.lin@intel.com> Signed-off-by: Zhihong Peng <zhihongx.peng@intel.com> --- v7: no change v8: no change v9: Modify the submit log --- lib/pipeline/rte_swx_pipeline.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/pipeline/rte_swx_pipeline.c b/lib/pipeline/rte_swx_pipeline.c index 1cd09a4b44..0acd6c6752 100644 --- a/lib/pipeline/rte_swx_pipeline.c +++ b/lib/pipeline/rte_swx_pipeline.c @@ -4642,7 +4642,7 @@ instr_meter_translate(struct rte_swx_pipeline *p, return 0; } - CHECK(0, EINVAL); + return -EINVAL; } static inline void @@ -5937,7 +5937,7 @@ instr_translate(struct rte_swx_pipeline *p, instr, data); - CHECK(0, EINVAL); + return -EINVAL; } static struct instruction_data * -- 2.25.1 ^ permalink raw reply [flat|nested] 117+ messages in thread
* [dpdk-dev] [PATCH v10 1/4] Enable ASan for memory detector on DPDK 2021-10-12 9:43 ` [dpdk-dev] [PATCH v9 3/3] pipeline: Fix compilation error with gcc ASan zhihongx.peng @ 2021-10-15 14:27 ` zhihongx.peng 2021-10-15 14:27 ` [dpdk-dev] [PATCH v10 2/4] DPDK code adapts to ASan zhihongx.peng ` (3 more replies) 2021-10-15 15:11 ` zhihongx.peng 1 sibling, 4 replies; 117+ messages in thread From: zhihongx.peng @ 2021-10-15 14:27 UTC (permalink / raw) To: david.marchand, anatoly.burakov, konstantin.ananyev, stephen, cristian.dumitrescu, john.mcnamara Cc: dev, xueqin.lin, Zhihong Peng From: Zhihong Peng <zhihongx.peng@intel.com> `AddressSanitizer <https://github.com/google/sanitizers/wiki/AddressSanitizer>` (ASan) is a widely-used debugging tool to detect memory access errors. It helps detect issues like use-after-free, various kinds of buffer overruns in C/C++ programs, and other similar errors, as well as printing out detailed debug information whenever an error is detected. We can enable ASan by adding below compilation options: -Dbuildtype=debug -Db_lundef=false -Db_sanitize=address "-Dbuildtype=debug": This is a non-essential option. When this option is added, if a memory error occurs, ASan can clearly show where the code is wrong. "-Db_lundef=false": When use clang to compile DPDK, this option must be added. Signed-off-by: Xueqin Lin <xueqin.lin@intel.com> Signed-off-by: Zhihong Peng <zhihongx.peng@intel.com> --- config/meson.build | 16 ++++++++++++ devtools/words-case.txt | 1 + doc/guides/prog_guide/asan.rst | 35 ++++++++++++++++++++++++++ doc/guides/prog_guide/index.rst | 1 + doc/guides/rel_notes/release_21_11.rst | 9 +++++++ 5 files changed, 62 insertions(+) create mode 100644 doc/guides/prog_guide/asan.rst diff --git a/config/meson.build b/config/meson.build index 4cdf589e20..a7b71581dd 100644 --- a/config/meson.build +++ b/config/meson.build @@ -411,6 +411,22 @@ if get_option('b_lto') endif endif +if get_option('b_sanitize') == 'address' or get_option('b_sanitize') == 'address,undefined' + if is_windows + error('asan is not supported on windows') + endif + + if cc.get_id() == 'gcc' + asan_dep = cc.find_library('asan', required: true) + if (not cc.links('int main(int argc, char *argv[]) { return 0; }', + dependencies: asan_dep)) + error('broken dependency, "libasan"') + endif + add_project_link_arguments('-lasan', language: 'c') + dpdk_extra_ldflags += '-lasan' + endif +endif + if get_option('default_library') == 'both' error( ''' Unsupported value "both" for "default_library" option. diff --git a/devtools/words-case.txt b/devtools/words-case.txt index 0bbad48626..ada6910fa0 100644 --- a/devtools/words-case.txt +++ b/devtools/words-case.txt @@ -5,6 +5,7 @@ API Arm armv7 armv8 +ASan BAR CRC DCB diff --git a/doc/guides/prog_guide/asan.rst b/doc/guides/prog_guide/asan.rst new file mode 100644 index 0000000000..b732288af3 --- /dev/null +++ b/doc/guides/prog_guide/asan.rst @@ -0,0 +1,35 @@ +.. Copyright (c) <2021>, Intel Corporation + All rights reserved. + +Memory error detect standard tool - AddressSanitizer(ASan) +========================================================== + +`AddressSanitizer +<https://github.com/google/sanitizers/wiki/AddressSanitizer>` (ASan) +is a widely-used debugging tool to detect memory access errors. +It helps detect issues like use-after-free, various kinds of buffer +overruns in C/C++ programs, and other similar errors, as well as +printing out detailed debug information whenever an error is detected. + +AddressSanitizer is a part of LLVM (3.1+) and GCC (4.8+). + +Usage +----- + +meson build +^^^^^^^^^^^ + +To enable ASan in meson build system, use following meson build command: + +Example usage:: + + gcc : meson build -Dbuildtype=debug -Db_sanitize=address + ninja -C build + clang: meson build -Dbuildtype=debug -Db_lundef=false -Db_sanitize=address + ninja -C build + +.. Note:: + + a) DPDK test has been completed in ubuntu18.04/ubuntu20.04/redhat8.3. To compile with gcc in + centos, libasan needs to be installed separately. + b) If the program uses cmdline, when a memory bug occurs, need to execute the "stty echo" command. diff --git a/doc/guides/prog_guide/index.rst b/doc/guides/prog_guide/index.rst index 2dce507f46..df8a4b93e1 100644 --- a/doc/guides/prog_guide/index.rst +++ b/doc/guides/prog_guide/index.rst @@ -71,3 +71,4 @@ Programmer's Guide lto profile_app glossary + asan diff --git a/doc/guides/rel_notes/release_21_11.rst b/doc/guides/rel_notes/release_21_11.rst index 4c56cdfeaa..b6c4174dc6 100644 --- a/doc/guides/rel_notes/release_21_11.rst +++ b/doc/guides/rel_notes/release_21_11.rst @@ -159,6 +159,15 @@ New Features * Added tests to verify tunnel header verification in IPsec inbound. * Added tests to verify inner checksum. +* **Enable ASan for memory detector on DPDK.** + + `AddressSanitizer + <https://github.com/google/sanitizers/wiki/AddressSanitizer>` (ASan) + is a widely-used debugging tool to detect memory access errors. + It helps detect issues like use-after-free, various kinds of buffer + overruns in C/C++ programs, and other similar errors, as well as + printing out detailed debug information whenever an error is detected. + Removed Items ------------- -- 2.25.1 ^ permalink raw reply [flat|nested] 117+ messages in thread
* [dpdk-dev] [PATCH v10 2/4] DPDK code adapts to ASan 2021-10-15 14:27 ` [dpdk-dev] [PATCH v10 1/4] Enable ASan for memory detector on DPDK zhihongx.peng @ 2021-10-15 14:27 ` zhihongx.peng 2021-10-15 14:27 ` [dpdk-dev] [PATCH v10 3/4] pipeline: Fix compilation error with gcc ASan zhihongx.peng ` (2 subsequent siblings) 3 siblings, 0 replies; 117+ messages in thread From: zhihongx.peng @ 2021-10-15 14:27 UTC (permalink / raw) To: david.marchand, anatoly.burakov, konstantin.ananyev, stephen, cristian.dumitrescu, john.mcnamara Cc: dev, xueqin.lin, Zhihong Peng From: Zhihong Peng <zhihongx.peng@intel.com> DPDK ASan functionality is currently only supported Linux x86_64. Support other platforms, need to define ASAN_SHADOW_OFFSET value according to google ASan document, and configure meson (config/meson.build). Here is an example of heap-buffer-overflow bug: ...... char *p = rte_zmalloc(NULL, 7, 0); p[7] = 'a'; ...... Here is an example of use-after-free bug: ...... char *p = rte_zmalloc(NULL, 7, 0); rte_free(p); *p = 'a'; ...... Signed-off-by: Xueqin Lin <xueqin.lin@intel.com> Signed-off-by: Zhihong Peng <zhihongx.peng@intel.com> --- config/meson.build | 4 + doc/guides/prog_guide/asan.rst | 68 +++++++++++- lib/eal/common/malloc_elem.c | 26 ++++- lib/eal/common/malloc_elem.h | 194 ++++++++++++++++++++++++++++++++- lib/eal/common/malloc_heap.c | 12 ++ lib/eal/common/rte_malloc.c | 9 +- 6 files changed, 306 insertions(+), 7 deletions(-) diff --git a/config/meson.build b/config/meson.build index a7b71581dd..849c4beb8b 100644 --- a/config/meson.build +++ b/config/meson.build @@ -425,6 +425,10 @@ if get_option('b_sanitize') == 'address' or get_option('b_sanitize') == 'address add_project_link_arguments('-lasan', language: 'c') dpdk_extra_ldflags += '-lasan' endif + + if is_linux and arch_subdir == 'x86' + dpdk_conf.set10('RTE_MALLOC_ASAN', true) + endif endif if get_option('default_library') == 'both' diff --git a/doc/guides/prog_guide/asan.rst b/doc/guides/prog_guide/asan.rst index b732288af3..fde1953476 100644 --- a/doc/guides/prog_guide/asan.rst +++ b/doc/guides/prog_guide/asan.rst @@ -13,6 +13,68 @@ printing out detailed debug information whenever an error is detected. AddressSanitizer is a part of LLVM (3.1+) and GCC (4.8+). +DPDK ASan functionality is currently only supported Linux x86_64. +Support other platforms, need to define ASAN_SHADOW_OFFSET value +according to google ASan document, and configure meson +(config/meson.build). + +Example heap-buffer-overflow error +---------------------------------- + +Following error was reported when ASan was enabled:: + + Applied 9 bytes of memory, but accessed the 10th byte of memory, + so heap-buffer-overflow appeared. + +Below code results in this error:: + + Add code to helloworld: + char *p = rte_zmalloc(NULL, 9, 0); + if (!p) { + printf("rte_zmalloc error."); + return -1; + } + p[9] = 'a'; + +The error log:: + + ==369953==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x7fb17f465809 at pc 0x5652e6707b84 bp 0x7ffea70eea20 sp 0x7ffea70eea10 WRITE of size 1 at 0x7fb17f465809 thread T0 + #0 0x5652e6707b83 in main ../examples/helloworld/main.c:47 + #1 0x7fb94953c0b2 in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x270b2) + #2 0x5652e67079bd in _start (/home/pzh/asan_test/x86_64-native-linuxapp-gcc/examples/dpdk-helloworld+0x8329bd) + + Address 0x7fb17f465809 is a wild pointer. + SUMMARY: AddressSanitizer: heap-buffer-overflow ../examples/helloworld/main.c:47 in main + +Example use-after-free error +---------------------------- + +Following error was reported when ASan was enabled:: + + Applied for 9 bytes of memory, and accessed the first byte after + released, so heap-use-after-free appeared. + +Below code results in this error:: + + Add code to helloworld: + char *p = rte_zmalloc(NULL, 9, 0); + if (!p) { + printf("rte_zmalloc error."); + return -1; + } + rte_free(p); + *p = 'a'; + +The error log:: + + ==417048==ERROR: AddressSanitizer: heap-use-after-free on address 0x7fc83f465800 at pc 0x564308a39b89 bp 0x7ffc8c85bf50 sp 0x7ffc8c85bf40 WRITE of size 1 at 0x7fc83f465800 thread T0 + #0 0x564308a39b88 in main ../examples/helloworld/main.c:48 + #1 0x7fd0079c60b2 in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x270b2) + #2 0x564308a399bd in _start (/home/pzh/asan_test/x86_64-native-linuxapp-gcc/examples/dpdk-helloworld+0x8329bd) + + Address 0x7fc83f465800 is a wild pointer. + SUMMARY: AddressSanitizer: heap-use-after-free ../examples/helloworld/main.c:48 in main + Usage ----- @@ -30,6 +92,8 @@ Example usage:: .. Note:: - a) DPDK test has been completed in ubuntu18.04/ubuntu20.04/redhat8.3. To compile with gcc in + a) Some of the features of ASan (for example, 'Display memory application location, currently + displayed as a wild pointer') are not currently supported by DPDK's implementation. + b) DPDK test has been completed in ubuntu18.04/ubuntu20.04/redhat8.3. To compile with gcc in centos, libasan needs to be installed separately. - b) If the program uses cmdline, when a memory bug occurs, need to execute the "stty echo" command. + c) If the program uses cmdline, when a memory bug occurs, need to execute the "stty echo" command. diff --git a/lib/eal/common/malloc_elem.c b/lib/eal/common/malloc_elem.c index c2c9461f1d..bdd20a162e 100644 --- a/lib/eal/common/malloc_elem.c +++ b/lib/eal/common/malloc_elem.c @@ -446,6 +446,8 @@ malloc_elem_alloc(struct malloc_elem *elem, size_t size, unsigned align, struct malloc_elem *new_free_elem = RTE_PTR_ADD(new_elem, size + MALLOC_ELEM_OVERHEAD); + asan_clear_split_alloczone(new_free_elem); + split_elem(elem, new_free_elem); malloc_elem_free_list_insert(new_free_elem); @@ -458,6 +460,8 @@ malloc_elem_alloc(struct malloc_elem *elem, size_t size, unsigned align, elem->state = ELEM_BUSY; elem->pad = old_elem_size; + asan_clear_alloczone(elem); + /* put a dummy header in padding, to point to real element header */ if (elem->pad > 0) { /* pad will be at least 64-bytes, as everything * is cache-line aligned */ @@ -470,12 +474,18 @@ malloc_elem_alloc(struct malloc_elem *elem, size_t size, unsigned align, return new_elem; } + asan_clear_split_alloczone(new_elem); + /* we are going to split the element in two. The original element * remains free, and the new element is the one allocated. * Re-insert original element, in case its new size makes it * belong on a different list. */ + split_elem(elem, new_elem); + + asan_clear_alloczone(new_elem); + new_elem->state = ELEM_BUSY; malloc_elem_free_list_insert(elem); @@ -601,6 +611,8 @@ malloc_elem_hide_region(struct malloc_elem *elem, void *start, size_t len) if (next && next_elem_is_adjacent(elem)) { len_after = RTE_PTR_DIFF(next, hide_end); if (len_after >= MALLOC_ELEM_OVERHEAD + MIN_DATA_SIZE) { + asan_clear_split_alloczone(hide_end); + /* split after */ split_elem(elem, hide_end); @@ -615,6 +627,8 @@ malloc_elem_hide_region(struct malloc_elem *elem, void *start, size_t len) if (prev && prev_elem_is_adjacent(elem)) { len_before = RTE_PTR_DIFF(hide_start, elem); if (len_before >= MALLOC_ELEM_OVERHEAD + MIN_DATA_SIZE) { + asan_clear_split_alloczone(hide_start); + /* split before */ split_elem(elem, hide_start); @@ -628,6 +642,8 @@ malloc_elem_hide_region(struct malloc_elem *elem, void *start, size_t len) } } + asan_clear_alloczone(elem); + remove_elem(elem); } @@ -641,8 +657,10 @@ malloc_elem_resize(struct malloc_elem *elem, size_t size) const size_t new_size = size + elem->pad + MALLOC_ELEM_OVERHEAD; /* if we request a smaller size, then always return ok */ - if (elem->size >= new_size) + if (elem->size >= new_size) { + asan_clear_alloczone(elem); return 0; + } /* check if there is a next element, it's free and adjacent */ if (!elem->next || elem->next->state != ELEM_FREE || @@ -661,9 +679,15 @@ malloc_elem_resize(struct malloc_elem *elem, size_t size) /* now we have a big block together. Lets cut it down a bit, by splitting */ struct malloc_elem *split_pt = RTE_PTR_ADD(elem, new_size); split_pt = RTE_PTR_ALIGN_CEIL(split_pt, RTE_CACHE_LINE_SIZE); + + asan_clear_split_alloczone(split_pt); + split_elem(elem, split_pt); malloc_elem_free_list_insert(split_pt); } + + asan_clear_alloczone(elem); + return 0; } diff --git a/lib/eal/common/malloc_elem.h b/lib/eal/common/malloc_elem.h index a1e5f7f02c..a06c11ac8b 100644 --- a/lib/eal/common/malloc_elem.h +++ b/lib/eal/common/malloc_elem.h @@ -36,10 +36,20 @@ struct malloc_elem { uint64_t header_cookie; /* Cookie marking start of data */ /* trailer cookie at start + size */ #endif +#ifdef RTE_MALLOC_ASAN + size_t user_size; + uint64_t asan_cookie[2]; /* must be next to header_cookie */ +#endif } __rte_cache_aligned; +static const unsigned int MALLOC_ELEM_HEADER_LEN = sizeof(struct malloc_elem); + #ifndef RTE_MALLOC_DEBUG -static const unsigned MALLOC_ELEM_TRAILER_LEN = 0; +#ifdef RTE_MALLOC_ASAN +static const unsigned int MALLOC_ELEM_TRAILER_LEN = RTE_CACHE_LINE_SIZE; +#else +static const unsigned int MALLOC_ELEM_TRAILER_LEN; +#endif /* dummy function - just check if pointer is non-null */ static inline int @@ -55,7 +65,7 @@ set_trailer(struct malloc_elem *elem __rte_unused){ } #else -static const unsigned MALLOC_ELEM_TRAILER_LEN = RTE_CACHE_LINE_SIZE; +static const unsigned int MALLOC_ELEM_TRAILER_LEN = RTE_CACHE_LINE_SIZE; #define MALLOC_HEADER_COOKIE 0xbadbadbadadd2e55ULL /**< Header cookie. */ #define MALLOC_TRAILER_COOKIE 0xadd2e55badbadbadULL /**< Trailer cookie.*/ @@ -90,9 +100,187 @@ malloc_elem_cookies_ok(const struct malloc_elem *elem) #endif -static const unsigned MALLOC_ELEM_HEADER_LEN = sizeof(struct malloc_elem); #define MALLOC_ELEM_OVERHEAD (MALLOC_ELEM_HEADER_LEN + MALLOC_ELEM_TRAILER_LEN) +#ifdef RTE_MALLOC_ASAN + +#ifdef RTE_ARCH_X86_64 +#define ASAN_SHADOW_OFFSET 0x00007fff8000 +#endif + +#define ASAN_SHADOW_GRAIN_SIZE 8 +#define ASAN_MEM_FREE_FLAG 0xfd +#define ASAN_MEM_REDZONE_FLAG 0xfa +#define ASAN_SHADOW_SCALE 3 + +#define ASAN_MEM_SHIFT(mem) ((void *)((uintptr_t)(mem) >> ASAN_SHADOW_SCALE)) +#define ASAN_MEM_TO_SHADOW(mem) \ + RTE_PTR_ADD(ASAN_MEM_SHIFT(mem), ASAN_SHADOW_OFFSET) + +#if defined(__clang__) +__attribute__((no_sanitize("address", "hwaddress"))) +#else +__attribute__((no_sanitize_address)) +#endif +static inline void +asan_set_shadow(void *addr, char val) +{ + *(char *)addr = val; +} + +static inline void +asan_set_zone(void *ptr, size_t len, uint32_t val) +{ + size_t offset, i; + void *shadow; + size_t zone_len = len / ASAN_SHADOW_GRAIN_SIZE; + if (len % ASAN_SHADOW_GRAIN_SIZE != 0) + zone_len += 1; + + for (i = 0; i < zone_len; i++) { + offset = i * ASAN_SHADOW_GRAIN_SIZE; + shadow = ASAN_MEM_TO_SHADOW((uintptr_t)ptr + offset); + asan_set_shadow(shadow, val); + } +} + +/* + * When the memory is released, the release mark is + * set in the corresponding range of the shadow area. + */ +static inline void +asan_set_freezone(void *ptr, size_t size) +{ + asan_set_zone(ptr, size, ASAN_MEM_FREE_FLAG); +} + +/* + * When the memory is allocated, memory state must set as accessible. + */ +static inline void +asan_clear_alloczone(struct malloc_elem *elem) +{ + asan_set_zone((void *)elem, elem->size, 0x0); +} + +static inline void +asan_clear_split_alloczone(struct malloc_elem *elem) +{ + void *ptr = RTE_PTR_SUB(elem, MALLOC_ELEM_TRAILER_LEN); + asan_set_zone(ptr, MALLOC_ELEM_OVERHEAD, 0x0); +} + +/* + * When the memory is allocated, the memory boundary is + * marked in the corresponding range of the shadow area. + * Requirement: redzone >= 16, is a power of two. + */ +static inline void +asan_set_redzone(struct malloc_elem *elem, size_t user_size) +{ + uintptr_t head_redzone; + uintptr_t tail_redzone; + void *front_shadow; + void *tail_shadow; + uint32_t val; + + if (elem != NULL) { + if (elem->state != ELEM_PAD) + elem = RTE_PTR_ADD(elem, elem->pad); + + elem->user_size = user_size; + + /* Set mark before the start of the allocated memory */ + head_redzone = (uintptr_t)RTE_PTR_ADD(elem, + MALLOC_ELEM_HEADER_LEN - ASAN_SHADOW_GRAIN_SIZE); + front_shadow = ASAN_MEM_TO_SHADOW(head_redzone); + asan_set_shadow(front_shadow, ASAN_MEM_REDZONE_FLAG); + front_shadow = ASAN_MEM_TO_SHADOW(head_redzone + - ASAN_SHADOW_GRAIN_SIZE); + asan_set_shadow(front_shadow, ASAN_MEM_REDZONE_FLAG); + + /* Set mark after the end of the allocated memory */ + tail_redzone = (uintptr_t)RTE_PTR_ADD(elem, + MALLOC_ELEM_HEADER_LEN + + elem->user_size); + tail_shadow = ASAN_MEM_TO_SHADOW(tail_redzone); + val = (tail_redzone % ASAN_SHADOW_GRAIN_SIZE); + val = (val == 0) ? ASAN_MEM_REDZONE_FLAG : val; + asan_set_shadow(tail_shadow, val); + tail_shadow = ASAN_MEM_TO_SHADOW(tail_redzone + + ASAN_SHADOW_GRAIN_SIZE); + asan_set_shadow(tail_shadow, ASAN_MEM_REDZONE_FLAG); + } +} + +/* + * When the memory is released, the mark of the memory boundary + * in the corresponding range of the shadow area is cleared. + * Requirement: redzone >= 16, is a power of two. + */ +static inline void +asan_clear_redzone(struct malloc_elem *elem) +{ + uintptr_t head_redzone; + uintptr_t tail_redzone; + void *head_shadow; + void *tail_shadow; + + if (elem != NULL) { + elem = RTE_PTR_ADD(elem, elem->pad); + + /* Clear mark before the start of the allocated memory */ + head_redzone = (uintptr_t)RTE_PTR_ADD(elem, + MALLOC_ELEM_HEADER_LEN - ASAN_SHADOW_GRAIN_SIZE); + head_shadow = ASAN_MEM_TO_SHADOW(head_redzone); + asan_set_shadow(head_shadow, 0x00); + head_shadow = ASAN_MEM_TO_SHADOW(head_redzone + - ASAN_SHADOW_GRAIN_SIZE); + asan_set_shadow(head_shadow, 0x00); + + /* Clear mark after the end of the allocated memory */ + tail_redzone = (uintptr_t)RTE_PTR_ADD(elem, + MALLOC_ELEM_HEADER_LEN + elem->user_size); + tail_shadow = ASAN_MEM_TO_SHADOW(tail_redzone); + asan_set_shadow(tail_shadow, 0x00); + tail_shadow = ASAN_MEM_TO_SHADOW(tail_redzone + + ASAN_SHADOW_GRAIN_SIZE); + asan_set_shadow(tail_shadow, 0x00); + } +} + +static inline size_t +old_malloc_size(struct malloc_elem *elem) +{ + if (elem->state != ELEM_PAD) + elem = RTE_PTR_ADD(elem, elem->pad); + + return elem->user_size; +} +#else +static inline void +asan_set_freezone(void *ptr __rte_unused, size_t size __rte_unused) { } + +static inline void +asan_clear_alloczone(struct malloc_elem *elem __rte_unused) { } + +static inline void +asan_clear_split_alloczone(struct malloc_elem *elem __rte_unused) { } + +static inline void +asan_set_redzone(struct malloc_elem *elem __rte_unused, + size_t user_size __rte_unused) { } + +static inline void +asan_clear_redzone(struct malloc_elem *elem __rte_unused) { } + +static inline size_t +old_malloc_size(struct malloc_elem *elem) +{ + return elem->size - elem->pad - MALLOC_ELEM_OVERHEAD; +} +#endif + /* * Given a pointer to the start of a memory block returned by malloc, get * the actual malloc_elem header for that block. diff --git a/lib/eal/common/malloc_heap.c b/lib/eal/common/malloc_heap.c index ee400f38ec..775d6789df 100644 --- a/lib/eal/common/malloc_heap.c +++ b/lib/eal/common/malloc_heap.c @@ -237,6 +237,7 @@ heap_alloc(struct malloc_heap *heap, const char *type __rte_unused, size_t size, unsigned int flags, size_t align, size_t bound, bool contig) { struct malloc_elem *elem; + size_t user_size = size; size = RTE_CACHE_LINE_ROUNDUP(size); align = RTE_CACHE_LINE_ROUNDUP(align); @@ -250,6 +251,8 @@ heap_alloc(struct malloc_heap *heap, const char *type __rte_unused, size_t size, /* increase heap's count of allocated elements */ heap->alloc_count++; + + asan_set_redzone(elem, user_size); } return elem == NULL ? NULL : (void *)(&elem[1]); @@ -270,6 +273,8 @@ heap_alloc_biggest(struct malloc_heap *heap, const char *type __rte_unused, /* increase heap's count of allocated elements */ heap->alloc_count++; + + asan_set_redzone(elem, size); } return elem == NULL ? NULL : (void *)(&elem[1]); @@ -841,6 +846,8 @@ malloc_heap_free(struct malloc_elem *elem) if (!malloc_elem_cookies_ok(elem) || elem->state != ELEM_BUSY) return -1; + asan_clear_redzone(elem); + /* elem may be merged with previous element, so keep heap address */ heap = elem->heap; msl = elem->msl; @@ -848,6 +855,9 @@ malloc_heap_free(struct malloc_elem *elem) rte_spinlock_lock(&(heap->lock)); + void *asan_ptr = RTE_PTR_ADD(elem, MALLOC_ELEM_HEADER_LEN + elem->pad); + size_t asan_data_len = elem->size - MALLOC_ELEM_OVERHEAD - elem->pad; + /* mark element as free */ elem->state = ELEM_FREE; @@ -1001,6 +1011,8 @@ malloc_heap_free(struct malloc_elem *elem) rte_mcfg_mem_write_unlock(); free_unlock: + asan_set_freezone(asan_ptr, asan_data_len); + rte_spinlock_unlock(&(heap->lock)); return ret; } diff --git a/lib/eal/common/rte_malloc.c b/lib/eal/common/rte_malloc.c index 9d39e58c08..d0bec26920 100644 --- a/lib/eal/common/rte_malloc.c +++ b/lib/eal/common/rte_malloc.c @@ -162,6 +162,8 @@ rte_calloc(const char *type, size_t num, size_t size, unsigned align) void * rte_realloc_socket(void *ptr, size_t size, unsigned int align, int socket) { + size_t user_size; + if (ptr == NULL) return rte_malloc_socket(NULL, size, align, socket); @@ -171,6 +173,8 @@ rte_realloc_socket(void *ptr, size_t size, unsigned int align, int socket) return NULL; } + user_size = size; + size = RTE_CACHE_LINE_ROUNDUP(size), align = RTE_CACHE_LINE_ROUNDUP(align); /* check requested socket id and alignment matches first, and if ok, @@ -181,6 +185,9 @@ rte_realloc_socket(void *ptr, size_t size, unsigned int align, int socket) RTE_PTR_ALIGN(ptr, align) == ptr && malloc_heap_resize(elem, size) == 0) { rte_eal_trace_mem_realloc(size, align, socket, ptr); + + asan_set_redzone(elem, user_size); + return ptr; } @@ -192,7 +199,7 @@ rte_realloc_socket(void *ptr, size_t size, unsigned int align, int socket) if (new_ptr == NULL) return NULL; /* elem: |pad|data_elem|data|trailer| */ - const size_t old_size = elem->size - elem->pad - MALLOC_ELEM_OVERHEAD; + const size_t old_size = old_malloc_size(elem); rte_memcpy(new_ptr, ptr, old_size < size ? old_size : size); rte_free(ptr); -- 2.25.1 ^ permalink raw reply [flat|nested] 117+ messages in thread
* [dpdk-dev] [PATCH v10 3/4] pipeline: Fix compilation error with gcc ASan 2021-10-15 14:27 ` [dpdk-dev] [PATCH v10 1/4] Enable ASan for memory detector on DPDK zhihongx.peng 2021-10-15 14:27 ` [dpdk-dev] [PATCH v10 2/4] DPDK code adapts to ASan zhihongx.peng @ 2021-10-15 14:27 ` zhihongx.peng 2021-10-15 14:27 ` [dpdk-dev] [PATCH v10 4/4] performance-thread: Fix cross compilation failed zhihongx.peng 2021-10-19 9:02 ` [dpdk-dev] [PATCH v10 1/4] Enable ASan for memory detector on DPDK Mcnamara, John 3 siblings, 0 replies; 117+ messages in thread From: zhihongx.peng @ 2021-10-15 14:27 UTC (permalink / raw) To: david.marchand, anatoly.burakov, konstantin.ananyev, stephen, cristian.dumitrescu, john.mcnamara Cc: dev, xueqin.lin, Zhihong Peng, stable From: Zhihong Peng <zhihongx.peng@intel.com> The gcc will check code more stricter when ASan enabled. "Control reaches end of non-void function" error occurs here. Fixes: f38913b7fb8e ("pipeline: add meter array to SWX") Cc: stable@dpdk.org Signed-off-by: Xueqin Lin <xueqin.lin@intel.com> Signed-off-by: Zhihong Peng <zhihongx.peng@intel.com> --- lib/pipeline/rte_swx_pipeline.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/pipeline/rte_swx_pipeline.c b/lib/pipeline/rte_swx_pipeline.c index 1cd09a4b44..0acd6c6752 100644 --- a/lib/pipeline/rte_swx_pipeline.c +++ b/lib/pipeline/rte_swx_pipeline.c @@ -4642,7 +4642,7 @@ instr_meter_translate(struct rte_swx_pipeline *p, return 0; } - CHECK(0, EINVAL); + return -EINVAL; } static inline void @@ -5937,7 +5937,7 @@ instr_translate(struct rte_swx_pipeline *p, instr, data); - CHECK(0, EINVAL); + return -EINVAL; } static struct instruction_data * -- 2.25.1 ^ permalink raw reply [flat|nested] 117+ messages in thread
* [dpdk-dev] [PATCH v10 4/4] performance-thread: Fix cross compilation failed 2021-10-15 14:27 ` [dpdk-dev] [PATCH v10 1/4] Enable ASan for memory detector on DPDK zhihongx.peng 2021-10-15 14:27 ` [dpdk-dev] [PATCH v10 2/4] DPDK code adapts to ASan zhihongx.peng 2021-10-15 14:27 ` [dpdk-dev] [PATCH v10 3/4] pipeline: Fix compilation error with gcc ASan zhihongx.peng @ 2021-10-15 14:27 ` zhihongx.peng 2021-10-19 9:02 ` [dpdk-dev] [PATCH v10 1/4] Enable ASan for memory detector on DPDK Mcnamara, John 3 siblings, 0 replies; 117+ messages in thread From: zhihongx.peng @ 2021-10-15 14:27 UTC (permalink / raw) To: david.marchand, anatoly.burakov, konstantin.ananyev, stephen, cristian.dumitrescu, john.mcnamara Cc: dev, xueqin.lin, Zhihong Peng, stable From: Zhihong Peng <zhihongx.peng@intel.com> The gcc(arm-linux-gcc) will check code more stricter when ASan enabled. "strncpy specified bound XX equals destination size" error occurs here. Fixes: 116819b9ed0d ("examples/performance-thread: add lthread subsystem") Cc: stable@dpdk.org Signed-off-by: Xueqin Lin <xueqin.lin@intel.com> Signed-off-by: Zhihong Peng <zhihongx.peng@intel.com> --- examples/performance-thread/common/lthread.c | 2 +- examples/performance-thread/common/lthread_cond.c | 4 ++-- examples/performance-thread/common/lthread_mutex.c | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/examples/performance-thread/common/lthread.c b/examples/performance-thread/common/lthread.c index 3f1f48db43..dd4b3b27ee 100644 --- a/examples/performance-thread/common/lthread.c +++ b/examples/performance-thread/common/lthread.c @@ -463,6 +463,6 @@ void lthread_set_funcname(const char *f) { struct lthread *lt = THIS_LTHREAD; - strncpy(lt->funcname, f, sizeof(lt->funcname)); + strncpy(lt->funcname, f, sizeof(lt->funcname) - 1); lt->funcname[sizeof(lt->funcname)-1] = 0; } diff --git a/examples/performance-thread/common/lthread_cond.c b/examples/performance-thread/common/lthread_cond.c index cdcc7a7b5a..6ec8bc7e82 100644 --- a/examples/performance-thread/common/lthread_cond.c +++ b/examples/performance-thread/common/lthread_cond.c @@ -57,9 +57,9 @@ lthread_cond_init(char *name, struct lthread_cond **cond, } if (name == NULL) - strncpy(c->name, "no name", sizeof(c->name)); + strncpy(c->name, "no name", sizeof(c->name) - 1); else - strncpy(c->name, name, sizeof(c->name)); + strncpy(c->name, name, sizeof(c->name) - 1); c->name[sizeof(c->name)-1] = 0; c->root_sched = THIS_SCHED; diff --git a/examples/performance-thread/common/lthread_mutex.c b/examples/performance-thread/common/lthread_mutex.c index 01da6cad4f..7e5da609b1 100644 --- a/examples/performance-thread/common/lthread_mutex.c +++ b/examples/performance-thread/common/lthread_mutex.c @@ -52,9 +52,9 @@ lthread_mutex_init(char *name, struct lthread_mutex **mutex, } if (name == NULL) - strncpy(m->name, "no name", sizeof(m->name)); + strncpy(m->name, "no name", sizeof(m->name) - 1); else - strncpy(m->name, name, sizeof(m->name)); + strncpy(m->name, name, sizeof(m->name) - 1); m->name[sizeof(m->name)-1] = 0; m->root_sched = THIS_SCHED; -- 2.25.1 ^ permalink raw reply [flat|nested] 117+ messages in thread
* Re: [dpdk-dev] [PATCH v10 1/4] Enable ASan for memory detector on DPDK 2021-10-15 14:27 ` [dpdk-dev] [PATCH v10 1/4] Enable ASan for memory detector on DPDK zhihongx.peng ` (2 preceding siblings ...) 2021-10-15 14:27 ` [dpdk-dev] [PATCH v10 4/4] performance-thread: Fix cross compilation failed zhihongx.peng @ 2021-10-19 9:02 ` Mcnamara, John 2021-10-19 9:28 ` Peng, ZhihongX 3 siblings, 1 reply; 117+ messages in thread From: Mcnamara, John @ 2021-10-19 9:02 UTC (permalink / raw) To: Peng, ZhihongX, david.marchand, Burakov, Anatoly, Ananyev, Konstantin, stephen, Dumitrescu, Cristian Cc: dev, Lin, Xueqin > -----Original Message----- > From: Peng, ZhihongX <zhihongx.peng@intel.com> > Sent: Friday, October 15, 2021 3:28 PM > To: david.marchand@redhat.com; Burakov, Anatoly > <anatoly.burakov@intel.com>; Ananyev, Konstantin > <konstantin.ananyev@intel.com>; stephen@networkplumber.org; Dumitrescu, > Cristian <cristian.dumitrescu@intel.com>; Mcnamara, John > <john.mcnamara@intel.com> > Cc: dev@dpdk.org; Lin, Xueqin <xueqin.lin@intel.com>; Peng, ZhihongX > <zhihongx.peng@intel.com> > Subject: [PATCH v10 1/4] Enable ASan for memory detector on DPDK > > From: Zhihong Peng <zhihongx.peng@intel.com> > > `AddressSanitizer Good work on this. It is a good initiative. Some comments on the docs below. > diff --git a/doc/guides/prog_guide/asan.rst > b/doc/guides/prog_guide/asan.rst new file mode 100644 index > 0000000000..b732288af3 > --- /dev/null > +++ b/doc/guides/prog_guide/asan.rst > @@ -0,0 +1,35 @@ > +.. Copyright (c) <2021>, Intel Corporation > + All rights reserved. > + > +Memory error detect standard tool - AddressSanitizer(ASan) > +========================================================== > + > +`AddressSanitizer > +<https://github.com/google/sanitizers/wiki/AddressSanitizer>` (ASan) is There are a number of minor doc and grammar issues in the rst for this patch. Rather than going through each one here is a rework of the asan.rst with these issues fixed: .. SPDX-License-Identifier: BSD-3-Clause Copyright(c) 2021 Intel Corporation Running Address Sanitizer ========================================================== `AddressSanitizer <https://github.com/google/sanitizers/wiki/AddressSanitizer>`_ (ASan) is a widely-used debugging tool for detecting memory access errors. It helps detect issues like use-after-free, various kinds of buffer overruns and similar errors, as well as printing out detailed debug information whenever an error is detected. To enable ASan in the meson build system, use following meson build command: Example usage: * gcc:: meson build -Dbuildtype=debug -Db_sanitize=address ninja -C build * clang:: meson build -Dbuildtype=debug -Db_lundef=false -Db_sanitize=address ninja -C build AddressSanitizer is a part of LLVM (3.1+) and GCC (4.8+). .. Note:: a) To compile with gcc in Centos, libasan needs to be installed separately. b) If the program being tested uses cmdline you will need to execute the "stty echo" command when a error occurs. > diff --git a/doc/guides/prog_guide/index.rst > b/doc/guides/prog_guide/index.rst index 2dce507f46..df8a4b93e1 100644 > --- a/doc/guides/prog_guide/index.rst > +++ b/doc/guides/prog_guide/index.rst > @@ -71,3 +71,4 @@ Programmer's Guide > lto > profile_app > glossary > + asan The asan file should go before "glossary" and after "profile_app". > diff --git a/doc/guides/rel_notes/release_21_11.rst > b/doc/guides/rel_notes/release_21_11.rst > index 4c56cdfeaa..b6c4174dc6 100644 > --- a/doc/guides/rel_notes/release_21_11.rst > +++ b/doc/guides/rel_notes/release_21_11.rst > @@ -159,6 +159,15 @@ New Features > * Added tests to verify tunnel header verification in IPsec inbound. > * Added tests to verify inner checksum. > > +* **Enable ASan for memory detector on DPDK.** Change to "**Enable ASan Address Sanitization.**" > + > + `AddressSanitizer > + <https://github.com/google/sanitizers/wiki/AddressSanitizer>` (ASan) This text should match the edited text above. John ^ permalink raw reply [flat|nested] 117+ messages in thread
* Re: [dpdk-dev] [PATCH v10 1/4] Enable ASan for memory detector on DPDK 2021-10-19 9:02 ` [dpdk-dev] [PATCH v10 1/4] Enable ASan for memory detector on DPDK Mcnamara, John @ 2021-10-19 9:28 ` Peng, ZhihongX 0 siblings, 0 replies; 117+ messages in thread From: Peng, ZhihongX @ 2021-10-19 9:28 UTC (permalink / raw) To: Mcnamara, John, david.marchand, Burakov, Anatoly, Ananyev, Konstantin, stephen, Dumitrescu, Cristian Cc: dev, Lin, Xueqin > -----Original Message----- > From: Mcnamara, John <john.mcnamara@intel.com> > Sent: Tuesday, October 19, 2021 5:03 PM > To: Peng, ZhihongX <zhihongx.peng@intel.com>; > david.marchand@redhat.com; Burakov, Anatoly > <anatoly.burakov@intel.com>; Ananyev, Konstantin > <konstantin.ananyev@intel.com>; stephen@networkplumber.org; > Dumitrescu, Cristian <cristian.dumitrescu@intel.com> > Cc: dev@dpdk.org; Lin, Xueqin <xueqin.lin@intel.com> > Subject: RE: [PATCH v10 1/4] Enable ASan for memory detector on DPDK > > > -----Original Message----- > > From: Peng, ZhihongX <zhihongx.peng@intel.com> > > Sent: Friday, October 15, 2021 3:28 PM > > To: david.marchand@redhat.com; Burakov, Anatoly > > <anatoly.burakov@intel.com>; Ananyev, Konstantin > > <konstantin.ananyev@intel.com>; stephen@networkplumber.org; > > Dumitrescu, Cristian <cristian.dumitrescu@intel.com>; Mcnamara, John > > <john.mcnamara@intel.com> > > Cc: dev@dpdk.org; Lin, Xueqin <xueqin.lin@intel.com>; Peng, ZhihongX > > <zhihongx.peng@intel.com> > > Subject: [PATCH v10 1/4] Enable ASan for memory detector on DPDK > > > > From: Zhihong Peng <zhihongx.peng@intel.com> > > > > `AddressSanitizer > > > Good work on this. It is a good initiative. Some comments on the docs below. > > > > diff --git a/doc/guides/prog_guide/asan.rst > > b/doc/guides/prog_guide/asan.rst new file mode 100644 index > > 0000000000..b732288af3 > > --- /dev/null > > +++ b/doc/guides/prog_guide/asan.rst > > @@ -0,0 +1,35 @@ > > +.. Copyright (c) <2021>, Intel Corporation > > + All rights reserved. > > + > > +Memory error detect standard tool - AddressSanitizer(ASan) > > > +========================================================= > = > > + > > +`AddressSanitizer > > +<https://github.com/google/sanitizers/wiki/AddressSanitizer>` (ASan) > > +is > > There are a number of minor doc and grammar issues in the rst for this patch. > Rather than going through each one here is a rework of the asan.rst with > these issues fixed: > > .. SPDX-License-Identifier: BSD-3-Clause > Copyright(c) 2021 Intel Corporation > The v11 version will be fixed. > Running Address Sanitizer > ========================================================== > > `AddressSanitizer > <https://github.com/google/sanitizers/wiki/AddressSanitizer>`_ (ASan) is a The v11 version will be fixed. > widely-used debugging tool for detecting memory access errors. > It helps detect issues like use-after-free, various kinds of buffer overruns > and similar errors, as well as printing out detailed debug information > whenever an error is detected. > > To enable ASan in the meson build system, use following meson build > command: > > Example usage: > > * gcc:: > > meson build -Dbuildtype=debug -Db_sanitize=address > ninja -C build > > * clang:: > > meson build -Dbuildtype=debug -Db_lundef=false -Db_sanitize=address > ninja -C build > > AddressSanitizer is a part of LLVM (3.1+) and GCC (4.8+). The v11 version will be fixed. > .. Note:: > > a) To compile with gcc in Centos, libasan needs to be installed separately. The v11 version will be fixed. > b) If the program being tested uses cmdline you will need to execute the > "stty echo" command when a error occurs. The v11 version will be fixed. > > > > > > > diff --git a/doc/guides/prog_guide/index.rst > > b/doc/guides/prog_guide/index.rst index 2dce507f46..df8a4b93e1 100644 > > --- a/doc/guides/prog_guide/index.rst > > +++ b/doc/guides/prog_guide/index.rst > > @@ -71,3 +71,4 @@ Programmer's Guide > > lto > > profile_app > > glossary > > + asan > > > The asan file should go before "glossary" and after "profile_app". > > > > diff --git a/doc/guides/rel_notes/release_21_11.rst > > b/doc/guides/rel_notes/release_21_11.rst > > index 4c56cdfeaa..b6c4174dc6 100644 > > --- a/doc/guides/rel_notes/release_21_11.rst > > +++ b/doc/guides/rel_notes/release_21_11.rst > > @@ -159,6 +159,15 @@ New Features > > * Added tests to verify tunnel header verification in IPsec inbound. > > * Added tests to verify inner checksum. > > > > +* **Enable ASan for memory detector on DPDK.** > > Change to "**Enable ASan Address Sanitization.**" > > > > + > > + `AddressSanitizer > > + <https://github.com/google/sanitizers/wiki/AddressSanitizer>` > > + (ASan) > > This text should match the edited text above. > > John > ^ permalink raw reply [flat|nested] 117+ messages in thread
* [dpdk-dev] [PATCH v10 1/4] Enable ASan for memory detector on DPDK 2021-10-12 9:43 ` [dpdk-dev] [PATCH v9 3/3] pipeline: Fix compilation error with gcc ASan zhihongx.peng 2021-10-15 14:27 ` [dpdk-dev] [PATCH v10 1/4] Enable ASan for memory detector on DPDK zhihongx.peng @ 2021-10-15 15:11 ` zhihongx.peng 2021-10-15 15:11 ` [dpdk-dev] [PATCH v10 2/4] DPDK code adapts to ASan zhihongx.peng ` (3 more replies) 1 sibling, 4 replies; 117+ messages in thread From: zhihongx.peng @ 2021-10-15 15:11 UTC (permalink / raw) To: david.marchand, anatoly.burakov, konstantin.ananyev, stephen, cristian.dumitrescu, john.mcnamara Cc: dev, xueqin.lin, Zhihong Peng From: Zhihong Peng <zhihongx.peng@intel.com> `AddressSanitizer <https://github.com/google/sanitizers/wiki/AddressSanitizer>` (ASan) is a widely-used debugging tool to detect memory access errors. It helps detect issues like use-after-free, various kinds of buffer overruns in C/C++ programs, and other similar errors, as well as printing out detailed debug information whenever an error is detected. We can enable ASan by adding below compilation options: -Dbuildtype=debug -Db_lundef=false -Db_sanitize=address "-Dbuildtype=debug": This is a non-essential option. When this option is added, if a memory error occurs, ASan can clearly show where the code is wrong. "-Db_lundef=false": When use clang to compile DPDK, this option must be added. Signed-off-by: Xueqin Lin <xueqin.lin@intel.com> Signed-off-by: Zhihong Peng <zhihongx.peng@intel.com> --- v7: 1) Split doc and code into two. 2) Modify asan.rst doc v8: No change. v9: 1) Add the check of libasan library. 2) Add release notes. v10:1) Split doc and code into two. 2) Meson supports asan. --- config/meson.build | 16 ++++++++++++ devtools/words-case.txt | 1 + doc/guides/prog_guide/asan.rst | 35 ++++++++++++++++++++++++++ doc/guides/prog_guide/index.rst | 1 + doc/guides/rel_notes/release_21_11.rst | 9 +++++++ 5 files changed, 62 insertions(+) create mode 100644 doc/guides/prog_guide/asan.rst diff --git a/config/meson.build b/config/meson.build index 4cdf589e20..a7b71581dd 100644 --- a/config/meson.build +++ b/config/meson.build @@ -411,6 +411,22 @@ if get_option('b_lto') endif endif +if get_option('b_sanitize') == 'address' or get_option('b_sanitize') == 'address,undefined' + if is_windows + error('ASan is not supported on windows') + endif + + if cc.get_id() == 'gcc' + asan_dep = cc.find_library('asan', required: true) + if (not cc.links('int main(int argc, char *argv[]) { return 0; }', + dependencies: asan_dep)) + error('broken dependency, "libasan"') + endif + add_project_link_arguments('-lasan', language: 'c') + dpdk_extra_ldflags += '-lasan' + endif +endif + if get_option('default_library') == 'both' error( ''' Unsupported value "both" for "default_library" option. diff --git a/devtools/words-case.txt b/devtools/words-case.txt index 0bbad48626..ada6910fa0 100644 --- a/devtools/words-case.txt +++ b/devtools/words-case.txt @@ -5,6 +5,7 @@ API Arm armv7 armv8 +ASan BAR CRC DCB diff --git a/doc/guides/prog_guide/asan.rst b/doc/guides/prog_guide/asan.rst new file mode 100644 index 0000000000..b732288af3 --- /dev/null +++ b/doc/guides/prog_guide/asan.rst @@ -0,0 +1,35 @@ +.. Copyright (c) <2021>, Intel Corporation + All rights reserved. + +Memory error detect standard tool - AddressSanitizer(ASan) +========================================================== + +`AddressSanitizer +<https://github.com/google/sanitizers/wiki/AddressSanitizer>` (ASan) +is a widely-used debugging tool to detect memory access errors. +It helps detect issues like use-after-free, various kinds of buffer +overruns in C/C++ programs, and other similar errors, as well as +printing out detailed debug information whenever an error is detected. + +AddressSanitizer is a part of LLVM (3.1+) and GCC (4.8+). + +Usage +----- + +meson build +^^^^^^^^^^^ + +To enable ASan in meson build system, use following meson build command: + +Example usage:: + + gcc : meson build -Dbuildtype=debug -Db_sanitize=address + ninja -C build + clang: meson build -Dbuildtype=debug -Db_lundef=false -Db_sanitize=address + ninja -C build + +.. Note:: + + a) DPDK test has been completed in ubuntu18.04/ubuntu20.04/redhat8.3. To compile with gcc in + centos, libasan needs to be installed separately. + b) If the program uses cmdline, when a memory bug occurs, need to execute the "stty echo" command. diff --git a/doc/guides/prog_guide/index.rst b/doc/guides/prog_guide/index.rst index 2dce507f46..df8a4b93e1 100644 --- a/doc/guides/prog_guide/index.rst +++ b/doc/guides/prog_guide/index.rst @@ -71,3 +71,4 @@ Programmer's Guide lto profile_app glossary + asan diff --git a/doc/guides/rel_notes/release_21_11.rst b/doc/guides/rel_notes/release_21_11.rst index 4c56cdfeaa..b6c4174dc6 100644 --- a/doc/guides/rel_notes/release_21_11.rst +++ b/doc/guides/rel_notes/release_21_11.rst @@ -159,6 +159,15 @@ New Features * Added tests to verify tunnel header verification in IPsec inbound. * Added tests to verify inner checksum. +* **Enable ASan for memory detector on DPDK.** + + `AddressSanitizer + <https://github.com/google/sanitizers/wiki/AddressSanitizer>` (ASan) + is a widely-used debugging tool to detect memory access errors. + It helps detect issues like use-after-free, various kinds of buffer + overruns in C/C++ programs, and other similar errors, as well as + printing out detailed debug information whenever an error is detected. + Removed Items ------------- -- 2.25.1 ^ permalink raw reply [flat|nested] 117+ messages in thread
* [dpdk-dev] [PATCH v10 2/4] DPDK code adapts to ASan 2021-10-15 15:11 ` zhihongx.peng @ 2021-10-15 15:11 ` zhihongx.peng 2021-10-15 15:11 ` [dpdk-dev] [PATCH v10 3/4] pipeline: Fix compilation error with gcc ASan zhihongx.peng ` (2 subsequent siblings) 3 siblings, 0 replies; 117+ messages in thread From: zhihongx.peng @ 2021-10-15 15:11 UTC (permalink / raw) To: david.marchand, anatoly.burakov, konstantin.ananyev, stephen, cristian.dumitrescu, john.mcnamara Cc: dev, xueqin.lin, Zhihong Peng From: Zhihong Peng <zhihongx.peng@intel.com> DPDK ASan functionality is currently only supported Linux x86_64. Support other platforms, need to define ASAN_SHADOW_OFFSET value according to google ASan document, and configure meson (config/meson.build). Here is an example of heap-buffer-overflow bug: ...... char *p = rte_zmalloc(NULL, 7, 0); p[7] = 'a'; ...... Here is an example of use-after-free bug: ...... char *p = rte_zmalloc(NULL, 7, 0); rte_free(p); *p = 'a'; ...... Signed-off-by: Xueqin Lin <xueqin.lin@intel.com> Signed-off-by: Zhihong Peng <zhihongx.peng@intel.com> --- v7: Split doc and code into two. v8: No change. v9: Modify the definition of RTE_MALLOC_ASAN. v10:Modify the definition of RTE_MALLOC_ASAN. --- config/meson.build | 4 + doc/guides/prog_guide/asan.rst | 68 +++++++++++- lib/eal/common/malloc_elem.c | 26 ++++- lib/eal/common/malloc_elem.h | 194 ++++++++++++++++++++++++++++++++- lib/eal/common/malloc_heap.c | 12 ++ lib/eal/common/rte_malloc.c | 9 +- 6 files changed, 306 insertions(+), 7 deletions(-) diff --git a/config/meson.build b/config/meson.build index a7b71581dd..849c4beb8b 100644 --- a/config/meson.build +++ b/config/meson.build @@ -425,6 +425,10 @@ if get_option('b_sanitize') == 'address' or get_option('b_sanitize') == 'address add_project_link_arguments('-lasan', language: 'c') dpdk_extra_ldflags += '-lasan' endif + + if is_linux and arch_subdir == 'x86' + dpdk_conf.set10('RTE_MALLOC_ASAN', true) + endif endif if get_option('default_library') == 'both' diff --git a/doc/guides/prog_guide/asan.rst b/doc/guides/prog_guide/asan.rst index b732288af3..fde1953476 100644 --- a/doc/guides/prog_guide/asan.rst +++ b/doc/guides/prog_guide/asan.rst @@ -13,6 +13,68 @@ printing out detailed debug information whenever an error is detected. AddressSanitizer is a part of LLVM (3.1+) and GCC (4.8+). +DPDK ASan functionality is currently only supported Linux x86_64. +Support other platforms, need to define ASAN_SHADOW_OFFSET value +according to google ASan document, and configure meson +(config/meson.build). + +Example heap-buffer-overflow error +---------------------------------- + +Following error was reported when ASan was enabled:: + + Applied 9 bytes of memory, but accessed the 10th byte of memory, + so heap-buffer-overflow appeared. + +Below code results in this error:: + + Add code to helloworld: + char *p = rte_zmalloc(NULL, 9, 0); + if (!p) { + printf("rte_zmalloc error."); + return -1; + } + p[9] = 'a'; + +The error log:: + + ==369953==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x7fb17f465809 at pc 0x5652e6707b84 bp 0x7ffea70eea20 sp 0x7ffea70eea10 WRITE of size 1 at 0x7fb17f465809 thread T0 + #0 0x5652e6707b83 in main ../examples/helloworld/main.c:47 + #1 0x7fb94953c0b2 in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x270b2) + #2 0x5652e67079bd in _start (/home/pzh/asan_test/x86_64-native-linuxapp-gcc/examples/dpdk-helloworld+0x8329bd) + + Address 0x7fb17f465809 is a wild pointer. + SUMMARY: AddressSanitizer: heap-buffer-overflow ../examples/helloworld/main.c:47 in main + +Example use-after-free error +---------------------------- + +Following error was reported when ASan was enabled:: + + Applied for 9 bytes of memory, and accessed the first byte after + released, so heap-use-after-free appeared. + +Below code results in this error:: + + Add code to helloworld: + char *p = rte_zmalloc(NULL, 9, 0); + if (!p) { + printf("rte_zmalloc error."); + return -1; + } + rte_free(p); + *p = 'a'; + +The error log:: + + ==417048==ERROR: AddressSanitizer: heap-use-after-free on address 0x7fc83f465800 at pc 0x564308a39b89 bp 0x7ffc8c85bf50 sp 0x7ffc8c85bf40 WRITE of size 1 at 0x7fc83f465800 thread T0 + #0 0x564308a39b88 in main ../examples/helloworld/main.c:48 + #1 0x7fd0079c60b2 in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x270b2) + #2 0x564308a399bd in _start (/home/pzh/asan_test/x86_64-native-linuxapp-gcc/examples/dpdk-helloworld+0x8329bd) + + Address 0x7fc83f465800 is a wild pointer. + SUMMARY: AddressSanitizer: heap-use-after-free ../examples/helloworld/main.c:48 in main + Usage ----- @@ -30,6 +92,8 @@ Example usage:: .. Note:: - a) DPDK test has been completed in ubuntu18.04/ubuntu20.04/redhat8.3. To compile with gcc in + a) Some of the features of ASan (for example, 'Display memory application location, currently + displayed as a wild pointer') are not currently supported by DPDK's implementation. + b) DPDK test has been completed in ubuntu18.04/ubuntu20.04/redhat8.3. To compile with gcc in centos, libasan needs to be installed separately. - b) If the program uses cmdline, when a memory bug occurs, need to execute the "stty echo" command. + c) If the program uses cmdline, when a memory bug occurs, need to execute the "stty echo" command. diff --git a/lib/eal/common/malloc_elem.c b/lib/eal/common/malloc_elem.c index c2c9461f1d..bdd20a162e 100644 --- a/lib/eal/common/malloc_elem.c +++ b/lib/eal/common/malloc_elem.c @@ -446,6 +446,8 @@ malloc_elem_alloc(struct malloc_elem *elem, size_t size, unsigned align, struct malloc_elem *new_free_elem = RTE_PTR_ADD(new_elem, size + MALLOC_ELEM_OVERHEAD); + asan_clear_split_alloczone(new_free_elem); + split_elem(elem, new_free_elem); malloc_elem_free_list_insert(new_free_elem); @@ -458,6 +460,8 @@ malloc_elem_alloc(struct malloc_elem *elem, size_t size, unsigned align, elem->state = ELEM_BUSY; elem->pad = old_elem_size; + asan_clear_alloczone(elem); + /* put a dummy header in padding, to point to real element header */ if (elem->pad > 0) { /* pad will be at least 64-bytes, as everything * is cache-line aligned */ @@ -470,12 +474,18 @@ malloc_elem_alloc(struct malloc_elem *elem, size_t size, unsigned align, return new_elem; } + asan_clear_split_alloczone(new_elem); + /* we are going to split the element in two. The original element * remains free, and the new element is the one allocated. * Re-insert original element, in case its new size makes it * belong on a different list. */ + split_elem(elem, new_elem); + + asan_clear_alloczone(new_elem); + new_elem->state = ELEM_BUSY; malloc_elem_free_list_insert(elem); @@ -601,6 +611,8 @@ malloc_elem_hide_region(struct malloc_elem *elem, void *start, size_t len) if (next && next_elem_is_adjacent(elem)) { len_after = RTE_PTR_DIFF(next, hide_end); if (len_after >= MALLOC_ELEM_OVERHEAD + MIN_DATA_SIZE) { + asan_clear_split_alloczone(hide_end); + /* split after */ split_elem(elem, hide_end); @@ -615,6 +627,8 @@ malloc_elem_hide_region(struct malloc_elem *elem, void *start, size_t len) if (prev && prev_elem_is_adjacent(elem)) { len_before = RTE_PTR_DIFF(hide_start, elem); if (len_before >= MALLOC_ELEM_OVERHEAD + MIN_DATA_SIZE) { + asan_clear_split_alloczone(hide_start); + /* split before */ split_elem(elem, hide_start); @@ -628,6 +642,8 @@ malloc_elem_hide_region(struct malloc_elem *elem, void *start, size_t len) } } + asan_clear_alloczone(elem); + remove_elem(elem); } @@ -641,8 +657,10 @@ malloc_elem_resize(struct malloc_elem *elem, size_t size) const size_t new_size = size + elem->pad + MALLOC_ELEM_OVERHEAD; /* if we request a smaller size, then always return ok */ - if (elem->size >= new_size) + if (elem->size >= new_size) { + asan_clear_alloczone(elem); return 0; + } /* check if there is a next element, it's free and adjacent */ if (!elem->next || elem->next->state != ELEM_FREE || @@ -661,9 +679,15 @@ malloc_elem_resize(struct malloc_elem *elem, size_t size) /* now we have a big block together. Lets cut it down a bit, by splitting */ struct malloc_elem *split_pt = RTE_PTR_ADD(elem, new_size); split_pt = RTE_PTR_ALIGN_CEIL(split_pt, RTE_CACHE_LINE_SIZE); + + asan_clear_split_alloczone(split_pt); + split_elem(elem, split_pt); malloc_elem_free_list_insert(split_pt); } + + asan_clear_alloczone(elem); + return 0; } diff --git a/lib/eal/common/malloc_elem.h b/lib/eal/common/malloc_elem.h index a1e5f7f02c..a06c11ac8b 100644 --- a/lib/eal/common/malloc_elem.h +++ b/lib/eal/common/malloc_elem.h @@ -36,10 +36,20 @@ struct malloc_elem { uint64_t header_cookie; /* Cookie marking start of data */ /* trailer cookie at start + size */ #endif +#ifdef RTE_MALLOC_ASAN + size_t user_size; + uint64_t asan_cookie[2]; /* must be next to header_cookie */ +#endif } __rte_cache_aligned; +static const unsigned int MALLOC_ELEM_HEADER_LEN = sizeof(struct malloc_elem); + #ifndef RTE_MALLOC_DEBUG -static const unsigned MALLOC_ELEM_TRAILER_LEN = 0; +#ifdef RTE_MALLOC_ASAN +static const unsigned int MALLOC_ELEM_TRAILER_LEN = RTE_CACHE_LINE_SIZE; +#else +static const unsigned int MALLOC_ELEM_TRAILER_LEN; +#endif /* dummy function - just check if pointer is non-null */ static inline int @@ -55,7 +65,7 @@ set_trailer(struct malloc_elem *elem __rte_unused){ } #else -static const unsigned MALLOC_ELEM_TRAILER_LEN = RTE_CACHE_LINE_SIZE; +static const unsigned int MALLOC_ELEM_TRAILER_LEN = RTE_CACHE_LINE_SIZE; #define MALLOC_HEADER_COOKIE 0xbadbadbadadd2e55ULL /**< Header cookie. */ #define MALLOC_TRAILER_COOKIE 0xadd2e55badbadbadULL /**< Trailer cookie.*/ @@ -90,9 +100,187 @@ malloc_elem_cookies_ok(const struct malloc_elem *elem) #endif -static const unsigned MALLOC_ELEM_HEADER_LEN = sizeof(struct malloc_elem); #define MALLOC_ELEM_OVERHEAD (MALLOC_ELEM_HEADER_LEN + MALLOC_ELEM_TRAILER_LEN) +#ifdef RTE_MALLOC_ASAN + +#ifdef RTE_ARCH_X86_64 +#define ASAN_SHADOW_OFFSET 0x00007fff8000 +#endif + +#define ASAN_SHADOW_GRAIN_SIZE 8 +#define ASAN_MEM_FREE_FLAG 0xfd +#define ASAN_MEM_REDZONE_FLAG 0xfa +#define ASAN_SHADOW_SCALE 3 + +#define ASAN_MEM_SHIFT(mem) ((void *)((uintptr_t)(mem) >> ASAN_SHADOW_SCALE)) +#define ASAN_MEM_TO_SHADOW(mem) \ + RTE_PTR_ADD(ASAN_MEM_SHIFT(mem), ASAN_SHADOW_OFFSET) + +#if defined(__clang__) +__attribute__((no_sanitize("address", "hwaddress"))) +#else +__attribute__((no_sanitize_address)) +#endif +static inline void +asan_set_shadow(void *addr, char val) +{ + *(char *)addr = val; +} + +static inline void +asan_set_zone(void *ptr, size_t len, uint32_t val) +{ + size_t offset, i; + void *shadow; + size_t zone_len = len / ASAN_SHADOW_GRAIN_SIZE; + if (len % ASAN_SHADOW_GRAIN_SIZE != 0) + zone_len += 1; + + for (i = 0; i < zone_len; i++) { + offset = i * ASAN_SHADOW_GRAIN_SIZE; + shadow = ASAN_MEM_TO_SHADOW((uintptr_t)ptr + offset); + asan_set_shadow(shadow, val); + } +} + +/* + * When the memory is released, the release mark is + * set in the corresponding range of the shadow area. + */ +static inline void +asan_set_freezone(void *ptr, size_t size) +{ + asan_set_zone(ptr, size, ASAN_MEM_FREE_FLAG); +} + +/* + * When the memory is allocated, memory state must set as accessible. + */ +static inline void +asan_clear_alloczone(struct malloc_elem *elem) +{ + asan_set_zone((void *)elem, elem->size, 0x0); +} + +static inline void +asan_clear_split_alloczone(struct malloc_elem *elem) +{ + void *ptr = RTE_PTR_SUB(elem, MALLOC_ELEM_TRAILER_LEN); + asan_set_zone(ptr, MALLOC_ELEM_OVERHEAD, 0x0); +} + +/* + * When the memory is allocated, the memory boundary is + * marked in the corresponding range of the shadow area. + * Requirement: redzone >= 16, is a power of two. + */ +static inline void +asan_set_redzone(struct malloc_elem *elem, size_t user_size) +{ + uintptr_t head_redzone; + uintptr_t tail_redzone; + void *front_shadow; + void *tail_shadow; + uint32_t val; + + if (elem != NULL) { + if (elem->state != ELEM_PAD) + elem = RTE_PTR_ADD(elem, elem->pad); + + elem->user_size = user_size; + + /* Set mark before the start of the allocated memory */ + head_redzone = (uintptr_t)RTE_PTR_ADD(elem, + MALLOC_ELEM_HEADER_LEN - ASAN_SHADOW_GRAIN_SIZE); + front_shadow = ASAN_MEM_TO_SHADOW(head_redzone); + asan_set_shadow(front_shadow, ASAN_MEM_REDZONE_FLAG); + front_shadow = ASAN_MEM_TO_SHADOW(head_redzone + - ASAN_SHADOW_GRAIN_SIZE); + asan_set_shadow(front_shadow, ASAN_MEM_REDZONE_FLAG); + + /* Set mark after the end of the allocated memory */ + tail_redzone = (uintptr_t)RTE_PTR_ADD(elem, + MALLOC_ELEM_HEADER_LEN + + elem->user_size); + tail_shadow = ASAN_MEM_TO_SHADOW(tail_redzone); + val = (tail_redzone % ASAN_SHADOW_GRAIN_SIZE); + val = (val == 0) ? ASAN_MEM_REDZONE_FLAG : val; + asan_set_shadow(tail_shadow, val); + tail_shadow = ASAN_MEM_TO_SHADOW(tail_redzone + + ASAN_SHADOW_GRAIN_SIZE); + asan_set_shadow(tail_shadow, ASAN_MEM_REDZONE_FLAG); + } +} + +/* + * When the memory is released, the mark of the memory boundary + * in the corresponding range of the shadow area is cleared. + * Requirement: redzone >= 16, is a power of two. + */ +static inline void +asan_clear_redzone(struct malloc_elem *elem) +{ + uintptr_t head_redzone; + uintptr_t tail_redzone; + void *head_shadow; + void *tail_shadow; + + if (elem != NULL) { + elem = RTE_PTR_ADD(elem, elem->pad); + + /* Clear mark before the start of the allocated memory */ + head_redzone = (uintptr_t)RTE_PTR_ADD(elem, + MALLOC_ELEM_HEADER_LEN - ASAN_SHADOW_GRAIN_SIZE); + head_shadow = ASAN_MEM_TO_SHADOW(head_redzone); + asan_set_shadow(head_shadow, 0x00); + head_shadow = ASAN_MEM_TO_SHADOW(head_redzone + - ASAN_SHADOW_GRAIN_SIZE); + asan_set_shadow(head_shadow, 0x00); + + /* Clear mark after the end of the allocated memory */ + tail_redzone = (uintptr_t)RTE_PTR_ADD(elem, + MALLOC_ELEM_HEADER_LEN + elem->user_size); + tail_shadow = ASAN_MEM_TO_SHADOW(tail_redzone); + asan_set_shadow(tail_shadow, 0x00); + tail_shadow = ASAN_MEM_TO_SHADOW(tail_redzone + + ASAN_SHADOW_GRAIN_SIZE); + asan_set_shadow(tail_shadow, 0x00); + } +} + +static inline size_t +old_malloc_size(struct malloc_elem *elem) +{ + if (elem->state != ELEM_PAD) + elem = RTE_PTR_ADD(elem, elem->pad); + + return elem->user_size; +} +#else +static inline void +asan_set_freezone(void *ptr __rte_unused, size_t size __rte_unused) { } + +static inline void +asan_clear_alloczone(struct malloc_elem *elem __rte_unused) { } + +static inline void +asan_clear_split_alloczone(struct malloc_elem *elem __rte_unused) { } + +static inline void +asan_set_redzone(struct malloc_elem *elem __rte_unused, + size_t user_size __rte_unused) { } + +static inline void +asan_clear_redzone(struct malloc_elem *elem __rte_unused) { } + +static inline size_t +old_malloc_size(struct malloc_elem *elem) +{ + return elem->size - elem->pad - MALLOC_ELEM_OVERHEAD; +} +#endif + /* * Given a pointer to the start of a memory block returned by malloc, get * the actual malloc_elem header for that block. diff --git a/lib/eal/common/malloc_heap.c b/lib/eal/common/malloc_heap.c index ee400f38ec..775d6789df 100644 --- a/lib/eal/common/malloc_heap.c +++ b/lib/eal/common/malloc_heap.c @@ -237,6 +237,7 @@ heap_alloc(struct malloc_heap *heap, const char *type __rte_unused, size_t size, unsigned int flags, size_t align, size_t bound, bool contig) { struct malloc_elem *elem; + size_t user_size = size; size = RTE_CACHE_LINE_ROUNDUP(size); align = RTE_CACHE_LINE_ROUNDUP(align); @@ -250,6 +251,8 @@ heap_alloc(struct malloc_heap *heap, const char *type __rte_unused, size_t size, /* increase heap's count of allocated elements */ heap->alloc_count++; + + asan_set_redzone(elem, user_size); } return elem == NULL ? NULL : (void *)(&elem[1]); @@ -270,6 +273,8 @@ heap_alloc_biggest(struct malloc_heap *heap, const char *type __rte_unused, /* increase heap's count of allocated elements */ heap->alloc_count++; + + asan_set_redzone(elem, size); } return elem == NULL ? NULL : (void *)(&elem[1]); @@ -841,6 +846,8 @@ malloc_heap_free(struct malloc_elem *elem) if (!malloc_elem_cookies_ok(elem) || elem->state != ELEM_BUSY) return -1; + asan_clear_redzone(elem); + /* elem may be merged with previous element, so keep heap address */ heap = elem->heap; msl = elem->msl; @@ -848,6 +855,9 @@ malloc_heap_free(struct malloc_elem *elem) rte_spinlock_lock(&(heap->lock)); + void *asan_ptr = RTE_PTR_ADD(elem, MALLOC_ELEM_HEADER_LEN + elem->pad); + size_t asan_data_len = elem->size - MALLOC_ELEM_OVERHEAD - elem->pad; + /* mark element as free */ elem->state = ELEM_FREE; @@ -1001,6 +1011,8 @@ malloc_heap_free(struct malloc_elem *elem) rte_mcfg_mem_write_unlock(); free_unlock: + asan_set_freezone(asan_ptr, asan_data_len); + rte_spinlock_unlock(&(heap->lock)); return ret; } diff --git a/lib/eal/common/rte_malloc.c b/lib/eal/common/rte_malloc.c index 9d39e58c08..d0bec26920 100644 --- a/lib/eal/common/rte_malloc.c +++ b/lib/eal/common/rte_malloc.c @@ -162,6 +162,8 @@ rte_calloc(const char *type, size_t num, size_t size, unsigned align) void * rte_realloc_socket(void *ptr, size_t size, unsigned int align, int socket) { + size_t user_size; + if (ptr == NULL) return rte_malloc_socket(NULL, size, align, socket); @@ -171,6 +173,8 @@ rte_realloc_socket(void *ptr, size_t size, unsigned int align, int socket) return NULL; } + user_size = size; + size = RTE_CACHE_LINE_ROUNDUP(size), align = RTE_CACHE_LINE_ROUNDUP(align); /* check requested socket id and alignment matches first, and if ok, @@ -181,6 +185,9 @@ rte_realloc_socket(void *ptr, size_t size, unsigned int align, int socket) RTE_PTR_ALIGN(ptr, align) == ptr && malloc_heap_resize(elem, size) == 0) { rte_eal_trace_mem_realloc(size, align, socket, ptr); + + asan_set_redzone(elem, user_size); + return ptr; } @@ -192,7 +199,7 @@ rte_realloc_socket(void *ptr, size_t size, unsigned int align, int socket) if (new_ptr == NULL) return NULL; /* elem: |pad|data_elem|data|trailer| */ - const size_t old_size = elem->size - elem->pad - MALLOC_ELEM_OVERHEAD; + const size_t old_size = old_malloc_size(elem); rte_memcpy(new_ptr, ptr, old_size < size ? old_size : size); rte_free(ptr); -- 2.25.1 ^ permalink raw reply [flat|nested] 117+ messages in thread
* [dpdk-dev] [PATCH v10 3/4] pipeline: Fix compilation error with gcc ASan 2021-10-15 15:11 ` zhihongx.peng 2021-10-15 15:11 ` [dpdk-dev] [PATCH v10 2/4] DPDK code adapts to ASan zhihongx.peng @ 2021-10-15 15:11 ` zhihongx.peng 2021-10-18 12:21 ` Dumitrescu, Cristian 2021-10-15 15:11 ` [dpdk-dev] [PATCH v10 4/4] performance-thread: Fix cross compilation failed zhihongx.peng 2021-10-19 5:52 ` [dpdk-dev] [PATCH v10 " Peng, ZhihongX 3 siblings, 1 reply; 117+ messages in thread From: zhihongx.peng @ 2021-10-15 15:11 UTC (permalink / raw) To: david.marchand, anatoly.burakov, konstantin.ananyev, stephen, cristian.dumitrescu, john.mcnamara Cc: dev, xueqin.lin, Zhihong Peng, stable From: Zhihong Peng <zhihongx.peng@intel.com> The gcc will check code more stricter when ASan enabled. "Control reaches end of non-void function" error occurs here. Fixes: f38913b7fb8e ("pipeline: add meter array to SWX") Cc: stable@dpdk.org Signed-off-by: Xueqin Lin <xueqin.lin@intel.com> Signed-off-by: Zhihong Peng <zhihongx.peng@intel.com> --- v7: no change v8: no change v9: Modify the submit log v10:no change --- lib/pipeline/rte_swx_pipeline.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/pipeline/rte_swx_pipeline.c b/lib/pipeline/rte_swx_pipeline.c index 1cd09a4b44..0acd6c6752 100644 --- a/lib/pipeline/rte_swx_pipeline.c +++ b/lib/pipeline/rte_swx_pipeline.c @@ -4642,7 +4642,7 @@ instr_meter_translate(struct rte_swx_pipeline *p, return 0; } - CHECK(0, EINVAL); + return -EINVAL; } static inline void @@ -5937,7 +5937,7 @@ instr_translate(struct rte_swx_pipeline *p, instr, data); - CHECK(0, EINVAL); + return -EINVAL; } static struct instruction_data * -- 2.25.1 ^ permalink raw reply [flat|nested] 117+ messages in thread
* Re: [dpdk-dev] [PATCH v10 3/4] pipeline: Fix compilation error with gcc ASan 2021-10-15 15:11 ` [dpdk-dev] [PATCH v10 3/4] pipeline: Fix compilation error with gcc ASan zhihongx.peng @ 2021-10-18 12:21 ` Dumitrescu, Cristian 2021-10-18 12:54 ` Peng, ZhihongX 0 siblings, 1 reply; 117+ messages in thread From: Dumitrescu, Cristian @ 2021-10-18 12:21 UTC (permalink / raw) To: Peng, ZhihongX, david.marchand, Burakov, Anatoly, Ananyev, Konstantin, stephen, Mcnamara, John Cc: dev, Lin, Xueqin, stable > -----Original Message----- > From: Peng, ZhihongX <zhihongx.peng@intel.com> > Sent: Friday, October 15, 2021 4:11 PM > To: david.marchand@redhat.com; Burakov, Anatoly > <anatoly.burakov@intel.com>; Ananyev, Konstantin > <konstantin.ananyev@intel.com>; stephen@networkplumber.org; > Dumitrescu, Cristian <cristian.dumitrescu@intel.com>; Mcnamara, John > <john.mcnamara@intel.com> > Cc: dev@dpdk.org; Lin, Xueqin <xueqin.lin@intel.com>; Peng, ZhihongX > <zhihongx.peng@intel.com>; stable@dpdk.org > Subject: [PATCH v10 3/4] pipeline: Fix compilation error with gcc ASan > > From: Zhihong Peng <zhihongx.peng@intel.com> > > The gcc will check code more stricter when ASan enabled. > "Control reaches end of non-void function" error occurs here. > > Fixes: f38913b7fb8e ("pipeline: add meter array to SWX") > Cc: stable@dpdk.org > > Signed-off-by: Xueqin Lin <xueqin.lin@intel.com> > Signed-off-by: Zhihong Peng <zhihongx.peng@intel.com> > --- > v7: no change > v8: no change > v9: Modify the submit log > v10:no change > --- > lib/pipeline/rte_swx_pipeline.c | 4 ++-- > 1 file changed, 2 insertions(+), 2 deletions(-) > > diff --git a/lib/pipeline/rte_swx_pipeline.c b/lib/pipeline/rte_swx_pipeline.c > index 1cd09a4b44..0acd6c6752 100644 > --- a/lib/pipeline/rte_swx_pipeline.c > +++ b/lib/pipeline/rte_swx_pipeline.c > @@ -4642,7 +4642,7 @@ instr_meter_translate(struct rte_swx_pipeline *p, > return 0; > } > > - CHECK(0, EINVAL); > + return -EINVAL; > } > > static inline void > @@ -5937,7 +5937,7 @@ instr_translate(struct rte_swx_pipeline *p, > instr, > data); > > - CHECK(0, EINVAL); > + return -EINVAL; > } > > static struct instruction_data * > -- > 2.25.1 NACK. This is a false issue, no bug is here. CHECK(0, EINVAL) translates to an unconditional return -EINVAL. Does this tool work correctly when macros are present? Maybe the tool should parse the preprocessed C code as opposed to initial C code? Regards, Cristian ^ permalink raw reply [flat|nested] 117+ messages in thread
* Re: [dpdk-dev] [PATCH v10 3/4] pipeline: Fix compilation error with gcc ASan 2021-10-18 12:21 ` Dumitrescu, Cristian @ 2021-10-18 12:54 ` Peng, ZhihongX 2021-10-19 11:26 ` Dumitrescu, Cristian 0 siblings, 1 reply; 117+ messages in thread From: Peng, ZhihongX @ 2021-10-18 12:54 UTC (permalink / raw) To: Dumitrescu, Cristian, david.marchand, Burakov, Anatoly, Ananyev, Konstantin, stephen, Mcnamara, John Cc: dev, Lin, Xueqin, stable > -----Original Message----- > From: Dumitrescu, Cristian <cristian.dumitrescu@intel.com> > Sent: Monday, October 18, 2021 8:22 PM > To: Peng, ZhihongX <zhihongx.peng@intel.com>; > david.marchand@redhat.com; Burakov, Anatoly > <anatoly.burakov@intel.com>; Ananyev, Konstantin > <konstantin.ananyev@intel.com>; stephen@networkplumber.org; > Mcnamara, John <john.mcnamara@intel.com> > Cc: dev@dpdk.org; Lin, Xueqin <xueqin.lin@intel.com>; stable@dpdk.org > Subject: RE: [PATCH v10 3/4] pipeline: Fix compilation error with gcc ASan > > > > > -----Original Message----- > > From: Peng, ZhihongX <zhihongx.peng@intel.com> > > Sent: Friday, October 15, 2021 4:11 PM > > To: david.marchand@redhat.com; Burakov, Anatoly > > <anatoly.burakov@intel.com>; Ananyev, Konstantin > > <konstantin.ananyev@intel.com>; stephen@networkplumber.org; > > Dumitrescu, Cristian <cristian.dumitrescu@intel.com>; Mcnamara, John > > <john.mcnamara@intel.com> > > Cc: dev@dpdk.org; Lin, Xueqin <xueqin.lin@intel.com>; Peng, ZhihongX > > <zhihongx.peng@intel.com>; stable@dpdk.org > > Subject: [PATCH v10 3/4] pipeline: Fix compilation error with gcc ASan > > > > From: Zhihong Peng <zhihongx.peng@intel.com> > > > > The gcc will check code more stricter when ASan enabled. > > "Control reaches end of non-void function" error occurs here. > > > > Fixes: f38913b7fb8e ("pipeline: add meter array to SWX") > > Cc: stable@dpdk.org > > > > Signed-off-by: Xueqin Lin <xueqin.lin@intel.com> > > Signed-off-by: Zhihong Peng <zhihongx.peng@intel.com> > > --- > > v7: no change > > v8: no change > > v9: Modify the submit log > > v10:no change > > --- > > lib/pipeline/rte_swx_pipeline.c | 4 ++-- > > 1 file changed, 2 insertions(+), 2 deletions(-) > > > > diff --git a/lib/pipeline/rte_swx_pipeline.c > > b/lib/pipeline/rte_swx_pipeline.c index 1cd09a4b44..0acd6c6752 100644 > > --- a/lib/pipeline/rte_swx_pipeline.c > > +++ b/lib/pipeline/rte_swx_pipeline.c > > @@ -4642,7 +4642,7 @@ instr_meter_translate(struct rte_swx_pipeline *p, > > return 0; > > } > > > > - CHECK(0, EINVAL); > > + return -EINVAL; > > } > > > > static inline void > > @@ -5937,7 +5937,7 @@ instr_translate(struct rte_swx_pipeline *p, > > instr, > > data); > > > > - CHECK(0, EINVAL); > > + return -EINVAL; > > } > > > > static struct instruction_data * > > -- > > 2.25.1 > > NACK. > > This is a false issue, no bug is here. CHECK(0, EINVAL) translates to an > unconditional return -EINVAL. > Does this tool work correctly when macros are present? Maybe the tool > should parse the preprocessed C code as opposed to initial C code? Yes, this is not a bug, it just solves the problem that cannot be passed after adding the asan compiler option. Only part of the macro reports errors, which may be caused by the tool itself, but this tool is part of gcc and clang, so we still have to make the code not report errors. > Regards, > Cristian ^ permalink raw reply [flat|nested] 117+ messages in thread
* Re: [dpdk-dev] [PATCH v10 3/4] pipeline: Fix compilation error with gcc ASan 2021-10-18 12:54 ` Peng, ZhihongX @ 2021-10-19 11:26 ` Dumitrescu, Cristian 2021-10-19 12:11 ` Peng, ZhihongX 0 siblings, 1 reply; 117+ messages in thread From: Dumitrescu, Cristian @ 2021-10-19 11:26 UTC (permalink / raw) To: Peng, ZhihongX, david.marchand, Burakov, Anatoly, Ananyev, Konstantin, stephen, Mcnamara, John Cc: dev, Lin, Xueqin, stable > -----Original Message----- > From: Peng, ZhihongX <zhihongx.peng@intel.com> > Sent: Monday, October 18, 2021 1:55 PM > To: Dumitrescu, Cristian <cristian.dumitrescu@intel.com>; > david.marchand@redhat.com; Burakov, Anatoly > <anatoly.burakov@intel.com>; Ananyev, Konstantin > <konstantin.ananyev@intel.com>; stephen@networkplumber.org; > Mcnamara, John <john.mcnamara@intel.com> > Cc: dev@dpdk.org; Lin, Xueqin <xueqin.lin@intel.com>; stable@dpdk.org > Subject: RE: [PATCH v10 3/4] pipeline: Fix compilation error with gcc ASan > > > -----Original Message----- > > From: Dumitrescu, Cristian <cristian.dumitrescu@intel.com> > > Sent: Monday, October 18, 2021 8:22 PM > > To: Peng, ZhihongX <zhihongx.peng@intel.com>; > > david.marchand@redhat.com; Burakov, Anatoly > > <anatoly.burakov@intel.com>; Ananyev, Konstantin > > <konstantin.ananyev@intel.com>; stephen@networkplumber.org; > > Mcnamara, John <john.mcnamara@intel.com> > > Cc: dev@dpdk.org; Lin, Xueqin <xueqin.lin@intel.com>; stable@dpdk.org > > Subject: RE: [PATCH v10 3/4] pipeline: Fix compilation error with gcc ASan > > > > > > > > > -----Original Message----- > > > From: Peng, ZhihongX <zhihongx.peng@intel.com> > > > Sent: Friday, October 15, 2021 4:11 PM > > > To: david.marchand@redhat.com; Burakov, Anatoly > > > <anatoly.burakov@intel.com>; Ananyev, Konstantin > > > <konstantin.ananyev@intel.com>; stephen@networkplumber.org; > > > Dumitrescu, Cristian <cristian.dumitrescu@intel.com>; Mcnamara, John > > > <john.mcnamara@intel.com> > > > Cc: dev@dpdk.org; Lin, Xueqin <xueqin.lin@intel.com>; Peng, ZhihongX > > > <zhihongx.peng@intel.com>; stable@dpdk.org > > > Subject: [PATCH v10 3/4] pipeline: Fix compilation error with gcc ASan > > > > > > From: Zhihong Peng <zhihongx.peng@intel.com> > > > > > > The gcc will check code more stricter when ASan enabled. > > > "Control reaches end of non-void function" error occurs here. > > > > > > Fixes: f38913b7fb8e ("pipeline: add meter array to SWX") > > > Cc: stable@dpdk.org > > > > > > Signed-off-by: Xueqin Lin <xueqin.lin@intel.com> > > > Signed-off-by: Zhihong Peng <zhihongx.peng@intel.com> > > > --- > > > v7: no change > > > v8: no change > > > v9: Modify the submit log > > > v10:no change > > > --- > > > lib/pipeline/rte_swx_pipeline.c | 4 ++-- > > > 1 file changed, 2 insertions(+), 2 deletions(-) > > > > > > diff --git a/lib/pipeline/rte_swx_pipeline.c > > > b/lib/pipeline/rte_swx_pipeline.c index 1cd09a4b44..0acd6c6752 100644 > > > --- a/lib/pipeline/rte_swx_pipeline.c > > > +++ b/lib/pipeline/rte_swx_pipeline.c > > > @@ -4642,7 +4642,7 @@ instr_meter_translate(struct rte_swx_pipeline > *p, > > > return 0; > > > } > > > > > > - CHECK(0, EINVAL); > > > + return -EINVAL; > > > } > > > > > > static inline void > > > @@ -5937,7 +5937,7 @@ instr_translate(struct rte_swx_pipeline *p, > > > instr, > > > data); > > > > > > - CHECK(0, EINVAL); > > > + return -EINVAL; > > > } > > > > > > static struct instruction_data * > > > -- > > > 2.25.1 > > > > NACK. > > > > This is a false issue, no bug is here. CHECK(0, EINVAL) translates to an > > unconditional return -EINVAL. > > Does this tool work correctly when macros are present? Maybe the tool > > should parse the preprocessed C code as opposed to initial C code? > > Yes, this is not a bug, it just solves the problem that cannot be passed after > adding the asan compiler option. > Only part of the macro reports errors, which may be caused by the tool itself, > but this tool is part of gcc and clang, so we still have to make the code not > report errors. > > Regards, > > Cristian Hi Zhihong, If this is not a bug in the pipeline library, why then does your patch has fix in the tile, has the Fixes label and CC-es stable@dpdk.org? Please remove and rephrase accordingly. I agree this is not a bug, and based on your statements I understand this is a sort of issue or limitation with the tool . I would prefer we fix the tool rather than fixing correct code in order to please the tool. This is likely not going to be an isolated case, but a recurring issue. Hence, I am reluctantly OK to ack this patch in order to allow the tool in (after the "fix" claim is removed), hopefully the tool will prove its benefit to DPDK. Regards, Cristian ^ permalink raw reply [flat|nested] 117+ messages in thread
* Re: [dpdk-dev] [PATCH v10 3/4] pipeline: Fix compilation error with gcc ASan 2021-10-19 11:26 ` Dumitrescu, Cristian @ 2021-10-19 12:11 ` Peng, ZhihongX 0 siblings, 0 replies; 117+ messages in thread From: Peng, ZhihongX @ 2021-10-19 12:11 UTC (permalink / raw) To: Dumitrescu, Cristian, david.marchand, Burakov, Anatoly, Ananyev, Konstantin, stephen, Mcnamara, John Cc: dev, Lin, Xueqin, stable > -----Original Message----- > From: Dumitrescu, Cristian <cristian.dumitrescu@intel.com> > Sent: Tuesday, October 19, 2021 7:26 PM > To: Peng, ZhihongX <zhihongx.peng@intel.com>; > david.marchand@redhat.com; Burakov, Anatoly > <anatoly.burakov@intel.com>; Ananyev, Konstantin > <konstantin.ananyev@intel.com>; stephen@networkplumber.org; > Mcnamara, John <john.mcnamara@intel.com> > Cc: dev@dpdk.org; Lin, Xueqin <xueqin.lin@intel.com>; stable@dpdk.org > Subject: RE: [PATCH v10 3/4] pipeline: Fix compilation error with gcc ASan > > > > > -----Original Message----- > > From: Peng, ZhihongX <zhihongx.peng@intel.com> > > Sent: Monday, October 18, 2021 1:55 PM > > To: Dumitrescu, Cristian <cristian.dumitrescu@intel.com>; > > david.marchand@redhat.com; Burakov, Anatoly > > <anatoly.burakov@intel.com>; Ananyev, Konstantin > > <konstantin.ananyev@intel.com>; stephen@networkplumber.org; > Mcnamara, > > John <john.mcnamara@intel.com> > > Cc: dev@dpdk.org; Lin, Xueqin <xueqin.lin@intel.com>; stable@dpdk.org > > Subject: RE: [PATCH v10 3/4] pipeline: Fix compilation error with gcc > > ASan > > > > > -----Original Message----- > > > From: Dumitrescu, Cristian <cristian.dumitrescu@intel.com> > > > Sent: Monday, October 18, 2021 8:22 PM > > > To: Peng, ZhihongX <zhihongx.peng@intel.com>; > > > david.marchand@redhat.com; Burakov, Anatoly > > > <anatoly.burakov@intel.com>; Ananyev, Konstantin > > > <konstantin.ananyev@intel.com>; stephen@networkplumber.org; > > > Mcnamara, John <john.mcnamara@intel.com> > > > Cc: dev@dpdk.org; Lin, Xueqin <xueqin.lin@intel.com>; > > > stable@dpdk.org > > > Subject: RE: [PATCH v10 3/4] pipeline: Fix compilation error with > > > gcc ASan > > > > > > > > > > > > > -----Original Message----- > > > > From: Peng, ZhihongX <zhihongx.peng@intel.com> > > > > Sent: Friday, October 15, 2021 4:11 PM > > > > To: david.marchand@redhat.com; Burakov, Anatoly > > > > <anatoly.burakov@intel.com>; Ananyev, Konstantin > > > > <konstantin.ananyev@intel.com>; stephen@networkplumber.org; > > > > Dumitrescu, Cristian <cristian.dumitrescu@intel.com>; Mcnamara, > > > > John <john.mcnamara@intel.com> > > > > Cc: dev@dpdk.org; Lin, Xueqin <xueqin.lin@intel.com>; Peng, > > > > ZhihongX <zhihongx.peng@intel.com>; stable@dpdk.org > > > > Subject: [PATCH v10 3/4] pipeline: Fix compilation error with gcc > > > > ASan > > > > > > > > From: Zhihong Peng <zhihongx.peng@intel.com> > > > > > > > > The gcc will check code more stricter when ASan enabled. > > > > "Control reaches end of non-void function" error occurs here. > > > > > > > > Fixes: f38913b7fb8e ("pipeline: add meter array to SWX") > > > > Cc: stable@dpdk.org > > > > > > > > Signed-off-by: Xueqin Lin <xueqin.lin@intel.com> > > > > Signed-off-by: Zhihong Peng <zhihongx.peng@intel.com> > > > > --- > > > > v7: no change > > > > v8: no change > > > > v9: Modify the submit log > > > > v10:no change > > > > --- > > > > lib/pipeline/rte_swx_pipeline.c | 4 ++-- > > > > 1 file changed, 2 insertions(+), 2 deletions(-) > > > > > > > > diff --git a/lib/pipeline/rte_swx_pipeline.c > > > > b/lib/pipeline/rte_swx_pipeline.c index 1cd09a4b44..0acd6c6752 > > > > 100644 > > > > --- a/lib/pipeline/rte_swx_pipeline.c > > > > +++ b/lib/pipeline/rte_swx_pipeline.c > > > > @@ -4642,7 +4642,7 @@ instr_meter_translate(struct > > > > rte_swx_pipeline > > *p, > > > > return 0; > > > > } > > > > > > > > - CHECK(0, EINVAL); > > > > + return -EINVAL; > > > > } > > > > > > > > static inline void > > > > @@ -5937,7 +5937,7 @@ instr_translate(struct rte_swx_pipeline *p, > > > > instr, > > > > data); > > > > > > > > - CHECK(0, EINVAL); > > > > + return -EINVAL; > > > > } > > > > > > > > static struct instruction_data * > > > > -- > > > > 2.25.1 > > > > > > NACK. > > > > > > This is a false issue, no bug is here. CHECK(0, EINVAL) translates > > > to an unconditional return -EINVAL. > > > Does this tool work correctly when macros are present? Maybe the > > > tool should parse the preprocessed C code as opposed to initial C code? > > > > Yes, this is not a bug, it just solves the problem that cannot be > > passed after adding the asan compiler option. > > Only part of the macro reports errors, which may be caused by the tool > > itself, but this tool is part of gcc and clang, so we still have to > > make the code not report errors. > > > Regards, > > > Cristian > > Hi Zhihong, > > If this is not a bug in the pipeline library, why then does your patch has fix in > the tile, has the Fixes label and CC-es stable@dpdk.org? Please remove and > rephrase accordingly. > > I agree this is not a bug, and based on your statements I understand this is a > sort of issue or limitation with the tool . I would prefer we fix the tool rather > than fixing correct code in order to please the tool. This is likely not going to > be an isolated case, but a recurring issue. > > Hence, I am reluctantly OK to ack this patch in order to allow the tool in (after > the "fix" claim is removed), hopefully the tool will prove its benefit to DPDK. ok, I will delete it. > Regards, > Cristian ^ permalink raw reply [flat|nested] 117+ messages in thread
* [dpdk-dev] [PATCH v10 4/4] performance-thread: Fix cross compilation failed 2021-10-15 15:11 ` zhihongx.peng 2021-10-15 15:11 ` [dpdk-dev] [PATCH v10 2/4] DPDK code adapts to ASan zhihongx.peng 2021-10-15 15:11 ` [dpdk-dev] [PATCH v10 3/4] pipeline: Fix compilation error with gcc ASan zhihongx.peng @ 2021-10-15 15:11 ` zhihongx.peng 2021-10-19 6:02 ` Peng, ZhihongX 2021-10-19 10:12 ` [dpdk-dev] [PATCH v11 1/4] Enable ASan for memory detector on DPDK zhihongx.peng 2021-10-19 5:52 ` [dpdk-dev] [PATCH v10 " Peng, ZhihongX 3 siblings, 2 replies; 117+ messages in thread From: zhihongx.peng @ 2021-10-15 15:11 UTC (permalink / raw) To: david.marchand, anatoly.burakov, konstantin.ananyev, stephen, cristian.dumitrescu, john.mcnamara Cc: dev, xueqin.lin, Zhihong Peng, stable From: Zhihong Peng <zhihongx.peng@intel.com> The gcc(arm-linux-gcc) will check code more stricter when ASan enabled. "strncpy specified bound XX equals destination size" error occurs here. Fixes: 116819b9ed0d ("examples/performance-thread: add lthread subsystem") Cc: stable@dpdk.org Signed-off-by: Xueqin Lin <xueqin.lin@intel.com> Signed-off-by: Zhihong Peng <zhihongx.peng@intel.com> --- examples/performance-thread/common/lthread.c | 2 +- examples/performance-thread/common/lthread_cond.c | 4 ++-- examples/performance-thread/common/lthread_mutex.c | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/examples/performance-thread/common/lthread.c b/examples/performance-thread/common/lthread.c index 3f1f48db43..dd4b3b27ee 100644 --- a/examples/performance-thread/common/lthread.c +++ b/examples/performance-thread/common/lthread.c @@ -463,6 +463,6 @@ void lthread_set_funcname(const char *f) { struct lthread *lt = THIS_LTHREAD; - strncpy(lt->funcname, f, sizeof(lt->funcname)); + strncpy(lt->funcname, f, sizeof(lt->funcname) - 1); lt->funcname[sizeof(lt->funcname)-1] = 0; } diff --git a/examples/performance-thread/common/lthread_cond.c b/examples/performance-thread/common/lthread_cond.c index cdcc7a7b5a..6ec8bc7e82 100644 --- a/examples/performance-thread/common/lthread_cond.c +++ b/examples/performance-thread/common/lthread_cond.c @@ -57,9 +57,9 @@ lthread_cond_init(char *name, struct lthread_cond **cond, } if (name == NULL) - strncpy(c->name, "no name", sizeof(c->name)); + strncpy(c->name, "no name", sizeof(c->name) - 1); else - strncpy(c->name, name, sizeof(c->name)); + strncpy(c->name, name, sizeof(c->name) - 1); c->name[sizeof(c->name)-1] = 0; c->root_sched = THIS_SCHED; diff --git a/examples/performance-thread/common/lthread_mutex.c b/examples/performance-thread/common/lthread_mutex.c index 01da6cad4f..7e5da609b1 100644 --- a/examples/performance-thread/common/lthread_mutex.c +++ b/examples/performance-thread/common/lthread_mutex.c @@ -52,9 +52,9 @@ lthread_mutex_init(char *name, struct lthread_mutex **mutex, } if (name == NULL) - strncpy(m->name, "no name", sizeof(m->name)); + strncpy(m->name, "no name", sizeof(m->name) - 1); else - strncpy(m->name, name, sizeof(m->name)); + strncpy(m->name, name, sizeof(m->name) - 1); m->name[sizeof(m->name)-1] = 0; m->root_sched = THIS_SCHED; -- 2.25.1 ^ permalink raw reply [flat|nested] 117+ messages in thread
* Re: [dpdk-dev] [PATCH v10 4/4] performance-thread: Fix cross compilation failed 2021-10-15 15:11 ` [dpdk-dev] [PATCH v10 4/4] performance-thread: Fix cross compilation failed zhihongx.peng @ 2021-10-19 6:02 ` Peng, ZhihongX 2021-10-19 10:12 ` [dpdk-dev] [PATCH v11 1/4] Enable ASan for memory detector on DPDK zhihongx.peng 1 sibling, 0 replies; 117+ messages in thread From: Peng, ZhihongX @ 2021-10-19 6:02 UTC (permalink / raw) To: david.marchand, Burakov, Anatoly, Ananyev, Konstantin, stephen, Dumitrescu, Cristian, Mcnamara, John Cc: dev, Lin, Xueqin, stable > -----Original Message----- > From: Peng, ZhihongX <zhihongx.peng@intel.com> > Sent: Friday, October 15, 2021 11:11 PM > To: david.marchand@redhat.com; Burakov, Anatoly > <anatoly.burakov@intel.com>; Ananyev, Konstantin > <konstantin.ananyev@intel.com>; stephen@networkplumber.org; > Dumitrescu, Cristian <cristian.dumitrescu@intel.com>; Mcnamara, John > <john.mcnamara@intel.com> > Cc: dev@dpdk.org; Lin, Xueqin <xueqin.lin@intel.com>; Peng, ZhihongX > <ZhihongX.Peng@intel.com>; stable@dpdk.org > Subject: [PATCH v10 4/4] performance-thread: Fix cross compilation failed > > From: Zhihong Peng <zhihongx.peng@intel.com> > > The gcc(arm-linux-gcc) will check code more stricter when ASan enabled. > "strncpy specified bound XX equals destination size" error occurs here. > > Fixes: 116819b9ed0d ("examples/performance-thread: add lthread > subsystem") > Cc: stable@dpdk.org > > Signed-off-by: Xueqin Lin <xueqin.lin@intel.com> > Signed-off-by: Zhihong Peng <zhihongx.peng@intel.com> > --- > examples/performance-thread/common/lthread.c | 2 +- > examples/performance-thread/common/lthread_cond.c | 4 ++-- > examples/performance-thread/common/lthread_mutex.c | 4 ++-- > 3 files changed, 5 insertions(+), 5 deletions(-) > > diff --git a/examples/performance-thread/common/lthread.c > b/examples/performance-thread/common/lthread.c > index 3f1f48db43..dd4b3b27ee 100644 > --- a/examples/performance-thread/common/lthread.c > +++ b/examples/performance-thread/common/lthread.c > @@ -463,6 +463,6 @@ void lthread_set_funcname(const char *f) { > struct lthread *lt = THIS_LTHREAD; > > - strncpy(lt->funcname, f, sizeof(lt->funcname)); > + strncpy(lt->funcname, f, sizeof(lt->funcname) - 1); > lt->funcname[sizeof(lt->funcname)-1] = 0; } diff --git > a/examples/performance-thread/common/lthread_cond.c > b/examples/performance-thread/common/lthread_cond.c > index cdcc7a7b5a..6ec8bc7e82 100644 > --- a/examples/performance-thread/common/lthread_cond.c > +++ b/examples/performance-thread/common/lthread_cond.c > @@ -57,9 +57,9 @@ lthread_cond_init(char *name, struct lthread_cond > **cond, > } > > if (name == NULL) > - strncpy(c->name, "no name", sizeof(c->name)); > + strncpy(c->name, "no name", sizeof(c->name) - 1); > else > - strncpy(c->name, name, sizeof(c->name)); > + strncpy(c->name, name, sizeof(c->name) - 1); > c->name[sizeof(c->name)-1] = 0; > > c->root_sched = THIS_SCHED; > diff --git a/examples/performance-thread/common/lthread_mutex.c > b/examples/performance-thread/common/lthread_mutex.c > index 01da6cad4f..7e5da609b1 100644 > --- a/examples/performance-thread/common/lthread_mutex.c > +++ b/examples/performance-thread/common/lthread_mutex.c > @@ -52,9 +52,9 @@ lthread_mutex_init(char *name, struct lthread_mutex > **mutex, > } > > if (name == NULL) > - strncpy(m->name, "no name", sizeof(m->name)); > + strncpy(m->name, "no name", sizeof(m->name) - 1); > else > - strncpy(m->name, name, sizeof(m->name)); > + strncpy(m->name, name, sizeof(m->name) - 1); > m->name[sizeof(m->name)-1] = 0; > > m->root_sched = THIS_SCHED; > -- > 2.25.1 Hi, John Can you give me an ack? Thanks! ^ permalink raw reply [flat|nested] 117+ messages in thread
* [dpdk-dev] [PATCH v11 1/4] Enable ASan for memory detector on DPDK 2021-10-15 15:11 ` [dpdk-dev] [PATCH v10 4/4] performance-thread: Fix cross compilation failed zhihongx.peng 2021-10-19 6:02 ` Peng, ZhihongX @ 2021-10-19 10:12 ` zhihongx.peng 2021-10-19 10:12 ` [dpdk-dev] [PATCH v11 2/4] DPDK code adapts to ASan zhihongx.peng ` (3 more replies) 1 sibling, 4 replies; 117+ messages in thread From: zhihongx.peng @ 2021-10-19 10:12 UTC (permalink / raw) To: david.marchand, anatoly.burakov, konstantin.ananyev, stephen, cristian.dumitrescu, john.mcnamara, bruce.richardson Cc: dev, xueqin.lin, Zhihong Peng From: Zhihong Peng <zhihongx.peng@intel.com> `AddressSanitizer <https://github.com/google/sanitizers/wiki/AddressSanitizer>`_ (ASan) is a widely-used debugging tool to detect memory access errors. It helps detect issues like use-after-free, various kinds of buffer overruns in C/C++ programs, and other similar errors, as well as printing out detailed debug information whenever an error is detected. We can enable ASan by adding below compilation options: -Dbuildtype=debug -Db_lundef=false -Db_sanitize=address "-Dbuildtype=debug": This is a non-essential option. When this option is added, if a memory error occurs, ASan can clearly show where the code is wrong. "-Db_lundef=false": When use clang to compile DPDK, this option must be added. Signed-off-by: Xueqin Lin <xueqin.lin@intel.com> Signed-off-by: Zhihong Peng <zhihongx.peng@intel.com> --- v7: 1) Split doc and code into two. 2) Modify asan.rst doc v8: No change. v9: 1) Add the check of libasan library. 2) Add release notes. v10:1) Split doc and code into two. 2) Meson supports asan. v11:Modify the document. --- config/meson.build | 16 +++++++++++ devtools/words-case.txt | 1 + doc/guides/prog_guide/asan.rst | 40 ++++++++++++++++++++++++++ doc/guides/prog_guide/index.rst | 1 + doc/guides/rel_notes/release_21_11.rst | 9 ++++++ 5 files changed, 67 insertions(+) create mode 100644 doc/guides/prog_guide/asan.rst diff --git a/config/meson.build b/config/meson.build index 4cdf589e20..f02b0e9c6d 100644 --- a/config/meson.build +++ b/config/meson.build @@ -411,6 +411,22 @@ if get_option('b_lto') endif endif +if get_option('b_sanitize') == 'address' or get_option('b_sanitize') == 'address,undefined' + if is_windows + error('ASan is not supported on windows') + endif + + if cc.get_id() == 'gcc' + asan_dep = cc.find_library('asan', required: true) + if (not cc.links('int main(int argc, char *argv[]) { return 0; }', + dependencies: asan_dep)) + error('broken dependency, "libasan"') + endif + add_project_link_arguments('-lasan', language: 'c') + dpdk_extra_ldflags += '-lasan' + endif +endif + if get_option('default_library') == 'both' error( ''' Unsupported value "both" for "default_library" option. diff --git a/devtools/words-case.txt b/devtools/words-case.txt index 0bbad48626..ada6910fa0 100644 --- a/devtools/words-case.txt +++ b/devtools/words-case.txt @@ -5,6 +5,7 @@ API Arm armv7 armv8 +ASan BAR CRC DCB diff --git a/doc/guides/prog_guide/asan.rst b/doc/guides/prog_guide/asan.rst new file mode 100644 index 0000000000..969676ebe8 --- /dev/null +++ b/doc/guides/prog_guide/asan.rst @@ -0,0 +1,40 @@ +.. SPDX-License-Identifier: BSD-3-Clause + Copyright(c) 2021 Intel Corporation + +Running Address Sanitizer +========================= + +`AddressSanitizer +<https://github.com/google/sanitizers/wiki/AddressSanitizer>`_ (ASan) +is a widely-used debugging tool to detect memory access errors. +It helps detect issues like use-after-free, various kinds of buffer +overruns in C/C++ programs, and other similar errors, as well as +printing out detailed debug information whenever an error is detected. + +AddressSanitizer is a part of LLVM (3.1+) and GCC (4.8+). + +Usage +----- + +meson build +^^^^^^^^^^^ + +To enable ASan in meson build system, use following meson build command: + +Example usage:: + +* gcc:: + + meson build -Dbuildtype=debug -Db_sanitize=address + ninja -C build + +* clang:: + + meson build -Dbuildtype=debug -Db_lundef=false -Db_sanitize=address + ninja -C build + +.. Note:: + + a) To compile with gcc in centos, libasan needs to be installed separately. + b) If the program being tested uses cmdline you will need to execute the + "stty echo" command when a error occurs. diff --git a/doc/guides/prog_guide/index.rst b/doc/guides/prog_guide/index.rst index 89af28dacb..561d787fcf 100644 --- a/doc/guides/prog_guide/index.rst +++ b/doc/guides/prog_guide/index.rst @@ -72,3 +72,4 @@ Programmer's Guide lto profile_app glossary + asan diff --git a/doc/guides/rel_notes/release_21_11.rst b/doc/guides/rel_notes/release_21_11.rst index d5435a64aa..1fa8eb3368 100644 --- a/doc/guides/rel_notes/release_21_11.rst +++ b/doc/guides/rel_notes/release_21_11.rst @@ -167,6 +167,15 @@ New Features * Added tests to verify tunnel header verification in IPsec inbound. * Added tests to verify inner checksum. +* **Enable ASan for memory detector on DPDK.** + + `AddressSanitizer + <https://github.com/google/sanitizers/wiki/AddressSanitizer>` (ASan) + is a widely-used debugging tool to detect memory access errors. + It helps detect issues like use-after-free, various kinds of buffer + overruns in C/C++ programs, and other similar errors, as well as + printing out detailed debug information whenever an error is detected. + Removed Items ------------- -- 2.25.1 ^ permalink raw reply [flat|nested] 117+ messages in thread
* [dpdk-dev] [PATCH v11 2/4] DPDK code adapts to ASan 2021-10-19 10:12 ` [dpdk-dev] [PATCH v11 1/4] Enable ASan for memory detector on DPDK zhihongx.peng @ 2021-10-19 10:12 ` zhihongx.peng 2021-10-19 10:12 ` [dpdk-dev] [PATCH v11 3/4] pipeline: Fix compilation error with gcc ASan zhihongx.peng ` (2 subsequent siblings) 3 siblings, 0 replies; 117+ messages in thread From: zhihongx.peng @ 2021-10-19 10:12 UTC (permalink / raw) To: david.marchand, anatoly.burakov, konstantin.ananyev, stephen, cristian.dumitrescu, john.mcnamara, bruce.richardson Cc: dev, xueqin.lin, Zhihong Peng From: Zhihong Peng <zhihongx.peng@intel.com> DPDK ASan functionality is currently only supported Linux x86_64. Support other platforms, need to define ASAN_SHADOW_OFFSET value according to google ASan document, and configure meson (config/meson.build). Here is an example of heap-buffer-overflow bug: ...... char *p = rte_zmalloc(NULL, 7, 0); p[7] = 'a'; ...... Here is an example of use-after-free bug: ...... char *p = rte_zmalloc(NULL, 7, 0); rte_free(p); *p = 'a'; ...... Signed-off-by: Xueqin Lin <xueqin.lin@intel.com> Signed-off-by: Zhihong Peng <zhihongx.peng@intel.com> --- v7: Split doc and code into two. v8: No change. v9: Modify the definition of RTE_MALLOC_ASAN. v10:Modify the definition of RTE_MALLOC_ASAN v11:No change. --- config/meson.build | 4 + doc/guides/prog_guide/asan.rst | 68 +++++++++++- lib/eal/common/malloc_elem.c | 26 ++++- lib/eal/common/malloc_elem.h | 194 ++++++++++++++++++++++++++++++++- lib/eal/common/malloc_heap.c | 12 ++ lib/eal/common/rte_malloc.c | 9 +- 6 files changed, 306 insertions(+), 7 deletions(-) diff --git a/config/meson.build b/config/meson.build index f02b0e9c6d..bf751583bd 100644 --- a/config/meson.build +++ b/config/meson.build @@ -425,6 +425,10 @@ if get_option('b_sanitize') == 'address' or get_option('b_sanitize') == 'address add_project_link_arguments('-lasan', language: 'c') dpdk_extra_ldflags += '-lasan' endif + + if is_linux and arch_subdir == 'x86' + dpdk_conf.set10('RTE_MALLOC_ASAN', true) + endif endif if get_option('default_library') == 'both' diff --git a/doc/guides/prog_guide/asan.rst b/doc/guides/prog_guide/asan.rst index 969676ebe8..0cb2ca8751 100644 --- a/doc/guides/prog_guide/asan.rst +++ b/doc/guides/prog_guide/asan.rst @@ -13,6 +13,68 @@ printing out detailed debug information whenever an error is detected. AddressSanitizer is a part of LLVM (3.1+) and GCC (4.8+). +DPDK ASan functionality is currently only supported Linux x86_64. +Support other platforms, need to define ASAN_SHADOW_OFFSET value +according to google ASan document, and configure meson +(config/meson.build). + +Example heap-buffer-overflow error +---------------------------------- + +Following error was reported when ASan was enabled:: + + Applied 9 bytes of memory, but accessed the 10th byte of memory, + so heap-buffer-overflow appeared. + +Below code results in this error:: + + Add code to helloworld: + char *p = rte_zmalloc(NULL, 9, 0); + if (!p) { + printf("rte_zmalloc error."); + return -1; + } + p[9] = 'a'; + +The error log:: + + ==369953==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x7fb17f465809 at pc 0x5652e6707b84 bp 0x7ffea70eea20 sp 0x7ffea70eea10 WRITE of size 1 at 0x7fb17f465809 thread T0 + #0 0x5652e6707b83 in main ../examples/helloworld/main.c:47 + #1 0x7fb94953c0b2 in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x270b2) + #2 0x5652e67079bd in _start (/home/pzh/asan_test/x86_64-native-linuxapp-gcc/examples/dpdk-helloworld+0x8329bd) + + Address 0x7fb17f465809 is a wild pointer. + SUMMARY: AddressSanitizer: heap-buffer-overflow ../examples/helloworld/main.c:47 in main + +Example use-after-free error +---------------------------- + +Following error was reported when ASan was enabled:: + + Applied for 9 bytes of memory, and accessed the first byte after + released, so heap-use-after-free appeared. + +Below code results in this error:: + + Add code to helloworld: + char *p = rte_zmalloc(NULL, 9, 0); + if (!p) { + printf("rte_zmalloc error."); + return -1; + } + rte_free(p); + *p = 'a'; + +The error log:: + + ==417048==ERROR: AddressSanitizer: heap-use-after-free on address 0x7fc83f465800 at pc 0x564308a39b89 bp 0x7ffc8c85bf50 sp 0x7ffc8c85bf40 WRITE of size 1 at 0x7fc83f465800 thread T0 + #0 0x564308a39b88 in main ../examples/helloworld/main.c:48 + #1 0x7fd0079c60b2 in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x270b2) + #2 0x564308a399bd in _start (/home/pzh/asan_test/x86_64-native-linuxapp-gcc/examples/dpdk-helloworld+0x8329bd) + + Address 0x7fc83f465800 is a wild pointer. + SUMMARY: AddressSanitizer: heap-use-after-free ../examples/helloworld/main.c:48 in main + Usage ----- @@ -35,6 +97,8 @@ Example usage:: .. Note:: - a) To compile with gcc in centos, libasan needs to be installed separately. - b) If the program being tested uses cmdline you will need to execute the + a) Some of the features of ASan (for example, 'Display memory application location, currently + displayed as a wild pointer') are not currently supported by DPDK's implementation. + b) To compile with gcc in centos, libasan needs to be installed separately. + c) If the program being tested uses cmdline you will need to execute the "stty echo" command when a error occurs. diff --git a/lib/eal/common/malloc_elem.c b/lib/eal/common/malloc_elem.c index c2c9461f1d..bdd20a162e 100644 --- a/lib/eal/common/malloc_elem.c +++ b/lib/eal/common/malloc_elem.c @@ -446,6 +446,8 @@ malloc_elem_alloc(struct malloc_elem *elem, size_t size, unsigned align, struct malloc_elem *new_free_elem = RTE_PTR_ADD(new_elem, size + MALLOC_ELEM_OVERHEAD); + asan_clear_split_alloczone(new_free_elem); + split_elem(elem, new_free_elem); malloc_elem_free_list_insert(new_free_elem); @@ -458,6 +460,8 @@ malloc_elem_alloc(struct malloc_elem *elem, size_t size, unsigned align, elem->state = ELEM_BUSY; elem->pad = old_elem_size; + asan_clear_alloczone(elem); + /* put a dummy header in padding, to point to real element header */ if (elem->pad > 0) { /* pad will be at least 64-bytes, as everything * is cache-line aligned */ @@ -470,12 +474,18 @@ malloc_elem_alloc(struct malloc_elem *elem, size_t size, unsigned align, return new_elem; } + asan_clear_split_alloczone(new_elem); + /* we are going to split the element in two. The original element * remains free, and the new element is the one allocated. * Re-insert original element, in case its new size makes it * belong on a different list. */ + split_elem(elem, new_elem); + + asan_clear_alloczone(new_elem); + new_elem->state = ELEM_BUSY; malloc_elem_free_list_insert(elem); @@ -601,6 +611,8 @@ malloc_elem_hide_region(struct malloc_elem *elem, void *start, size_t len) if (next && next_elem_is_adjacent(elem)) { len_after = RTE_PTR_DIFF(next, hide_end); if (len_after >= MALLOC_ELEM_OVERHEAD + MIN_DATA_SIZE) { + asan_clear_split_alloczone(hide_end); + /* split after */ split_elem(elem, hide_end); @@ -615,6 +627,8 @@ malloc_elem_hide_region(struct malloc_elem *elem, void *start, size_t len) if (prev && prev_elem_is_adjacent(elem)) { len_before = RTE_PTR_DIFF(hide_start, elem); if (len_before >= MALLOC_ELEM_OVERHEAD + MIN_DATA_SIZE) { + asan_clear_split_alloczone(hide_start); + /* split before */ split_elem(elem, hide_start); @@ -628,6 +642,8 @@ malloc_elem_hide_region(struct malloc_elem *elem, void *start, size_t len) } } + asan_clear_alloczone(elem); + remove_elem(elem); } @@ -641,8 +657,10 @@ malloc_elem_resize(struct malloc_elem *elem, size_t size) const size_t new_size = size + elem->pad + MALLOC_ELEM_OVERHEAD; /* if we request a smaller size, then always return ok */ - if (elem->size >= new_size) + if (elem->size >= new_size) { + asan_clear_alloczone(elem); return 0; + } /* check if there is a next element, it's free and adjacent */ if (!elem->next || elem->next->state != ELEM_FREE || @@ -661,9 +679,15 @@ malloc_elem_resize(struct malloc_elem *elem, size_t size) /* now we have a big block together. Lets cut it down a bit, by splitting */ struct malloc_elem *split_pt = RTE_PTR_ADD(elem, new_size); split_pt = RTE_PTR_ALIGN_CEIL(split_pt, RTE_CACHE_LINE_SIZE); + + asan_clear_split_alloczone(split_pt); + split_elem(elem, split_pt); malloc_elem_free_list_insert(split_pt); } + + asan_clear_alloczone(elem); + return 0; } diff --git a/lib/eal/common/malloc_elem.h b/lib/eal/common/malloc_elem.h index a1e5f7f02c..a06c11ac8b 100644 --- a/lib/eal/common/malloc_elem.h +++ b/lib/eal/common/malloc_elem.h @@ -36,10 +36,20 @@ struct malloc_elem { uint64_t header_cookie; /* Cookie marking start of data */ /* trailer cookie at start + size */ #endif +#ifdef RTE_MALLOC_ASAN + size_t user_size; + uint64_t asan_cookie[2]; /* must be next to header_cookie */ +#endif } __rte_cache_aligned; +static const unsigned int MALLOC_ELEM_HEADER_LEN = sizeof(struct malloc_elem); + #ifndef RTE_MALLOC_DEBUG -static const unsigned MALLOC_ELEM_TRAILER_LEN = 0; +#ifdef RTE_MALLOC_ASAN +static const unsigned int MALLOC_ELEM_TRAILER_LEN = RTE_CACHE_LINE_SIZE; +#else +static const unsigned int MALLOC_ELEM_TRAILER_LEN; +#endif /* dummy function - just check if pointer is non-null */ static inline int @@ -55,7 +65,7 @@ set_trailer(struct malloc_elem *elem __rte_unused){ } #else -static const unsigned MALLOC_ELEM_TRAILER_LEN = RTE_CACHE_LINE_SIZE; +static const unsigned int MALLOC_ELEM_TRAILER_LEN = RTE_CACHE_LINE_SIZE; #define MALLOC_HEADER_COOKIE 0xbadbadbadadd2e55ULL /**< Header cookie. */ #define MALLOC_TRAILER_COOKIE 0xadd2e55badbadbadULL /**< Trailer cookie.*/ @@ -90,9 +100,187 @@ malloc_elem_cookies_ok(const struct malloc_elem *elem) #endif -static const unsigned MALLOC_ELEM_HEADER_LEN = sizeof(struct malloc_elem); #define MALLOC_ELEM_OVERHEAD (MALLOC_ELEM_HEADER_LEN + MALLOC_ELEM_TRAILER_LEN) +#ifdef RTE_MALLOC_ASAN + +#ifdef RTE_ARCH_X86_64 +#define ASAN_SHADOW_OFFSET 0x00007fff8000 +#endif + +#define ASAN_SHADOW_GRAIN_SIZE 8 +#define ASAN_MEM_FREE_FLAG 0xfd +#define ASAN_MEM_REDZONE_FLAG 0xfa +#define ASAN_SHADOW_SCALE 3 + +#define ASAN_MEM_SHIFT(mem) ((void *)((uintptr_t)(mem) >> ASAN_SHADOW_SCALE)) +#define ASAN_MEM_TO_SHADOW(mem) \ + RTE_PTR_ADD(ASAN_MEM_SHIFT(mem), ASAN_SHADOW_OFFSET) + +#if defined(__clang__) +__attribute__((no_sanitize("address", "hwaddress"))) +#else +__attribute__((no_sanitize_address)) +#endif +static inline void +asan_set_shadow(void *addr, char val) +{ + *(char *)addr = val; +} + +static inline void +asan_set_zone(void *ptr, size_t len, uint32_t val) +{ + size_t offset, i; + void *shadow; + size_t zone_len = len / ASAN_SHADOW_GRAIN_SIZE; + if (len % ASAN_SHADOW_GRAIN_SIZE != 0) + zone_len += 1; + + for (i = 0; i < zone_len; i++) { + offset = i * ASAN_SHADOW_GRAIN_SIZE; + shadow = ASAN_MEM_TO_SHADOW((uintptr_t)ptr + offset); + asan_set_shadow(shadow, val); + } +} + +/* + * When the memory is released, the release mark is + * set in the corresponding range of the shadow area. + */ +static inline void +asan_set_freezone(void *ptr, size_t size) +{ + asan_set_zone(ptr, size, ASAN_MEM_FREE_FLAG); +} + +/* + * When the memory is allocated, memory state must set as accessible. + */ +static inline void +asan_clear_alloczone(struct malloc_elem *elem) +{ + asan_set_zone((void *)elem, elem->size, 0x0); +} + +static inline void +asan_clear_split_alloczone(struct malloc_elem *elem) +{ + void *ptr = RTE_PTR_SUB(elem, MALLOC_ELEM_TRAILER_LEN); + asan_set_zone(ptr, MALLOC_ELEM_OVERHEAD, 0x0); +} + +/* + * When the memory is allocated, the memory boundary is + * marked in the corresponding range of the shadow area. + * Requirement: redzone >= 16, is a power of two. + */ +static inline void +asan_set_redzone(struct malloc_elem *elem, size_t user_size) +{ + uintptr_t head_redzone; + uintptr_t tail_redzone; + void *front_shadow; + void *tail_shadow; + uint32_t val; + + if (elem != NULL) { + if (elem->state != ELEM_PAD) + elem = RTE_PTR_ADD(elem, elem->pad); + + elem->user_size = user_size; + + /* Set mark before the start of the allocated memory */ + head_redzone = (uintptr_t)RTE_PTR_ADD(elem, + MALLOC_ELEM_HEADER_LEN - ASAN_SHADOW_GRAIN_SIZE); + front_shadow = ASAN_MEM_TO_SHADOW(head_redzone); + asan_set_shadow(front_shadow, ASAN_MEM_REDZONE_FLAG); + front_shadow = ASAN_MEM_TO_SHADOW(head_redzone + - ASAN_SHADOW_GRAIN_SIZE); + asan_set_shadow(front_shadow, ASAN_MEM_REDZONE_FLAG); + + /* Set mark after the end of the allocated memory */ + tail_redzone = (uintptr_t)RTE_PTR_ADD(elem, + MALLOC_ELEM_HEADER_LEN + + elem->user_size); + tail_shadow = ASAN_MEM_TO_SHADOW(tail_redzone); + val = (tail_redzone % ASAN_SHADOW_GRAIN_SIZE); + val = (val == 0) ? ASAN_MEM_REDZONE_FLAG : val; + asan_set_shadow(tail_shadow, val); + tail_shadow = ASAN_MEM_TO_SHADOW(tail_redzone + + ASAN_SHADOW_GRAIN_SIZE); + asan_set_shadow(tail_shadow, ASAN_MEM_REDZONE_FLAG); + } +} + +/* + * When the memory is released, the mark of the memory boundary + * in the corresponding range of the shadow area is cleared. + * Requirement: redzone >= 16, is a power of two. + */ +static inline void +asan_clear_redzone(struct malloc_elem *elem) +{ + uintptr_t head_redzone; + uintptr_t tail_redzone; + void *head_shadow; + void *tail_shadow; + + if (elem != NULL) { + elem = RTE_PTR_ADD(elem, elem->pad); + + /* Clear mark before the start of the allocated memory */ + head_redzone = (uintptr_t)RTE_PTR_ADD(elem, + MALLOC_ELEM_HEADER_LEN - ASAN_SHADOW_GRAIN_SIZE); + head_shadow = ASAN_MEM_TO_SHADOW(head_redzone); + asan_set_shadow(head_shadow, 0x00); + head_shadow = ASAN_MEM_TO_SHADOW(head_redzone + - ASAN_SHADOW_GRAIN_SIZE); + asan_set_shadow(head_shadow, 0x00); + + /* Clear mark after the end of the allocated memory */ + tail_redzone = (uintptr_t)RTE_PTR_ADD(elem, + MALLOC_ELEM_HEADER_LEN + elem->user_size); + tail_shadow = ASAN_MEM_TO_SHADOW(tail_redzone); + asan_set_shadow(tail_shadow, 0x00); + tail_shadow = ASAN_MEM_TO_SHADOW(tail_redzone + + ASAN_SHADOW_GRAIN_SIZE); + asan_set_shadow(tail_shadow, 0x00); + } +} + +static inline size_t +old_malloc_size(struct malloc_elem *elem) +{ + if (elem->state != ELEM_PAD) + elem = RTE_PTR_ADD(elem, elem->pad); + + return elem->user_size; +} +#else +static inline void +asan_set_freezone(void *ptr __rte_unused, size_t size __rte_unused) { } + +static inline void +asan_clear_alloczone(struct malloc_elem *elem __rte_unused) { } + +static inline void +asan_clear_split_alloczone(struct malloc_elem *elem __rte_unused) { } + +static inline void +asan_set_redzone(struct malloc_elem *elem __rte_unused, + size_t user_size __rte_unused) { } + +static inline void +asan_clear_redzone(struct malloc_elem *elem __rte_unused) { } + +static inline size_t +old_malloc_size(struct malloc_elem *elem) +{ + return elem->size - elem->pad - MALLOC_ELEM_OVERHEAD; +} +#endif + /* * Given a pointer to the start of a memory block returned by malloc, get * the actual malloc_elem header for that block. diff --git a/lib/eal/common/malloc_heap.c b/lib/eal/common/malloc_heap.c index ee400f38ec..775d6789df 100644 --- a/lib/eal/common/malloc_heap.c +++ b/lib/eal/common/malloc_heap.c @@ -237,6 +237,7 @@ heap_alloc(struct malloc_heap *heap, const char *type __rte_unused, size_t size, unsigned int flags, size_t align, size_t bound, bool contig) { struct malloc_elem *elem; + size_t user_size = size; size = RTE_CACHE_LINE_ROUNDUP(size); align = RTE_CACHE_LINE_ROUNDUP(align); @@ -250,6 +251,8 @@ heap_alloc(struct malloc_heap *heap, const char *type __rte_unused, size_t size, /* increase heap's count of allocated elements */ heap->alloc_count++; + + asan_set_redzone(elem, user_size); } return elem == NULL ? NULL : (void *)(&elem[1]); @@ -270,6 +273,8 @@ heap_alloc_biggest(struct malloc_heap *heap, const char *type __rte_unused, /* increase heap's count of allocated elements */ heap->alloc_count++; + + asan_set_redzone(elem, size); } return elem == NULL ? NULL : (void *)(&elem[1]); @@ -841,6 +846,8 @@ malloc_heap_free(struct malloc_elem *elem) if (!malloc_elem_cookies_ok(elem) || elem->state != ELEM_BUSY) return -1; + asan_clear_redzone(elem); + /* elem may be merged with previous element, so keep heap address */ heap = elem->heap; msl = elem->msl; @@ -848,6 +855,9 @@ malloc_heap_free(struct malloc_elem *elem) rte_spinlock_lock(&(heap->lock)); + void *asan_ptr = RTE_PTR_ADD(elem, MALLOC_ELEM_HEADER_LEN + elem->pad); + size_t asan_data_len = elem->size - MALLOC_ELEM_OVERHEAD - elem->pad; + /* mark element as free */ elem->state = ELEM_FREE; @@ -1001,6 +1011,8 @@ malloc_heap_free(struct malloc_elem *elem) rte_mcfg_mem_write_unlock(); free_unlock: + asan_set_freezone(asan_ptr, asan_data_len); + rte_spinlock_unlock(&(heap->lock)); return ret; } diff --git a/lib/eal/common/rte_malloc.c b/lib/eal/common/rte_malloc.c index 9d39e58c08..d0bec26920 100644 --- a/lib/eal/common/rte_malloc.c +++ b/lib/eal/common/rte_malloc.c @@ -162,6 +162,8 @@ rte_calloc(const char *type, size_t num, size_t size, unsigned align) void * rte_realloc_socket(void *ptr, size_t size, unsigned int align, int socket) { + size_t user_size; + if (ptr == NULL) return rte_malloc_socket(NULL, size, align, socket); @@ -171,6 +173,8 @@ rte_realloc_socket(void *ptr, size_t size, unsigned int align, int socket) return NULL; } + user_size = size; + size = RTE_CACHE_LINE_ROUNDUP(size), align = RTE_CACHE_LINE_ROUNDUP(align); /* check requested socket id and alignment matches first, and if ok, @@ -181,6 +185,9 @@ rte_realloc_socket(void *ptr, size_t size, unsigned int align, int socket) RTE_PTR_ALIGN(ptr, align) == ptr && malloc_heap_resize(elem, size) == 0) { rte_eal_trace_mem_realloc(size, align, socket, ptr); + + asan_set_redzone(elem, user_size); + return ptr; } @@ -192,7 +199,7 @@ rte_realloc_socket(void *ptr, size_t size, unsigned int align, int socket) if (new_ptr == NULL) return NULL; /* elem: |pad|data_elem|data|trailer| */ - const size_t old_size = elem->size - elem->pad - MALLOC_ELEM_OVERHEAD; + const size_t old_size = old_malloc_size(elem); rte_memcpy(new_ptr, ptr, old_size < size ? old_size : size); rte_free(ptr); -- 2.25.1 ^ permalink raw reply [flat|nested] 117+ messages in thread
* [dpdk-dev] [PATCH v11 3/4] pipeline: Fix compilation error with gcc ASan 2021-10-19 10:12 ` [dpdk-dev] [PATCH v11 1/4] Enable ASan for memory detector on DPDK zhihongx.peng 2021-10-19 10:12 ` [dpdk-dev] [PATCH v11 2/4] DPDK code adapts to ASan zhihongx.peng @ 2021-10-19 10:12 ` zhihongx.peng 2021-10-19 10:12 ` [dpdk-dev] [PATCH v11 4/4] performance-thread: Fix cross compilation failed zhihongx.peng 2021-10-19 10:24 ` [dpdk-dev] [PATCH v11 1/4] Enable ASan for memory detector on DPDK Peng, ZhihongX 3 siblings, 0 replies; 117+ messages in thread From: zhihongx.peng @ 2021-10-19 10:12 UTC (permalink / raw) To: david.marchand, anatoly.burakov, konstantin.ananyev, stephen, cristian.dumitrescu, john.mcnamara, bruce.richardson Cc: dev, xueqin.lin, Zhihong Peng, stable From: Zhihong Peng <zhihongx.peng@intel.com> The gcc will check code more stricter when ASan enabled. "Control reaches end of non-void function" error occurs here. Fixes: f38913b7fb8e ("pipeline: add meter array to SWX") Cc: stable@dpdk.org Signed-off-by: Xueqin Lin <xueqin.lin@intel.com> Signed-off-by: Zhihong Peng <zhihongx.peng@intel.com> --- v7: no change v8: no change v9: Modify the submit log v10:no change v11:no change --- lib/pipeline/rte_swx_pipeline.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/pipeline/rte_swx_pipeline.c b/lib/pipeline/rte_swx_pipeline.c index 1cd09a4b44..0acd6c6752 100644 --- a/lib/pipeline/rte_swx_pipeline.c +++ b/lib/pipeline/rte_swx_pipeline.c @@ -4642,7 +4642,7 @@ instr_meter_translate(struct rte_swx_pipeline *p, return 0; } - CHECK(0, EINVAL); + return -EINVAL; } static inline void @@ -5937,7 +5937,7 @@ instr_translate(struct rte_swx_pipeline *p, instr, data); - CHECK(0, EINVAL); + return -EINVAL; } static struct instruction_data * -- 2.25.1 ^ permalink raw reply [flat|nested] 117+ messages in thread
* [dpdk-dev] [PATCH v11 4/4] performance-thread: Fix cross compilation failed 2021-10-19 10:12 ` [dpdk-dev] [PATCH v11 1/4] Enable ASan for memory detector on DPDK zhihongx.peng 2021-10-19 10:12 ` [dpdk-dev] [PATCH v11 2/4] DPDK code adapts to ASan zhihongx.peng 2021-10-19 10:12 ` [dpdk-dev] [PATCH v11 3/4] pipeline: Fix compilation error with gcc ASan zhihongx.peng @ 2021-10-19 10:12 ` zhihongx.peng 2021-10-19 10:37 ` Bruce Richardson ` (3 more replies) 2021-10-19 10:24 ` [dpdk-dev] [PATCH v11 1/4] Enable ASan for memory detector on DPDK Peng, ZhihongX 3 siblings, 4 replies; 117+ messages in thread From: zhihongx.peng @ 2021-10-19 10:12 UTC (permalink / raw) To: david.marchand, anatoly.burakov, konstantin.ananyev, stephen, cristian.dumitrescu, john.mcnamara, bruce.richardson Cc: dev, xueqin.lin, Zhihong Peng, stable From: Zhihong Peng <zhihongx.peng@intel.com> The gcc(arm-linux-gcc) will check code more stricter when ASan enabled. "strncpy specified bound XX equals destination size" error occurs here. Fixes: 116819b9ed0d ("examples/performance-thread: add lthread subsystem") Cc: stable@dpdk.org Signed-off-by: Xueqin Lin <xueqin.lin@intel.com> Signed-off-by: Zhihong Peng <zhihongx.peng@intel.com> --- v11: Use rte_strlcpy to replace strncpy. --- examples/performance-thread/common/lthread.c | 4 ++-- examples/performance-thread/common/lthread_cond.c | 6 +++--- examples/performance-thread/common/lthread_mutex.c | 6 +++--- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/examples/performance-thread/common/lthread.c b/examples/performance-thread/common/lthread.c index 3f1f48db43..7949beb8e2 100644 --- a/examples/performance-thread/common/lthread.c +++ b/examples/performance-thread/common/lthread.c @@ -20,6 +20,7 @@ #include <sys/mman.h> #include <rte_log.h> +#include <rte_string_fns.h> #include <ctx.h> #include <stack.h> @@ -463,6 +464,5 @@ void lthread_set_funcname(const char *f) { struct lthread *lt = THIS_LTHREAD; - strncpy(lt->funcname, f, sizeof(lt->funcname)); - lt->funcname[sizeof(lt->funcname)-1] = 0; + rte_strlcpy(lt->funcname, f, sizeof(lt->funcname)); } diff --git a/examples/performance-thread/common/lthread_cond.c b/examples/performance-thread/common/lthread_cond.c index cdcc7a7b5a..398735c192 100644 --- a/examples/performance-thread/common/lthread_cond.c +++ b/examples/performance-thread/common/lthread_cond.c @@ -20,6 +20,7 @@ #include <rte_log.h> #include <rte_common.h> +#include <rte_string_fns.h> #include "lthread_api.h" #include "lthread_diag_api.h" @@ -57,10 +58,9 @@ lthread_cond_init(char *name, struct lthread_cond **cond, } if (name == NULL) - strncpy(c->name, "no name", sizeof(c->name)); + rte_strlcpy(c->name, "no name", sizeof(c->name)); else - strncpy(c->name, name, sizeof(c->name)); - c->name[sizeof(c->name)-1] = 0; + rte_strlcpy(c->name, name, sizeof(c->name)); c->root_sched = THIS_SCHED; diff --git a/examples/performance-thread/common/lthread_mutex.c b/examples/performance-thread/common/lthread_mutex.c index 01da6cad4f..1911e5ac67 100644 --- a/examples/performance-thread/common/lthread_mutex.c +++ b/examples/performance-thread/common/lthread_mutex.c @@ -19,6 +19,7 @@ #include <rte_log.h> #include <rte_spinlock.h> #include <rte_common.h> +#include <rte_string_fns.h> #include "lthread_api.h" #include "lthread_int.h" @@ -52,10 +53,9 @@ lthread_mutex_init(char *name, struct lthread_mutex **mutex, } if (name == NULL) - strncpy(m->name, "no name", sizeof(m->name)); + rte_strlcpy(m->name, "no name", sizeof(m->name)); else - strncpy(m->name, name, sizeof(m->name)); - m->name[sizeof(m->name)-1] = 0; + rte_strlcpy(m->name, name, sizeof(m->name)); m->root_sched = THIS_SCHED; m->owner = NULL; -- 2.25.1 ^ permalink raw reply [flat|nested] 117+ messages in thread
* Re: [dpdk-dev] [PATCH v11 4/4] performance-thread: Fix cross compilation failed 2021-10-19 10:12 ` [dpdk-dev] [PATCH v11 4/4] performance-thread: Fix cross compilation failed zhihongx.peng @ 2021-10-19 10:37 ` Bruce Richardson 2021-10-19 13:04 ` [dpdk-dev] [PATCH v12 1/4] Enable ASan for memory detector on DPDK zhihongx.peng ` (2 subsequent siblings) 3 siblings, 0 replies; 117+ messages in thread From: Bruce Richardson @ 2021-10-19 10:37 UTC (permalink / raw) To: zhihongx.peng Cc: david.marchand, anatoly.burakov, konstantin.ananyev, stephen, cristian.dumitrescu, john.mcnamara, dev, xueqin.lin, stable On Tue, Oct 19, 2021 at 06:12:07PM +0800, zhihongx.peng@intel.com wrote: > From: Zhihong Peng <zhihongx.peng@intel.com> > > The gcc(arm-linux-gcc) will check code more stricter when ASan enabled. > "strncpy specified bound XX equals destination size" error occurs here. > > Fixes: 116819b9ed0d ("examples/performance-thread: add lthread subsystem") > Cc: stable@dpdk.org > > Signed-off-by: Xueqin Lin <xueqin.lin@intel.com> > Signed-off-by: Zhihong Peng <zhihongx.peng@intel.com> > --- > v11: Use rte_strlcpy to replace strncpy. > --- +1 to using strlcpy, but it should be "strlcpy" rather than "rte_strlcpy" which should be used. Analysis tools are likely to be familiar with strlcpy but won't know the DPDK-specific version of it. With that change to remove the rte_ prefix Acked-by: Bruce Richardson <bruce.richardson@intel.com> /Bruce ^ permalink raw reply [flat|nested] 117+ messages in thread
* [dpdk-dev] [PATCH v12 1/4] Enable ASan for memory detector on DPDK 2021-10-19 10:12 ` [dpdk-dev] [PATCH v11 4/4] performance-thread: Fix cross compilation failed zhihongx.peng 2021-10-19 10:37 ` Bruce Richardson @ 2021-10-19 13:04 ` zhihongx.peng 2021-10-19 13:04 ` [dpdk-dev] [PATCH v12 2/4] DPDK code adapts to ASan zhihongx.peng ` (2 more replies) 2021-10-19 13:58 ` [dpdk-dev] [PATCH v12 1/4] Enable ASan Address Sanitization zhihongx.peng 2021-10-19 15:15 ` [dpdk-dev] [PATCH v12 1/4] Enable ASan AddressSanitizer zhihongx.peng 3 siblings, 3 replies; 117+ messages in thread From: zhihongx.peng @ 2021-10-19 13:04 UTC (permalink / raw) To: david.marchand, anatoly.burakov, konstantin.ananyev, stephen, cristian.dumitrescu, john.mcnamara, bruce.richardson Cc: dev, xueqin.lin, Zhihong Peng From: Zhihong Peng <zhihongx.peng@intel.com> `AddressSanitizer <https://github.com/google/sanitizers/wiki/AddressSanitizer>`_ (ASan) is a widely-used debugging tool to detect memory access errors. It helps detect issues like use-after-free, various kinds of buffer overruns in C/C++ programs, and other similar errors, as well as printing out detailed debug information whenever an error is detected. We can enable ASan by adding below compilation options: -Dbuildtype=debug -Db_lundef=false -Db_sanitize=address "-Dbuildtype=debug": This is a non-essential option. When this option is added, if a memory error occurs, ASan can clearly show where the code is wrong. "-Db_lundef=false": When use clang to compile DPDK, this option must be added. Signed-off-by: Xueqin Lin <xueqin.lin@intel.com> Signed-off-by: Zhihong Peng <zhihongx.peng@intel.com> --- v7: 1) Split doc and code into two. 2) Modify asan.rst doc v8: No change. v9: 1) Add the check of libasan library. 2) Add release notes. v10:1) Split doc and code into two. 2) Meson supports asan. v11:Modify the document. v12:No change. --- config/meson.build | 16 +++++++++++ devtools/words-case.txt | 1 + doc/guides/prog_guide/asan.rst | 40 ++++++++++++++++++++++++++ doc/guides/prog_guide/index.rst | 1 + doc/guides/rel_notes/release_21_11.rst | 9 ++++++ 5 files changed, 67 insertions(+) create mode 100644 doc/guides/prog_guide/asan.rst diff --git a/config/meson.build b/config/meson.build index 4cdf589e20..f02b0e9c6d 100644 --- a/config/meson.build +++ b/config/meson.build @@ -411,6 +411,22 @@ if get_option('b_lto') endif endif +if get_option('b_sanitize') == 'address' or get_option('b_sanitize') == 'address,undefined' + if is_windows + error('ASan is not supported on windows') + endif + + if cc.get_id() == 'gcc' + asan_dep = cc.find_library('asan', required: true) + if (not cc.links('int main(int argc, char *argv[]) { return 0; }', + dependencies: asan_dep)) + error('broken dependency, "libasan"') + endif + add_project_link_arguments('-lasan', language: 'c') + dpdk_extra_ldflags += '-lasan' + endif +endif + if get_option('default_library') == 'both' error( ''' Unsupported value "both" for "default_library" option. diff --git a/devtools/words-case.txt b/devtools/words-case.txt index 0bbad48626..ada6910fa0 100644 --- a/devtools/words-case.txt +++ b/devtools/words-case.txt @@ -5,6 +5,7 @@ API Arm armv7 armv8 +ASan BAR CRC DCB diff --git a/doc/guides/prog_guide/asan.rst b/doc/guides/prog_guide/asan.rst new file mode 100644 index 0000000000..969676ebe8 --- /dev/null +++ b/doc/guides/prog_guide/asan.rst @@ -0,0 +1,40 @@ +.. SPDX-License-Identifier: BSD-3-Clause + Copyright(c) 2021 Intel Corporation + +Running Address Sanitizer +========================= + +`AddressSanitizer +<https://github.com/google/sanitizers/wiki/AddressSanitizer>`_ (ASan) +is a widely-used debugging tool to detect memory access errors. +It helps detect issues like use-after-free, various kinds of buffer +overruns in C/C++ programs, and other similar errors, as well as +printing out detailed debug information whenever an error is detected. + +AddressSanitizer is a part of LLVM (3.1+) and GCC (4.8+). + +Usage +----- + +meson build +^^^^^^^^^^^ + +To enable ASan in meson build system, use following meson build command: + +Example usage:: + +* gcc:: + + meson build -Dbuildtype=debug -Db_sanitize=address + ninja -C build + +* clang:: + + meson build -Dbuildtype=debug -Db_lundef=false -Db_sanitize=address + ninja -C build + +.. Note:: + + a) To compile with gcc in centos, libasan needs to be installed separately. + b) If the program being tested uses cmdline you will need to execute the + "stty echo" command when a error occurs. diff --git a/doc/guides/prog_guide/index.rst b/doc/guides/prog_guide/index.rst index 89af28dacb..561d787fcf 100644 --- a/doc/guides/prog_guide/index.rst +++ b/doc/guides/prog_guide/index.rst @@ -72,3 +72,4 @@ Programmer's Guide lto profile_app glossary + asan diff --git a/doc/guides/rel_notes/release_21_11.rst b/doc/guides/rel_notes/release_21_11.rst index d5435a64aa..1fa8eb3368 100644 --- a/doc/guides/rel_notes/release_21_11.rst +++ b/doc/guides/rel_notes/release_21_11.rst @@ -167,6 +167,15 @@ New Features * Added tests to verify tunnel header verification in IPsec inbound. * Added tests to verify inner checksum. +* **Enable ASan for memory detector on DPDK.** + + `AddressSanitizer + <https://github.com/google/sanitizers/wiki/AddressSanitizer>` (ASan) + is a widely-used debugging tool to detect memory access errors. + It helps detect issues like use-after-free, various kinds of buffer + overruns in C/C++ programs, and other similar errors, as well as + printing out detailed debug information whenever an error is detected. + Removed Items ------------- -- 2.25.1 ^ permalink raw reply [flat|nested] 117+ messages in thread
* [dpdk-dev] [PATCH v12 2/4] DPDK code adapts to ASan 2021-10-19 13:04 ` [dpdk-dev] [PATCH v12 1/4] Enable ASan for memory detector on DPDK zhihongx.peng @ 2021-10-19 13:04 ` zhihongx.peng 2021-10-19 13:04 ` [dpdk-dev] [PATCH v12 3/4] pipeline: Fix compilation error with gcc ASan zhihongx.peng 2021-10-19 13:04 ` [dpdk-dev] [PATCH v12 4/4] performance-thread: Fix cross compilation failed zhihongx.peng 2 siblings, 0 replies; 117+ messages in thread From: zhihongx.peng @ 2021-10-19 13:04 UTC (permalink / raw) To: david.marchand, anatoly.burakov, konstantin.ananyev, stephen, cristian.dumitrescu, john.mcnamara, bruce.richardson Cc: dev, xueqin.lin, Zhihong Peng From: Zhihong Peng <zhihongx.peng@intel.com> DPDK ASan functionality is currently only supported Linux x86_64. Support other platforms, need to define ASAN_SHADOW_OFFSET value according to google ASan document, and configure meson (config/meson.build). Here is an example of heap-buffer-overflow bug: ...... char *p = rte_zmalloc(NULL, 7, 0); p[7] = 'a'; ...... Here is an example of use-after-free bug: ...... char *p = rte_zmalloc(NULL, 7, 0); rte_free(p); *p = 'a'; ...... Signed-off-by: Xueqin Lin <xueqin.lin@intel.com> Signed-off-by: Zhihong Peng <zhihongx.peng@intel.com> --- v7: Split doc and code into two. v8: No change. v9: Modify the definition of RTE_MALLOC_ASAN. v10:Modify the definition of RTE_MALLOC_ASAN. v11:No change. v12:No change. --- config/meson.build | 4 + doc/guides/prog_guide/asan.rst | 68 +++++++++++- lib/eal/common/malloc_elem.c | 26 ++++- lib/eal/common/malloc_elem.h | 194 ++++++++++++++++++++++++++++++++- lib/eal/common/malloc_heap.c | 12 ++ lib/eal/common/rte_malloc.c | 9 +- 6 files changed, 306 insertions(+), 7 deletions(-) diff --git a/config/meson.build b/config/meson.build index f02b0e9c6d..bf751583bd 100644 --- a/config/meson.build +++ b/config/meson.build @@ -425,6 +425,10 @@ if get_option('b_sanitize') == 'address' or get_option('b_sanitize') == 'address add_project_link_arguments('-lasan', language: 'c') dpdk_extra_ldflags += '-lasan' endif + + if is_linux and arch_subdir == 'x86' + dpdk_conf.set10('RTE_MALLOC_ASAN', true) + endif endif if get_option('default_library') == 'both' diff --git a/doc/guides/prog_guide/asan.rst b/doc/guides/prog_guide/asan.rst index 969676ebe8..0cb2ca8751 100644 --- a/doc/guides/prog_guide/asan.rst +++ b/doc/guides/prog_guide/asan.rst @@ -13,6 +13,68 @@ printing out detailed debug information whenever an error is detected. AddressSanitizer is a part of LLVM (3.1+) and GCC (4.8+). +DPDK ASan functionality is currently only supported Linux x86_64. +Support other platforms, need to define ASAN_SHADOW_OFFSET value +according to google ASan document, and configure meson +(config/meson.build). + +Example heap-buffer-overflow error +---------------------------------- + +Following error was reported when ASan was enabled:: + + Applied 9 bytes of memory, but accessed the 10th byte of memory, + so heap-buffer-overflow appeared. + +Below code results in this error:: + + Add code to helloworld: + char *p = rte_zmalloc(NULL, 9, 0); + if (!p) { + printf("rte_zmalloc error."); + return -1; + } + p[9] = 'a'; + +The error log:: + + ==369953==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x7fb17f465809 at pc 0x5652e6707b84 bp 0x7ffea70eea20 sp 0x7ffea70eea10 WRITE of size 1 at 0x7fb17f465809 thread T0 + #0 0x5652e6707b83 in main ../examples/helloworld/main.c:47 + #1 0x7fb94953c0b2 in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x270b2) + #2 0x5652e67079bd in _start (/home/pzh/asan_test/x86_64-native-linuxapp-gcc/examples/dpdk-helloworld+0x8329bd) + + Address 0x7fb17f465809 is a wild pointer. + SUMMARY: AddressSanitizer: heap-buffer-overflow ../examples/helloworld/main.c:47 in main + +Example use-after-free error +---------------------------- + +Following error was reported when ASan was enabled:: + + Applied for 9 bytes of memory, and accessed the first byte after + released, so heap-use-after-free appeared. + +Below code results in this error:: + + Add code to helloworld: + char *p = rte_zmalloc(NULL, 9, 0); + if (!p) { + printf("rte_zmalloc error."); + return -1; + } + rte_free(p); + *p = 'a'; + +The error log:: + + ==417048==ERROR: AddressSanitizer: heap-use-after-free on address 0x7fc83f465800 at pc 0x564308a39b89 bp 0x7ffc8c85bf50 sp 0x7ffc8c85bf40 WRITE of size 1 at 0x7fc83f465800 thread T0 + #0 0x564308a39b88 in main ../examples/helloworld/main.c:48 + #1 0x7fd0079c60b2 in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x270b2) + #2 0x564308a399bd in _start (/home/pzh/asan_test/x86_64-native-linuxapp-gcc/examples/dpdk-helloworld+0x8329bd) + + Address 0x7fc83f465800 is a wild pointer. + SUMMARY: AddressSanitizer: heap-use-after-free ../examples/helloworld/main.c:48 in main + Usage ----- @@ -35,6 +97,8 @@ Example usage:: .. Note:: - a) To compile with gcc in centos, libasan needs to be installed separately. - b) If the program being tested uses cmdline you will need to execute the + a) Some of the features of ASan (for example, 'Display memory application location, currently + displayed as a wild pointer') are not currently supported by DPDK's implementation. + b) To compile with gcc in centos, libasan needs to be installed separately. + c) If the program being tested uses cmdline you will need to execute the "stty echo" command when a error occurs. diff --git a/lib/eal/common/malloc_elem.c b/lib/eal/common/malloc_elem.c index c2c9461f1d..bdd20a162e 100644 --- a/lib/eal/common/malloc_elem.c +++ b/lib/eal/common/malloc_elem.c @@ -446,6 +446,8 @@ malloc_elem_alloc(struct malloc_elem *elem, size_t size, unsigned align, struct malloc_elem *new_free_elem = RTE_PTR_ADD(new_elem, size + MALLOC_ELEM_OVERHEAD); + asan_clear_split_alloczone(new_free_elem); + split_elem(elem, new_free_elem); malloc_elem_free_list_insert(new_free_elem); @@ -458,6 +460,8 @@ malloc_elem_alloc(struct malloc_elem *elem, size_t size, unsigned align, elem->state = ELEM_BUSY; elem->pad = old_elem_size; + asan_clear_alloczone(elem); + /* put a dummy header in padding, to point to real element header */ if (elem->pad > 0) { /* pad will be at least 64-bytes, as everything * is cache-line aligned */ @@ -470,12 +474,18 @@ malloc_elem_alloc(struct malloc_elem *elem, size_t size, unsigned align, return new_elem; } + asan_clear_split_alloczone(new_elem); + /* we are going to split the element in two. The original element * remains free, and the new element is the one allocated. * Re-insert original element, in case its new size makes it * belong on a different list. */ + split_elem(elem, new_elem); + + asan_clear_alloczone(new_elem); + new_elem->state = ELEM_BUSY; malloc_elem_free_list_insert(elem); @@ -601,6 +611,8 @@ malloc_elem_hide_region(struct malloc_elem *elem, void *start, size_t len) if (next && next_elem_is_adjacent(elem)) { len_after = RTE_PTR_DIFF(next, hide_end); if (len_after >= MALLOC_ELEM_OVERHEAD + MIN_DATA_SIZE) { + asan_clear_split_alloczone(hide_end); + /* split after */ split_elem(elem, hide_end); @@ -615,6 +627,8 @@ malloc_elem_hide_region(struct malloc_elem *elem, void *start, size_t len) if (prev && prev_elem_is_adjacent(elem)) { len_before = RTE_PTR_DIFF(hide_start, elem); if (len_before >= MALLOC_ELEM_OVERHEAD + MIN_DATA_SIZE) { + asan_clear_split_alloczone(hide_start); + /* split before */ split_elem(elem, hide_start); @@ -628,6 +642,8 @@ malloc_elem_hide_region(struct malloc_elem *elem, void *start, size_t len) } } + asan_clear_alloczone(elem); + remove_elem(elem); } @@ -641,8 +657,10 @@ malloc_elem_resize(struct malloc_elem *elem, size_t size) const size_t new_size = size + elem->pad + MALLOC_ELEM_OVERHEAD; /* if we request a smaller size, then always return ok */ - if (elem->size >= new_size) + if (elem->size >= new_size) { + asan_clear_alloczone(elem); return 0; + } /* check if there is a next element, it's free and adjacent */ if (!elem->next || elem->next->state != ELEM_FREE || @@ -661,9 +679,15 @@ malloc_elem_resize(struct malloc_elem *elem, size_t size) /* now we have a big block together. Lets cut it down a bit, by splitting */ struct malloc_elem *split_pt = RTE_PTR_ADD(elem, new_size); split_pt = RTE_PTR_ALIGN_CEIL(split_pt, RTE_CACHE_LINE_SIZE); + + asan_clear_split_alloczone(split_pt); + split_elem(elem, split_pt); malloc_elem_free_list_insert(split_pt); } + + asan_clear_alloczone(elem); + return 0; } diff --git a/lib/eal/common/malloc_elem.h b/lib/eal/common/malloc_elem.h index a1e5f7f02c..a06c11ac8b 100644 --- a/lib/eal/common/malloc_elem.h +++ b/lib/eal/common/malloc_elem.h @@ -36,10 +36,20 @@ struct malloc_elem { uint64_t header_cookie; /* Cookie marking start of data */ /* trailer cookie at start + size */ #endif +#ifdef RTE_MALLOC_ASAN + size_t user_size; + uint64_t asan_cookie[2]; /* must be next to header_cookie */ +#endif } __rte_cache_aligned; +static const unsigned int MALLOC_ELEM_HEADER_LEN = sizeof(struct malloc_elem); + #ifndef RTE_MALLOC_DEBUG -static const unsigned MALLOC_ELEM_TRAILER_LEN = 0; +#ifdef RTE_MALLOC_ASAN +static const unsigned int MALLOC_ELEM_TRAILER_LEN = RTE_CACHE_LINE_SIZE; +#else +static const unsigned int MALLOC_ELEM_TRAILER_LEN; +#endif /* dummy function - just check if pointer is non-null */ static inline int @@ -55,7 +65,7 @@ set_trailer(struct malloc_elem *elem __rte_unused){ } #else -static const unsigned MALLOC_ELEM_TRAILER_LEN = RTE_CACHE_LINE_SIZE; +static const unsigned int MALLOC_ELEM_TRAILER_LEN = RTE_CACHE_LINE_SIZE; #define MALLOC_HEADER_COOKIE 0xbadbadbadadd2e55ULL /**< Header cookie. */ #define MALLOC_TRAILER_COOKIE 0xadd2e55badbadbadULL /**< Trailer cookie.*/ @@ -90,9 +100,187 @@ malloc_elem_cookies_ok(const struct malloc_elem *elem) #endif -static const unsigned MALLOC_ELEM_HEADER_LEN = sizeof(struct malloc_elem); #define MALLOC_ELEM_OVERHEAD (MALLOC_ELEM_HEADER_LEN + MALLOC_ELEM_TRAILER_LEN) +#ifdef RTE_MALLOC_ASAN + +#ifdef RTE_ARCH_X86_64 +#define ASAN_SHADOW_OFFSET 0x00007fff8000 +#endif + +#define ASAN_SHADOW_GRAIN_SIZE 8 +#define ASAN_MEM_FREE_FLAG 0xfd +#define ASAN_MEM_REDZONE_FLAG 0xfa +#define ASAN_SHADOW_SCALE 3 + +#define ASAN_MEM_SHIFT(mem) ((void *)((uintptr_t)(mem) >> ASAN_SHADOW_SCALE)) +#define ASAN_MEM_TO_SHADOW(mem) \ + RTE_PTR_ADD(ASAN_MEM_SHIFT(mem), ASAN_SHADOW_OFFSET) + +#if defined(__clang__) +__attribute__((no_sanitize("address", "hwaddress"))) +#else +__attribute__((no_sanitize_address)) +#endif +static inline void +asan_set_shadow(void *addr, char val) +{ + *(char *)addr = val; +} + +static inline void +asan_set_zone(void *ptr, size_t len, uint32_t val) +{ + size_t offset, i; + void *shadow; + size_t zone_len = len / ASAN_SHADOW_GRAIN_SIZE; + if (len % ASAN_SHADOW_GRAIN_SIZE != 0) + zone_len += 1; + + for (i = 0; i < zone_len; i++) { + offset = i * ASAN_SHADOW_GRAIN_SIZE; + shadow = ASAN_MEM_TO_SHADOW((uintptr_t)ptr + offset); + asan_set_shadow(shadow, val); + } +} + +/* + * When the memory is released, the release mark is + * set in the corresponding range of the shadow area. + */ +static inline void +asan_set_freezone(void *ptr, size_t size) +{ + asan_set_zone(ptr, size, ASAN_MEM_FREE_FLAG); +} + +/* + * When the memory is allocated, memory state must set as accessible. + */ +static inline void +asan_clear_alloczone(struct malloc_elem *elem) +{ + asan_set_zone((void *)elem, elem->size, 0x0); +} + +static inline void +asan_clear_split_alloczone(struct malloc_elem *elem) +{ + void *ptr = RTE_PTR_SUB(elem, MALLOC_ELEM_TRAILER_LEN); + asan_set_zone(ptr, MALLOC_ELEM_OVERHEAD, 0x0); +} + +/* + * When the memory is allocated, the memory boundary is + * marked in the corresponding range of the shadow area. + * Requirement: redzone >= 16, is a power of two. + */ +static inline void +asan_set_redzone(struct malloc_elem *elem, size_t user_size) +{ + uintptr_t head_redzone; + uintptr_t tail_redzone; + void *front_shadow; + void *tail_shadow; + uint32_t val; + + if (elem != NULL) { + if (elem->state != ELEM_PAD) + elem = RTE_PTR_ADD(elem, elem->pad); + + elem->user_size = user_size; + + /* Set mark before the start of the allocated memory */ + head_redzone = (uintptr_t)RTE_PTR_ADD(elem, + MALLOC_ELEM_HEADER_LEN - ASAN_SHADOW_GRAIN_SIZE); + front_shadow = ASAN_MEM_TO_SHADOW(head_redzone); + asan_set_shadow(front_shadow, ASAN_MEM_REDZONE_FLAG); + front_shadow = ASAN_MEM_TO_SHADOW(head_redzone + - ASAN_SHADOW_GRAIN_SIZE); + asan_set_shadow(front_shadow, ASAN_MEM_REDZONE_FLAG); + + /* Set mark after the end of the allocated memory */ + tail_redzone = (uintptr_t)RTE_PTR_ADD(elem, + MALLOC_ELEM_HEADER_LEN + + elem->user_size); + tail_shadow = ASAN_MEM_TO_SHADOW(tail_redzone); + val = (tail_redzone % ASAN_SHADOW_GRAIN_SIZE); + val = (val == 0) ? ASAN_MEM_REDZONE_FLAG : val; + asan_set_shadow(tail_shadow, val); + tail_shadow = ASAN_MEM_TO_SHADOW(tail_redzone + + ASAN_SHADOW_GRAIN_SIZE); + asan_set_shadow(tail_shadow, ASAN_MEM_REDZONE_FLAG); + } +} + +/* + * When the memory is released, the mark of the memory boundary + * in the corresponding range of the shadow area is cleared. + * Requirement: redzone >= 16, is a power of two. + */ +static inline void +asan_clear_redzone(struct malloc_elem *elem) +{ + uintptr_t head_redzone; + uintptr_t tail_redzone; + void *head_shadow; + void *tail_shadow; + + if (elem != NULL) { + elem = RTE_PTR_ADD(elem, elem->pad); + + /* Clear mark before the start of the allocated memory */ + head_redzone = (uintptr_t)RTE_PTR_ADD(elem, + MALLOC_ELEM_HEADER_LEN - ASAN_SHADOW_GRAIN_SIZE); + head_shadow = ASAN_MEM_TO_SHADOW(head_redzone); + asan_set_shadow(head_shadow, 0x00); + head_shadow = ASAN_MEM_TO_SHADOW(head_redzone + - ASAN_SHADOW_GRAIN_SIZE); + asan_set_shadow(head_shadow, 0x00); + + /* Clear mark after the end of the allocated memory */ + tail_redzone = (uintptr_t)RTE_PTR_ADD(elem, + MALLOC_ELEM_HEADER_LEN + elem->user_size); + tail_shadow = ASAN_MEM_TO_SHADOW(tail_redzone); + asan_set_shadow(tail_shadow, 0x00); + tail_shadow = ASAN_MEM_TO_SHADOW(tail_redzone + + ASAN_SHADOW_GRAIN_SIZE); + asan_set_shadow(tail_shadow, 0x00); + } +} + +static inline size_t +old_malloc_size(struct malloc_elem *elem) +{ + if (elem->state != ELEM_PAD) + elem = RTE_PTR_ADD(elem, elem->pad); + + return elem->user_size; +} +#else +static inline void +asan_set_freezone(void *ptr __rte_unused, size_t size __rte_unused) { } + +static inline void +asan_clear_alloczone(struct malloc_elem *elem __rte_unused) { } + +static inline void +asan_clear_split_alloczone(struct malloc_elem *elem __rte_unused) { } + +static inline void +asan_set_redzone(struct malloc_elem *elem __rte_unused, + size_t user_size __rte_unused) { } + +static inline void +asan_clear_redzone(struct malloc_elem *elem __rte_unused) { } + +static inline size_t +old_malloc_size(struct malloc_elem *elem) +{ + return elem->size - elem->pad - MALLOC_ELEM_OVERHEAD; +} +#endif + /* * Given a pointer to the start of a memory block returned by malloc, get * the actual malloc_elem header for that block. diff --git a/lib/eal/common/malloc_heap.c b/lib/eal/common/malloc_heap.c index ee400f38ec..775d6789df 100644 --- a/lib/eal/common/malloc_heap.c +++ b/lib/eal/common/malloc_heap.c @@ -237,6 +237,7 @@ heap_alloc(struct malloc_heap *heap, const char *type __rte_unused, size_t size, unsigned int flags, size_t align, size_t bound, bool contig) { struct malloc_elem *elem; + size_t user_size = size; size = RTE_CACHE_LINE_ROUNDUP(size); align = RTE_CACHE_LINE_ROUNDUP(align); @@ -250,6 +251,8 @@ heap_alloc(struct malloc_heap *heap, const char *type __rte_unused, size_t size, /* increase heap's count of allocated elements */ heap->alloc_count++; + + asan_set_redzone(elem, user_size); } return elem == NULL ? NULL : (void *)(&elem[1]); @@ -270,6 +273,8 @@ heap_alloc_biggest(struct malloc_heap *heap, const char *type __rte_unused, /* increase heap's count of allocated elements */ heap->alloc_count++; + + asan_set_redzone(elem, size); } return elem == NULL ? NULL : (void *)(&elem[1]); @@ -841,6 +846,8 @@ malloc_heap_free(struct malloc_elem *elem) if (!malloc_elem_cookies_ok(elem) || elem->state != ELEM_BUSY) return -1; + asan_clear_redzone(elem); + /* elem may be merged with previous element, so keep heap address */ heap = elem->heap; msl = elem->msl; @@ -848,6 +855,9 @@ malloc_heap_free(struct malloc_elem *elem) rte_spinlock_lock(&(heap->lock)); + void *asan_ptr = RTE_PTR_ADD(elem, MALLOC_ELEM_HEADER_LEN + elem->pad); + size_t asan_data_len = elem->size - MALLOC_ELEM_OVERHEAD - elem->pad; + /* mark element as free */ elem->state = ELEM_FREE; @@ -1001,6 +1011,8 @@ malloc_heap_free(struct malloc_elem *elem) rte_mcfg_mem_write_unlock(); free_unlock: + asan_set_freezone(asan_ptr, asan_data_len); + rte_spinlock_unlock(&(heap->lock)); return ret; } diff --git a/lib/eal/common/rte_malloc.c b/lib/eal/common/rte_malloc.c index 9d39e58c08..d0bec26920 100644 --- a/lib/eal/common/rte_malloc.c +++ b/lib/eal/common/rte_malloc.c @@ -162,6 +162,8 @@ rte_calloc(const char *type, size_t num, size_t size, unsigned align) void * rte_realloc_socket(void *ptr, size_t size, unsigned int align, int socket) { + size_t user_size; + if (ptr == NULL) return rte_malloc_socket(NULL, size, align, socket); @@ -171,6 +173,8 @@ rte_realloc_socket(void *ptr, size_t size, unsigned int align, int socket) return NULL; } + user_size = size; + size = RTE_CACHE_LINE_ROUNDUP(size), align = RTE_CACHE_LINE_ROUNDUP(align); /* check requested socket id and alignment matches first, and if ok, @@ -181,6 +185,9 @@ rte_realloc_socket(void *ptr, size_t size, unsigned int align, int socket) RTE_PTR_ALIGN(ptr, align) == ptr && malloc_heap_resize(elem, size) == 0) { rte_eal_trace_mem_realloc(size, align, socket, ptr); + + asan_set_redzone(elem, user_size); + return ptr; } @@ -192,7 +199,7 @@ rte_realloc_socket(void *ptr, size_t size, unsigned int align, int socket) if (new_ptr == NULL) return NULL; /* elem: |pad|data_elem|data|trailer| */ - const size_t old_size = elem->size - elem->pad - MALLOC_ELEM_OVERHEAD; + const size_t old_size = old_malloc_size(elem); rte_memcpy(new_ptr, ptr, old_size < size ? old_size : size); rte_free(ptr); -- 2.25.1 ^ permalink raw reply [flat|nested] 117+ messages in thread
* [dpdk-dev] [PATCH v12 3/4] pipeline: Fix compilation error with gcc ASan 2021-10-19 13:04 ` [dpdk-dev] [PATCH v12 1/4] Enable ASan for memory detector on DPDK zhihongx.peng 2021-10-19 13:04 ` [dpdk-dev] [PATCH v12 2/4] DPDK code adapts to ASan zhihongx.peng @ 2021-10-19 13:04 ` zhihongx.peng 2021-10-19 13:04 ` [dpdk-dev] [PATCH v12 4/4] performance-thread: Fix cross compilation failed zhihongx.peng 2 siblings, 0 replies; 117+ messages in thread From: zhihongx.peng @ 2021-10-19 13:04 UTC (permalink / raw) To: david.marchand, anatoly.burakov, konstantin.ananyev, stephen, cristian.dumitrescu, john.mcnamara, bruce.richardson Cc: dev, xueqin.lin, Zhihong Peng From: Zhihong Peng <zhihongx.peng@intel.com> The gcc will check code more stricter when ASan enabled. "Control reaches end of non-void function" error occurs here. Fixes: f38913b7fb8e ("pipeline: add meter array to SWX") Cc: cristian.dumitrescu@intel.com Signed-off-by: Xueqin Lin <xueqin.lin@intel.com> Signed-off-by: Zhihong Peng <zhihongx.peng@intel.com> --- v7: no change. v8: no change. v9: Modify the submit log. v10:no change. v11:no change. v12:Modify the commit log. --- lib/pipeline/rte_swx_pipeline.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/pipeline/rte_swx_pipeline.c b/lib/pipeline/rte_swx_pipeline.c index 1cd09a4b44..0acd6c6752 100644 --- a/lib/pipeline/rte_swx_pipeline.c +++ b/lib/pipeline/rte_swx_pipeline.c @@ -4642,7 +4642,7 @@ instr_meter_translate(struct rte_swx_pipeline *p, return 0; } - CHECK(0, EINVAL); + return -EINVAL; } static inline void @@ -5937,7 +5937,7 @@ instr_translate(struct rte_swx_pipeline *p, instr, data); - CHECK(0, EINVAL); + return -EINVAL; } static struct instruction_data * -- 2.25.1 ^ permalink raw reply [flat|nested] 117+ messages in thread
* [dpdk-dev] [PATCH v12 4/4] performance-thread: Fix cross compilation failed 2021-10-19 13:04 ` [dpdk-dev] [PATCH v12 1/4] Enable ASan for memory detector on DPDK zhihongx.peng 2021-10-19 13:04 ` [dpdk-dev] [PATCH v12 2/4] DPDK code adapts to ASan zhihongx.peng 2021-10-19 13:04 ` [dpdk-dev] [PATCH v12 3/4] pipeline: Fix compilation error with gcc ASan zhihongx.peng @ 2021-10-19 13:04 ` zhihongx.peng 2 siblings, 0 replies; 117+ messages in thread From: zhihongx.peng @ 2021-10-19 13:04 UTC (permalink / raw) To: david.marchand, anatoly.burakov, konstantin.ananyev, stephen, cristian.dumitrescu, john.mcnamara, bruce.richardson Cc: dev, xueqin.lin, Zhihong Peng, stable From: Zhihong Peng <zhihongx.peng@intel.com> The gcc(arm-linux-gcc) will check code more stricter when ASan enabled. "strncpy specified bound XX equals destination size" error occurs here. Fixes: 116819b9ed0d ("examples/performance-thread: add lthread subsystem") Cc: stable@dpdk.org Signed-off-by: Xueqin Lin <xueqin.lin@intel.com> Signed-off-by: Zhihong Peng <zhihongx.peng@intel.com> --- v11: Use rte_strlcpy to replace strncpy. v12: Delete rte_strlcpy's rte_. --- examples/performance-thread/common/lthread.c | 4 ++-- examples/performance-thread/common/lthread_cond.c | 6 +++--- examples/performance-thread/common/lthread_mutex.c | 6 +++--- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/examples/performance-thread/common/lthread.c b/examples/performance-thread/common/lthread.c index 3f1f48db43..190e5874b1 100644 --- a/examples/performance-thread/common/lthread.c +++ b/examples/performance-thread/common/lthread.c @@ -20,6 +20,7 @@ #include <sys/mman.h> #include <rte_log.h> +#include <rte_string_fns.h> #include <ctx.h> #include <stack.h> @@ -463,6 +464,5 @@ void lthread_set_funcname(const char *f) { struct lthread *lt = THIS_LTHREAD; - strncpy(lt->funcname, f, sizeof(lt->funcname)); - lt->funcname[sizeof(lt->funcname)-1] = 0; + strlcpy(lt->funcname, f, sizeof(lt->funcname)); } diff --git a/examples/performance-thread/common/lthread_cond.c b/examples/performance-thread/common/lthread_cond.c index cdcc7a7b5a..e7be17089a 100644 --- a/examples/performance-thread/common/lthread_cond.c +++ b/examples/performance-thread/common/lthread_cond.c @@ -20,6 +20,7 @@ #include <rte_log.h> #include <rte_common.h> +#include <rte_string_fns.h> #include "lthread_api.h" #include "lthread_diag_api.h" @@ -57,10 +58,9 @@ lthread_cond_init(char *name, struct lthread_cond **cond, } if (name == NULL) - strncpy(c->name, "no name", sizeof(c->name)); + strlcpy(c->name, "no name", sizeof(c->name)); else - strncpy(c->name, name, sizeof(c->name)); - c->name[sizeof(c->name)-1] = 0; + strlcpy(c->name, name, sizeof(c->name)); c->root_sched = THIS_SCHED; diff --git a/examples/performance-thread/common/lthread_mutex.c b/examples/performance-thread/common/lthread_mutex.c index 01da6cad4f..709ab9f553 100644 --- a/examples/performance-thread/common/lthread_mutex.c +++ b/examples/performance-thread/common/lthread_mutex.c @@ -19,6 +19,7 @@ #include <rte_log.h> #include <rte_spinlock.h> #include <rte_common.h> +#include <rte_string_fns.h> #include "lthread_api.h" #include "lthread_int.h" @@ -52,10 +53,9 @@ lthread_mutex_init(char *name, struct lthread_mutex **mutex, } if (name == NULL) - strncpy(m->name, "no name", sizeof(m->name)); + strlcpy(m->name, "no name", sizeof(m->name)); else - strncpy(m->name, name, sizeof(m->name)); - m->name[sizeof(m->name)-1] = 0; + strlcpy(m->name, name, sizeof(m->name)); m->root_sched = THIS_SCHED; m->owner = NULL; -- 2.25.1 ^ permalink raw reply [flat|nested] 117+ messages in thread
* [dpdk-dev] [PATCH v12 1/4] Enable ASan Address Sanitization 2021-10-19 10:12 ` [dpdk-dev] [PATCH v11 4/4] performance-thread: Fix cross compilation failed zhihongx.peng 2021-10-19 10:37 ` Bruce Richardson 2021-10-19 13:04 ` [dpdk-dev] [PATCH v12 1/4] Enable ASan for memory detector on DPDK zhihongx.peng @ 2021-10-19 13:58 ` zhihongx.peng 2021-10-19 13:58 ` [dpdk-dev] [PATCH v12 2/4] DPDK code adapts to ASan zhihongx.peng ` (3 more replies) 2021-10-19 15:15 ` [dpdk-dev] [PATCH v12 1/4] Enable ASan AddressSanitizer zhihongx.peng 3 siblings, 4 replies; 117+ messages in thread From: zhihongx.peng @ 2021-10-19 13:58 UTC (permalink / raw) To: david.marchand, anatoly.burakov, konstantin.ananyev, stephen, cristian.dumitrescu, john.mcnamara, bruce.richardson Cc: dev, xueqin.lin, Zhihong Peng From: Zhihong Peng <zhihongx.peng@intel.com> `AddressSanitizer <https://github.com/google/sanitizers/wiki/AddressSanitizer>`_ (ASan) is a widely-used debugging tool to detect memory access errors. It helps detect issues like use-after-free, various kinds of buffer overruns in C/C++ programs, and other similar errors, as well as printing out detailed debug information whenever an error is detected. We can enable ASan by adding below compilation options: -Dbuildtype=debug -Db_lundef=false -Db_sanitize=address "-Dbuildtype=debug": This is a non-essential option. When this option is added, if a memory error occurs, ASan can clearly show where the code is wrong. "-Db_lundef=false": When use clang to compile DPDK, this option must be added. Signed-off-by: Xueqin Lin <xueqin.lin@intel.com> Signed-off-by: Zhihong Peng <zhihongx.peng@intel.com> --- v7: 1) Split doc and code into two. 2) Modify asan.rst doc v8: No change. v9: 1) Add the check of libasan library. 2) Add release notes. v10:1) Split doc and code into two. 2) Meson supports asan. v11:Modify the document. v12:No change. --- config/meson.build | 16 +++++++++++ devtools/words-case.txt | 1 + doc/guides/prog_guide/asan.rst | 40 ++++++++++++++++++++++++++ doc/guides/prog_guide/index.rst | 1 + doc/guides/rel_notes/release_21_11.rst | 9 ++++++ 5 files changed, 67 insertions(+) create mode 100644 doc/guides/prog_guide/asan.rst diff --git a/config/meson.build b/config/meson.build index 4cdf589e20..f02b0e9c6d 100644 --- a/config/meson.build +++ b/config/meson.build @@ -411,6 +411,22 @@ if get_option('b_lto') endif endif +if get_option('b_sanitize') == 'address' or get_option('b_sanitize') == 'address,undefined' + if is_windows + error('ASan is not supported on windows') + endif + + if cc.get_id() == 'gcc' + asan_dep = cc.find_library('asan', required: true) + if (not cc.links('int main(int argc, char *argv[]) { return 0; }', + dependencies: asan_dep)) + error('broken dependency, "libasan"') + endif + add_project_link_arguments('-lasan', language: 'c') + dpdk_extra_ldflags += '-lasan' + endif +endif + if get_option('default_library') == 'both' error( ''' Unsupported value "both" for "default_library" option. diff --git a/devtools/words-case.txt b/devtools/words-case.txt index 0bbad48626..ada6910fa0 100644 --- a/devtools/words-case.txt +++ b/devtools/words-case.txt @@ -5,6 +5,7 @@ API Arm armv7 armv8 +ASan BAR CRC DCB diff --git a/doc/guides/prog_guide/asan.rst b/doc/guides/prog_guide/asan.rst new file mode 100644 index 0000000000..969676ebe8 --- /dev/null +++ b/doc/guides/prog_guide/asan.rst @@ -0,0 +1,40 @@ +.. SPDX-License-Identifier: BSD-3-Clause + Copyright(c) 2021 Intel Corporation + +Running Address Sanitizer +========================= + +`AddressSanitizer +<https://github.com/google/sanitizers/wiki/AddressSanitizer>`_ (ASan) +is a widely-used debugging tool to detect memory access errors. +It helps detect issues like use-after-free, various kinds of buffer +overruns in C/C++ programs, and other similar errors, as well as +printing out detailed debug information whenever an error is detected. + +AddressSanitizer is a part of LLVM (3.1+) and GCC (4.8+). + +Usage +----- + +meson build +^^^^^^^^^^^ + +To enable ASan in meson build system, use following meson build command: + +Example usage:: + +* gcc:: + + meson build -Dbuildtype=debug -Db_sanitize=address + ninja -C build + +* clang:: + + meson build -Dbuildtype=debug -Db_lundef=false -Db_sanitize=address + ninja -C build + +.. Note:: + + a) To compile with gcc in centos, libasan needs to be installed separately. + b) If the program being tested uses cmdline you will need to execute the + "stty echo" command when a error occurs. diff --git a/doc/guides/prog_guide/index.rst b/doc/guides/prog_guide/index.rst index 89af28dacb..b95c460b19 100644 --- a/doc/guides/prog_guide/index.rst +++ b/doc/guides/prog_guide/index.rst @@ -71,4 +71,5 @@ Programmer's Guide writing_efficient_code lto profile_app + asan glossary diff --git a/doc/guides/rel_notes/release_21_11.rst b/doc/guides/rel_notes/release_21_11.rst index d5435a64aa..63d9fef1b4 100644 --- a/doc/guides/rel_notes/release_21_11.rst +++ b/doc/guides/rel_notes/release_21_11.rst @@ -167,6 +167,15 @@ New Features * Added tests to verify tunnel header verification in IPsec inbound. * Added tests to verify inner checksum. +* **Enable ASan Address Sanitization.** + + `AddressSanitizer + <https://github.com/google/sanitizers/wiki/AddressSanitizer>`_ (ASan) + is a widely-used debugging tool to detect memory access errors. + It helps detect issues like use-after-free, various kinds of buffer + overruns in C/C++ programs, and other similar errors, as well as + printing out detailed debug information whenever an error is detected. + Removed Items ------------- -- 2.25.1 ^ permalink raw reply [flat|nested] 117+ messages in thread
* [dpdk-dev] [PATCH v12 2/4] DPDK code adapts to ASan 2021-10-19 13:58 ` [dpdk-dev] [PATCH v12 1/4] Enable ASan Address Sanitization zhihongx.peng @ 2021-10-19 13:58 ` zhihongx.peng 2021-10-19 13:58 ` [dpdk-dev] [PATCH v12 3/4] Code changes to avoid the ASan error zhihongx.peng ` (2 subsequent siblings) 3 siblings, 0 replies; 117+ messages in thread From: zhihongx.peng @ 2021-10-19 13:58 UTC (permalink / raw) To: david.marchand, anatoly.burakov, konstantin.ananyev, stephen, cristian.dumitrescu, john.mcnamara, bruce.richardson Cc: dev, xueqin.lin, Zhihong Peng From: Zhihong Peng <zhihongx.peng@intel.com> DPDK ASan functionality is currently only supported Linux x86_64. Support other platforms, need to define ASAN_SHADOW_OFFSET value according to google ASan document, and configure meson (config/meson.build). Here is an example of heap-buffer-overflow bug: ...... char *p = rte_zmalloc(NULL, 7, 0); p[7] = 'a'; ...... Here is an example of use-after-free bug: ...... char *p = rte_zmalloc(NULL, 7, 0); rte_free(p); *p = 'a'; ...... Signed-off-by: Xueqin Lin <xueqin.lin@intel.com> Signed-off-by: Zhihong Peng <zhihongx.peng@intel.com> --- v7: Split doc and code into two. v8: No change. v9: Modify the definition of RTE_MALLOC_ASAN. v10:Modify the definition of RTE_MALLOC_ASAN. v11:No change. v12:No change. --- config/meson.build | 4 + doc/guides/prog_guide/asan.rst | 68 +++++++++++- lib/eal/common/malloc_elem.c | 26 ++++- lib/eal/common/malloc_elem.h | 194 ++++++++++++++++++++++++++++++++- lib/eal/common/malloc_heap.c | 12 ++ lib/eal/common/rte_malloc.c | 9 +- 6 files changed, 306 insertions(+), 7 deletions(-) diff --git a/config/meson.build b/config/meson.build index f02b0e9c6d..bf751583bd 100644 --- a/config/meson.build +++ b/config/meson.build @@ -425,6 +425,10 @@ if get_option('b_sanitize') == 'address' or get_option('b_sanitize') == 'address add_project_link_arguments('-lasan', language: 'c') dpdk_extra_ldflags += '-lasan' endif + + if is_linux and arch_subdir == 'x86' + dpdk_conf.set10('RTE_MALLOC_ASAN', true) + endif endif if get_option('default_library') == 'both' diff --git a/doc/guides/prog_guide/asan.rst b/doc/guides/prog_guide/asan.rst index 969676ebe8..0cb2ca8751 100644 --- a/doc/guides/prog_guide/asan.rst +++ b/doc/guides/prog_guide/asan.rst @@ -13,6 +13,68 @@ printing out detailed debug information whenever an error is detected. AddressSanitizer is a part of LLVM (3.1+) and GCC (4.8+). +DPDK ASan functionality is currently only supported Linux x86_64. +Support other platforms, need to define ASAN_SHADOW_OFFSET value +according to google ASan document, and configure meson +(config/meson.build). + +Example heap-buffer-overflow error +---------------------------------- + +Following error was reported when ASan was enabled:: + + Applied 9 bytes of memory, but accessed the 10th byte of memory, + so heap-buffer-overflow appeared. + +Below code results in this error:: + + Add code to helloworld: + char *p = rte_zmalloc(NULL, 9, 0); + if (!p) { + printf("rte_zmalloc error."); + return -1; + } + p[9] = 'a'; + +The error log:: + + ==369953==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x7fb17f465809 at pc 0x5652e6707b84 bp 0x7ffea70eea20 sp 0x7ffea70eea10 WRITE of size 1 at 0x7fb17f465809 thread T0 + #0 0x5652e6707b83 in main ../examples/helloworld/main.c:47 + #1 0x7fb94953c0b2 in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x270b2) + #2 0x5652e67079bd in _start (/home/pzh/asan_test/x86_64-native-linuxapp-gcc/examples/dpdk-helloworld+0x8329bd) + + Address 0x7fb17f465809 is a wild pointer. + SUMMARY: AddressSanitizer: heap-buffer-overflow ../examples/helloworld/main.c:47 in main + +Example use-after-free error +---------------------------- + +Following error was reported when ASan was enabled:: + + Applied for 9 bytes of memory, and accessed the first byte after + released, so heap-use-after-free appeared. + +Below code results in this error:: + + Add code to helloworld: + char *p = rte_zmalloc(NULL, 9, 0); + if (!p) { + printf("rte_zmalloc error."); + return -1; + } + rte_free(p); + *p = 'a'; + +The error log:: + + ==417048==ERROR: AddressSanitizer: heap-use-after-free on address 0x7fc83f465800 at pc 0x564308a39b89 bp 0x7ffc8c85bf50 sp 0x7ffc8c85bf40 WRITE of size 1 at 0x7fc83f465800 thread T0 + #0 0x564308a39b88 in main ../examples/helloworld/main.c:48 + #1 0x7fd0079c60b2 in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x270b2) + #2 0x564308a399bd in _start (/home/pzh/asan_test/x86_64-native-linuxapp-gcc/examples/dpdk-helloworld+0x8329bd) + + Address 0x7fc83f465800 is a wild pointer. + SUMMARY: AddressSanitizer: heap-use-after-free ../examples/helloworld/main.c:48 in main + Usage ----- @@ -35,6 +97,8 @@ Example usage:: .. Note:: - a) To compile with gcc in centos, libasan needs to be installed separately. - b) If the program being tested uses cmdline you will need to execute the + a) Some of the features of ASan (for example, 'Display memory application location, currently + displayed as a wild pointer') are not currently supported by DPDK's implementation. + b) To compile with gcc in centos, libasan needs to be installed separately. + c) If the program being tested uses cmdline you will need to execute the "stty echo" command when a error occurs. diff --git a/lib/eal/common/malloc_elem.c b/lib/eal/common/malloc_elem.c index c2c9461f1d..bdd20a162e 100644 --- a/lib/eal/common/malloc_elem.c +++ b/lib/eal/common/malloc_elem.c @@ -446,6 +446,8 @@ malloc_elem_alloc(struct malloc_elem *elem, size_t size, unsigned align, struct malloc_elem *new_free_elem = RTE_PTR_ADD(new_elem, size + MALLOC_ELEM_OVERHEAD); + asan_clear_split_alloczone(new_free_elem); + split_elem(elem, new_free_elem); malloc_elem_free_list_insert(new_free_elem); @@ -458,6 +460,8 @@ malloc_elem_alloc(struct malloc_elem *elem, size_t size, unsigned align, elem->state = ELEM_BUSY; elem->pad = old_elem_size; + asan_clear_alloczone(elem); + /* put a dummy header in padding, to point to real element header */ if (elem->pad > 0) { /* pad will be at least 64-bytes, as everything * is cache-line aligned */ @@ -470,12 +474,18 @@ malloc_elem_alloc(struct malloc_elem *elem, size_t size, unsigned align, return new_elem; } + asan_clear_split_alloczone(new_elem); + /* we are going to split the element in two. The original element * remains free, and the new element is the one allocated. * Re-insert original element, in case its new size makes it * belong on a different list. */ + split_elem(elem, new_elem); + + asan_clear_alloczone(new_elem); + new_elem->state = ELEM_BUSY; malloc_elem_free_list_insert(elem); @@ -601,6 +611,8 @@ malloc_elem_hide_region(struct malloc_elem *elem, void *start, size_t len) if (next && next_elem_is_adjacent(elem)) { len_after = RTE_PTR_DIFF(next, hide_end); if (len_after >= MALLOC_ELEM_OVERHEAD + MIN_DATA_SIZE) { + asan_clear_split_alloczone(hide_end); + /* split after */ split_elem(elem, hide_end); @@ -615,6 +627,8 @@ malloc_elem_hide_region(struct malloc_elem *elem, void *start, size_t len) if (prev && prev_elem_is_adjacent(elem)) { len_before = RTE_PTR_DIFF(hide_start, elem); if (len_before >= MALLOC_ELEM_OVERHEAD + MIN_DATA_SIZE) { + asan_clear_split_alloczone(hide_start); + /* split before */ split_elem(elem, hide_start); @@ -628,6 +642,8 @@ malloc_elem_hide_region(struct malloc_elem *elem, void *start, size_t len) } } + asan_clear_alloczone(elem); + remove_elem(elem); } @@ -641,8 +657,10 @@ malloc_elem_resize(struct malloc_elem *elem, size_t size) const size_t new_size = size + elem->pad + MALLOC_ELEM_OVERHEAD; /* if we request a smaller size, then always return ok */ - if (elem->size >= new_size) + if (elem->size >= new_size) { + asan_clear_alloczone(elem); return 0; + } /* check if there is a next element, it's free and adjacent */ if (!elem->next || elem->next->state != ELEM_FREE || @@ -661,9 +679,15 @@ malloc_elem_resize(struct malloc_elem *elem, size_t size) /* now we have a big block together. Lets cut it down a bit, by splitting */ struct malloc_elem *split_pt = RTE_PTR_ADD(elem, new_size); split_pt = RTE_PTR_ALIGN_CEIL(split_pt, RTE_CACHE_LINE_SIZE); + + asan_clear_split_alloczone(split_pt); + split_elem(elem, split_pt); malloc_elem_free_list_insert(split_pt); } + + asan_clear_alloczone(elem); + return 0; } diff --git a/lib/eal/common/malloc_elem.h b/lib/eal/common/malloc_elem.h index a1e5f7f02c..a06c11ac8b 100644 --- a/lib/eal/common/malloc_elem.h +++ b/lib/eal/common/malloc_elem.h @@ -36,10 +36,20 @@ struct malloc_elem { uint64_t header_cookie; /* Cookie marking start of data */ /* trailer cookie at start + size */ #endif +#ifdef RTE_MALLOC_ASAN + size_t user_size; + uint64_t asan_cookie[2]; /* must be next to header_cookie */ +#endif } __rte_cache_aligned; +static const unsigned int MALLOC_ELEM_HEADER_LEN = sizeof(struct malloc_elem); + #ifndef RTE_MALLOC_DEBUG -static const unsigned MALLOC_ELEM_TRAILER_LEN = 0; +#ifdef RTE_MALLOC_ASAN +static const unsigned int MALLOC_ELEM_TRAILER_LEN = RTE_CACHE_LINE_SIZE; +#else +static const unsigned int MALLOC_ELEM_TRAILER_LEN; +#endif /* dummy function - just check if pointer is non-null */ static inline int @@ -55,7 +65,7 @@ set_trailer(struct malloc_elem *elem __rte_unused){ } #else -static const unsigned MALLOC_ELEM_TRAILER_LEN = RTE_CACHE_LINE_SIZE; +static const unsigned int MALLOC_ELEM_TRAILER_LEN = RTE_CACHE_LINE_SIZE; #define MALLOC_HEADER_COOKIE 0xbadbadbadadd2e55ULL /**< Header cookie. */ #define MALLOC_TRAILER_COOKIE 0xadd2e55badbadbadULL /**< Trailer cookie.*/ @@ -90,9 +100,187 @@ malloc_elem_cookies_ok(const struct malloc_elem *elem) #endif -static const unsigned MALLOC_ELEM_HEADER_LEN = sizeof(struct malloc_elem); #define MALLOC_ELEM_OVERHEAD (MALLOC_ELEM_HEADER_LEN + MALLOC_ELEM_TRAILER_LEN) +#ifdef RTE_MALLOC_ASAN + +#ifdef RTE_ARCH_X86_64 +#define ASAN_SHADOW_OFFSET 0x00007fff8000 +#endif + +#define ASAN_SHADOW_GRAIN_SIZE 8 +#define ASAN_MEM_FREE_FLAG 0xfd +#define ASAN_MEM_REDZONE_FLAG 0xfa +#define ASAN_SHADOW_SCALE 3 + +#define ASAN_MEM_SHIFT(mem) ((void *)((uintptr_t)(mem) >> ASAN_SHADOW_SCALE)) +#define ASAN_MEM_TO_SHADOW(mem) \ + RTE_PTR_ADD(ASAN_MEM_SHIFT(mem), ASAN_SHADOW_OFFSET) + +#if defined(__clang__) +__attribute__((no_sanitize("address", "hwaddress"))) +#else +__attribute__((no_sanitize_address)) +#endif +static inline void +asan_set_shadow(void *addr, char val) +{ + *(char *)addr = val; +} + +static inline void +asan_set_zone(void *ptr, size_t len, uint32_t val) +{ + size_t offset, i; + void *shadow; + size_t zone_len = len / ASAN_SHADOW_GRAIN_SIZE; + if (len % ASAN_SHADOW_GRAIN_SIZE != 0) + zone_len += 1; + + for (i = 0; i < zone_len; i++) { + offset = i * ASAN_SHADOW_GRAIN_SIZE; + shadow = ASAN_MEM_TO_SHADOW((uintptr_t)ptr + offset); + asan_set_shadow(shadow, val); + } +} + +/* + * When the memory is released, the release mark is + * set in the corresponding range of the shadow area. + */ +static inline void +asan_set_freezone(void *ptr, size_t size) +{ + asan_set_zone(ptr, size, ASAN_MEM_FREE_FLAG); +} + +/* + * When the memory is allocated, memory state must set as accessible. + */ +static inline void +asan_clear_alloczone(struct malloc_elem *elem) +{ + asan_set_zone((void *)elem, elem->size, 0x0); +} + +static inline void +asan_clear_split_alloczone(struct malloc_elem *elem) +{ + void *ptr = RTE_PTR_SUB(elem, MALLOC_ELEM_TRAILER_LEN); + asan_set_zone(ptr, MALLOC_ELEM_OVERHEAD, 0x0); +} + +/* + * When the memory is allocated, the memory boundary is + * marked in the corresponding range of the shadow area. + * Requirement: redzone >= 16, is a power of two. + */ +static inline void +asan_set_redzone(struct malloc_elem *elem, size_t user_size) +{ + uintptr_t head_redzone; + uintptr_t tail_redzone; + void *front_shadow; + void *tail_shadow; + uint32_t val; + + if (elem != NULL) { + if (elem->state != ELEM_PAD) + elem = RTE_PTR_ADD(elem, elem->pad); + + elem->user_size = user_size; + + /* Set mark before the start of the allocated memory */ + head_redzone = (uintptr_t)RTE_PTR_ADD(elem, + MALLOC_ELEM_HEADER_LEN - ASAN_SHADOW_GRAIN_SIZE); + front_shadow = ASAN_MEM_TO_SHADOW(head_redzone); + asan_set_shadow(front_shadow, ASAN_MEM_REDZONE_FLAG); + front_shadow = ASAN_MEM_TO_SHADOW(head_redzone + - ASAN_SHADOW_GRAIN_SIZE); + asan_set_shadow(front_shadow, ASAN_MEM_REDZONE_FLAG); + + /* Set mark after the end of the allocated memory */ + tail_redzone = (uintptr_t)RTE_PTR_ADD(elem, + MALLOC_ELEM_HEADER_LEN + + elem->user_size); + tail_shadow = ASAN_MEM_TO_SHADOW(tail_redzone); + val = (tail_redzone % ASAN_SHADOW_GRAIN_SIZE); + val = (val == 0) ? ASAN_MEM_REDZONE_FLAG : val; + asan_set_shadow(tail_shadow, val); + tail_shadow = ASAN_MEM_TO_SHADOW(tail_redzone + + ASAN_SHADOW_GRAIN_SIZE); + asan_set_shadow(tail_shadow, ASAN_MEM_REDZONE_FLAG); + } +} + +/* + * When the memory is released, the mark of the memory boundary + * in the corresponding range of the shadow area is cleared. + * Requirement: redzone >= 16, is a power of two. + */ +static inline void +asan_clear_redzone(struct malloc_elem *elem) +{ + uintptr_t head_redzone; + uintptr_t tail_redzone; + void *head_shadow; + void *tail_shadow; + + if (elem != NULL) { + elem = RTE_PTR_ADD(elem, elem->pad); + + /* Clear mark before the start of the allocated memory */ + head_redzone = (uintptr_t)RTE_PTR_ADD(elem, + MALLOC_ELEM_HEADER_LEN - ASAN_SHADOW_GRAIN_SIZE); + head_shadow = ASAN_MEM_TO_SHADOW(head_redzone); + asan_set_shadow(head_shadow, 0x00); + head_shadow = ASAN_MEM_TO_SHADOW(head_redzone + - ASAN_SHADOW_GRAIN_SIZE); + asan_set_shadow(head_shadow, 0x00); + + /* Clear mark after the end of the allocated memory */ + tail_redzone = (uintptr_t)RTE_PTR_ADD(elem, + MALLOC_ELEM_HEADER_LEN + elem->user_size); + tail_shadow = ASAN_MEM_TO_SHADOW(tail_redzone); + asan_set_shadow(tail_shadow, 0x00); + tail_shadow = ASAN_MEM_TO_SHADOW(tail_redzone + + ASAN_SHADOW_GRAIN_SIZE); + asan_set_shadow(tail_shadow, 0x00); + } +} + +static inline size_t +old_malloc_size(struct malloc_elem *elem) +{ + if (elem->state != ELEM_PAD) + elem = RTE_PTR_ADD(elem, elem->pad); + + return elem->user_size; +} +#else +static inline void +asan_set_freezone(void *ptr __rte_unused, size_t size __rte_unused) { } + +static inline void +asan_clear_alloczone(struct malloc_elem *elem __rte_unused) { } + +static inline void +asan_clear_split_alloczone(struct malloc_elem *elem __rte_unused) { } + +static inline void +asan_set_redzone(struct malloc_elem *elem __rte_unused, + size_t user_size __rte_unused) { } + +static inline void +asan_clear_redzone(struct malloc_elem *elem __rte_unused) { } + +static inline size_t +old_malloc_size(struct malloc_elem *elem) +{ + return elem->size - elem->pad - MALLOC_ELEM_OVERHEAD; +} +#endif + /* * Given a pointer to the start of a memory block returned by malloc, get * the actual malloc_elem header for that block. diff --git a/lib/eal/common/malloc_heap.c b/lib/eal/common/malloc_heap.c index ee400f38ec..775d6789df 100644 --- a/lib/eal/common/malloc_heap.c +++ b/lib/eal/common/malloc_heap.c @@ -237,6 +237,7 @@ heap_alloc(struct malloc_heap *heap, const char *type __rte_unused, size_t size, unsigned int flags, size_t align, size_t bound, bool contig) { struct malloc_elem *elem; + size_t user_size = size; size = RTE_CACHE_LINE_ROUNDUP(size); align = RTE_CACHE_LINE_ROUNDUP(align); @@ -250,6 +251,8 @@ heap_alloc(struct malloc_heap *heap, const char *type __rte_unused, size_t size, /* increase heap's count of allocated elements */ heap->alloc_count++; + + asan_set_redzone(elem, user_size); } return elem == NULL ? NULL : (void *)(&elem[1]); @@ -270,6 +273,8 @@ heap_alloc_biggest(struct malloc_heap *heap, const char *type __rte_unused, /* increase heap's count of allocated elements */ heap->alloc_count++; + + asan_set_redzone(elem, size); } return elem == NULL ? NULL : (void *)(&elem[1]); @@ -841,6 +846,8 @@ malloc_heap_free(struct malloc_elem *elem) if (!malloc_elem_cookies_ok(elem) || elem->state != ELEM_BUSY) return -1; + asan_clear_redzone(elem); + /* elem may be merged with previous element, so keep heap address */ heap = elem->heap; msl = elem->msl; @@ -848,6 +855,9 @@ malloc_heap_free(struct malloc_elem *elem) rte_spinlock_lock(&(heap->lock)); + void *asan_ptr = RTE_PTR_ADD(elem, MALLOC_ELEM_HEADER_LEN + elem->pad); + size_t asan_data_len = elem->size - MALLOC_ELEM_OVERHEAD - elem->pad; + /* mark element as free */ elem->state = ELEM_FREE; @@ -1001,6 +1011,8 @@ malloc_heap_free(struct malloc_elem *elem) rte_mcfg_mem_write_unlock(); free_unlock: + asan_set_freezone(asan_ptr, asan_data_len); + rte_spinlock_unlock(&(heap->lock)); return ret; } diff --git a/lib/eal/common/rte_malloc.c b/lib/eal/common/rte_malloc.c index 9d39e58c08..d0bec26920 100644 --- a/lib/eal/common/rte_malloc.c +++ b/lib/eal/common/rte_malloc.c @@ -162,6 +162,8 @@ rte_calloc(const char *type, size_t num, size_t size, unsigned align) void * rte_realloc_socket(void *ptr, size_t size, unsigned int align, int socket) { + size_t user_size; + if (ptr == NULL) return rte_malloc_socket(NULL, size, align, socket); @@ -171,6 +173,8 @@ rte_realloc_socket(void *ptr, size_t size, unsigned int align, int socket) return NULL; } + user_size = size; + size = RTE_CACHE_LINE_ROUNDUP(size), align = RTE_CACHE_LINE_ROUNDUP(align); /* check requested socket id and alignment matches first, and if ok, @@ -181,6 +185,9 @@ rte_realloc_socket(void *ptr, size_t size, unsigned int align, int socket) RTE_PTR_ALIGN(ptr, align) == ptr && malloc_heap_resize(elem, size) == 0) { rte_eal_trace_mem_realloc(size, align, socket, ptr); + + asan_set_redzone(elem, user_size); + return ptr; } @@ -192,7 +199,7 @@ rte_realloc_socket(void *ptr, size_t size, unsigned int align, int socket) if (new_ptr == NULL) return NULL; /* elem: |pad|data_elem|data|trailer| */ - const size_t old_size = elem->size - elem->pad - MALLOC_ELEM_OVERHEAD; + const size_t old_size = old_malloc_size(elem); rte_memcpy(new_ptr, ptr, old_size < size ? old_size : size); rte_free(ptr); -- 2.25.1 ^ permalink raw reply [flat|nested] 117+ messages in thread
* [dpdk-dev] [PATCH v12 3/4] Code changes to avoid the ASan error 2021-10-19 13:58 ` [dpdk-dev] [PATCH v12 1/4] Enable ASan Address Sanitization zhihongx.peng 2021-10-19 13:58 ` [dpdk-dev] [PATCH v12 2/4] DPDK code adapts to ASan zhihongx.peng @ 2021-10-19 13:58 ` zhihongx.peng 2021-10-19 14:05 ` Dumitrescu, Cristian 2021-10-19 13:58 ` [dpdk-dev] [PATCH v12 4/4] performance-thread: Fix cross compilation failed zhihongx.peng 2021-10-19 14:47 ` [dpdk-dev] [PATCH v12 1/4] Enable ASan Address Sanitization Peng, ZhihongX 3 siblings, 1 reply; 117+ messages in thread From: zhihongx.peng @ 2021-10-19 13:58 UTC (permalink / raw) To: david.marchand, anatoly.burakov, konstantin.ananyev, stephen, cristian.dumitrescu, john.mcnamara, bruce.richardson Cc: dev, xueqin.lin, Zhihong Peng From: Zhihong Peng <zhihongx.peng@intel.com> Code changes to avoid the following ASan error: "Control reaches end of non-void function". Cc: cristian.dumitrescu@intel.com Signed-off-by: Xueqin Lin <xueqin.lin@intel.com> Signed-off-by: Zhihong Peng <zhihongx.peng@intel.com> --- v7: no change. v8: no change. v9: Modify the submit log. v10:no change. v11:no change. v12:Modify the commit log. --- lib/pipeline/rte_swx_pipeline.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/pipeline/rte_swx_pipeline.c b/lib/pipeline/rte_swx_pipeline.c index 1cd09a4b44..0acd6c6752 100644 --- a/lib/pipeline/rte_swx_pipeline.c +++ b/lib/pipeline/rte_swx_pipeline.c @@ -4642,7 +4642,7 @@ instr_meter_translate(struct rte_swx_pipeline *p, return 0; } - CHECK(0, EINVAL); + return -EINVAL; } static inline void @@ -5937,7 +5937,7 @@ instr_translate(struct rte_swx_pipeline *p, instr, data); - CHECK(0, EINVAL); + return -EINVAL; } static struct instruction_data * -- 2.25.1 ^ permalink raw reply [flat|nested] 117+ messages in thread
* Re: [dpdk-dev] [PATCH v12 3/4] Code changes to avoid the ASan error 2021-10-19 13:58 ` [dpdk-dev] [PATCH v12 3/4] Code changes to avoid the ASan error zhihongx.peng @ 2021-10-19 14:05 ` Dumitrescu, Cristian 0 siblings, 0 replies; 117+ messages in thread From: Dumitrescu, Cristian @ 2021-10-19 14:05 UTC (permalink / raw) To: Peng, ZhihongX, david.marchand, Burakov, Anatoly, Ananyev, Konstantin, stephen, Mcnamara, John, Richardson, Bruce Cc: dev, Lin, Xueqin > -----Original Message----- > From: Peng, ZhihongX <zhihongx.peng@intel.com> > Sent: Tuesday, October 19, 2021 2:59 PM > To: david.marchand@redhat.com; Burakov, Anatoly > <anatoly.burakov@intel.com>; Ananyev, Konstantin > <konstantin.ananyev@intel.com>; stephen@networkplumber.org; > Dumitrescu, Cristian <cristian.dumitrescu@intel.com>; Mcnamara, John > <john.mcnamara@intel.com>; Richardson, Bruce > <bruce.richardson@intel.com> > Cc: dev@dpdk.org; Lin, Xueqin <xueqin.lin@intel.com>; Peng, ZhihongX > <zhihongx.peng@intel.com> > Subject: [PATCH v12 3/4] Code changes to avoid the ASan error > > From: Zhihong Peng <zhihongx.peng@intel.com> > > Code changes to avoid the following ASan error: > "Control reaches end of non-void function". > > Cc: cristian.dumitrescu@intel.com > > Signed-off-by: Xueqin Lin <xueqin.lin@intel.com> > Signed-off-by: Zhihong Peng <zhihongx.peng@intel.com> > --- > v7: no change. > v8: no change. > v9: Modify the submit log. > v10:no change. > v11:no change. > v12:Modify the commit log. > --- > lib/pipeline/rte_swx_pipeline.c | 4 ++-- > 1 file changed, 2 insertions(+), 2 deletions(-) > > diff --git a/lib/pipeline/rte_swx_pipeline.c b/lib/pipeline/rte_swx_pipeline.c > index 1cd09a4b44..0acd6c6752 100644 > --- a/lib/pipeline/rte_swx_pipeline.c > +++ b/lib/pipeline/rte_swx_pipeline.c > @@ -4642,7 +4642,7 @@ instr_meter_translate(struct rte_swx_pipeline *p, > return 0; > } > > - CHECK(0, EINVAL); > + return -EINVAL; > } > > static inline void > @@ -5937,7 +5937,7 @@ instr_translate(struct rte_swx_pipeline *p, > instr, > data); > > - CHECK(0, EINVAL); > + return -EINVAL; > } > > static struct instruction_data * > -- > 2.25.1 This patch changes code working correctly just to please the tool. We should look at ways to fix the tooll, as this is likely going to be a recurring issue. Hopefully this tool is to provide value to DPDK users. Reluctantly, Acked-by: Cristian Dumitrescu <cristian.dumitrescu@intel.com> ^ permalink raw reply [flat|nested] 117+ messages in thread
* [dpdk-dev] [PATCH v12 4/4] performance-thread: Fix cross compilation failed 2021-10-19 13:58 ` [dpdk-dev] [PATCH v12 1/4] Enable ASan Address Sanitization zhihongx.peng 2021-10-19 13:58 ` [dpdk-dev] [PATCH v12 2/4] DPDK code adapts to ASan zhihongx.peng 2021-10-19 13:58 ` [dpdk-dev] [PATCH v12 3/4] Code changes to avoid the ASan error zhihongx.peng @ 2021-10-19 13:58 ` zhihongx.peng 2021-10-19 14:47 ` [dpdk-dev] [PATCH v12 1/4] Enable ASan Address Sanitization Peng, ZhihongX 3 siblings, 0 replies; 117+ messages in thread From: zhihongx.peng @ 2021-10-19 13:58 UTC (permalink / raw) To: david.marchand, anatoly.burakov, konstantin.ananyev, stephen, cristian.dumitrescu, john.mcnamara, bruce.richardson Cc: dev, xueqin.lin, Zhihong Peng, stable From: Zhihong Peng <zhihongx.peng@intel.com> The gcc(arm-linux-gcc) will check code more stricter when ASan enabled. "strncpy specified bound XX equals destination size" error occurs here. Fixes: 116819b9ed0d ("examples/performance-thread: add lthread subsystem") Cc: stable@dpdk.org Signed-off-by: Xueqin Lin <xueqin.lin@intel.com> Signed-off-by: Zhihong Peng <zhihongx.peng@intel.com> --- v11: Use rte_strlcpy to replace strncpy. v12: Delete rte_strlcpy's rte_. --- examples/performance-thread/common/lthread.c | 4 ++-- examples/performance-thread/common/lthread_cond.c | 6 +++--- examples/performance-thread/common/lthread_mutex.c | 6 +++--- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/examples/performance-thread/common/lthread.c b/examples/performance-thread/common/lthread.c index 3f1f48db43..190e5874b1 100644 --- a/examples/performance-thread/common/lthread.c +++ b/examples/performance-thread/common/lthread.c @@ -20,6 +20,7 @@ #include <sys/mman.h> #include <rte_log.h> +#include <rte_string_fns.h> #include <ctx.h> #include <stack.h> @@ -463,6 +464,5 @@ void lthread_set_funcname(const char *f) { struct lthread *lt = THIS_LTHREAD; - strncpy(lt->funcname, f, sizeof(lt->funcname)); - lt->funcname[sizeof(lt->funcname)-1] = 0; + strlcpy(lt->funcname, f, sizeof(lt->funcname)); } diff --git a/examples/performance-thread/common/lthread_cond.c b/examples/performance-thread/common/lthread_cond.c index cdcc7a7b5a..e7be17089a 100644 --- a/examples/performance-thread/common/lthread_cond.c +++ b/examples/performance-thread/common/lthread_cond.c @@ -20,6 +20,7 @@ #include <rte_log.h> #include <rte_common.h> +#include <rte_string_fns.h> #include "lthread_api.h" #include "lthread_diag_api.h" @@ -57,10 +58,9 @@ lthread_cond_init(char *name, struct lthread_cond **cond, } if (name == NULL) - strncpy(c->name, "no name", sizeof(c->name)); + strlcpy(c->name, "no name", sizeof(c->name)); else - strncpy(c->name, name, sizeof(c->name)); - c->name[sizeof(c->name)-1] = 0; + strlcpy(c->name, name, sizeof(c->name)); c->root_sched = THIS_SCHED; diff --git a/examples/performance-thread/common/lthread_mutex.c b/examples/performance-thread/common/lthread_mutex.c index 01da6cad4f..709ab9f553 100644 --- a/examples/performance-thread/common/lthread_mutex.c +++ b/examples/performance-thread/common/lthread_mutex.c @@ -19,6 +19,7 @@ #include <rte_log.h> #include <rte_spinlock.h> #include <rte_common.h> +#include <rte_string_fns.h> #include "lthread_api.h" #include "lthread_int.h" @@ -52,10 +53,9 @@ lthread_mutex_init(char *name, struct lthread_mutex **mutex, } if (name == NULL) - strncpy(m->name, "no name", sizeof(m->name)); + strlcpy(m->name, "no name", sizeof(m->name)); else - strncpy(m->name, name, sizeof(m->name)); - m->name[sizeof(m->name)-1] = 0; + strlcpy(m->name, name, sizeof(m->name)); m->root_sched = THIS_SCHED; m->owner = NULL; -- 2.25.1 ^ permalink raw reply [flat|nested] 117+ messages in thread
* Re: [dpdk-dev] [PATCH v12 1/4] Enable ASan Address Sanitization 2021-10-19 13:58 ` [dpdk-dev] [PATCH v12 1/4] Enable ASan Address Sanitization zhihongx.peng ` (2 preceding siblings ...) 2021-10-19 13:58 ` [dpdk-dev] [PATCH v12 4/4] performance-thread: Fix cross compilation failed zhihongx.peng @ 2021-10-19 14:47 ` Peng, ZhihongX 2021-10-19 15:17 ` Mcnamara, John 2021-10-19 15:20 ` David Marchand 3 siblings, 2 replies; 117+ messages in thread From: Peng, ZhihongX @ 2021-10-19 14:47 UTC (permalink / raw) To: david.marchand, Burakov, Anatoly, Ananyev, Konstantin, stephen, Dumitrescu, Cristian, Mcnamara, John, Richardson, Bruce Cc: dev, Lin, Xueqin Hi, David The v12 version has been submitted, and patch 3 and 4 has been acked. V10 information: I have compiled passed on the x86/arm/ppc platforms, directory targets as below : build-arm64-bluefield build-arm64-host-clang build-clang-shared build-gcc-shared build-ppc64le-power8 build-arm64-dpaa build-arm64-octeontx2 build-clang-static build-gcc-static build-x86-generic We do not support the windows platform due to standard google document(https://github.com/google/sanitizers/wiki/AddressSanitizer) also not support this. We also sent our cross-compilation log to you in an other email. Passed to run unit test for dpdk-testpmd simple on x86 platform. What else is blocking the process ? What's any action we should do for the code merge? High appreciate for your check and feedback. > -----Original Message----- > From: Peng, ZhihongX <zhihongx.peng@intel.com> > Sent: Tuesday, October 19, 2021 9:59 PM > To: david.marchand@redhat.com; Burakov, Anatoly > <anatoly.burakov@intel.com>; Ananyev, Konstantin > <konstantin.ananyev@intel.com>; stephen@networkplumber.org; > Dumitrescu, Cristian <cristian.dumitrescu@intel.com>; Mcnamara, John > <john.mcnamara@intel.com>; Richardson, Bruce > <bruce.richardson@intel.com> > Cc: dev@dpdk.org; Lin, Xueqin <xueqin.lin@intel.com>; Peng, ZhihongX > <zhihongx.peng@intel.com> > Subject: [PATCH v12 1/4] Enable ASan Address Sanitization > > From: Zhihong Peng <zhihongx.peng@intel.com> > > `AddressSanitizer > <https://github.com/google/sanitizers/wiki/AddressSanitizer>`_ (ASan) is a > widely-used debugging tool to detect memory access errors. > It helps detect issues like use-after-free, various kinds of buffer overruns in > C/C++ programs, and other similar errors, as well as printing out detailed > debug information whenever an error is detected. > > We can enable ASan by adding below compilation options: > -Dbuildtype=debug -Db_lundef=false -Db_sanitize=address > "-Dbuildtype=debug": This is a non-essential option. When this option is > added, if a memory error occurs, ASan can clearly show where the code is > wrong. > "-Db_lundef=false": When use clang to compile DPDK, this option must be > added. > > Signed-off-by: Xueqin Lin <xueqin.lin@intel.com> > Signed-off-by: Zhihong Peng <zhihongx.peng@intel.com> > --- > v7: 1) Split doc and code into two. > 2) Modify asan.rst doc > v8: No change. > v9: 1) Add the check of libasan library. > 2) Add release notes. > v10:1) Split doc and code into two. > 2) Meson supports asan. > v11:Modify the document. > v12:No change. > --- > config/meson.build | 16 +++++++++++ > devtools/words-case.txt | 1 + > doc/guides/prog_guide/asan.rst | 40 ++++++++++++++++++++++++++ > doc/guides/prog_guide/index.rst | 1 + > doc/guides/rel_notes/release_21_11.rst | 9 ++++++ > 5 files changed, 67 insertions(+) > create mode 100644 doc/guides/prog_guide/asan.rst > > diff --git a/config/meson.build b/config/meson.build index > 4cdf589e20..f02b0e9c6d 100644 > --- a/config/meson.build > +++ b/config/meson.build > @@ -411,6 +411,22 @@ if get_option('b_lto') > endif > endif > > +if get_option('b_sanitize') == 'address' or get_option('b_sanitize') == > 'address,undefined' > + if is_windows > + error('ASan is not supported on windows') > + endif > + > + if cc.get_id() == 'gcc' > + asan_dep = cc.find_library('asan', required: true) > + if (not cc.links('int main(int argc, char *argv[]) { return 0; }', > + dependencies: asan_dep)) > + error('broken dependency, "libasan"') > + endif > + add_project_link_arguments('-lasan', language: 'c') > + dpdk_extra_ldflags += '-lasan' > + endif > +endif > + > if get_option('default_library') == 'both' > error( ''' > Unsupported value "both" for "default_library" option. > diff --git a/devtools/words-case.txt b/devtools/words-case.txt index > 0bbad48626..ada6910fa0 100644 > --- a/devtools/words-case.txt > +++ b/devtools/words-case.txt > @@ -5,6 +5,7 @@ API > Arm > armv7 > armv8 > +ASan > BAR > CRC > DCB > diff --git a/doc/guides/prog_guide/asan.rst > b/doc/guides/prog_guide/asan.rst new file mode 100644 index > 0000000000..969676ebe8 > --- /dev/null > +++ b/doc/guides/prog_guide/asan.rst > @@ -0,0 +1,40 @@ > +.. SPDX-License-Identifier: BSD-3-Clause > + Copyright(c) 2021 Intel Corporation > + > +Running Address Sanitizer > +========================= > + > +`AddressSanitizer > +<https://github.com/google/sanitizers/wiki/AddressSanitizer>`_ (ASan) > +is a widely-used debugging tool to detect memory access errors. > +It helps detect issues like use-after-free, various kinds of buffer > +overruns in C/C++ programs, and other similar errors, as well as > +printing out detailed debug information whenever an error is detected. > + > +AddressSanitizer is a part of LLVM (3.1+) and GCC (4.8+). > + > +Usage > +----- > + > +meson build > +^^^^^^^^^^^ > + > +To enable ASan in meson build system, use following meson build > command: > + > +Example usage:: > + > +* gcc:: > + > + meson build -Dbuildtype=debug -Db_sanitize=address > + ninja -C build > + > +* clang:: > + > + meson build -Dbuildtype=debug -Db_lundef=false -Db_sanitize=address > + ninja -C build > + > +.. Note:: > + > + a) To compile with gcc in centos, libasan needs to be installed separately. > + b) If the program being tested uses cmdline you will need to execute the > + "stty echo" command when a error occurs. > diff --git a/doc/guides/prog_guide/index.rst > b/doc/guides/prog_guide/index.rst index 89af28dacb..b95c460b19 100644 > --- a/doc/guides/prog_guide/index.rst > +++ b/doc/guides/prog_guide/index.rst > @@ -71,4 +71,5 @@ Programmer's Guide > writing_efficient_code > lto > profile_app > + asan > glossary > diff --git a/doc/guides/rel_notes/release_21_11.rst > b/doc/guides/rel_notes/release_21_11.rst > index d5435a64aa..63d9fef1b4 100644 > --- a/doc/guides/rel_notes/release_21_11.rst > +++ b/doc/guides/rel_notes/release_21_11.rst > @@ -167,6 +167,15 @@ New Features > * Added tests to verify tunnel header verification in IPsec inbound. > * Added tests to verify inner checksum. > > +* **Enable ASan Address Sanitization.** > + > + `AddressSanitizer > + <https://github.com/google/sanitizers/wiki/AddressSanitizer>`_ (ASan) > + is a widely-used debugging tool to detect memory access errors. > + It helps detect issues like use-after-free, various kinds of buffer > + overruns in C/C++ programs, and other similar errors, as well as > + printing out detailed debug information whenever an error is detected. > + > > Removed Items > ------------- > -- > 2.25.1 ^ permalink raw reply [flat|nested] 117+ messages in thread
* Re: [dpdk-dev] [PATCH v12 1/4] Enable ASan Address Sanitization 2021-10-19 14:47 ` [dpdk-dev] [PATCH v12 1/4] Enable ASan Address Sanitization Peng, ZhihongX @ 2021-10-19 15:17 ` Mcnamara, John 2021-10-20 1:55 ` Peng, ZhihongX 2021-10-19 15:20 ` David Marchand 1 sibling, 1 reply; 117+ messages in thread From: Mcnamara, John @ 2021-10-19 15:17 UTC (permalink / raw) To: Peng, ZhihongX, david.marchand, Burakov, Anatoly, Ananyev, Konstantin, stephen, Dumitrescu, Cristian, Richardson, Bruce Cc: dev, Lin, Xueqin > > diff --git a/doc/guides/prog_guide/asan.rst > > b/doc/guides/prog_guide/asan.rst new file mode 100644 index > > 0000000000..969676ebe8 > > --- /dev/null > > +++ b/doc/guides/prog_guide/asan.rst > > @@ -0,0 +1,40 @@ > > +.. SPDX-License-Identifier: BSD-3-Clause > > + Copyright(c) 2021 Intel Corporation > > + > > ... > > + > > +Usage > > +----- > > + > > +meson build > > +^^^^^^^^^^^ These 2 additional header levels aren't needed. If you do another version you can leave them out. Apart from that: Acked-by: John McNamara <john.mcnamara@intel.com> ^ permalink raw reply [flat|nested] 117+ messages in thread
* Re: [dpdk-dev] [PATCH v12 1/4] Enable ASan Address Sanitization 2021-10-19 15:17 ` Mcnamara, John @ 2021-10-20 1:55 ` Peng, ZhihongX 0 siblings, 0 replies; 117+ messages in thread From: Peng, ZhihongX @ 2021-10-20 1:55 UTC (permalink / raw) To: Mcnamara, John, david.marchand, Burakov, Anatoly, Ananyev, Konstantin, stephen, Dumitrescu, Cristian, Richardson, Bruce Cc: dev, Lin, Xueqin > -----Original Message----- > From: Mcnamara, John <john.mcnamara@intel.com> > Sent: Tuesday, October 19, 2021 11:18 PM > To: Peng, ZhihongX <zhihongx.peng@intel.com>; > david.marchand@redhat.com; Burakov, Anatoly > <anatoly.burakov@intel.com>; Ananyev, Konstantin > <konstantin.ananyev@intel.com>; stephen@networkplumber.org; > Dumitrescu, Cristian <cristian.dumitrescu@intel.com>; Richardson, Bruce > <bruce.richardson@intel.com> > Cc: dev@dpdk.org; Lin, Xueqin <xueqin.lin@intel.com> > Subject: RE: [PATCH v12 1/4] Enable ASan Address Sanitization > > > > > diff --git a/doc/guides/prog_guide/asan.rst > > > b/doc/guides/prog_guide/asan.rst new file mode 100644 index > > > 0000000000..969676ebe8 > > > --- /dev/null > > > +++ b/doc/guides/prog_guide/asan.rst > > > @@ -0,0 +1,40 @@ > > > +.. SPDX-License-Identifier: BSD-3-Clause > > > + Copyright(c) 2021 Intel Corporation > > > + > > > > > ... > > > > + > > > +Usage > > > +----- > > > + > > > +meson build > > > +^^^^^^^^^^^ > > These 2 additional header levels aren't needed. If you do another version > you can leave them out. Apart from that: I will fix it in v13. > Acked-by: John McNamara <john.mcnamara@intel.com> ^ permalink raw reply [flat|nested] 117+ messages in thread
* Re: [dpdk-dev] [PATCH v12 1/4] Enable ASan Address Sanitization 2021-10-19 14:47 ` [dpdk-dev] [PATCH v12 1/4] Enable ASan Address Sanitization Peng, ZhihongX 2021-10-19 15:17 ` Mcnamara, John @ 2021-10-19 15:20 ` David Marchand 2021-10-20 2:41 ` Lin, Xueqin 1 sibling, 1 reply; 117+ messages in thread From: David Marchand @ 2021-10-19 15:20 UTC (permalink / raw) To: Peng, ZhihongX Cc: Burakov, Anatoly, Ananyev, Konstantin, stephen, Dumitrescu, Cristian, Mcnamara, John, Richardson, Bruce, dev, Lin, Xueqin On Tue, Oct 19, 2021 at 4:50 PM Peng, ZhihongX <zhihongx.peng@intel.com> wrote: > The v12 version has been submitted, and patch 3 and 4 has been acked. > > V10 information: > > I have compiled passed on the x86/arm/ppc platforms, directory targets as below : > build-arm64-bluefield build-arm64-host-clang build-clang-shared build-gcc-shared build-ppc64le-power8 > build-arm64-dpaa build-arm64-octeontx2 build-clang-static build-gcc-static build-x86-generic > > We do not support the windows platform due to standard google document(https://github.com/google/sanitizers/wiki/AddressSanitizer) > also not support this. > We also sent our cross-compilation log to you in an other email. Passed to run unit test for dpdk-testpmd simple on x86 platform. > What else is blocking the process ? > What's any action we should do for the code merge? High appreciate for your check and feedback. This series has seen so many revisions (often numbered the same, and while I was writting this reply, here is a new revision) in the last days that I put it at the end of my queue simply to wait for a stable state. By the way, simply pinging me to get your patches merged is easy, but if you care about the dpdk project, there are other series that need reviews/checks when merging, you are welcome to review and test patches. -- David Marchand ^ permalink raw reply [flat|nested] 117+ messages in thread
* Re: [dpdk-dev] [PATCH v12 1/4] Enable ASan Address Sanitization 2021-10-19 15:20 ` David Marchand @ 2021-10-20 2:41 ` Lin, Xueqin 0 siblings, 0 replies; 117+ messages in thread From: Lin, Xueqin @ 2021-10-20 2:41 UTC (permalink / raw) To: David Marchand, Peng, ZhihongX Cc: Burakov, Anatoly, Ananyev, Konstantin, stephen, Dumitrescu, Cristian, Mcnamara, John, Richardson, Bruce, dev > -----Original Message----- > From: David Marchand <david.marchand@redhat.com> > Sent: Tuesday, October 19, 2021 11:21 PM > To: Peng, ZhihongX <zhihongx.peng@intel.com> > Cc: Burakov, Anatoly <anatoly.burakov@intel.com>; Ananyev, Konstantin > <konstantin.ananyev@intel.com>; stephen@networkplumber.org; > Dumitrescu, Cristian <cristian.dumitrescu@intel.com>; Mcnamara, John > <john.mcnamara@intel.com>; Richardson, Bruce > <bruce.richardson@intel.com>; dev@dpdk.org; Lin, Xueqin > <xueqin.lin@intel.com> > Subject: Re: [dpdk-dev] [PATCH v12 1/4] Enable ASan Address Sanitization > > On Tue, Oct 19, 2021 at 4:50 PM Peng, ZhihongX <zhihongx.peng@intel.com> > wrote: > > The v12 version has been submitted, and patch 3 and 4 has been acked. > > > > V10 information: > > > > I have compiled passed on the x86/arm/ppc platforms, directory targets as > below : > > build-arm64-bluefield build-arm64-host-clang build-clang-shared build- > gcc-shared build-ppc64le-power8 > > build-arm64-dpaa build-arm64-octeontx2 build-clang-static build-gcc- > static build-x86-generic > > > > We do not support the windows platform due to standard google > > document(https://github.com/google/sanitizers/wiki/AddressSanitizer) > > also not support this. > > We also sent our cross-compilation log to you in an other email. Passed to > run unit test for dpdk-testpmd simple on x86 platform. > > What else is blocking the process ? > > What's any action we should do for the code merge? High appreciate for > your check and feedback. > > This series has seen so many revisions (often numbered the same, and while > I was writting this reply, here is a new revision) in the last days that I put it at > the end of my queue simply to wait for a stable state. > > By the way, simply pinging me to get your patches merged is easy, but if you > care about the dpdk project, there are other series that need reviews/checks > when merging, you are welcome to review and test patches. Really sorry to bring you so many trouble, apart from ASan implementation patch, also have other error build, doc... patches, recently receive some comments from other domain experts and maintainers. We are active to deal with them and have so many revisions. For the same numbers, it is our fault, sorry for that, and thanks a lot for your and reviewers' great support. Today we will send v13 version, and all the fix will be in this version. We also care about the quality, make sure to run cross-compilation build and unit test passed on our platforms then send the patch. Hope that everything is well and could meet the expectation, thanks a lot. > > > -- > David Marchand ^ permalink raw reply [flat|nested] 117+ messages in thread
* [dpdk-dev] [PATCH v12 1/4] Enable ASan AddressSanitizer 2021-10-19 10:12 ` [dpdk-dev] [PATCH v11 4/4] performance-thread: Fix cross compilation failed zhihongx.peng ` (2 preceding siblings ...) 2021-10-19 13:58 ` [dpdk-dev] [PATCH v12 1/4] Enable ASan Address Sanitization zhihongx.peng @ 2021-10-19 15:15 ` zhihongx.peng 2021-10-19 15:15 ` [dpdk-dev] [PATCH v12 2/4] DPDK code adapts to ASan zhihongx.peng ` (2 more replies) 3 siblings, 3 replies; 117+ messages in thread From: zhihongx.peng @ 2021-10-19 15:15 UTC (permalink / raw) To: david.marchand, thomas, anatoly.burakov, konstantin.ananyev, stephen, cristian.dumitrescu, john.mcnamara, bruce.richardson Cc: dev, xueqin.lin, Zhihong Peng From: Zhihong Peng <zhihongx.peng@intel.com> `AddressSanitizer <https://github.com/google/sanitizers/wiki/AddressSanitizer>`_ (ASan) is a widely-used debugging tool to detect memory access errors. It helps detect issues like use-after-free, various kinds of buffer overruns in C/C++ programs, and other similar errors, as well as printing out detailed debug information whenever an error is detected. We can enable ASan by adding below compilation options: -Dbuildtype=debug -Db_lundef=false -Db_sanitize=address "-Dbuildtype=debug": This is a non-essential option. When this option is added, if a memory error occurs, ASan can clearly show where the code is wrong. "-Db_lundef=false": When use clang to compile DPDK, this option must be added. Signed-off-by: Xueqin Lin <xueqin.lin@intel.com> Signed-off-by: Zhihong Peng <zhihongx.peng@intel.com> --- v7: 1) Split doc and code into two. 2) Modify asan.rst doc v8: No change. v9: 1) Add the check of libasan library. 2) Add release notes. v10:1) Split doc and code into two. 2) Meson supports asan. v11:Modify the document. v12:No change. --- config/meson.build | 16 +++++++++++ devtools/words-case.txt | 1 + doc/guides/prog_guide/asan.rst | 40 ++++++++++++++++++++++++++ doc/guides/prog_guide/index.rst | 1 + doc/guides/rel_notes/release_21_11.rst | 9 ++++++ 5 files changed, 67 insertions(+) create mode 100644 doc/guides/prog_guide/asan.rst diff --git a/config/meson.build b/config/meson.build index 4cdf589e20..f02b0e9c6d 100644 --- a/config/meson.build +++ b/config/meson.build @@ -411,6 +411,22 @@ if get_option('b_lto') endif endif +if get_option('b_sanitize') == 'address' or get_option('b_sanitize') == 'address,undefined' + if is_windows + error('ASan is not supported on windows') + endif + + if cc.get_id() == 'gcc' + asan_dep = cc.find_library('asan', required: true) + if (not cc.links('int main(int argc, char *argv[]) { return 0; }', + dependencies: asan_dep)) + error('broken dependency, "libasan"') + endif + add_project_link_arguments('-lasan', language: 'c') + dpdk_extra_ldflags += '-lasan' + endif +endif + if get_option('default_library') == 'both' error( ''' Unsupported value "both" for "default_library" option. diff --git a/devtools/words-case.txt b/devtools/words-case.txt index 0bbad48626..ada6910fa0 100644 --- a/devtools/words-case.txt +++ b/devtools/words-case.txt @@ -5,6 +5,7 @@ API Arm armv7 armv8 +ASan BAR CRC DCB diff --git a/doc/guides/prog_guide/asan.rst b/doc/guides/prog_guide/asan.rst new file mode 100644 index 0000000000..969676ebe8 --- /dev/null +++ b/doc/guides/prog_guide/asan.rst @@ -0,0 +1,40 @@ +.. SPDX-License-Identifier: BSD-3-Clause + Copyright(c) 2021 Intel Corporation + +Running Address Sanitizer +========================= + +`AddressSanitizer +<https://github.com/google/sanitizers/wiki/AddressSanitizer>`_ (ASan) +is a widely-used debugging tool to detect memory access errors. +It helps detect issues like use-after-free, various kinds of buffer +overruns in C/C++ programs, and other similar errors, as well as +printing out detailed debug information whenever an error is detected. + +AddressSanitizer is a part of LLVM (3.1+) and GCC (4.8+). + +Usage +----- + +meson build +^^^^^^^^^^^ + +To enable ASan in meson build system, use following meson build command: + +Example usage:: + +* gcc:: + + meson build -Dbuildtype=debug -Db_sanitize=address + ninja -C build + +* clang:: + + meson build -Dbuildtype=debug -Db_lundef=false -Db_sanitize=address + ninja -C build + +.. Note:: + + a) To compile with gcc in centos, libasan needs to be installed separately. + b) If the program being tested uses cmdline you will need to execute the + "stty echo" command when a error occurs. diff --git a/doc/guides/prog_guide/index.rst b/doc/guides/prog_guide/index.rst index 89af28dacb..b95c460b19 100644 --- a/doc/guides/prog_guide/index.rst +++ b/doc/guides/prog_guide/index.rst @@ -71,4 +71,5 @@ Programmer's Guide writing_efficient_code lto profile_app + asan glossary diff --git a/doc/guides/rel_notes/release_21_11.rst b/doc/guides/rel_notes/release_21_11.rst index d5435a64aa..63d9fef1b4 100644 --- a/doc/guides/rel_notes/release_21_11.rst +++ b/doc/guides/rel_notes/release_21_11.rst @@ -167,6 +167,15 @@ New Features * Added tests to verify tunnel header verification in IPsec inbound. * Added tests to verify inner checksum. +* **Enable ASan AddressSanitizer.** + + `AddressSanitizer + <https://github.com/google/sanitizers/wiki/AddressSanitizer>`_ (ASan) + is a widely-used debugging tool to detect memory access errors. + It helps detect issues like use-after-free, various kinds of buffer + overruns in C/C++ programs, and other similar errors, as well as + printing out detailed debug information whenever an error is detected. + Removed Items ------------- -- 2.25.1 ^ permalink raw reply [flat|nested] 117+ messages in thread
* [dpdk-dev] [PATCH v12 2/4] DPDK code adapts to ASan 2021-10-19 15:15 ` [dpdk-dev] [PATCH v12 1/4] Enable ASan AddressSanitizer zhihongx.peng @ 2021-10-19 15:15 ` zhihongx.peng 2021-10-19 15:15 ` [dpdk-dev] [PATCH v12 3/4] Code changes to avoid the ASan error zhihongx.peng 2021-10-19 15:15 ` [dpdk-dev] [PATCH v12 4/4] performance-thread: Fix cross compilation failed zhihongx.peng 2 siblings, 0 replies; 117+ messages in thread From: zhihongx.peng @ 2021-10-19 15:15 UTC (permalink / raw) To: david.marchand, thomas, anatoly.burakov, konstantin.ananyev, stephen, cristian.dumitrescu, john.mcnamara, bruce.richardson Cc: dev, xueqin.lin, Zhihong Peng From: Zhihong Peng <zhihongx.peng@intel.com> DPDK ASan functionality is currently only supported Linux x86_64. Support other platforms, need to define ASAN_SHADOW_OFFSET value according to google ASan document, and configure meson (config/meson.build). Here is an example of heap-buffer-overflow bug: ...... char *p = rte_zmalloc(NULL, 7, 0); p[7] = 'a'; ...... Here is an example of use-after-free bug: ...... char *p = rte_zmalloc(NULL, 7, 0); rte_free(p); *p = 'a'; ...... Signed-off-by: Xueqin Lin <xueqin.lin@intel.com> Signed-off-by: Zhihong Peng <zhihongx.peng@intel.com> --- v7: Split doc and code into two. v8: No change. v9: Modify the definition of RTE_MALLOC_ASAN. v10:Modify the definition of RTE_MALLOC_ASAN. v11:No change. v12:No change. --- config/meson.build | 4 + doc/guides/prog_guide/asan.rst | 68 +++++++++++- lib/eal/common/malloc_elem.c | 26 ++++- lib/eal/common/malloc_elem.h | 194 ++++++++++++++++++++++++++++++++- lib/eal/common/malloc_heap.c | 12 ++ lib/eal/common/rte_malloc.c | 9 +- 6 files changed, 306 insertions(+), 7 deletions(-) diff --git a/config/meson.build b/config/meson.build index f02b0e9c6d..bf751583bd 100644 --- a/config/meson.build +++ b/config/meson.build @@ -425,6 +425,10 @@ if get_option('b_sanitize') == 'address' or get_option('b_sanitize') == 'address add_project_link_arguments('-lasan', language: 'c') dpdk_extra_ldflags += '-lasan' endif + + if is_linux and arch_subdir == 'x86' + dpdk_conf.set10('RTE_MALLOC_ASAN', true) + endif endif if get_option('default_library') == 'both' diff --git a/doc/guides/prog_guide/asan.rst b/doc/guides/prog_guide/asan.rst index 969676ebe8..0cb2ca8751 100644 --- a/doc/guides/prog_guide/asan.rst +++ b/doc/guides/prog_guide/asan.rst @@ -13,6 +13,68 @@ printing out detailed debug information whenever an error is detected. AddressSanitizer is a part of LLVM (3.1+) and GCC (4.8+). +DPDK ASan functionality is currently only supported Linux x86_64. +Support other platforms, need to define ASAN_SHADOW_OFFSET value +according to google ASan document, and configure meson +(config/meson.build). + +Example heap-buffer-overflow error +---------------------------------- + +Following error was reported when ASan was enabled:: + + Applied 9 bytes of memory, but accessed the 10th byte of memory, + so heap-buffer-overflow appeared. + +Below code results in this error:: + + Add code to helloworld: + char *p = rte_zmalloc(NULL, 9, 0); + if (!p) { + printf("rte_zmalloc error."); + return -1; + } + p[9] = 'a'; + +The error log:: + + ==369953==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x7fb17f465809 at pc 0x5652e6707b84 bp 0x7ffea70eea20 sp 0x7ffea70eea10 WRITE of size 1 at 0x7fb17f465809 thread T0 + #0 0x5652e6707b83 in main ../examples/helloworld/main.c:47 + #1 0x7fb94953c0b2 in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x270b2) + #2 0x5652e67079bd in _start (/home/pzh/asan_test/x86_64-native-linuxapp-gcc/examples/dpdk-helloworld+0x8329bd) + + Address 0x7fb17f465809 is a wild pointer. + SUMMARY: AddressSanitizer: heap-buffer-overflow ../examples/helloworld/main.c:47 in main + +Example use-after-free error +---------------------------- + +Following error was reported when ASan was enabled:: + + Applied for 9 bytes of memory, and accessed the first byte after + released, so heap-use-after-free appeared. + +Below code results in this error:: + + Add code to helloworld: + char *p = rte_zmalloc(NULL, 9, 0); + if (!p) { + printf("rte_zmalloc error."); + return -1; + } + rte_free(p); + *p = 'a'; + +The error log:: + + ==417048==ERROR: AddressSanitizer: heap-use-after-free on address 0x7fc83f465800 at pc 0x564308a39b89 bp 0x7ffc8c85bf50 sp 0x7ffc8c85bf40 WRITE of size 1 at 0x7fc83f465800 thread T0 + #0 0x564308a39b88 in main ../examples/helloworld/main.c:48 + #1 0x7fd0079c60b2 in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x270b2) + #2 0x564308a399bd in _start (/home/pzh/asan_test/x86_64-native-linuxapp-gcc/examples/dpdk-helloworld+0x8329bd) + + Address 0x7fc83f465800 is a wild pointer. + SUMMARY: AddressSanitizer: heap-use-after-free ../examples/helloworld/main.c:48 in main + Usage ----- @@ -35,6 +97,8 @@ Example usage:: .. Note:: - a) To compile with gcc in centos, libasan needs to be installed separately. - b) If the program being tested uses cmdline you will need to execute the + a) Some of the features of ASan (for example, 'Display memory application location, currently + displayed as a wild pointer') are not currently supported by DPDK's implementation. + b) To compile with gcc in centos, libasan needs to be installed separately. + c) If the program being tested uses cmdline you will need to execute the "stty echo" command when a error occurs. diff --git a/lib/eal/common/malloc_elem.c b/lib/eal/common/malloc_elem.c index c2c9461f1d..bdd20a162e 100644 --- a/lib/eal/common/malloc_elem.c +++ b/lib/eal/common/malloc_elem.c @@ -446,6 +446,8 @@ malloc_elem_alloc(struct malloc_elem *elem, size_t size, unsigned align, struct malloc_elem *new_free_elem = RTE_PTR_ADD(new_elem, size + MALLOC_ELEM_OVERHEAD); + asan_clear_split_alloczone(new_free_elem); + split_elem(elem, new_free_elem); malloc_elem_free_list_insert(new_free_elem); @@ -458,6 +460,8 @@ malloc_elem_alloc(struct malloc_elem *elem, size_t size, unsigned align, elem->state = ELEM_BUSY; elem->pad = old_elem_size; + asan_clear_alloczone(elem); + /* put a dummy header in padding, to point to real element header */ if (elem->pad > 0) { /* pad will be at least 64-bytes, as everything * is cache-line aligned */ @@ -470,12 +474,18 @@ malloc_elem_alloc(struct malloc_elem *elem, size_t size, unsigned align, return new_elem; } + asan_clear_split_alloczone(new_elem); + /* we are going to split the element in two. The original element * remains free, and the new element is the one allocated. * Re-insert original element, in case its new size makes it * belong on a different list. */ + split_elem(elem, new_elem); + + asan_clear_alloczone(new_elem); + new_elem->state = ELEM_BUSY; malloc_elem_free_list_insert(elem); @@ -601,6 +611,8 @@ malloc_elem_hide_region(struct malloc_elem *elem, void *start, size_t len) if (next && next_elem_is_adjacent(elem)) { len_after = RTE_PTR_DIFF(next, hide_end); if (len_after >= MALLOC_ELEM_OVERHEAD + MIN_DATA_SIZE) { + asan_clear_split_alloczone(hide_end); + /* split after */ split_elem(elem, hide_end); @@ -615,6 +627,8 @@ malloc_elem_hide_region(struct malloc_elem *elem, void *start, size_t len) if (prev && prev_elem_is_adjacent(elem)) { len_before = RTE_PTR_DIFF(hide_start, elem); if (len_before >= MALLOC_ELEM_OVERHEAD + MIN_DATA_SIZE) { + asan_clear_split_alloczone(hide_start); + /* split before */ split_elem(elem, hide_start); @@ -628,6 +642,8 @@ malloc_elem_hide_region(struct malloc_elem *elem, void *start, size_t len) } } + asan_clear_alloczone(elem); + remove_elem(elem); } @@ -641,8 +657,10 @@ malloc_elem_resize(struct malloc_elem *elem, size_t size) const size_t new_size = size + elem->pad + MALLOC_ELEM_OVERHEAD; /* if we request a smaller size, then always return ok */ - if (elem->size >= new_size) + if (elem->size >= new_size) { + asan_clear_alloczone(elem); return 0; + } /* check if there is a next element, it's free and adjacent */ if (!elem->next || elem->next->state != ELEM_FREE || @@ -661,9 +679,15 @@ malloc_elem_resize(struct malloc_elem *elem, size_t size) /* now we have a big block together. Lets cut it down a bit, by splitting */ struct malloc_elem *split_pt = RTE_PTR_ADD(elem, new_size); split_pt = RTE_PTR_ALIGN_CEIL(split_pt, RTE_CACHE_LINE_SIZE); + + asan_clear_split_alloczone(split_pt); + split_elem(elem, split_pt); malloc_elem_free_list_insert(split_pt); } + + asan_clear_alloczone(elem); + return 0; } diff --git a/lib/eal/common/malloc_elem.h b/lib/eal/common/malloc_elem.h index a1e5f7f02c..a06c11ac8b 100644 --- a/lib/eal/common/malloc_elem.h +++ b/lib/eal/common/malloc_elem.h @@ -36,10 +36,20 @@ struct malloc_elem { uint64_t header_cookie; /* Cookie marking start of data */ /* trailer cookie at start + size */ #endif +#ifdef RTE_MALLOC_ASAN + size_t user_size; + uint64_t asan_cookie[2]; /* must be next to header_cookie */ +#endif } __rte_cache_aligned; +static const unsigned int MALLOC_ELEM_HEADER_LEN = sizeof(struct malloc_elem); + #ifndef RTE_MALLOC_DEBUG -static const unsigned MALLOC_ELEM_TRAILER_LEN = 0; +#ifdef RTE_MALLOC_ASAN +static const unsigned int MALLOC_ELEM_TRAILER_LEN = RTE_CACHE_LINE_SIZE; +#else +static const unsigned int MALLOC_ELEM_TRAILER_LEN; +#endif /* dummy function - just check if pointer is non-null */ static inline int @@ -55,7 +65,7 @@ set_trailer(struct malloc_elem *elem __rte_unused){ } #else -static const unsigned MALLOC_ELEM_TRAILER_LEN = RTE_CACHE_LINE_SIZE; +static const unsigned int MALLOC_ELEM_TRAILER_LEN = RTE_CACHE_LINE_SIZE; #define MALLOC_HEADER_COOKIE 0xbadbadbadadd2e55ULL /**< Header cookie. */ #define MALLOC_TRAILER_COOKIE 0xadd2e55badbadbadULL /**< Trailer cookie.*/ @@ -90,9 +100,187 @@ malloc_elem_cookies_ok(const struct malloc_elem *elem) #endif -static const unsigned MALLOC_ELEM_HEADER_LEN = sizeof(struct malloc_elem); #define MALLOC_ELEM_OVERHEAD (MALLOC_ELEM_HEADER_LEN + MALLOC_ELEM_TRAILER_LEN) +#ifdef RTE_MALLOC_ASAN + +#ifdef RTE_ARCH_X86_64 +#define ASAN_SHADOW_OFFSET 0x00007fff8000 +#endif + +#define ASAN_SHADOW_GRAIN_SIZE 8 +#define ASAN_MEM_FREE_FLAG 0xfd +#define ASAN_MEM_REDZONE_FLAG 0xfa +#define ASAN_SHADOW_SCALE 3 + +#define ASAN_MEM_SHIFT(mem) ((void *)((uintptr_t)(mem) >> ASAN_SHADOW_SCALE)) +#define ASAN_MEM_TO_SHADOW(mem) \ + RTE_PTR_ADD(ASAN_MEM_SHIFT(mem), ASAN_SHADOW_OFFSET) + +#if defined(__clang__) +__attribute__((no_sanitize("address", "hwaddress"))) +#else +__attribute__((no_sanitize_address)) +#endif +static inline void +asan_set_shadow(void *addr, char val) +{ + *(char *)addr = val; +} + +static inline void +asan_set_zone(void *ptr, size_t len, uint32_t val) +{ + size_t offset, i; + void *shadow; + size_t zone_len = len / ASAN_SHADOW_GRAIN_SIZE; + if (len % ASAN_SHADOW_GRAIN_SIZE != 0) + zone_len += 1; + + for (i = 0; i < zone_len; i++) { + offset = i * ASAN_SHADOW_GRAIN_SIZE; + shadow = ASAN_MEM_TO_SHADOW((uintptr_t)ptr + offset); + asan_set_shadow(shadow, val); + } +} + +/* + * When the memory is released, the release mark is + * set in the corresponding range of the shadow area. + */ +static inline void +asan_set_freezone(void *ptr, size_t size) +{ + asan_set_zone(ptr, size, ASAN_MEM_FREE_FLAG); +} + +/* + * When the memory is allocated, memory state must set as accessible. + */ +static inline void +asan_clear_alloczone(struct malloc_elem *elem) +{ + asan_set_zone((void *)elem, elem->size, 0x0); +} + +static inline void +asan_clear_split_alloczone(struct malloc_elem *elem) +{ + void *ptr = RTE_PTR_SUB(elem, MALLOC_ELEM_TRAILER_LEN); + asan_set_zone(ptr, MALLOC_ELEM_OVERHEAD, 0x0); +} + +/* + * When the memory is allocated, the memory boundary is + * marked in the corresponding range of the shadow area. + * Requirement: redzone >= 16, is a power of two. + */ +static inline void +asan_set_redzone(struct malloc_elem *elem, size_t user_size) +{ + uintptr_t head_redzone; + uintptr_t tail_redzone; + void *front_shadow; + void *tail_shadow; + uint32_t val; + + if (elem != NULL) { + if (elem->state != ELEM_PAD) + elem = RTE_PTR_ADD(elem, elem->pad); + + elem->user_size = user_size; + + /* Set mark before the start of the allocated memory */ + head_redzone = (uintptr_t)RTE_PTR_ADD(elem, + MALLOC_ELEM_HEADER_LEN - ASAN_SHADOW_GRAIN_SIZE); + front_shadow = ASAN_MEM_TO_SHADOW(head_redzone); + asan_set_shadow(front_shadow, ASAN_MEM_REDZONE_FLAG); + front_shadow = ASAN_MEM_TO_SHADOW(head_redzone + - ASAN_SHADOW_GRAIN_SIZE); + asan_set_shadow(front_shadow, ASAN_MEM_REDZONE_FLAG); + + /* Set mark after the end of the allocated memory */ + tail_redzone = (uintptr_t)RTE_PTR_ADD(elem, + MALLOC_ELEM_HEADER_LEN + + elem->user_size); + tail_shadow = ASAN_MEM_TO_SHADOW(tail_redzone); + val = (tail_redzone % ASAN_SHADOW_GRAIN_SIZE); + val = (val == 0) ? ASAN_MEM_REDZONE_FLAG : val; + asan_set_shadow(tail_shadow, val); + tail_shadow = ASAN_MEM_TO_SHADOW(tail_redzone + + ASAN_SHADOW_GRAIN_SIZE); + asan_set_shadow(tail_shadow, ASAN_MEM_REDZONE_FLAG); + } +} + +/* + * When the memory is released, the mark of the memory boundary + * in the corresponding range of the shadow area is cleared. + * Requirement: redzone >= 16, is a power of two. + */ +static inline void +asan_clear_redzone(struct malloc_elem *elem) +{ + uintptr_t head_redzone; + uintptr_t tail_redzone; + void *head_shadow; + void *tail_shadow; + + if (elem != NULL) { + elem = RTE_PTR_ADD(elem, elem->pad); + + /* Clear mark before the start of the allocated memory */ + head_redzone = (uintptr_t)RTE_PTR_ADD(elem, + MALLOC_ELEM_HEADER_LEN - ASAN_SHADOW_GRAIN_SIZE); + head_shadow = ASAN_MEM_TO_SHADOW(head_redzone); + asan_set_shadow(head_shadow, 0x00); + head_shadow = ASAN_MEM_TO_SHADOW(head_redzone + - ASAN_SHADOW_GRAIN_SIZE); + asan_set_shadow(head_shadow, 0x00); + + /* Clear mark after the end of the allocated memory */ + tail_redzone = (uintptr_t)RTE_PTR_ADD(elem, + MALLOC_ELEM_HEADER_LEN + elem->user_size); + tail_shadow = ASAN_MEM_TO_SHADOW(tail_redzone); + asan_set_shadow(tail_shadow, 0x00); + tail_shadow = ASAN_MEM_TO_SHADOW(tail_redzone + + ASAN_SHADOW_GRAIN_SIZE); + asan_set_shadow(tail_shadow, 0x00); + } +} + +static inline size_t +old_malloc_size(struct malloc_elem *elem) +{ + if (elem->state != ELEM_PAD) + elem = RTE_PTR_ADD(elem, elem->pad); + + return elem->user_size; +} +#else +static inline void +asan_set_freezone(void *ptr __rte_unused, size_t size __rte_unused) { } + +static inline void +asan_clear_alloczone(struct malloc_elem *elem __rte_unused) { } + +static inline void +asan_clear_split_alloczone(struct malloc_elem *elem __rte_unused) { } + +static inline void +asan_set_redzone(struct malloc_elem *elem __rte_unused, + size_t user_size __rte_unused) { } + +static inline void +asan_clear_redzone(struct malloc_elem *elem __rte_unused) { } + +static inline size_t +old_malloc_size(struct malloc_elem *elem) +{ + return elem->size - elem->pad - MALLOC_ELEM_OVERHEAD; +} +#endif + /* * Given a pointer to the start of a memory block returned by malloc, get * the actual malloc_elem header for that block. diff --git a/lib/eal/common/malloc_heap.c b/lib/eal/common/malloc_heap.c index ee400f38ec..775d6789df 100644 --- a/lib/eal/common/malloc_heap.c +++ b/lib/eal/common/malloc_heap.c @@ -237,6 +237,7 @@ heap_alloc(struct malloc_heap *heap, const char *type __rte_unused, size_t size, unsigned int flags, size_t align, size_t bound, bool contig) { struct malloc_elem *elem; + size_t user_size = size; size = RTE_CACHE_LINE_ROUNDUP(size); align = RTE_CACHE_LINE_ROUNDUP(align); @@ -250,6 +251,8 @@ heap_alloc(struct malloc_heap *heap, const char *type __rte_unused, size_t size, /* increase heap's count of allocated elements */ heap->alloc_count++; + + asan_set_redzone(elem, user_size); } return elem == NULL ? NULL : (void *)(&elem[1]); @@ -270,6 +273,8 @@ heap_alloc_biggest(struct malloc_heap *heap, const char *type __rte_unused, /* increase heap's count of allocated elements */ heap->alloc_count++; + + asan_set_redzone(elem, size); } return elem == NULL ? NULL : (void *)(&elem[1]); @@ -841,6 +846,8 @@ malloc_heap_free(struct malloc_elem *elem) if (!malloc_elem_cookies_ok(elem) || elem->state != ELEM_BUSY) return -1; + asan_clear_redzone(elem); + /* elem may be merged with previous element, so keep heap address */ heap = elem->heap; msl = elem->msl; @@ -848,6 +855,9 @@ malloc_heap_free(struct malloc_elem *elem) rte_spinlock_lock(&(heap->lock)); + void *asan_ptr = RTE_PTR_ADD(elem, MALLOC_ELEM_HEADER_LEN + elem->pad); + size_t asan_data_len = elem->size - MALLOC_ELEM_OVERHEAD - elem->pad; + /* mark element as free */ elem->state = ELEM_FREE; @@ -1001,6 +1011,8 @@ malloc_heap_free(struct malloc_elem *elem) rte_mcfg_mem_write_unlock(); free_unlock: + asan_set_freezone(asan_ptr, asan_data_len); + rte_spinlock_unlock(&(heap->lock)); return ret; } diff --git a/lib/eal/common/rte_malloc.c b/lib/eal/common/rte_malloc.c index 9d39e58c08..d0bec26920 100644 --- a/lib/eal/common/rte_malloc.c +++ b/lib/eal/common/rte_malloc.c @@ -162,6 +162,8 @@ rte_calloc(const char *type, size_t num, size_t size, unsigned align) void * rte_realloc_socket(void *ptr, size_t size, unsigned int align, int socket) { + size_t user_size; + if (ptr == NULL) return rte_malloc_socket(NULL, size, align, socket); @@ -171,6 +173,8 @@ rte_realloc_socket(void *ptr, size_t size, unsigned int align, int socket) return NULL; } + user_size = size; + size = RTE_CACHE_LINE_ROUNDUP(size), align = RTE_CACHE_LINE_ROUNDUP(align); /* check requested socket id and alignment matches first, and if ok, @@ -181,6 +185,9 @@ rte_realloc_socket(void *ptr, size_t size, unsigned int align, int socket) RTE_PTR_ALIGN(ptr, align) == ptr && malloc_heap_resize(elem, size) == 0) { rte_eal_trace_mem_realloc(size, align, socket, ptr); + + asan_set_redzone(elem, user_size); + return ptr; } @@ -192,7 +199,7 @@ rte_realloc_socket(void *ptr, size_t size, unsigned int align, int socket) if (new_ptr == NULL) return NULL; /* elem: |pad|data_elem|data|trailer| */ - const size_t old_size = elem->size - elem->pad - MALLOC_ELEM_OVERHEAD; + const size_t old_size = old_malloc_size(elem); rte_memcpy(new_ptr, ptr, old_size < size ? old_size : size); rte_free(ptr); -- 2.25.1 ^ permalink raw reply [flat|nested] 117+ messages in thread
* [dpdk-dev] [PATCH v12 3/4] Code changes to avoid the ASan error 2021-10-19 15:15 ` [dpdk-dev] [PATCH v12 1/4] Enable ASan AddressSanitizer zhihongx.peng 2021-10-19 15:15 ` [dpdk-dev] [PATCH v12 2/4] DPDK code adapts to ASan zhihongx.peng @ 2021-10-19 15:15 ` zhihongx.peng 2021-10-19 15:15 ` [dpdk-dev] [PATCH v12 4/4] performance-thread: Fix cross compilation failed zhihongx.peng 2 siblings, 0 replies; 117+ messages in thread From: zhihongx.peng @ 2021-10-19 15:15 UTC (permalink / raw) To: david.marchand, thomas, anatoly.burakov, konstantin.ananyev, stephen, cristian.dumitrescu, john.mcnamara, bruce.richardson Cc: dev, xueqin.lin, Zhihong Peng From: Zhihong Peng <zhihongx.peng@intel.com> Code changes to avoid the following ASan error: "Control reaches end of non-void function". Cc: cristian.dumitrescu@intel.com Signed-off-by: Xueqin Lin <xueqin.lin@intel.com> Signed-off-by: Zhihong Peng <zhihongx.peng@intel.com> --- v7: no change. v8: no change. v9: Modify the submit log. v10:no change. v11:no change. v12:Modify the commit log. --- lib/pipeline/rte_swx_pipeline.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/pipeline/rte_swx_pipeline.c b/lib/pipeline/rte_swx_pipeline.c index 1cd09a4b44..0acd6c6752 100644 --- a/lib/pipeline/rte_swx_pipeline.c +++ b/lib/pipeline/rte_swx_pipeline.c @@ -4642,7 +4642,7 @@ instr_meter_translate(struct rte_swx_pipeline *p, return 0; } - CHECK(0, EINVAL); + return -EINVAL; } static inline void @@ -5937,7 +5937,7 @@ instr_translate(struct rte_swx_pipeline *p, instr, data); - CHECK(0, EINVAL); + return -EINVAL; } static struct instruction_data * -- 2.25.1 ^ permalink raw reply [flat|nested] 117+ messages in thread
* [dpdk-dev] [PATCH v12 4/4] performance-thread: Fix cross compilation failed 2021-10-19 15:15 ` [dpdk-dev] [PATCH v12 1/4] Enable ASan AddressSanitizer zhihongx.peng 2021-10-19 15:15 ` [dpdk-dev] [PATCH v12 2/4] DPDK code adapts to ASan zhihongx.peng 2021-10-19 15:15 ` [dpdk-dev] [PATCH v12 3/4] Code changes to avoid the ASan error zhihongx.peng @ 2021-10-19 15:15 ` zhihongx.peng 2021-10-20 7:46 ` [dpdk-dev] [PATCH v13 1/4] enable ASan AddressSanitizer zhihongx.peng 2 siblings, 1 reply; 117+ messages in thread From: zhihongx.peng @ 2021-10-19 15:15 UTC (permalink / raw) To: david.marchand, thomas, anatoly.burakov, konstantin.ananyev, stephen, cristian.dumitrescu, john.mcnamara, bruce.richardson Cc: dev, xueqin.lin, Zhihong Peng, stable From: Zhihong Peng <zhihongx.peng@intel.com> The gcc(arm-linux-gcc) will check code more stricter when ASan enabled. "strncpy specified bound XX equals destination size" error occurs here. Fixes: 116819b9ed0d ("examples/performance-thread: add lthread subsystem") Cc: stable@dpdk.org Signed-off-by: Xueqin Lin <xueqin.lin@intel.com> Signed-off-by: Zhihong Peng <zhihongx.peng@intel.com> --- v11: Use rte_strlcpy to replace strncpy. v12: Delete rte_strlcpy's rte_. --- examples/performance-thread/common/lthread.c | 4 ++-- examples/performance-thread/common/lthread_cond.c | 6 +++--- examples/performance-thread/common/lthread_mutex.c | 6 +++--- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/examples/performance-thread/common/lthread.c b/examples/performance-thread/common/lthread.c index 3f1f48db43..190e5874b1 100644 --- a/examples/performance-thread/common/lthread.c +++ b/examples/performance-thread/common/lthread.c @@ -20,6 +20,7 @@ #include <sys/mman.h> #include <rte_log.h> +#include <rte_string_fns.h> #include <ctx.h> #include <stack.h> @@ -463,6 +464,5 @@ void lthread_set_funcname(const char *f) { struct lthread *lt = THIS_LTHREAD; - strncpy(lt->funcname, f, sizeof(lt->funcname)); - lt->funcname[sizeof(lt->funcname)-1] = 0; + strlcpy(lt->funcname, f, sizeof(lt->funcname)); } diff --git a/examples/performance-thread/common/lthread_cond.c b/examples/performance-thread/common/lthread_cond.c index cdcc7a7b5a..e7be17089a 100644 --- a/examples/performance-thread/common/lthread_cond.c +++ b/examples/performance-thread/common/lthread_cond.c @@ -20,6 +20,7 @@ #include <rte_log.h> #include <rte_common.h> +#include <rte_string_fns.h> #include "lthread_api.h" #include "lthread_diag_api.h" @@ -57,10 +58,9 @@ lthread_cond_init(char *name, struct lthread_cond **cond, } if (name == NULL) - strncpy(c->name, "no name", sizeof(c->name)); + strlcpy(c->name, "no name", sizeof(c->name)); else - strncpy(c->name, name, sizeof(c->name)); - c->name[sizeof(c->name)-1] = 0; + strlcpy(c->name, name, sizeof(c->name)); c->root_sched = THIS_SCHED; diff --git a/examples/performance-thread/common/lthread_mutex.c b/examples/performance-thread/common/lthread_mutex.c index 01da6cad4f..709ab9f553 100644 --- a/examples/performance-thread/common/lthread_mutex.c +++ b/examples/performance-thread/common/lthread_mutex.c @@ -19,6 +19,7 @@ #include <rte_log.h> #include <rte_spinlock.h> #include <rte_common.h> +#include <rte_string_fns.h> #include "lthread_api.h" #include "lthread_int.h" @@ -52,10 +53,9 @@ lthread_mutex_init(char *name, struct lthread_mutex **mutex, } if (name == NULL) - strncpy(m->name, "no name", sizeof(m->name)); + strlcpy(m->name, "no name", sizeof(m->name)); else - strncpy(m->name, name, sizeof(m->name)); - m->name[sizeof(m->name)-1] = 0; + strlcpy(m->name, name, sizeof(m->name)); m->root_sched = THIS_SCHED; m->owner = NULL; -- 2.25.1 ^ permalink raw reply [flat|nested] 117+ messages in thread
* [dpdk-dev] [PATCH v13 1/4] enable ASan AddressSanitizer 2021-10-19 15:15 ` [dpdk-dev] [PATCH v12 4/4] performance-thread: Fix cross compilation failed zhihongx.peng @ 2021-10-20 7:46 ` zhihongx.peng 2021-10-20 7:46 ` [dpdk-dev] [PATCH v13 2/4] DPDK code adapts to ASan zhihongx.peng ` (4 more replies) 0 siblings, 5 replies; 117+ messages in thread From: zhihongx.peng @ 2021-10-20 7:46 UTC (permalink / raw) To: david.marchand, thomas, anatoly.burakov, konstantin.ananyev, stephen, cristian.dumitrescu, john.mcnamara, bruce.richardson Cc: dev, xueqin.lin, Zhihong Peng From: Zhihong Peng <zhihongx.peng@intel.com> `AddressSanitizer <https://github.com/google/sanitizers/wiki/AddressSanitizer>`_ (ASan) is a widely-used debugging tool to detect memory access errors. It helps to detect issues like use-after-free, various kinds of buffer overruns in C/C++ programs, and other similar errors, as well as printing out detailed debug information whenever an error is detected. We can enable ASan by adding below compilation options: -Dbuildtype=debug -Db_lundef=false -Db_sanitize=address "-Dbuildtype=debug": This is a non-essential option. When this option is added, if a memory error occurs, ASan can clearly show where the code is wrong. "-Db_lundef=false": When use clang to compile DPDK, this option must be added. Signed-off-by: Xueqin Lin <xueqin.lin@intel.com> Signed-off-by: Zhihong Peng <zhihongx.peng@intel.com> Acked-by: John McNamara <john.mcnamara@intel.com> --- v7: 1) Split doc and code into two. 2) Modify asan.rst doc v8: No change. v9: 1) Add the check of libasan library. 2) Add release notes. v10:1) Split doc and code into two. 2) Meson supports asan. v11:Modify the document. v12:No change. v13:Modify the document. --- config/meson.build | 16 ++++++++++++++ devtools/words-case.txt | 1 + doc/guides/prog_guide/asan.rst | 30 ++++++++++++++++++++++++++ doc/guides/prog_guide/index.rst | 1 + doc/guides/rel_notes/release_21_11.rst | 9 ++++++++ 5 files changed, 57 insertions(+) create mode 100644 doc/guides/prog_guide/asan.rst diff --git a/config/meson.build b/config/meson.build index 4cdf589e20..f02b0e9c6d 100644 --- a/config/meson.build +++ b/config/meson.build @@ -411,6 +411,22 @@ if get_option('b_lto') endif endif +if get_option('b_sanitize') == 'address' or get_option('b_sanitize') == 'address,undefined' + if is_windows + error('ASan is not supported on windows') + endif + + if cc.get_id() == 'gcc' + asan_dep = cc.find_library('asan', required: true) + if (not cc.links('int main(int argc, char *argv[]) { return 0; }', + dependencies: asan_dep)) + error('broken dependency, "libasan"') + endif + add_project_link_arguments('-lasan', language: 'c') + dpdk_extra_ldflags += '-lasan' + endif +endif + if get_option('default_library') == 'both' error( ''' Unsupported value "both" for "default_library" option. diff --git a/devtools/words-case.txt b/devtools/words-case.txt index 0bbad48626..ada6910fa0 100644 --- a/devtools/words-case.txt +++ b/devtools/words-case.txt @@ -5,6 +5,7 @@ API Arm armv7 armv8 +ASan BAR CRC DCB diff --git a/doc/guides/prog_guide/asan.rst b/doc/guides/prog_guide/asan.rst new file mode 100644 index 0000000000..6888fc9a87 --- /dev/null +++ b/doc/guides/prog_guide/asan.rst @@ -0,0 +1,30 @@ +.. SPDX-License-Identifier: BSD-3-Clause + Copyright(c) 2021 Intel Corporation + +Running AddressSanitizer +======================== + +`AddressSanitizer +<https://github.com/google/sanitizers/wiki/AddressSanitizer>`_ (ASan) +is a widely-used debugging tool to detect memory access errors. +It helps to detect issues like use-after-free, various kinds of buffer +overruns in C/C++ programs, and other similar errors, as well as +printing out detailed debug information whenever an error is detected. + +AddressSanitizer is a part of LLVM (3.1+) and GCC (4.8+). + +Add following meson build commands to enable ASan in the meson build system: + +* gcc:: + + -Dbuildtype=debug -Db_sanitize=address + +* clang:: + + -Dbuildtype=debug -Db_lundef=false -Db_sanitize=address + +.. Note:: + + a) If compile with gcc in centos, libasan needs to be installed separately. + b) If the program is tested using cmdline, you may need to execute the + "stty echo" command when an error occurs. diff --git a/doc/guides/prog_guide/index.rst b/doc/guides/prog_guide/index.rst index 89af28dacb..b95c460b19 100644 --- a/doc/guides/prog_guide/index.rst +++ b/doc/guides/prog_guide/index.rst @@ -71,4 +71,5 @@ Programmer's Guide writing_efficient_code lto profile_app + asan glossary diff --git a/doc/guides/rel_notes/release_21_11.rst b/doc/guides/rel_notes/release_21_11.rst index 3362c52a73..10f4275b1b 100644 --- a/doc/guides/rel_notes/release_21_11.rst +++ b/doc/guides/rel_notes/release_21_11.rst @@ -173,6 +173,15 @@ New Features * Added tests to verify tunnel header verification in IPsec inbound. * Added tests to verify inner checksum. +* **Enable ASan AddressSanitizer.** + + `AddressSanitizer + <https://github.com/google/sanitizers/wiki/AddressSanitizer>`_ (ASan) + is a widely-used debugging tool to detect memory access errors. + It helps to detect issues like use-after-free, various kinds of buffer + overruns in C/C++ programs, and other similar errors, as well as + printing out detailed debug information whenever an error is detected. + Removed Items ------------- -- 2.25.1 ^ permalink raw reply [flat|nested] 117+ messages in thread
* [dpdk-dev] [PATCH v13 2/4] DPDK code adapts to ASan 2021-10-20 7:46 ` [dpdk-dev] [PATCH v13 1/4] enable ASan AddressSanitizer zhihongx.peng @ 2021-10-20 7:46 ` zhihongx.peng 2021-10-29 9:23 ` David Marchand 2021-10-20 7:46 ` [dpdk-dev] [PATCH v13 3/4] code changes to avoid the ASan error zhihongx.peng ` (3 subsequent siblings) 4 siblings, 1 reply; 117+ messages in thread From: zhihongx.peng @ 2021-10-20 7:46 UTC (permalink / raw) To: david.marchand, thomas, anatoly.burakov, konstantin.ananyev, stephen, cristian.dumitrescu, john.mcnamara, bruce.richardson Cc: dev, xueqin.lin, Zhihong Peng From: Zhihong Peng <zhihongx.peng@intel.com> DPDK ASan functionality is currently only supported on Linux x86_64. If want to support on other platforms, need to define ASAN_SHADOW_OFFSET value according to google ASan document, and configure meson file (config/meson.build). Signed-off-by: Xueqin Lin <xueqin.lin@intel.com> Signed-off-by: Zhihong Peng <zhihongx.peng@intel.com> Acked-by: Anatoly Burakov <anatoly.burakov@intel.com> --- v7: Split doc and code into two. v8: No change. v9: Modify the definition of RTE_MALLOC_ASAN. v10:Modify the definition of RTE_MALLOC_ASAN. v11:No change. v12:No change. v13:Modify the document. --- config/meson.build | 4 + doc/guides/prog_guide/asan.rst | 58 +++++++++- lib/eal/common/malloc_elem.c | 26 ++++- lib/eal/common/malloc_elem.h | 194 ++++++++++++++++++++++++++++++++- lib/eal/common/malloc_heap.c | 12 ++ lib/eal/common/rte_malloc.c | 9 +- 6 files changed, 296 insertions(+), 7 deletions(-) diff --git a/config/meson.build b/config/meson.build index f02b0e9c6d..bf751583bd 100644 --- a/config/meson.build +++ b/config/meson.build @@ -425,6 +425,10 @@ if get_option('b_sanitize') == 'address' or get_option('b_sanitize') == 'address add_project_link_arguments('-lasan', language: 'c') dpdk_extra_ldflags += '-lasan' endif + + if is_linux and arch_subdir == 'x86' + dpdk_conf.set10('RTE_MALLOC_ASAN', true) + endif endif if get_option('default_library') == 'both' diff --git a/doc/guides/prog_guide/asan.rst b/doc/guides/prog_guide/asan.rst index 6888fc9a87..02591ca68a 100644 --- a/doc/guides/prog_guide/asan.rst +++ b/doc/guides/prog_guide/asan.rst @@ -13,6 +13,58 @@ printing out detailed debug information whenever an error is detected. AddressSanitizer is a part of LLVM (3.1+) and GCC (4.8+). +DPDK ASan functionality is currently only supported on Linux x86_64. +If want to support on other platforms, need to define ASAN_SHADOW_OFFSET +value according to google ASan document, and configure meson file +(config/meson.build). + +Example heap-buffer-overflow error +---------------------------------- + +Add below unit test code in examples/helloworld/main.c:: + + Add code to helloworld: + char *p = rte_zmalloc(NULL, 9, 0); + if (!p) { + printf("rte_zmalloc error."); + return -1; + } + p[9] = 'a'; + +Above code will result in heap-buffer-overflow error if ASan is enabled, because apply 9 bytes of memory but access the tenth byte, detailed error log as below:: + + ==369953==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x7fb17f465809 at pc 0x5652e6707b84 bp 0x7ffea70eea20 sp 0x7ffea70eea10 WRITE of size 1 at 0x7fb17f465809 thread T0 + #0 0x5652e6707b83 in main ../examples/helloworld/main.c:47 + #1 0x7fb94953c0b2 in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x270b2) + #2 0x5652e67079bd in _start (/home/pzh/asan_test/x86_64-native-linuxapp-gcc/examples/dpdk-helloworld+0x8329bd) + + Address 0x7fb17f465809 is a wild pointer. + SUMMARY: AddressSanitizer: heap-buffer-overflow ../examples/helloworld/main.c:47 in main + +Example use-after-free error +---------------------------- + +Add below unit test code in examples/helloworld/main.c:: + + Add code to helloworld: + char *p = rte_zmalloc(NULL, 9, 0); + if (!p) { + printf("rte_zmalloc error."); + return -1; + } + rte_free(p); + *p = 'a'; + +Above code will result in use-after-free error if ASan is enabled, because apply 9 bytes of memory but access the first byte after release, detailed error log as below:: + + ==417048==ERROR: AddressSanitizer: heap-use-after-free on address 0x7fc83f465800 at pc 0x564308a39b89 bp 0x7ffc8c85bf50 sp 0x7ffc8c85bf40 WRITE of size 1 at 0x7fc83f465800 thread T0 + #0 0x564308a39b88 in main ../examples/helloworld/main.c:48 + #1 0x7fd0079c60b2 in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x270b2) + #2 0x564308a399bd in _start (/home/pzh/asan_test/x86_64-native-linuxapp-gcc/examples/dpdk-helloworld+0x8329bd) + + Address 0x7fc83f465800 is a wild pointer. + SUMMARY: AddressSanitizer: heap-use-after-free ../examples/helloworld/main.c:48 in main + Add following meson build commands to enable ASan in the meson build system: * gcc:: @@ -25,6 +77,8 @@ Add following meson build commands to enable ASan in the meson build system: .. Note:: - a) If compile with gcc in centos, libasan needs to be installed separately. - b) If the program is tested using cmdline, you may need to execute the + a) Some of the features of ASan (for example, 'Display memory application location, currently + displayed as a wild pointer') are not currently supported by DPDK's implementation. + b) If compile with gcc in centos, libasan needs to be installed separately. + c) If the program is tested using cmdline, you may need to execute the "stty echo" command when an error occurs. diff --git a/lib/eal/common/malloc_elem.c b/lib/eal/common/malloc_elem.c index c2c9461f1d..bdd20a162e 100644 --- a/lib/eal/common/malloc_elem.c +++ b/lib/eal/common/malloc_elem.c @@ -446,6 +446,8 @@ malloc_elem_alloc(struct malloc_elem *elem, size_t size, unsigned align, struct malloc_elem *new_free_elem = RTE_PTR_ADD(new_elem, size + MALLOC_ELEM_OVERHEAD); + asan_clear_split_alloczone(new_free_elem); + split_elem(elem, new_free_elem); malloc_elem_free_list_insert(new_free_elem); @@ -458,6 +460,8 @@ malloc_elem_alloc(struct malloc_elem *elem, size_t size, unsigned align, elem->state = ELEM_BUSY; elem->pad = old_elem_size; + asan_clear_alloczone(elem); + /* put a dummy header in padding, to point to real element header */ if (elem->pad > 0) { /* pad will be at least 64-bytes, as everything * is cache-line aligned */ @@ -470,12 +474,18 @@ malloc_elem_alloc(struct malloc_elem *elem, size_t size, unsigned align, return new_elem; } + asan_clear_split_alloczone(new_elem); + /* we are going to split the element in two. The original element * remains free, and the new element is the one allocated. * Re-insert original element, in case its new size makes it * belong on a different list. */ + split_elem(elem, new_elem); + + asan_clear_alloczone(new_elem); + new_elem->state = ELEM_BUSY; malloc_elem_free_list_insert(elem); @@ -601,6 +611,8 @@ malloc_elem_hide_region(struct malloc_elem *elem, void *start, size_t len) if (next && next_elem_is_adjacent(elem)) { len_after = RTE_PTR_DIFF(next, hide_end); if (len_after >= MALLOC_ELEM_OVERHEAD + MIN_DATA_SIZE) { + asan_clear_split_alloczone(hide_end); + /* split after */ split_elem(elem, hide_end); @@ -615,6 +627,8 @@ malloc_elem_hide_region(struct malloc_elem *elem, void *start, size_t len) if (prev && prev_elem_is_adjacent(elem)) { len_before = RTE_PTR_DIFF(hide_start, elem); if (len_before >= MALLOC_ELEM_OVERHEAD + MIN_DATA_SIZE) { + asan_clear_split_alloczone(hide_start); + /* split before */ split_elem(elem, hide_start); @@ -628,6 +642,8 @@ malloc_elem_hide_region(struct malloc_elem *elem, void *start, size_t len) } } + asan_clear_alloczone(elem); + remove_elem(elem); } @@ -641,8 +657,10 @@ malloc_elem_resize(struct malloc_elem *elem, size_t size) const size_t new_size = size + elem->pad + MALLOC_ELEM_OVERHEAD; /* if we request a smaller size, then always return ok */ - if (elem->size >= new_size) + if (elem->size >= new_size) { + asan_clear_alloczone(elem); return 0; + } /* check if there is a next element, it's free and adjacent */ if (!elem->next || elem->next->state != ELEM_FREE || @@ -661,9 +679,15 @@ malloc_elem_resize(struct malloc_elem *elem, size_t size) /* now we have a big block together. Lets cut it down a bit, by splitting */ struct malloc_elem *split_pt = RTE_PTR_ADD(elem, new_size); split_pt = RTE_PTR_ALIGN_CEIL(split_pt, RTE_CACHE_LINE_SIZE); + + asan_clear_split_alloczone(split_pt); + split_elem(elem, split_pt); malloc_elem_free_list_insert(split_pt); } + + asan_clear_alloczone(elem); + return 0; } diff --git a/lib/eal/common/malloc_elem.h b/lib/eal/common/malloc_elem.h index a1e5f7f02c..a06c11ac8b 100644 --- a/lib/eal/common/malloc_elem.h +++ b/lib/eal/common/malloc_elem.h @@ -36,10 +36,20 @@ struct malloc_elem { uint64_t header_cookie; /* Cookie marking start of data */ /* trailer cookie at start + size */ #endif +#ifdef RTE_MALLOC_ASAN + size_t user_size; + uint64_t asan_cookie[2]; /* must be next to header_cookie */ +#endif } __rte_cache_aligned; +static const unsigned int MALLOC_ELEM_HEADER_LEN = sizeof(struct malloc_elem); + #ifndef RTE_MALLOC_DEBUG -static const unsigned MALLOC_ELEM_TRAILER_LEN = 0; +#ifdef RTE_MALLOC_ASAN +static const unsigned int MALLOC_ELEM_TRAILER_LEN = RTE_CACHE_LINE_SIZE; +#else +static const unsigned int MALLOC_ELEM_TRAILER_LEN; +#endif /* dummy function - just check if pointer is non-null */ static inline int @@ -55,7 +65,7 @@ set_trailer(struct malloc_elem *elem __rte_unused){ } #else -static const unsigned MALLOC_ELEM_TRAILER_LEN = RTE_CACHE_LINE_SIZE; +static const unsigned int MALLOC_ELEM_TRAILER_LEN = RTE_CACHE_LINE_SIZE; #define MALLOC_HEADER_COOKIE 0xbadbadbadadd2e55ULL /**< Header cookie. */ #define MALLOC_TRAILER_COOKIE 0xadd2e55badbadbadULL /**< Trailer cookie.*/ @@ -90,9 +100,187 @@ malloc_elem_cookies_ok(const struct malloc_elem *elem) #endif -static const unsigned MALLOC_ELEM_HEADER_LEN = sizeof(struct malloc_elem); #define MALLOC_ELEM_OVERHEAD (MALLOC_ELEM_HEADER_LEN + MALLOC_ELEM_TRAILER_LEN) +#ifdef RTE_MALLOC_ASAN + +#ifdef RTE_ARCH_X86_64 +#define ASAN_SHADOW_OFFSET 0x00007fff8000 +#endif + +#define ASAN_SHADOW_GRAIN_SIZE 8 +#define ASAN_MEM_FREE_FLAG 0xfd +#define ASAN_MEM_REDZONE_FLAG 0xfa +#define ASAN_SHADOW_SCALE 3 + +#define ASAN_MEM_SHIFT(mem) ((void *)((uintptr_t)(mem) >> ASAN_SHADOW_SCALE)) +#define ASAN_MEM_TO_SHADOW(mem) \ + RTE_PTR_ADD(ASAN_MEM_SHIFT(mem), ASAN_SHADOW_OFFSET) + +#if defined(__clang__) +__attribute__((no_sanitize("address", "hwaddress"))) +#else +__attribute__((no_sanitize_address)) +#endif +static inline void +asan_set_shadow(void *addr, char val) +{ + *(char *)addr = val; +} + +static inline void +asan_set_zone(void *ptr, size_t len, uint32_t val) +{ + size_t offset, i; + void *shadow; + size_t zone_len = len / ASAN_SHADOW_GRAIN_SIZE; + if (len % ASAN_SHADOW_GRAIN_SIZE != 0) + zone_len += 1; + + for (i = 0; i < zone_len; i++) { + offset = i * ASAN_SHADOW_GRAIN_SIZE; + shadow = ASAN_MEM_TO_SHADOW((uintptr_t)ptr + offset); + asan_set_shadow(shadow, val); + } +} + +/* + * When the memory is released, the release mark is + * set in the corresponding range of the shadow area. + */ +static inline void +asan_set_freezone(void *ptr, size_t size) +{ + asan_set_zone(ptr, size, ASAN_MEM_FREE_FLAG); +} + +/* + * When the memory is allocated, memory state must set as accessible. + */ +static inline void +asan_clear_alloczone(struct malloc_elem *elem) +{ + asan_set_zone((void *)elem, elem->size, 0x0); +} + +static inline void +asan_clear_split_alloczone(struct malloc_elem *elem) +{ + void *ptr = RTE_PTR_SUB(elem, MALLOC_ELEM_TRAILER_LEN); + asan_set_zone(ptr, MALLOC_ELEM_OVERHEAD, 0x0); +} + +/* + * When the memory is allocated, the memory boundary is + * marked in the corresponding range of the shadow area. + * Requirement: redzone >= 16, is a power of two. + */ +static inline void +asan_set_redzone(struct malloc_elem *elem, size_t user_size) +{ + uintptr_t head_redzone; + uintptr_t tail_redzone; + void *front_shadow; + void *tail_shadow; + uint32_t val; + + if (elem != NULL) { + if (elem->state != ELEM_PAD) + elem = RTE_PTR_ADD(elem, elem->pad); + + elem->user_size = user_size; + + /* Set mark before the start of the allocated memory */ + head_redzone = (uintptr_t)RTE_PTR_ADD(elem, + MALLOC_ELEM_HEADER_LEN - ASAN_SHADOW_GRAIN_SIZE); + front_shadow = ASAN_MEM_TO_SHADOW(head_redzone); + asan_set_shadow(front_shadow, ASAN_MEM_REDZONE_FLAG); + front_shadow = ASAN_MEM_TO_SHADOW(head_redzone + - ASAN_SHADOW_GRAIN_SIZE); + asan_set_shadow(front_shadow, ASAN_MEM_REDZONE_FLAG); + + /* Set mark after the end of the allocated memory */ + tail_redzone = (uintptr_t)RTE_PTR_ADD(elem, + MALLOC_ELEM_HEADER_LEN + + elem->user_size); + tail_shadow = ASAN_MEM_TO_SHADOW(tail_redzone); + val = (tail_redzone % ASAN_SHADOW_GRAIN_SIZE); + val = (val == 0) ? ASAN_MEM_REDZONE_FLAG : val; + asan_set_shadow(tail_shadow, val); + tail_shadow = ASAN_MEM_TO_SHADOW(tail_redzone + + ASAN_SHADOW_GRAIN_SIZE); + asan_set_shadow(tail_shadow, ASAN_MEM_REDZONE_FLAG); + } +} + +/* + * When the memory is released, the mark of the memory boundary + * in the corresponding range of the shadow area is cleared. + * Requirement: redzone >= 16, is a power of two. + */ +static inline void +asan_clear_redzone(struct malloc_elem *elem) +{ + uintptr_t head_redzone; + uintptr_t tail_redzone; + void *head_shadow; + void *tail_shadow; + + if (elem != NULL) { + elem = RTE_PTR_ADD(elem, elem->pad); + + /* Clear mark before the start of the allocated memory */ + head_redzone = (uintptr_t)RTE_PTR_ADD(elem, + MALLOC_ELEM_HEADER_LEN - ASAN_SHADOW_GRAIN_SIZE); + head_shadow = ASAN_MEM_TO_SHADOW(head_redzone); + asan_set_shadow(head_shadow, 0x00); + head_shadow = ASAN_MEM_TO_SHADOW(head_redzone + - ASAN_SHADOW_GRAIN_SIZE); + asan_set_shadow(head_shadow, 0x00); + + /* Clear mark after the end of the allocated memory */ + tail_redzone = (uintptr_t)RTE_PTR_ADD(elem, + MALLOC_ELEM_HEADER_LEN + elem->user_size); + tail_shadow = ASAN_MEM_TO_SHADOW(tail_redzone); + asan_set_shadow(tail_shadow, 0x00); + tail_shadow = ASAN_MEM_TO_SHADOW(tail_redzone + + ASAN_SHADOW_GRAIN_SIZE); + asan_set_shadow(tail_shadow, 0x00); + } +} + +static inline size_t +old_malloc_size(struct malloc_elem *elem) +{ + if (elem->state != ELEM_PAD) + elem = RTE_PTR_ADD(elem, elem->pad); + + return elem->user_size; +} +#else +static inline void +asan_set_freezone(void *ptr __rte_unused, size_t size __rte_unused) { } + +static inline void +asan_clear_alloczone(struct malloc_elem *elem __rte_unused) { } + +static inline void +asan_clear_split_alloczone(struct malloc_elem *elem __rte_unused) { } + +static inline void +asan_set_redzone(struct malloc_elem *elem __rte_unused, + size_t user_size __rte_unused) { } + +static inline void +asan_clear_redzone(struct malloc_elem *elem __rte_unused) { } + +static inline size_t +old_malloc_size(struct malloc_elem *elem) +{ + return elem->size - elem->pad - MALLOC_ELEM_OVERHEAD; +} +#endif + /* * Given a pointer to the start of a memory block returned by malloc, get * the actual malloc_elem header for that block. diff --git a/lib/eal/common/malloc_heap.c b/lib/eal/common/malloc_heap.c index ee400f38ec..775d6789df 100644 --- a/lib/eal/common/malloc_heap.c +++ b/lib/eal/common/malloc_heap.c @@ -237,6 +237,7 @@ heap_alloc(struct malloc_heap *heap, const char *type __rte_unused, size_t size, unsigned int flags, size_t align, size_t bound, bool contig) { struct malloc_elem *elem; + size_t user_size = size; size = RTE_CACHE_LINE_ROUNDUP(size); align = RTE_CACHE_LINE_ROUNDUP(align); @@ -250,6 +251,8 @@ heap_alloc(struct malloc_heap *heap, const char *type __rte_unused, size_t size, /* increase heap's count of allocated elements */ heap->alloc_count++; + + asan_set_redzone(elem, user_size); } return elem == NULL ? NULL : (void *)(&elem[1]); @@ -270,6 +273,8 @@ heap_alloc_biggest(struct malloc_heap *heap, const char *type __rte_unused, /* increase heap's count of allocated elements */ heap->alloc_count++; + + asan_set_redzone(elem, size); } return elem == NULL ? NULL : (void *)(&elem[1]); @@ -841,6 +846,8 @@ malloc_heap_free(struct malloc_elem *elem) if (!malloc_elem_cookies_ok(elem) || elem->state != ELEM_BUSY) return -1; + asan_clear_redzone(elem); + /* elem may be merged with previous element, so keep heap address */ heap = elem->heap; msl = elem->msl; @@ -848,6 +855,9 @@ malloc_heap_free(struct malloc_elem *elem) rte_spinlock_lock(&(heap->lock)); + void *asan_ptr = RTE_PTR_ADD(elem, MALLOC_ELEM_HEADER_LEN + elem->pad); + size_t asan_data_len = elem->size - MALLOC_ELEM_OVERHEAD - elem->pad; + /* mark element as free */ elem->state = ELEM_FREE; @@ -1001,6 +1011,8 @@ malloc_heap_free(struct malloc_elem *elem) rte_mcfg_mem_write_unlock(); free_unlock: + asan_set_freezone(asan_ptr, asan_data_len); + rte_spinlock_unlock(&(heap->lock)); return ret; } diff --git a/lib/eal/common/rte_malloc.c b/lib/eal/common/rte_malloc.c index 9d39e58c08..d0bec26920 100644 --- a/lib/eal/common/rte_malloc.c +++ b/lib/eal/common/rte_malloc.c @@ -162,6 +162,8 @@ rte_calloc(const char *type, size_t num, size_t size, unsigned align) void * rte_realloc_socket(void *ptr, size_t size, unsigned int align, int socket) { + size_t user_size; + if (ptr == NULL) return rte_malloc_socket(NULL, size, align, socket); @@ -171,6 +173,8 @@ rte_realloc_socket(void *ptr, size_t size, unsigned int align, int socket) return NULL; } + user_size = size; + size = RTE_CACHE_LINE_ROUNDUP(size), align = RTE_CACHE_LINE_ROUNDUP(align); /* check requested socket id and alignment matches first, and if ok, @@ -181,6 +185,9 @@ rte_realloc_socket(void *ptr, size_t size, unsigned int align, int socket) RTE_PTR_ALIGN(ptr, align) == ptr && malloc_heap_resize(elem, size) == 0) { rte_eal_trace_mem_realloc(size, align, socket, ptr); + + asan_set_redzone(elem, user_size); + return ptr; } @@ -192,7 +199,7 @@ rte_realloc_socket(void *ptr, size_t size, unsigned int align, int socket) if (new_ptr == NULL) return NULL; /* elem: |pad|data_elem|data|trailer| */ - const size_t old_size = elem->size - elem->pad - MALLOC_ELEM_OVERHEAD; + const size_t old_size = old_malloc_size(elem); rte_memcpy(new_ptr, ptr, old_size < size ? old_size : size); rte_free(ptr); -- 2.25.1 ^ permalink raw reply [flat|nested] 117+ messages in thread
* Re: [dpdk-dev] [PATCH v13 2/4] DPDK code adapts to ASan 2021-10-20 7:46 ` [dpdk-dev] [PATCH v13 2/4] DPDK code adapts to ASan zhihongx.peng @ 2021-10-29 9:23 ` David Marchand 2021-10-29 12:54 ` Peng, ZhihongX 0 siblings, 1 reply; 117+ messages in thread From: David Marchand @ 2021-10-29 9:23 UTC (permalink / raw) To: Zhihong Peng Cc: Thomas Monjalon, Burakov, Anatoly, Ananyev, Konstantin, Stephen Hemminger, Cristian Dumitrescu, Mcnamara, John, Bruce Richardson, dev, Xueqin Lin On Wed, Oct 20, 2021 at 9:47 AM <zhihongx.peng@intel.com> wrote: > > From: Zhihong Peng <zhihongx.peng@intel.com> > > DPDK ASan functionality is currently only supported on Linux x86_64. > If want to support on other platforms, need to define ASAN_SHADOW_OFFSET > value according to google ASan document, and configure meson file > (config/meson.build). > > Signed-off-by: Xueqin Lin <xueqin.lin@intel.com> > Signed-off-by: Zhihong Peng <zhihongx.peng@intel.com> > Acked-by: Anatoly Burakov <anatoly.burakov@intel.com> Suggests following title/commitlog: """ mem: instrument allocator for ASan This patch adds necessary hooks in the memory allocator for ASan. This feature is currently available in DPDK only on Linux x86_64. If other OS/architectures want to support it, ASAN_SHADOW_OFFSET must be defined and RTE_MALLOC_ASAN must be set accordingly in meson. """ > --- > v7: Split doc and code into two. > v8: No change. > v9: Modify the definition of RTE_MALLOC_ASAN. > v10:Modify the definition of RTE_MALLOC_ASAN. > v11:No change. > v12:No change. > v13:Modify the document. > --- > config/meson.build | 4 + > doc/guides/prog_guide/asan.rst | 58 +++++++++- > lib/eal/common/malloc_elem.c | 26 ++++- > lib/eal/common/malloc_elem.h | 194 ++++++++++++++++++++++++++++++++- > lib/eal/common/malloc_heap.c | 12 ++ > lib/eal/common/rte_malloc.c | 9 +- > 6 files changed, 296 insertions(+), 7 deletions(-) > > diff --git a/config/meson.build b/config/meson.build > index f02b0e9c6d..bf751583bd 100644 > --- a/config/meson.build > +++ b/config/meson.build > @@ -425,6 +425,10 @@ if get_option('b_sanitize') == 'address' or get_option('b_sanitize') == 'address > add_project_link_arguments('-lasan', language: 'c') > dpdk_extra_ldflags += '-lasan' > endif > + > + if is_linux and arch_subdir == 'x86' Missing a check on "and dpdk_conf.get('RTE_ARCH_64')" for i386 build. > + dpdk_conf.set10('RTE_MALLOC_ASAN', true) > + endif > endif > > if get_option('default_library') == 'both' > diff --git a/doc/guides/prog_guide/asan.rst b/doc/guides/prog_guide/asan.rst > index 6888fc9a87..02591ca68a 100644 > --- a/doc/guides/prog_guide/asan.rst > +++ b/doc/guides/prog_guide/asan.rst > @@ -13,6 +13,58 @@ printing out detailed debug information whenever an error is detected. > > AddressSanitizer is a part of LLVM (3.1+) and GCC (4.8+). I'll move the examples after the explanations on how to enable ASan. > > +DPDK ASan functionality is currently only supported on Linux x86_64. > +If want to support on other platforms, need to define ASAN_SHADOW_OFFSET > +value according to google ASan document, and configure meson file > +(config/meson.build). DPDK ASan functionnality is vague. Suggests rewording: """ ASan is aware of DPDK memory allocations, thanks to added instrumentation. This is only enabled on x86_64 at the moment. Other architectures may have to define ASAN_SHADOW_OFFSET. """ > + > +Example heap-buffer-overflow error > +---------------------------------- > + > +Add below unit test code in examples/helloworld/main.c:: > + > + Add code to helloworld: > + char *p = rte_zmalloc(NULL, 9, 0); > + if (!p) { > + printf("rte_zmalloc error."); > + return -1; > + } > + p[9] = 'a'; > + > +Above code will result in heap-buffer-overflow error if ASan is enabled, because apply 9 bytes of memory but access the tenth byte, detailed error log as below:: > + > + ==369953==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x7fb17f465809 at pc 0x5652e6707b84 bp 0x7ffea70eea20 sp 0x7ffea70eea10 WRITE of size 1 at 0x7fb17f465809 thread T0 > + #0 0x5652e6707b83 in main ../examples/helloworld/main.c:47 > + #1 0x7fb94953c0b2 in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x270b2) > + #2 0x5652e67079bd in _start (/home/pzh/asan_test/x86_64-native-linuxapp-gcc/examples/dpdk-helloworld+0x8329bd) > + > + Address 0x7fb17f465809 is a wild pointer. > + SUMMARY: AddressSanitizer: heap-buffer-overflow ../examples/helloworld/main.c:47 in main I'll move the note update here: """ Note:: - Some of the features of ASan (for example, 'Display memory application location, currently displayed as a wild pointer') are not currently supported with DPDK allocations. """ > + > +Example use-after-free error > +---------------------------- > + > +Add below unit test code in examples/helloworld/main.c:: > + > + Add code to helloworld: > + char *p = rte_zmalloc(NULL, 9, 0); > + if (!p) { > + printf("rte_zmalloc error."); > + return -1; > + } > + rte_free(p); > + *p = 'a'; > + > +Above code will result in use-after-free error if ASan is enabled, because apply 9 bytes of memory but access the first byte after release, detailed error log as below:: > + > + ==417048==ERROR: AddressSanitizer: heap-use-after-free on address 0x7fc83f465800 at pc 0x564308a39b89 bp 0x7ffc8c85bf50 sp 0x7ffc8c85bf40 WRITE of size 1 at 0x7fc83f465800 thread T0 > + #0 0x564308a39b88 in main ../examples/helloworld/main.c:48 > + #1 0x7fd0079c60b2 in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x270b2) > + #2 0x564308a399bd in _start (/home/pzh/asan_test/x86_64-native-linuxapp-gcc/examples/dpdk-helloworld+0x8329bd) > + > + Address 0x7fc83f465800 is a wild pointer. > + SUMMARY: AddressSanitizer: heap-use-after-free ../examples/helloworld/main.c:48 in main > + > Add following meson build commands to enable ASan in the meson build system: > > * gcc:: > @@ -25,6 +77,8 @@ Add following meson build commands to enable ASan in the meson build system: > > .. Note:: > > - a) If compile with gcc in centos, libasan needs to be installed separately. > - b) If the program is tested using cmdline, you may need to execute the > + a) Some of the features of ASan (for example, 'Display memory application location, currently > + displayed as a wild pointer') are not currently supported by DPDK's implementation. > + b) If compile with gcc in centos, libasan needs to be installed separately. > + c) If the program is tested using cmdline, you may need to execute the > "stty echo" command when an error occurs. -- David Marchand ^ permalink raw reply [flat|nested] 117+ messages in thread
* Re: [dpdk-dev] [PATCH v13 2/4] DPDK code adapts to ASan 2021-10-29 9:23 ` David Marchand @ 2021-10-29 12:54 ` Peng, ZhihongX 0 siblings, 0 replies; 117+ messages in thread From: Peng, ZhihongX @ 2021-10-29 12:54 UTC (permalink / raw) To: David Marchand Cc: Thomas Monjalon, Burakov, Anatoly, Ananyev, Konstantin, Stephen Hemminger, Dumitrescu, Cristian, Mcnamara, John, Richardson, Bruce, dev, Lin, Xueqin > -----Original Message----- > From: David Marchand <david.marchand@redhat.com> > Sent: Friday, October 29, 2021 5:23 PM > To: Peng, ZhihongX <zhihongx.peng@intel.com> > Cc: Thomas Monjalon <thomas@monjalon.net>; Burakov, Anatoly > <anatoly.burakov@intel.com>; Ananyev, Konstantin > <konstantin.ananyev@intel.com>; Stephen Hemminger > <stephen@networkplumber.org>; Dumitrescu, Cristian > <cristian.dumitrescu@intel.com>; Mcnamara, John > <john.mcnamara@intel.com>; Richardson, Bruce > <bruce.richardson@intel.com>; dev <dev@dpdk.org>; Lin, Xueqin > <xueqin.lin@intel.com> > Subject: Re: [PATCH v13 2/4] DPDK code adapts to ASan > > On Wed, Oct 20, 2021 at 9:47 AM <zhihongx.peng@intel.com> wrote: > > > > From: Zhihong Peng <zhihongx.peng@intel.com> > > > > DPDK ASan functionality is currently only supported on Linux x86_64. > > If want to support on other platforms, need to define > > ASAN_SHADOW_OFFSET value according to google ASan document, and > > configure meson file (config/meson.build). > > > > Signed-off-by: Xueqin Lin <xueqin.lin@intel.com> > > Signed-off-by: Zhihong Peng <zhihongx.peng@intel.com> > > Acked-by: Anatoly Burakov <anatoly.burakov@intel.com> > > Suggests following title/commitlog: > """ > mem: instrument allocator for ASan > > This patch adds necessary hooks in the memory allocator for ASan. > > This feature is currently available in DPDK only on Linux x86_64. > If other OS/architectures want to support it, ASAN_SHADOW_OFFSET must > be defined and RTE_MALLOC_ASAN must be set accordingly in meson. > """ > > > --- > > v7: Split doc and code into two. > > v8: No change. > > v9: Modify the definition of RTE_MALLOC_ASAN. > > v10:Modify the definition of RTE_MALLOC_ASAN. > > v11:No change. > > v12:No change. > > v13:Modify the document. > > --- > > config/meson.build | 4 + > > doc/guides/prog_guide/asan.rst | 58 +++++++++- > > lib/eal/common/malloc_elem.c | 26 ++++- > > lib/eal/common/malloc_elem.h | 194 > ++++++++++++++++++++++++++++++++- > > lib/eal/common/malloc_heap.c | 12 ++ > > lib/eal/common/rte_malloc.c | 9 +- > > 6 files changed, 296 insertions(+), 7 deletions(-) > > > > diff --git a/config/meson.build b/config/meson.build index > > f02b0e9c6d..bf751583bd 100644 > > --- a/config/meson.build > > +++ b/config/meson.build > > @@ -425,6 +425,10 @@ if get_option('b_sanitize') == 'address' or > get_option('b_sanitize') == 'address > > add_project_link_arguments('-lasan', language: 'c') > > dpdk_extra_ldflags += '-lasan' > > endif > > + > > + if is_linux and arch_subdir == 'x86' > > Missing a check on "and dpdk_conf.get('RTE_ARCH_64')" for i386 build. > > > > + dpdk_conf.set10('RTE_MALLOC_ASAN', true) > > + endif > > endif > > > > if get_option('default_library') == 'both' > > diff --git a/doc/guides/prog_guide/asan.rst > > b/doc/guides/prog_guide/asan.rst index 6888fc9a87..02591ca68a 100644 > > --- a/doc/guides/prog_guide/asan.rst > > +++ b/doc/guides/prog_guide/asan.rst > > @@ -13,6 +13,58 @@ printing out detailed debug information whenever > an error is detected. > > > > AddressSanitizer is a part of LLVM (3.1+) and GCC (4.8+). > > I'll move the examples after the explanations on how to enable ASan. > > > > > > +DPDK ASan functionality is currently only supported on Linux x86_64. > > +If want to support on other platforms, need to define > > +ASAN_SHADOW_OFFSET value according to google ASan document, and > > +configure meson file (config/meson.build). > > DPDK ASan functionnality is vague. > > Suggests rewording: > """ > ASan is aware of DPDK memory allocations, thanks to added instrumentation. > This is only enabled on x86_64 at the moment. > Other architectures may have to define ASAN_SHADOW_OFFSET. > """ > > > > + > > +Example heap-buffer-overflow error > > +---------------------------------- > > + > > +Add below unit test code in examples/helloworld/main.c:: > > + > > + Add code to helloworld: > > + char *p = rte_zmalloc(NULL, 9, 0); > > + if (!p) { > > + printf("rte_zmalloc error."); > > + return -1; > > + } > > + p[9] = 'a'; > > + > > +Above code will result in heap-buffer-overflow error if ASan is enabled, > because apply 9 bytes of memory but access the tenth byte, detailed error > log as below:: > > + > > + ==369953==ERROR: AddressSanitizer: heap-buffer-overflow on address > 0x7fb17f465809 at pc 0x5652e6707b84 bp 0x7ffea70eea20 sp > 0x7ffea70eea10 WRITE of size 1 at 0x7fb17f465809 thread T0 > > + #0 0x5652e6707b83 in main ../examples/helloworld/main.c:47 > > + #1 0x7fb94953c0b2 in __libc_start_main (/lib/x86_64-linux- > gnu/libc.so.6+0x270b2) > > + #2 0x5652e67079bd in _start > > + (/home/pzh/asan_test/x86_64-native-linuxapp-gcc/examples/dpdk- > hellow > > + orld+0x8329bd) > > + > > + Address 0x7fb17f465809 is a wild pointer. > > + SUMMARY: AddressSanitizer: heap-buffer-overflow > > + ../examples/helloworld/main.c:47 in main > > I'll move the note update here: > """ > Note:: > > - Some of the features of ASan (for example, 'Display memory application > location, currently > displayed as a wild pointer') are not currently supported with DPDK > allocations. > > """ I agree with the above changes. > > + > > +Example use-after-free error > > +---------------------------- > > + > > +Add below unit test code in examples/helloworld/main.c:: > > + > > + Add code to helloworld: > > + char *p = rte_zmalloc(NULL, 9, 0); > > + if (!p) { > > + printf("rte_zmalloc error."); > > + return -1; > > + } > > + rte_free(p); > > + *p = 'a'; > > + > > +Above code will result in use-after-free error if ASan is enabled, because > apply 9 bytes of memory but access the first byte after release, detailed error > log as below:: > > + > > + ==417048==ERROR: AddressSanitizer: heap-use-after-free on address > 0x7fc83f465800 at pc 0x564308a39b89 bp 0x7ffc8c85bf50 sp 0x7ffc8c85bf40 > WRITE of size 1 at 0x7fc83f465800 thread T0 > > + #0 0x564308a39b88 in main ../examples/helloworld/main.c:48 > > + #1 0x7fd0079c60b2 in __libc_start_main (/lib/x86_64-linux- > gnu/libc.so.6+0x270b2) > > + #2 0x564308a399bd in _start > > + (/home/pzh/asan_test/x86_64-native-linuxapp-gcc/examples/dpdk- > hellow > > + orld+0x8329bd) > > + > > + Address 0x7fc83f465800 is a wild pointer. > > + SUMMARY: AddressSanitizer: heap-use-after-free > > + ../examples/helloworld/main.c:48 in main > > + > > Add following meson build commands to enable ASan in the meson build > system: > > > > * gcc:: > > @@ -25,6 +77,8 @@ Add following meson build commands to enable ASan > in the meson build system: > > > > .. Note:: > > > > - a) If compile with gcc in centos, libasan needs to be installed separately. > > - b) If the program is tested using cmdline, you may need to execute the > > + a) Some of the features of ASan (for example, 'Display memory > application location, currently > > + displayed as a wild pointer') are not currently supported by DPDK's > implementation. > > + b) If compile with gcc in centos, libasan needs to be installed separately. > > + c) If the program is tested using cmdline, you may need to > > + execute the > > "stty echo" command when an error occurs. > > > -- > David Marchand ^ permalink raw reply [flat|nested] 117+ messages in thread
* [dpdk-dev] [PATCH v13 3/4] code changes to avoid the ASan error 2021-10-20 7:46 ` [dpdk-dev] [PATCH v13 1/4] enable ASan AddressSanitizer zhihongx.peng 2021-10-20 7:46 ` [dpdk-dev] [PATCH v13 2/4] DPDK code adapts to ASan zhihongx.peng @ 2021-10-20 7:46 ` zhihongx.peng 2021-10-20 7:46 ` [dpdk-dev] [PATCH v13 4/4] performance-thread: avoid cross compilation fail zhihongx.peng ` (2 subsequent siblings) 4 siblings, 0 replies; 117+ messages in thread From: zhihongx.peng @ 2021-10-20 7:46 UTC (permalink / raw) To: david.marchand, thomas, anatoly.burakov, konstantin.ananyev, stephen, cristian.dumitrescu, john.mcnamara, bruce.richardson Cc: dev, xueqin.lin, Zhihong Peng From: Zhihong Peng <zhihongx.peng@intel.com> Code changes to avoid the following ASan error: "Control reaches end of non-void function". Signed-off-by: Xueqin Lin <xueqin.lin@intel.com> Signed-off-by: Zhihong Peng <zhihongx.peng@intel.com> Acked-by: Cristian Dumitrescu <cristian.dumitrescu@intel.com> --- v7: no change. v8: no change. v9: Modify the submit log. v10:no change. v11:no change. v12:Modify the commit log. v13:Modify the commit log. --- lib/pipeline/rte_swx_pipeline.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/pipeline/rte_swx_pipeline.c b/lib/pipeline/rte_swx_pipeline.c index 1cd09a4b44..0acd6c6752 100644 --- a/lib/pipeline/rte_swx_pipeline.c +++ b/lib/pipeline/rte_swx_pipeline.c @@ -4642,7 +4642,7 @@ instr_meter_translate(struct rte_swx_pipeline *p, return 0; } - CHECK(0, EINVAL); + return -EINVAL; } static inline void @@ -5937,7 +5937,7 @@ instr_translate(struct rte_swx_pipeline *p, instr, data); - CHECK(0, EINVAL); + return -EINVAL; } static struct instruction_data * -- 2.25.1 ^ permalink raw reply [flat|nested] 117+ messages in thread
* [dpdk-dev] [PATCH v13 4/4] performance-thread: avoid cross compilation fail 2021-10-20 7:46 ` [dpdk-dev] [PATCH v13 1/4] enable ASan AddressSanitizer zhihongx.peng 2021-10-20 7:46 ` [dpdk-dev] [PATCH v13 2/4] DPDK code adapts to ASan zhihongx.peng 2021-10-20 7:46 ` [dpdk-dev] [PATCH v13 3/4] code changes to avoid the ASan error zhihongx.peng @ 2021-10-20 7:46 ` zhihongx.peng 2021-10-25 1:58 ` [dpdk-dev] [PATCH v13 1/4] enable ASan AddressSanitizer Peng, ZhihongX 2021-10-29 9:21 ` David Marchand 4 siblings, 0 replies; 117+ messages in thread From: zhihongx.peng @ 2021-10-20 7:46 UTC (permalink / raw) To: david.marchand, thomas, anatoly.burakov, konstantin.ananyev, stephen, cristian.dumitrescu, john.mcnamara, bruce.richardson Cc: dev, xueqin.lin, Zhihong Peng From: Zhihong Peng <zhihongx.peng@intel.com> Code changes to avoid the following ASan error: "strncpy specified bound XX equals destination size". Signed-off-by: Xueqin Lin <xueqin.lin@intel.com> Signed-off-by: Zhihong Peng <zhihongx.peng@intel.com> Acked-by: Bruce Richardson <bruce.richardson@intel.com> --- v11: Use rte_strlcpy to replace strncpy. v12: Delete rte_strlcpy's rte_. v13: Modify the commit log. --- examples/performance-thread/common/lthread.c | 4 ++-- examples/performance-thread/common/lthread_cond.c | 6 +++--- examples/performance-thread/common/lthread_mutex.c | 6 +++--- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/examples/performance-thread/common/lthread.c b/examples/performance-thread/common/lthread.c index 98123f34f8..009374a8c3 100644 --- a/examples/performance-thread/common/lthread.c +++ b/examples/performance-thread/common/lthread.c @@ -20,6 +20,7 @@ #include <sys/mman.h> #include <rte_log.h> +#include <rte_string_fns.h> #include <ctx.h> #include <stack.h> @@ -465,6 +466,5 @@ void lthread_set_funcname(const char *f) { struct lthread *lt = THIS_LTHREAD; - strncpy(lt->funcname, f, sizeof(lt->funcname)); - lt->funcname[sizeof(lt->funcname)-1] = 0; + strlcpy(lt->funcname, f, sizeof(lt->funcname)); } diff --git a/examples/performance-thread/common/lthread_cond.c b/examples/performance-thread/common/lthread_cond.c index cdcc7a7b5a..e7be17089a 100644 --- a/examples/performance-thread/common/lthread_cond.c +++ b/examples/performance-thread/common/lthread_cond.c @@ -20,6 +20,7 @@ #include <rte_log.h> #include <rte_common.h> +#include <rte_string_fns.h> #include "lthread_api.h" #include "lthread_diag_api.h" @@ -57,10 +58,9 @@ lthread_cond_init(char *name, struct lthread_cond **cond, } if (name == NULL) - strncpy(c->name, "no name", sizeof(c->name)); + strlcpy(c->name, "no name", sizeof(c->name)); else - strncpy(c->name, name, sizeof(c->name)); - c->name[sizeof(c->name)-1] = 0; + strlcpy(c->name, name, sizeof(c->name)); c->root_sched = THIS_SCHED; diff --git a/examples/performance-thread/common/lthread_mutex.c b/examples/performance-thread/common/lthread_mutex.c index 061fc5c19a..f3ec7c1c60 100644 --- a/examples/performance-thread/common/lthread_mutex.c +++ b/examples/performance-thread/common/lthread_mutex.c @@ -19,6 +19,7 @@ #include <rte_log.h> #include <rte_spinlock.h> #include <rte_common.h> +#include <rte_string_fns.h> #include "lthread_api.h" #include "lthread_int.h" @@ -52,10 +53,9 @@ lthread_mutex_init(char *name, struct lthread_mutex **mutex, } if (name == NULL) - strncpy(m->name, "no name", sizeof(m->name)); + strlcpy(m->name, "no name", sizeof(m->name)); else - strncpy(m->name, name, sizeof(m->name)); - m->name[sizeof(m->name)-1] = 0; + strlcpy(m->name, name, sizeof(m->name)); m->root_sched = THIS_SCHED; m->owner = NULL; -- 2.25.1 ^ permalink raw reply [flat|nested] 117+ messages in thread
* Re: [dpdk-dev] [PATCH v13 1/4] enable ASan AddressSanitizer 2021-10-20 7:46 ` [dpdk-dev] [PATCH v13 1/4] enable ASan AddressSanitizer zhihongx.peng ` (2 preceding siblings ...) 2021-10-20 7:46 ` [dpdk-dev] [PATCH v13 4/4] performance-thread: avoid cross compilation fail zhihongx.peng @ 2021-10-25 1:58 ` Peng, ZhihongX 2021-10-29 9:21 ` David Marchand 4 siblings, 0 replies; 117+ messages in thread From: Peng, ZhihongX @ 2021-10-25 1:58 UTC (permalink / raw) To: david.marchand, thomas, Burakov, Anatoly, Ananyev, Konstantin, stephen, Dumitrescu, Cristian, Mcnamara, John, Richardson, Bruce Cc: dev, Lin, Xueqin > -----Original Message----- > From: Peng, ZhihongX <zhihongx.peng@intel.com> > Sent: Wednesday, October 20, 2021 3:47 PM > To: david.marchand@redhat.com; thomas@monjalon.net; Burakov, Anatoly > <anatoly.burakov@intel.com>; Ananyev, Konstantin > <konstantin.ananyev@intel.com>; stephen@networkplumber.org; > Dumitrescu, Cristian <cristian.dumitrescu@intel.com>; Mcnamara, John > <john.mcnamara@intel.com>; Richardson, Bruce > <bruce.richardson@intel.com> > Cc: dev@dpdk.org; Lin, Xueqin <xueqin.lin@intel.com>; Peng, ZhihongX > <zhihongx.peng@intel.com> > Subject: [PATCH v13 1/4] enable ASan AddressSanitizer > > From: Zhihong Peng <zhihongx.peng@intel.com> > > `AddressSanitizer > <https://github.com/google/sanitizers/wiki/AddressSanitizer>`_ (ASan) is a > widely-used debugging tool to detect memory access errors. > It helps to detect issues like use-after-free, various kinds of buffer overruns > in C/C++ programs, and other similar errors, as well as printing out detailed > debug information whenever an error is detected. > > We can enable ASan by adding below compilation options: > -Dbuildtype=debug -Db_lundef=false -Db_sanitize=address > "-Dbuildtype=debug": This is a non-essential option. When this option is > added, if a memory error occurs, ASan can clearly show where the code is > wrong. > "-Db_lundef=false": When use clang to compile DPDK, this option must be > added. > > Signed-off-by: Xueqin Lin <xueqin.lin@intel.com> > Signed-off-by: Zhihong Peng <zhihongx.peng@intel.com> > Acked-by: John McNamara <john.mcnamara@intel.com> > --- > v7: 1) Split doc and code into two. > 2) Modify asan.rst doc > v8: No change. > v9: 1) Add the check of libasan library. > 2) Add release notes. > v10:1) Split doc and code into two. > 2) Meson supports asan. > v11:Modify the document. > v12:No change. > v13:Modify the document. > --- > config/meson.build | 16 ++++++++++++++ > devtools/words-case.txt | 1 + > doc/guides/prog_guide/asan.rst | 30 ++++++++++++++++++++++++++ > doc/guides/prog_guide/index.rst | 1 + > doc/guides/rel_notes/release_21_11.rst | 9 ++++++++ > 5 files changed, 57 insertions(+) > create mode 100644 doc/guides/prog_guide/asan.rst > > diff --git a/config/meson.build b/config/meson.build index > 4cdf589e20..f02b0e9c6d 100644 > --- a/config/meson.build > +++ b/config/meson.build > @@ -411,6 +411,22 @@ if get_option('b_lto') > endif > endif > > +if get_option('b_sanitize') == 'address' or get_option('b_sanitize') == > 'address,undefined' > + if is_windows > + error('ASan is not supported on windows') > + endif > + > + if cc.get_id() == 'gcc' > + asan_dep = cc.find_library('asan', required: true) > + if (not cc.links('int main(int argc, char *argv[]) { return 0; }', > + dependencies: asan_dep)) > + error('broken dependency, "libasan"') > + endif > + add_project_link_arguments('-lasan', language: 'c') > + dpdk_extra_ldflags += '-lasan' > + endif > +endif > + > if get_option('default_library') == 'both' > error( ''' > Unsupported value "both" for "default_library" option. > diff --git a/devtools/words-case.txt b/devtools/words-case.txt index > 0bbad48626..ada6910fa0 100644 > --- a/devtools/words-case.txt > +++ b/devtools/words-case.txt > @@ -5,6 +5,7 @@ API > Arm > armv7 > armv8 > +ASan > BAR > CRC > DCB > diff --git a/doc/guides/prog_guide/asan.rst > b/doc/guides/prog_guide/asan.rst new file mode 100644 index > 0000000000..6888fc9a87 > --- /dev/null > +++ b/doc/guides/prog_guide/asan.rst > @@ -0,0 +1,30 @@ > +.. SPDX-License-Identifier: BSD-3-Clause > + Copyright(c) 2021 Intel Corporation > + > +Running AddressSanitizer > +======================== > + > +`AddressSanitizer > +<https://github.com/google/sanitizers/wiki/AddressSanitizer>`_ (ASan) > +is a widely-used debugging tool to detect memory access errors. > +It helps to detect issues like use-after-free, various kinds of buffer > +overruns in C/C++ programs, and other similar errors, as well as > +printing out detailed debug information whenever an error is detected. > + > +AddressSanitizer is a part of LLVM (3.1+) and GCC (4.8+). > + > +Add following meson build commands to enable ASan in the meson build > system: > + > +* gcc:: > + > + -Dbuildtype=debug -Db_sanitize=address > + > +* clang:: > + > + -Dbuildtype=debug -Db_lundef=false -Db_sanitize=address > + > +.. Note:: > + > + a) If compile with gcc in centos, libasan needs to be installed separately. > + b) If the program is tested using cmdline, you may need to execute the > + "stty echo" command when an error occurs. > diff --git a/doc/guides/prog_guide/index.rst > b/doc/guides/prog_guide/index.rst index 89af28dacb..b95c460b19 100644 > --- a/doc/guides/prog_guide/index.rst > +++ b/doc/guides/prog_guide/index.rst > @@ -71,4 +71,5 @@ Programmer's Guide > writing_efficient_code > lto > profile_app > + asan > glossary > diff --git a/doc/guides/rel_notes/release_21_11.rst > b/doc/guides/rel_notes/release_21_11.rst > index 3362c52a73..10f4275b1b 100644 > --- a/doc/guides/rel_notes/release_21_11.rst > +++ b/doc/guides/rel_notes/release_21_11.rst > @@ -173,6 +173,15 @@ New Features > * Added tests to verify tunnel header verification in IPsec inbound. > * Added tests to verify inner checksum. > > +* **Enable ASan AddressSanitizer.** > + > + `AddressSanitizer > + <https://github.com/google/sanitizers/wiki/AddressSanitizer>`_ (ASan) > + is a widely-used debugging tool to detect memory access errors. > + It helps to detect issues like use-after-free, various kinds of > + buffer overruns in C/C++ programs, and other similar errors, as well > + as printing out detailed debug information whenever an error is detected. > + > > Removed Items > ------------- > -- > 2.25.1 Hi David, Thomas This is our last stable patch, and we have resolved the comments from the community. Hope to get your comments! ^ permalink raw reply [flat|nested] 117+ messages in thread
* Re: [dpdk-dev] [PATCH v13 1/4] enable ASan AddressSanitizer 2021-10-20 7:46 ` [dpdk-dev] [PATCH v13 1/4] enable ASan AddressSanitizer zhihongx.peng ` (3 preceding siblings ...) 2021-10-25 1:58 ` [dpdk-dev] [PATCH v13 1/4] enable ASan AddressSanitizer Peng, ZhihongX @ 2021-10-29 9:21 ` David Marchand 2021-10-29 12:48 ` Peng, ZhihongX 2021-10-29 14:50 ` David Marchand 4 siblings, 2 replies; 117+ messages in thread From: David Marchand @ 2021-10-29 9:21 UTC (permalink / raw) To: Zhihong Peng Cc: Thomas Monjalon, Burakov, Anatoly, Ananyev, Konstantin, Stephen Hemminger, Cristian Dumitrescu, Mcnamara, John, Bruce Richardson, dev, Xueqin Lin Replying on this patch since there is no cover letter. This series looks acceptable to me for rc2. Patch 3 and 4 will be merged first, since they fix issues that would be hit with ASan enabled. I have comments mainly on rewording in commitlogs and documentation. If you are fine with those comments, I'll fix them before pushing this series. On Wed, Oct 20, 2021 at 9:47 AM <zhihongx.peng@intel.com> wrote: > > From: Zhihong Peng <zhihongx.peng@intel.com> > > `AddressSanitizer > <https://github.com/google/sanitizers/wiki/AddressSanitizer>`_ (ASan) > is a widely-used debugging tool to detect memory access errors. > It helps to detect issues like use-after-free, various kinds of buffer > overruns in C/C++ programs, and other similar errors, as well as > printing out detailed debug information whenever an error is detected. > > We can enable ASan by adding below compilation options: > -Dbuildtype=debug -Db_lundef=false -Db_sanitize=address > "-Dbuildtype=debug": This is a non-essential option. When this option > is added, if a memory error occurs, ASan can clearly show where the > code is wrong. > "-Db_lundef=false": When use clang to compile DPDK, this option must > be added. > > Signed-off-by: Xueqin Lin <xueqin.lin@intel.com> > Signed-off-by: Zhihong Peng <zhihongx.peng@intel.com> > Acked-by: John McNamara <john.mcnamara@intel.com> This patch affects the build process so it should be reflected in the patch title. I find the commitlog hard to read. Suggesting following rewording: """ build: enable AddressSanitizer AddressSanitizer [1] a.k.a. ASan is a widely-used debugging tool to detect memory access errors. It helps to detect issues like use-after-free, various kinds of buffer overruns in C/C++ programs, and other similar errors, as well as printing out detailed debug information whenever an error is detected. ASan is integrated with gcc and clang and can be enabled via a meson option: -Db_sanitize=address See the documentation for details (especially regarding clang). Enabling ASan has an impact on performance since additional checks are added to generated binaries. Enabling ASan with Windows is currently not supported in DPDK. 1: https://github.com/google/sanitizers/wiki/AddressSanitizer """ > --- > v7: 1) Split doc and code into two. > 2) Modify asan.rst doc > v8: No change. > v9: 1) Add the check of libasan library. > 2) Add release notes. > v10:1) Split doc and code into two. > 2) Meson supports asan. > v11:Modify the document. > v12:No change. > v13:Modify the document. > --- > config/meson.build | 16 ++++++++++++++ > devtools/words-case.txt | 1 + > doc/guides/prog_guide/asan.rst | 30 ++++++++++++++++++++++++++ > doc/guides/prog_guide/index.rst | 1 + > doc/guides/rel_notes/release_21_11.rst | 9 ++++++++ > 5 files changed, 57 insertions(+) > create mode 100644 doc/guides/prog_guide/asan.rst > > diff --git a/config/meson.build b/config/meson.build > index 4cdf589e20..f02b0e9c6d 100644 > --- a/config/meson.build > +++ b/config/meson.build > @@ -411,6 +411,22 @@ if get_option('b_lto') > endif > endif > > +if get_option('b_sanitize') == 'address' or get_option('b_sanitize') == 'address,undefined' > + if is_windows > + error('ASan is not supported on windows') > + endif I see clang started supporting ASan for Windows in version 8. https://releases.llvm.org/8.0.0/tools/clang/docs/AddressSanitizer.html I also found some blog about adding ASan support in MSVC. https://devblogs.microsoft.com/cppblog/addresssanitizer-asan-for-windows-with-msvc/ Keeping this limitation is acceptable for now, but I added a mention in commitlog. > + > + if cc.get_id() == 'gcc' > + asan_dep = cc.find_library('asan', required: true) > + if (not cc.links('int main(int argc, char *argv[]) { return 0; }', > + dependencies: asan_dep)) > + error('broken dependency, "libasan"') > + endif > + add_project_link_arguments('-lasan', language: 'c') > + dpdk_extra_ldflags += '-lasan' > + endif > +endif > + > if get_option('default_library') == 'both' > error( ''' > Unsupported value "both" for "default_library" option. > diff --git a/devtools/words-case.txt b/devtools/words-case.txt > index 0bbad48626..ada6910fa0 100644 > --- a/devtools/words-case.txt > +++ b/devtools/words-case.txt > @@ -5,6 +5,7 @@ API > Arm > armv7 > armv8 > +ASan > BAR > CRC > DCB > diff --git a/doc/guides/prog_guide/asan.rst b/doc/guides/prog_guide/asan.rst > new file mode 100644 > index 0000000000..6888fc9a87 > --- /dev/null > +++ b/doc/guides/prog_guide/asan.rst > @@ -0,0 +1,30 @@ > +.. SPDX-License-Identifier: BSD-3-Clause > + Copyright(c) 2021 Intel Corporation > + > +Running AddressSanitizer > +======================== > + > +`AddressSanitizer > +<https://github.com/google/sanitizers/wiki/AddressSanitizer>`_ (ASan) > +is a widely-used debugging tool to detect memory access errors. > +It helps to detect issues like use-after-free, various kinds of buffer > +overruns in C/C++ programs, and other similar errors, as well as > +printing out detailed debug information whenever an error is detected. > + > +AddressSanitizer is a part of LLVM (3.1+) and GCC (4.8+). > + > +Add following meson build commands to enable ASan in the meson build system: > + > +* gcc:: > + > + -Dbuildtype=debug -Db_sanitize=address > + > +* clang:: > + > + -Dbuildtype=debug -Db_lundef=false -Db_sanitize=address Suggests adding some explanations here and replacing like: """ ... AddressSanitizer is a part of LLVM (3.1+) and GCC (4.8+). Enabling ASan is done by passing the -Db_sanitize=address option to the meson build system, see :ref:`linux_gsg_compiling_dpdk` for details. The way ASan is integrated with clang requires to allow undefined symbols when linking code. To do this, the -Db_lundef=false option must be added. Additionally, passing -Dbuildtype=debug option might help getting more readable ASan reports. Example:: - gcc: meson setup -Db_sanitize=address <build_dir> - clang: meson setup -Db_sanitize=address -Db_lundef=false <build_dir> """ > + > +.. Note:: > + > + a) If compile with gcc in centos, libasan needs to be installed separately. > + b) If the program is tested using cmdline, you may need to execute the > + "stty echo" command when an error occurs. """ - The libasan package must be installed when compiling with gcc in Centos/RHEL. - If the program is tested using cmdline, you may need to execute the "stty echo" command when an error occurs. """ > diff --git a/doc/guides/prog_guide/index.rst b/doc/guides/prog_guide/index.rst > index 89af28dacb..b95c460b19 100644 > --- a/doc/guides/prog_guide/index.rst > +++ b/doc/guides/prog_guide/index.rst > @@ -71,4 +71,5 @@ Programmer's Guide > writing_efficient_code > lto > profile_app > + asan > glossary > diff --git a/doc/guides/rel_notes/release_21_11.rst b/doc/guides/rel_notes/release_21_11.rst > index 3362c52a73..10f4275b1b 100644 > --- a/doc/guides/rel_notes/release_21_11.rst > +++ b/doc/guides/rel_notes/release_21_11.rst > @@ -173,6 +173,15 @@ New Features > * Added tests to verify tunnel header verification in IPsec inbound. > * Added tests to verify inner checksum. > > +* **Enable ASan AddressSanitizer.** Should be in past form: * **Added ASan support.** > + > + `AddressSanitizer > + <https://github.com/google/sanitizers/wiki/AddressSanitizer>`_ (ASan) > + is a widely-used debugging tool to detect memory access errors. > + It helps to detect issues like use-after-free, various kinds of buffer > + overruns in C/C++ programs, and other similar errors, as well as > + printing out detailed debug information whenever an error is detected. -- David Marchand ^ permalink raw reply [flat|nested] 117+ messages in thread
* Re: [dpdk-dev] [PATCH v13 1/4] enable ASan AddressSanitizer 2021-10-29 9:21 ` David Marchand @ 2021-10-29 12:48 ` Peng, ZhihongX 2021-10-29 14:50 ` David Marchand 1 sibling, 0 replies; 117+ messages in thread From: Peng, ZhihongX @ 2021-10-29 12:48 UTC (permalink / raw) To: David Marchand Cc: Thomas Monjalon, Burakov, Anatoly, Ananyev, Konstantin, Stephen Hemminger, Dumitrescu, Cristian, Mcnamara, John, Richardson, Bruce, dev, Lin, Xueqin > -----Original Message----- > From: David Marchand <david.marchand@redhat.com> > Sent: Friday, October 29, 2021 5:22 PM > To: Peng, ZhihongX <zhihongx.peng@intel.com> > Cc: Thomas Monjalon <thomas@monjalon.net>; Burakov, Anatoly > <anatoly.burakov@intel.com>; Ananyev, Konstantin > <konstantin.ananyev@intel.com>; Stephen Hemminger > <stephen@networkplumber.org>; Dumitrescu, Cristian > <cristian.dumitrescu@intel.com>; Mcnamara, John > <john.mcnamara@intel.com>; Richardson, Bruce > <bruce.richardson@intel.com>; dev <dev@dpdk.org>; Lin, Xueqin > <xueqin.lin@intel.com> > Subject: Re: [PATCH v13 1/4] enable ASan AddressSanitizer > > Replying on this patch since there is no cover letter. > > This series looks acceptable to me for rc2. > > Patch 3 and 4 will be merged first, since they fix issues that would be hit with > ASan enabled. > > I have comments mainly on rewording in commitlogs and documentation. > > If you are fine with those comments, I'll fix them before pushing this series. Thank you very much for your help, I agree with this modification, it's good. You have done a lot of work for asan patch, thank you again. Regards, Peng,Zhihong > > > On Wed, Oct 20, 2021 at 9:47 AM <zhihongx.peng@intel.com> wrote: > > > > From: Zhihong Peng <zhihongx.peng@intel.com> > > > > `AddressSanitizer > > <https://github.com/google/sanitizers/wiki/AddressSanitizer>`_ (ASan) > > is a widely-used debugging tool to detect memory access errors. > > It helps to detect issues like use-after-free, various kinds of buffer > > overruns in C/C++ programs, and other similar errors, as well as > > printing out detailed debug information whenever an error is detected. > > > > We can enable ASan by adding below compilation options: > > -Dbuildtype=debug -Db_lundef=false -Db_sanitize=address > > "-Dbuildtype=debug": This is a non-essential option. When this option > > is added, if a memory error occurs, ASan can clearly show where the > > code is wrong. > > "-Db_lundef=false": When use clang to compile DPDK, this option must > > be added. > > > > Signed-off-by: Xueqin Lin <xueqin.lin@intel.com> > > Signed-off-by: Zhihong Peng <zhihongx.peng@intel.com> > > Acked-by: John McNamara <john.mcnamara@intel.com> > > This patch affects the build process so it should be reflected in the patch title. > I find the commitlog hard to read. > > > Suggesting following rewording: > """ > build: enable AddressSanitizer > > AddressSanitizer [1] a.k.a. ASan is a widely-used debugging tool to detect > memory access errors. > It helps to detect issues like use-after-free, various kinds of buffer overruns > in C/C++ programs, and other similar errors, as well as printing out detailed > debug information whenever an error is detected. > > ASan is integrated with gcc and clang and can be enabled via a meson > option: -Db_sanitize=address > See the documentation for details (especially regarding clang). > > Enabling ASan has an impact on performance since additional checks are > added to generated binaries. > > Enabling ASan with Windows is currently not supported in DPDK. > > > 1: https://github.com/google/sanitizers/wiki/AddressSanitizer > """ > > > --- > > v7: 1) Split doc and code into two. > > 2) Modify asan.rst doc > > v8: No change. > > v9: 1) Add the check of libasan library. > > 2) Add release notes. > > v10:1) Split doc and code into two. > > 2) Meson supports asan. > > v11:Modify the document. > > v12:No change. > > v13:Modify the document. > > --- > > config/meson.build | 16 ++++++++++++++ > > devtools/words-case.txt | 1 + > > doc/guides/prog_guide/asan.rst | 30 ++++++++++++++++++++++++++ > > doc/guides/prog_guide/index.rst | 1 + > > doc/guides/rel_notes/release_21_11.rst | 9 ++++++++ > > 5 files changed, 57 insertions(+) > > create mode 100644 doc/guides/prog_guide/asan.rst > > > > diff --git a/config/meson.build b/config/meson.build index > > 4cdf589e20..f02b0e9c6d 100644 > > --- a/config/meson.build > > +++ b/config/meson.build > > @@ -411,6 +411,22 @@ if get_option('b_lto') > > endif > > endif > > > > +if get_option('b_sanitize') == 'address' or get_option('b_sanitize') == > 'address,undefined' > > + if is_windows > > + error('ASan is not supported on windows') > > + endif > > I see clang started supporting ASan for Windows in version 8. > https://releases.llvm.org/8.0.0/tools/clang/docs/AddressSanitizer.html > > I also found some blog about adding ASan support in MSVC. > https://devblogs.microsoft.com/cppblog/addresssanitizer-asan-for- > windows-with-msvc/ > > Keeping this limitation is acceptable for now, but I added a mention in > commitlog. > > > > + > > + if cc.get_id() == 'gcc' > > + asan_dep = cc.find_library('asan', required: true) > > + if (not cc.links('int main(int argc, char *argv[]) { return 0; }', > > + dependencies: asan_dep)) > > + error('broken dependency, "libasan"') > > + endif > > + add_project_link_arguments('-lasan', language: 'c') > > + dpdk_extra_ldflags += '-lasan' > > + endif > > +endif > > + > > if get_option('default_library') =