* [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: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 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-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: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: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-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
* [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] 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 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
* 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
* [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] 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 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
* 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 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
* 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-24 2:20 ` [dpdk-dev] [PATCH v4 1/2] Enable ASan for memory detector " zhihongx.peng
@ 2021-09-27 12:02 ` Burakov, Anatoly
2021-09-30 5:18 ` Peng, ZhihongX
2 siblings, 1 reply; 117+ messages in thread
From: Burakov, Anatoly @ 2021-09-27 12:02 UTC (permalink / raw)
To: zhihongx.peng, konstantin.ananyev, stephen; +Cc: dev, xueqin.lin
On 18-Sep-21 8:41 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';
> ......
>
> 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.
I think the description is a little to wordy and unnecessarily redundant
(pun intended!) in places. Suggested rewording:
`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.
> +
> +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.
I think all of the terms used here should be properly capitalized, e.g.
"ASan" instead of "asan", "DPDK" instead of "dpdk", etc.
Also, i don't quite follow the 'a)' section - what exactly is a wild
pointer? What makes a pointer 'wild', and why do we care? If the point
is that DPDK's ASan implementation does not support all ASan features,
then maybe this could be worded better, e.g. like so:
a) Some of the features of ASan (for example, 'wild pointers') are not
currently supported by DPDK's implementation
> 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;
This doesn't look like it's changing anything - maybe drop this
particular hun from the patch?
>
> #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)
Maybe make it more pointer-safe, like so?
#define ASAN_MEM_SHIFT(mem) ((void*)((uintptr_t)mem >> 3))
#define ASAN_BASE_ADDR 0x00007fff8000
#define ASAN_MEM_TO_SHADOW(mem) RTE_PTR_ADD(ASAN_MEM_SHIFT(mem),
ASAN_BASE_ADDR)
We could even add a new rte_common.h macros for RTE_PTR_LSHIFT/RSHIFT.
The advantage of this suggestion is that you won't need to make so many
typecasts any more.
> +
> +#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));
How about
shadow = ASAN_MEM_TO_SHADOW(RTE_PTR_ADD(ptr, offset));
? The 'shadow' variable can be void*, so you can drop the cast here too.
> + 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);
I think DPDK code style is to declare all variables at the beginning of
the block.
> + 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);
It seems to me that the code can be rewritten to be more understandable.
For example, you could opt for more meaningful variable names and not
reuse 'ptr' for everything. Ex.:
elem_start = RTE_PTR_ADD(elem, MALLOC_ELEM_HEADER_LEN);
start_ptr = RTE_PTR_SUB(elem_start, ASAN_SHADOW_GRAIN_SIZE);
start_shadow = ASAN_MEM_TO_SHADOW(start_ptr);
// just following your code here, but why are we doing it twice?
start_redzone = RTE_PTR_SUB(start_shadow, ASAN_SHADOW_GRAIN_SIZE);
asan_set_shadow(start_shadow, ASAN_MEM_REDZONE_FLAG);
asan_set_shadow(start_redzone, ASAN_MEM_REDZONE_FLAG);
...
And similar for the rest of the code: more meaningful variable names,
more intermediate variables, less raw bit/ptr manipulation please :)
> + }
> +}
> +
> +/*
> + * 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);
It seems to me that you're repeating a lot of code here that you already
have in `asan_set_redzone()` - maybe factor common bits out into a
function? For example,
static inlind void ptr_set_shadow(void *ptr, int shadow_flags) {
startptr = RTE_PTR_SUB(ptr, ASAN_SHADOW_GRAIN_SIZE);
shadow = ASAN_MEM_TO_SHADOW(startptr);
asan_set_shadow(shadow, shadow_flags);
shadow = RTE_PTR_SUB(shadow, ASAN_SHADOW_GRAIN_SIZE);
asan_set_shadow(shadow, shadow_flags);
}
...
and then call this from wherever you like? Because this pattern seems to
be repeating everywhere, the only difference being the ptr value and the
set_shadow flags.
> + }
> +}
> +
> +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;
Why is this here? Accidental change?
> }
>
> static inline struct meter *
> @@ -8025,7 +8025,7 @@ instr_translate(struct rte_swx_pipeline *p,
> instr,
> data);
>
> - CHECK(0, EINVAL);
> + return -EINVAL;
Same as above - accidental change?
> }
>
> static struct instruction_data *
>
--
Thanks,
Anatoly
^ permalink raw reply [flat|nested] 117+ messages in thread
* Re: [dpdk-dev] [PATCH v3] Enable AddressSanitizer feature on DPDK
2021-09-27 12:02 ` [dpdk-dev] [PATCH v3] Enable AddressSanitizer feature " Burakov, Anatoly
@ 2021-09-30 5:18 ` Peng, ZhihongX
0 siblings, 0 replies; 117+ messages in thread
From: Peng, ZhihongX @ 2021-09-30 5:18 UTC (permalink / raw)
To: Burakov, Anatoly, Ananyev, Konstantin, stephen; +Cc: dev, Lin, Xueqin
> -----Original Message-----
> From: Burakov, Anatoly <anatoly.burakov@intel.com>
> Sent: Monday, September 27, 2021 8:03 PM
> To: Peng, ZhihongX <zhihongx.peng@intel.com>; Ananyev, Konstantin
> <konstantin.ananyev@intel.com>; stephen@networkplumber.org
> Cc: dev@dpdk.org; Lin, Xueqin <xueqin.lin@intel.com>
> Subject: Re: [PATCH v3] Enable AddressSanitizer feature on DPDK
>
> On 18-Sep-21 8:41 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';
> > ......
> >
> > 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.
>
> I think the description is a little to wordy and unnecessarily redundant (pun
> intended!) in places. Suggested rewording:
>
> `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.
>
The v6 version will be modified.
> > +
> > +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.
> > + 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.
>
> I think all of the terms used here should be properly capitalized, e.g.
> "ASan" instead of "asan", "DPDK" instead of "dpdk", etc.
>
> Also, i don't quite follow the 'a)' section - what exactly is a wild pointer? What
> makes a pointer 'wild', and why do we care? If the point is that DPDK's ASan
> implementation does not support all ASan features, then maybe this could
> be worded better, e.g. like so:
>
> a) Some of the features of ASan (for example, 'wild pointers') are not
> currently supported by DPDK's implementation
The v6 version will be modified.
> > 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;
>
> This doesn't look like it's changing anything - maybe drop this particular hun
> from the patch?
>
Solve the issue of static code inspection.
> >
> > #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)
>
> Maybe make it more pointer-safe, like so?
>
> #define ASAN_MEM_SHIFT(mem) ((void*)((uintptr_t)mem >> 3)) #define
> ASAN_BASE_ADDR 0x00007fff8000 #define ASAN_MEM_TO_SHADOW(mem)
> RTE_PTR_ADD(ASAN_MEM_SHIFT(mem),
> ASAN_BASE_ADDR)
>
The v6 version will be modified.
> We could even add a new rte_common.h macros for
> RTE_PTR_LSHIFT/RSHIFT.
> The advantage of this suggestion is that you won't need to make so many
> typecasts any more.
>
> > +
> > +#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));
>
> How about
>
> shadow = ASAN_MEM_TO_SHADOW(RTE_PTR_ADD(ptr, offset));
>
> ? The 'shadow' variable can be void*, so you can drop the cast here too.
>
The v6 version will be modified.
> > + 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);
>
> I think DPDK code style is to declare all variables at the beginning of
> the block.
The v6 version will be modified.
>
> > + 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);
>
> It seems to me that the code can be rewritten to be more understandable.
> For example, you could opt for more meaningful variable names and not
> reuse 'ptr' for everything. Ex.:
>
> elem_start = RTE_PTR_ADD(elem, MALLOC_ELEM_HEADER_LEN);
> start_ptr = RTE_PTR_SUB(elem_start, ASAN_SHADOW_GRAIN_SIZE);
> start_shadow = ASAN_MEM_TO_SHADOW(start_ptr);
> // just following your code here, but why are we doing it twice?
> start_redzone = RTE_PTR_SUB(start_shadow,
> ASAN_SHADOW_GRAIN_SIZE);
>
> asan_set_shadow(start_shadow, ASAN_MEM_REDZONE_FLAG);
> asan_set_shadow(start_redzone, ASAN_MEM_REDZONE_FLAG);
>
> ...
>
> And similar for the rest of the code: more meaningful variable names,
> more intermediate variables, less raw bit/ptr manipulation please :)
The v6 version will be modified.
> > + }
> > +}
> > +
> > +/*
> > + * 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);
>
> It seems to me that you're repeating a lot of code here that you already
> have in `asan_set_redzone()` - maybe factor common bits out into a
> function? For example,
>
> static inlind void ptr_set_shadow(void *ptr, int shadow_flags) {
> startptr = RTE_PTR_SUB(ptr, ASAN_SHADOW_GRAIN_SIZE);
> shadow = ASAN_MEM_TO_SHADOW(startptr);
> asan_set_shadow(shadow, shadow_flags);
> shadow = RTE_PTR_SUB(shadow, ASAN_SHADOW_GRAIN_SIZE);
> asan_set_shadow(shadow, shadow_flags);
> }
>
> ...
>
> and then call this from wherever you like? Because this pattern seems to
> be repeating everywhere, the only difference being the ptr value and the
> set_shadow flags.
The variable names have been modified to make the code clearer.
Extracting public functions is not particularly useful, and
this is a logical implementation, not general code.
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);
> >
> > 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;
>
> Why is this here? Accidental change?
>
After adding ASan, the gcc compilation check will be stricter.
"Control reaches end of non-void function" error occurs here. Will be split into two patches.
> > }
> >
> > static inline struct meter *
> > @@ -8025,7 +8025,7 @@ instr_translate(struct rte_swx_pipeline *p,
> > instr,
> > data);
> >
> > - CHECK(0, EINVAL);
> > + return -EINVAL;
>
> Same as above - accidental change?
>
> > }
> >
> > static struct instruction_data *
> >
>
> --
> Thanks,
> Anatoly
^ 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] [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] [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
* [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
* 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
* [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
* 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-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
* [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
* [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
* 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 v9 1/3] Enable ASan for memory detector on DPDK
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 ` [dpdk-dev] [PATCH v9 3/3] pipeline: Fix compilation error with gcc ASan zhihongx.peng
@ 2021-10-13 16:44 ` David Marchand
2021-10-14 6:46 ` Peng, ZhihongX
2021-10-14 11:42 ` Peng, ZhihongX
2 siblings, 2 replies; 117+ messages in thread
From: David Marchand @ 2021-10-13 16:44 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>
>
> `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.
>
Comments below concern added feature with patch 2.
From here...
> 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';
> ......
... to here.
>
> 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>
More problematic, linking an external (out of meson) application to a
dpdk compiled with ASan is broken.
My environment contains following targets compiled using
./devtools/test-meson-builds.sh:
$ ls $HOME/builds/
build-arm64-bluefield build-arm64-host-clang build-clang-shared
build-gcc-shared build-ppc64le-power8 build-x86-mingw
build-arm64-dpaa build-arm64-octeontx2 build-clang-static
build-gcc-static build-x86-generic
I stopped at patch 1, configured following target to have ASan in
them, like this:
$ meson configure $HOME/builds/build-gcc-static -Db_sanitize=address
$ meson configure $HOME/builds/build-clang-shared -Db_sanitize=address
-Db_lundef=false
$ meson configure $HOME/builds/build-x86-generic -Db_sanitize=address
^^^^^^^^^^^^^^^^^
This is the target for which we test
linking a dpdk application out of meson.
$ meson configure $HOME/builds/build-arm64-bluefield -Db_sanitize=address
Then ran the check:
$ ./devtools/test-meson-builds.sh
...
...
...
## Building cmdline
/usr/bin/ld: /usr/lib64/libasan.so.6: warning: the use of `tmpnam' is
dangerous, better use `mkstemp'
/usr/bin/ld: /usr/lib64/libasan.so.6: warning: the use of `tempnam' is
dangerous, better use `mkstemp'
/usr/bin/ld: /usr/lib64/libasan.so.6: warning: the use of `tmpnam_r'
is dangerous, better use `mkstemp'
## Building helloworld
/usr/bin/ld: /usr/lib64/libasan.so.6: warning: the use of `tmpnam' is
dangerous, better use `mkstemp'
/usr/bin/ld: /usr/lib64/libasan.so.6: warning: the use of `tempnam' is
dangerous, better use `mkstemp'
/usr/bin/ld: /usr/lib64/libasan.so.6: warning: the use of `tmpnam_r'
is dangerous, better use `mkstemp'
/usr/bin/ld: /home/dmarchan/builds/build-x86-generic/install/usr/local/lib/librte_common_dpaax.a(common_dpaax_dpaax_iova_table.c.o):
in function `read_memory_node':
/home/dmarchan/builds/build-x86-generic/../../dpdk/drivers/common/dpaax/dpaax_iova_table.c:57:
undefined reference to `__asan_option_detect_stack_use_after_return'
/usr/bin/ld: /home/dmarchan/builds/build-x86-generic/../../dpdk/drivers/common/dpaax/dpaax_iova_table.c:65:
undefined reference to `__asan_report_store4'
/usr/bin/ld: /home/dmarchan/builds/build-x86-generic/../../dpdk/drivers/common/dpaax/dpaax_iova_table.c:61:
undefined reference to `__asan_report_store_n'
/usr/bin/ld: /home/dmarchan/builds/build-x86-generic/../../dpdk/drivers/common/dpaax/dpaax_iova_table.c:60:
undefined reference to `__asan_report_store_n'
/usr/bin/ld: /home/dmarchan/builds/build-x86-generic/../../dpdk/drivers/common/dpaax/dpaax_iova_table.c:57:
undefined reference to `__asan_stack_malloc_3'
/usr/bin/ld: /home/dmarchan/builds/build-x86-generic/../../dpdk/drivers/common/dpaax/dpaax_iova_table.c:77:
undefined reference to `__asan_report_load8'
/usr/bin/ld: /home/dmarchan/builds/build-x86-generic/../../dpdk/drivers/common/dpaax/dpaax_iova_table.c:86:
undefined reference to `__asan_report_load8'
/usr/bin/ld: /home/dmarchan/builds/build-x86-generic/../../dpdk/drivers/common/dpaax/dpaax_iova_table.c:86:
undefined reference to `__asan_report_load8'
/usr/bin/ld: /home/dmarchan/builds/build-x86-generic/../../dpdk/drivers/common/dpaax/dpaax_iova_table.c:88:
undefined reference to `__asan_report_load8'
/usr/bin/ld: /home/dmarchan/builds/build-x86-generic/../../dpdk/drivers/common/dpaax/dpaax_iova_table.c:88:
undefined reference to `__asan_report_load8'
/usr/bin/ld: /home/dmarchan/builds/build-x86-generic/install/usr/local/lib/librte_common_dpaax.a(common_dpaax_dpaax_iova_table.c.o):/home/dmarchan/builds/build-x86-generic/../../dpdk/drivers/common/dpaax/dpaax_iova_table.c:102:
more undefined references to `__asan_report_load8' follow
/usr/bin/ld: /home/dmarchan/builds/build-x86-generic/install/usr/local/lib/librte_common_dpaax.a(common_dpaax_dpaax_iova_table.c.o):
in function `read_memory_node':
/home/dmarchan/builds/build-x86-generic/../../dpdk/drivers/common/dpaax/dpaax_iova_table.c:119:
undefined reference to `__asan_report_store4'
/usr/bin/ld: /home/dmarchan/builds/build-x86-generic/../../dpdk/drivers/common/dpaax/dpaax_iova_table.c:145:
undefined reference to `__asan_report_load16'
/usr/bin/ld: /home/dmarchan/builds/build-x86-generic/../../dpdk/drivers/common/dpaax/dpaax_iova_table.c:135:
undefined reference to `__asan_report_store_n'
/usr/bin/ld: /home/dmarchan/builds/build-x86-generic/../../dpdk/drivers/common/dpaax/dpaax_iova_table.c:143:
undefined reference to `__asan_report_load8'
/usr/bin/ld: /home/dmarchan/builds/build-x86-generic/install/usr/local/lib/librte_common_dpaax.a(common_dpaax_dpaax_iova_table.c.o):
in function `rotate_8':
/home/dmarchan/builds/build-x86-generic/../../dpdk/drivers/common/dpaax/dpaax_iova_table.c:40:
undefined reference to `__asan_report_store4'
/usr/bin/ld: /home/dmarchan/builds/build-x86-generic/../../dpdk/drivers/common/dpaax/dpaax_iova_table.c:39:
undefined reference to `__asan_report_store4'
/usr/bin/ld: /home/dmarchan/builds/build-x86-generic/../../dpdk/drivers/common/dpaax/dpaax_iova_table.c:40:
undefined reference to `__asan_report_store4'
/usr/bin/ld: /home/dmarchan/builds/build-x86-generic/install/usr/local/lib/librte_common_dpaax.a(common_dpaax_dpaax_iova_table.c.o):
in function `read_memory_node':
/home/dmarchan/builds/build-x86-generic/../../dpdk/drivers/common/dpaax/dpaax_iova_table.c:143:
undefined reference to `__asan_report_load8'
*flood of errors about undefined symbols from libasan*
Tried to debug, in-tree compiled testpmd shows:
$ readelf -d $HOME/builds/build-x86-generic/app/dpdk-testpmd | grep asan
0x0000000000000001 (NEEDED) Shared library: [libasan.so.6]
But I see nothing in ninja:
$ grep -i asan $HOME/builds/build-x86-generic/build.ninja
And for external link, looking at an "installed" .pc:
$ PKG_CONFIG_PATH=/home/dmarchan/builds/build-x86-generic/install/usr/local/lib/pkgconfig
pkg-config libdpdk --libs | grep asan
Some more comments about splitting doc with patch2:
> ---
> 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+).
Same problem, below should be in patch 2, from here...
> +
> +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
... till here.
> +
> +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
l_undef unnecessary for gcc.
> + 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.
This comment should be in patch 2, there is nothing specific in patch
1 about DPDK wrt ASan.
> + 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
>
--
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-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 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 v9 1/3] Enable ASan for memory detector on DPDK
2021-10-13 16:44 ` [dpdk-dev] [PATCH v9 1/3] " David Marchand
@ 2021-10-14 6:46 ` Peng, ZhihongX
2021-10-14 7:04 ` Thomas Monjalon
2021-10-14 11:42 ` Peng, ZhihongX
1 sibling, 1 reply; 117+ messages in thread
From: Peng, ZhihongX @ 2021-10-14 6:46 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 1/3] Enable ASan for memory detector on DPDK
>
> On Tue, Oct 12, 2021 at 11:54 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 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.
> >
>
>
> Comments below concern added feature with patch 2.
> From here...
>
> > 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';
> > ......
> ... to here.
>
>
> >
> > 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>
>
>
> More problematic, linking an external (out of meson) application to a dpdk
> compiled with ASan is broken.
>
> My environment contains following targets compiled using
> ./devtools/test-meson-builds.sh:
> $ ls $HOME/builds/
> build-arm64-bluefield build-arm64-host-clang build-clang-shared build-gcc-
> shared build-ppc64le-power8 build-x86-mingw
> build-arm64-dpaa build-arm64-octeontx2 build-clang-static
> build-gcc-static build-x86-generic
>
> I stopped at patch 1, configured following target to have ASan in them, like
> this:
>
> $ meson configure $HOME/builds/build-gcc-static -Db_sanitize=address
> $ meson configure $HOME/builds/build-clang-shared -Db_sanitize=address -
> Db_lundef=false $ meson configure $HOME/builds/build-x86-generic -
> Db_sanitize=address
> ^^^^^^^^^^^^^^^^^
> This is the target for which we test linking a dpdk application
> out of meson.
>
> $ meson configure $HOME/builds/build-arm64-bluefield -
> Db_sanitize=address
I don't know your test platform , arm or x86.
This is our compilation command:
Gcc is 9.3.0 or 10.3.0
CC=gcc meson -Db_sanitize=address x86_64-native-linuxapp-gcc
ninja -C x86_64-native-linuxapp-gcc
meson configure -Dexamples=helloworld x86_64-native-linuxapp-gcc
ninja -C x86_64-native-linuxapp-gcc
I don’t know how you get this parameter $HOME/builds/build-x86-generic.
Can you send me all your configuration, I will reproduce this error in our environment.
Thank you very much for your help!
> Then ran the check:
> $ ./devtools/test-meson-builds.sh
> ...
> ...
> ...
> ## Building cmdline
> /usr/bin/ld: /usr/lib64/libasan.so.6: warning: the use of `tmpnam' is
> dangerous, better use `mkstemp'
> /usr/bin/ld: /usr/lib64/libasan.so.6: warning: the use of `tempnam' is
> dangerous, better use `mkstemp'
> /usr/bin/ld: /usr/lib64/libasan.so.6: warning: the use of `tmpnam_r'
> is dangerous, better use `mkstemp'
> ## Building helloworld
> /usr/bin/ld: /usr/lib64/libasan.so.6: warning: the use of `tmpnam' is
> dangerous, better use `mkstemp'
> /usr/bin/ld: /usr/lib64/libasan.so.6: warning: the use of `tempnam' is
> dangerous, better use `mkstemp'
> /usr/bin/ld: /usr/lib64/libasan.so.6: warning: the use of `tmpnam_r'
> is dangerous, better use `mkstemp'
> /usr/bin/ld: /home/dmarchan/builds/build-x86-
> generic/install/usr/local/lib/librte_common_dpaax.a(common_dpaax_dpaax
> _iova_table.c.o):
> in function `read_memory_node':
> /home/dmarchan/builds/build-x86-
> generic/../../dpdk/drivers/common/dpaax/dpaax_iova_table.c:57:
> undefined reference to `__asan_option_detect_stack_use_after_return'
> /usr/bin/ld: /home/dmarchan/builds/build-x86-
> generic/../../dpdk/drivers/common/dpaax/dpaax_iova_table.c:65:
> undefined reference to `__asan_report_store4'
> /usr/bin/ld: /home/dmarchan/builds/build-x86-
> generic/../../dpdk/drivers/common/dpaax/dpaax_iova_table.c:61:
> undefined reference to `__asan_report_store_n'
> /usr/bin/ld: /home/dmarchan/builds/build-x86-
> generic/../../dpdk/drivers/common/dpaax/dpaax_iova_table.c:60:
> undefined reference to `__asan_report_store_n'
> /usr/bin/ld: /home/dmarchan/builds/build-x86-
> generic/../../dpdk/drivers/common/dpaax/dpaax_iova_table.c:57:
> undefined reference to `__asan_stack_malloc_3'
> /usr/bin/ld: /home/dmarchan/builds/build-x86-
> generic/../../dpdk/drivers/common/dpaax/dpaax_iova_table.c:77:
> undefined reference to `__asan_report_load8'
> /usr/bin/ld: /home/dmarchan/builds/build-x86-
> generic/../../dpdk/drivers/common/dpaax/dpaax_iova_table.c:86:
> undefined reference to `__asan_report_load8'
> /usr/bin/ld: /home/dmarchan/builds/build-x86-
> generic/../../dpdk/drivers/common/dpaax/dpaax_iova_table.c:86:
> undefined reference to `__asan_report_load8'
> /usr/bin/ld: /home/dmarchan/builds/build-x86-
> generic/../../dpdk/drivers/common/dpaax/dpaax_iova_table.c:88:
> undefined reference to `__asan_report_load8'
> /usr/bin/ld: /home/dmarchan/builds/build-x86-
> generic/../../dpdk/drivers/common/dpaax/dpaax_iova_table.c:88:
> undefined reference to `__asan_report_load8'
> /usr/bin/ld: /home/dmarchan/builds/build-x86-
> generic/install/usr/local/lib/librte_common_dpaax.a(common_dpaax_dpaax
> _iova_table.c.o):/home/dmarchan/builds/build-x86-
> generic/../../dpdk/drivers/common/dpaax/dpaax_iova_table.c:102:
> more undefined references to `__asan_report_load8' follow
> /usr/bin/ld: /home/dmarchan/builds/build-x86-
> generic/install/usr/local/lib/librte_common_dpaax.a(common_dpaax_dpaax
> _iova_table.c.o):
> in function `read_memory_node':
> /home/dmarchan/builds/build-x86-
> generic/../../dpdk/drivers/common/dpaax/dpaax_iova_table.c:119:
> undefined reference to `__asan_report_store4'
> /usr/bin/ld: /home/dmarchan/builds/build-x86-
> generic/../../dpdk/drivers/common/dpaax/dpaax_iova_table.c:145:
> undefined reference to `__asan_report_load16'
> /usr/bin/ld: /home/dmarchan/builds/build-x86-
> generic/../../dpdk/drivers/common/dpaax/dpaax_iova_table.c:135:
> undefined reference to `__asan_report_store_n'
> /usr/bin/ld: /home/dmarchan/builds/build-x86-
> generic/../../dpdk/drivers/common/dpaax/dpaax_iova_table.c:143:
> undefined reference to `__asan_report_load8'
> /usr/bin/ld: /home/dmarchan/builds/build-x86-
> generic/install/usr/local/lib/librte_common_dpaax.a(common_dpaax_dpaax
> _iova_table.c.o):
> in function `rotate_8':
> /home/dmarchan/builds/build-x86-
> generic/../../dpdk/drivers/common/dpaax/dpaax_iova_table.c:40:
> undefined reference to `__asan_report_store4'
> /usr/bin/ld: /home/dmarchan/builds/build-x86-
> generic/../../dpdk/drivers/common/dpaax/dpaax_iova_table.c:39:
> undefined reference to `__asan_report_store4'
> /usr/bin/ld: /home/dmarchan/builds/build-x86-
> generic/../../dpdk/drivers/common/dpaax/dpaax_iova_table.c:40:
> undefined reference to `__asan_report_store4'
> /usr/bin/ld: /home/dmarchan/builds/build-x86-
> generic/install/usr/local/lib/librte_common_dpaax.a(common_dpaax_dpaax
> _iova_table.c.o):
> in function `read_memory_node':
> /home/dmarchan/builds/build-x86-
> generic/../../dpdk/drivers/common/dpaax/dpaax_iova_table.c:143:
> undefined reference to `__asan_report_load8'
> *flood of errors about undefined symbols from libasan*
>
>
> Tried to debug, in-tree compiled testpmd shows:
> $ readelf -d $HOME/builds/build-x86-generic/app/dpdk-testpmd | grep asan
> 0x0000000000000001 (NEEDED) Shared library: [libasan.so.6]
> But I see nothing in ninja:
> $ grep -i asan $HOME/builds/build-x86-generic/build.ninja
>
> And for external link, looking at an "installed" .pc:
> $ PKG_CONFIG_PATH=/home/dmarchan/builds/build-x86-
> generic/install/usr/local/lib/pkgconfig
> pkg-config libdpdk --libs | grep asan
>
>
>
> Some more comments about splitting doc with patch2:
>
>
> > ---
> > 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+).
>
> Same problem, below should be in patch 2, from here...
>
>
> > +
> > +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-
> hellow
> > + orld+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-
> hellow
> > + orld+0x8329bd)
> > +
> > + Address 0x7fc83f465800 is a wild pointer.
> > + SUMMARY: AddressSanitizer: heap-use-after-free
> > + ../examples/helloworld/main.c:48 in main
>
> ... till here.
>
>
> > +
> > +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
>
> l_undef unnecessary for gcc.
>
>
> > + 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.
>
> This comment should be in patch 2, there is nothing specific in patch
> 1 about DPDK wrt ASan.
>
>
> > + 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
> >
>
>
> --
> 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-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
* Re: [dpdk-dev] [PATCH v9 1/3] Enable ASan for memory detector on DPDK
2021-10-14 6:46 ` Peng, ZhihongX
@ 2021-10-14 7:04 ` Thomas Monjalon
0 siblings, 0 replies; 117+ messages in thread
From: Thomas Monjalon @ 2021-10-14 7:04 UTC (permalink / raw)
To: Peng, ZhihongX
Cc: David Marchand, dev, Burakov, Anatoly, Ananyev, Konstantin,
Stephen Hemminger, dev, Lin, Xueqin, Richardson, Bruce
14/10/2021 08:46, Peng, ZhihongX:
> From: David Marchand <david.marchand@redhat.com>
> > More problematic, linking an external (out of meson) application to a dpdk
> > compiled with ASan is broken.
> >
> > My environment contains following targets compiled using
> > ./devtools/test-meson-builds.sh:
> > $ ls $HOME/builds/
> > build-arm64-bluefield build-arm64-host-clang build-clang-shared build-gcc-
> > shared build-ppc64le-power8 build-x86-mingw
> > build-arm64-dpaa build-arm64-octeontx2 build-clang-static
> > build-gcc-static build-x86-generic
> >
> > I stopped at patch 1, configured following target to have ASan in them, like
> > this:
> >
> > $ meson configure $HOME/builds/build-gcc-static -Db_sanitize=address
> > $ meson configure $HOME/builds/build-clang-shared -Db_sanitize=address -
> > Db_lundef=false $ meson configure $HOME/builds/build-x86-generic -
> > Db_sanitize=address
> > ^^^^^^^^^^^^^^^^^
> > This is the target for which we test linking a dpdk application
> > out of meson.
> >
> > $ meson configure $HOME/builds/build-arm64-bluefield -
> > Db_sanitize=address
>
> I don't know your test platform , arm or x86.
>
> This is our compilation command:
> Gcc is 9.3.0 or 10.3.0
> CC=gcc meson -Db_sanitize=address x86_64-native-linuxapp-gcc
> ninja -C x86_64-native-linuxapp-gcc
> meson configure -Dexamples=helloworld x86_64-native-linuxapp-gcc
> ninja -C x86_64-native-linuxapp-gcc
>
> I don’t know how you get this parameter $HOME/builds/build-x86-generic.
>
> Can you send me all your configuration, I will reproduce this error in our environment.
This is written just below:
> Thank you very much for your help!
>
> > Then ran the check:
> > $ ./devtools/test-meson-builds.sh
Here, this is the command recommended to run in the contributing guide.
https://doc.dpdk.org/guides/contributing/patches.html#checking-compilation
You need, at the very minimum, to install an Arm cross-compiler.
As you are changing stuff in the compilation of the project,
please become familiar with testing compilation in multiple environments.
^ permalink raw reply [flat|nested] 117+ messages in thread
* Re: [dpdk-dev] [PATCH v9 1/3] Enable ASan for memory detector on DPDK
2021-10-13 16:44 ` [dpdk-dev] [PATCH v9 1/3] " David Marchand
2021-10-14 6:46 ` Peng, ZhihongX
@ 2021-10-14 11:42 ` Peng, ZhihongX
1 sibling, 0 replies; 117+ messages in thread
From: Peng, ZhihongX @ 2021-10-14 11:42 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 1/3] Enable ASan for memory detector on DPDK
>
> On Tue, Oct 12, 2021 at 11:54 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 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.
> >
>
>
> Comments below concern added feature with patch 2.
> From here...
The v10 version will be fixed.
> > 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';
> > ......
> ... to here.
>
>
> >
> > 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>
>
>
> More problematic, linking an external (out of meson) application to a dpdk
> compiled with ASan is broken.
>
> My environment contains following targets compiled using
> ./devtools/test-meson-builds.sh:
> $ ls $HOME/builds/
> build-arm64-bluefield build-arm64-host-clang build-clang-shared build-gcc-
> shared build-ppc64le-power8 build-x86-mingw
> build-arm64-dpaa build-arm64-octeontx2 build-clang-static
> build-gcc-static build-x86-generic
>
> I stopped at patch 1, configured following target to have ASan in them, like
> this:
>
> $ meson configure $HOME/builds/build-gcc-static -Db_sanitize=address
> $ meson configure $HOME/builds/build-clang-shared -Db_sanitize=address -
> Db_lundef=false $ meson configure $HOME/builds/build-x86-generic -
> Db_sanitize=address
> ^^^^^^^^^^^^^^^^^
> This is the target for which we test linking a dpdk application
> out of meson.
>
> $ meson configure $HOME/builds/build-arm64-bluefield -
> Db_sanitize=address
>
> Then ran the check:
> $ ./devtools/test-meson-builds.sh
> ...
> ...
> ...
> ## Building cmdline
> /usr/bin/ld: /usr/lib64/libasan.so.6: warning: the use of `tmpnam' is
> dangerous, better use `mkstemp'
> /usr/bin/ld: /usr/lib64/libasan.so.6: warning: the use of `tempnam' is
> dangerous, better use `mkstemp'
> /usr/bin/ld: /usr/lib64/libasan.so.6: warning: the use of `tmpnam_r'
> is dangerous, better use `mkstemp'
> ## Building helloworld
> /usr/bin/ld: /usr/lib64/libasan.so.6: warning: the use of `tmpnam' is
> dangerous, better use `mkstemp'
> /usr/bin/ld: /usr/lib64/libasan.so.6: warning: the use of `tempnam' is
> dangerous, better use `mkstemp'
> /usr/bin/ld: /usr/lib64/libasan.so.6: warning: the use of `tmpnam_r'
> is dangerous, better use `mkstemp'
> /usr/bin/ld: /home/dmarchan/builds/build-x86-
> generic/install/usr/local/lib/librte_common_dpaax.a(common_dpaax_dpaax
> _iova_table.c.o):
> in function `read_memory_node':
> /home/dmarchan/builds/build-x86-
> generic/../../dpdk/drivers/common/dpaax/dpaax_iova_table.c:57:
> undefined reference to `__asan_option_detect_stack_use_after_return'
> /usr/bin/ld: /home/dmarchan/builds/build-x86-
> generic/../../dpdk/drivers/common/dpaax/dpaax_iova_table.c:65:
> undefined reference to `__asan_report_store4'
> /usr/bin/ld: /home/dmarchan/builds/build-x86-
> generic/../../dpdk/drivers/common/dpaax/dpaax_iova_table.c:61:
> undefined reference to `__asan_report_store_n'
> /usr/bin/ld: /home/dmarchan/builds/build-x86-
> generic/../../dpdk/drivers/common/dpaax/dpaax_iova_table.c:60:
> undefined reference to `__asan_report_store_n'
> /usr/bin/ld: /home/dmarchan/builds/build-x86-
> generic/../../dpdk/drivers/common/dpaax/dpaax_iova_table.c:57:
> undefined reference to `__asan_stack_malloc_3'
> /usr/bin/ld: /home/dmarchan/builds/build-x86-
> generic/../../dpdk/drivers/common/dpaax/dpaax_iova_table.c:77:
> undefined reference to `__asan_report_load8'
> /usr/bin/ld: /home/dmarchan/builds/build-x86-
> generic/../../dpdk/drivers/common/dpaax/dpaax_iova_table.c:86:
> undefined reference to `__asan_report_load8'
> /usr/bin/ld: /home/dmarchan/builds/build-x86-
> generic/../../dpdk/drivers/common/dpaax/dpaax_iova_table.c:86:
> undefined reference to `__asan_report_load8'
> /usr/bin/ld: /home/dmarchan/builds/build-x86-
> generic/../../dpdk/drivers/common/dpaax/dpaax_iova_table.c:88:
> undefined reference to `__asan_report_load8'
> /usr/bin/ld: /home/dmarchan/builds/build-x86-
> generic/../../dpdk/drivers/common/dpaax/dpaax_iova_table.c:88:
> undefined reference to `__asan_report_load8'
> /usr/bin/ld: /home/dmarchan/builds/build-x86-
> generic/install/usr/local/lib/librte_common_dpaax.a(common_dpaax_dpaax
> _iova_table.c.o):/home/dmarchan/builds/build-x86-
> generic/../../dpdk/drivers/common/dpaax/dpaax_iova_table.c:102:
> more undefined references to `__asan_report_load8' follow
> /usr/bin/ld: /home/dmarchan/builds/build-x86-
> generic/install/usr/local/lib/librte_common_dpaax.a(common_dpaax_dpaax
> _iova_table.c.o):
> in function `read_memory_node':
> /home/dmarchan/builds/build-x86-
> generic/../../dpdk/drivers/common/dpaax/dpaax_iova_table.c:119:
> undefined reference to `__asan_report_store4'
> /usr/bin/ld: /home/dmarchan/builds/build-x86-
> generic/../../dpdk/drivers/common/dpaax/dpaax_iova_table.c:145:
> undefined reference to `__asan_report_load16'
> /usr/bin/ld: /home/dmarchan/builds/build-x86-
> generic/../../dpdk/drivers/common/dpaax/dpaax_iova_table.c:135:
> undefined reference to `__asan_report_store_n'
> /usr/bin/ld: /home/dmarchan/builds/build-x86-
> generic/../../dpdk/drivers/common/dpaax/dpaax_iova_table.c:143:
> undefined reference to `__asan_report_load8'
> /usr/bin/ld: /home/dmarchan/builds/build-x86-
> generic/install/usr/local/lib/librte_common_dpaax.a(common_dpaax_dpaax
> _iova_table.c.o):
> in function `rotate_8':
> /home/dmarchan/builds/build-x86-
> generic/../../dpdk/drivers/common/dpaax/dpaax_iova_table.c:40:
> undefined reference to `__asan_report_store4'
> /usr/bin/ld: /home/dmarchan/builds/build-x86-
> generic/../../dpdk/drivers/common/dpaax/dpaax_iova_table.c:39:
> undefined reference to `__asan_report_store4'
> /usr/bin/ld: /home/dmarchan/builds/build-x86-
> generic/../../dpdk/drivers/common/dpaax/dpaax_iova_table.c:40:
> undefined reference to `__asan_report_store4'
> /usr/bin/ld: /home/dmarchan/builds/build-x86-
> generic/install/usr/local/lib/librte_common_dpaax.a(common_dpaax_dpaax
> _iova_table.c.o):
> in function `read_memory_node':
> /home/dmarchan/builds/build-x86-
> generic/../../dpdk/drivers/common/dpaax/dpaax_iova_table.c:143:
> undefined reference to `__asan_report_load8'
> *flood of errors about undefined symbols from libasan*
>
>
> Tried to debug, in-tree compiled testpmd shows:
> $ readelf -d $HOME/builds/build-x86-generic/app/dpdk-testpmd | grep asan
> 0x0000000000000001 (NEEDED) Shared library: [libasan.so.6]
> But I see nothing in ninja:
> $ grep -i asan $HOME/builds/build-x86-generic/build.ninja
>
> And for external link, looking at an "installed" .pc:
> $ PKG_CONFIG_PATH=/home/dmarchan/builds/build-x86-
> generic/install/usr/local/lib/pkgconfig
> pkg-config libdpdk --libs | grep asan
Need to link -lasan when using gcc, but don’t need to link when using clang.
You added it in the v6 version comments, but need to restrict when using clang.
All 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
>
>
> Some more comments about splitting doc with patch2:
>
>
> > ---
> > 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+).
>
> Same problem, below should be in patch 2, from here...
The v10 version will be fixed.
>
> > +
> > +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-
> hellow
> > + orld+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-
> hellow
> > + orld+0x8329bd)
> > +
> > + Address 0x7fc83f465800 is a wild pointer.
> > + SUMMARY: AddressSanitizer: heap-use-after-free
> > + ../examples/helloworld/main.c:48 in main
>
> ... till here.
>
>
> > +
> > +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
>
> l_undef unnecessary for gcc.
The v10 version will be fixed.
>
> > + 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.
>
> This comment should be in patch 2, there is nothing specific in patch
> 1 about DPDK wrt ASan.
The v10 version will be fixed.
>
> > + 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
> >
>
>
> --
> 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 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
* [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
* [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 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 1/4] Enable ASan for memory detector on DPDK
2021-10-15 15:11 ` zhihongx.peng
` (2 preceding siblings ...)
2021-10-15 15:11 ` [dpdk-dev] [PATCH v10 4/4] performance-thread: Fix cross compilation failed zhihongx.peng
@ 2021-10-19 5:52 ` Peng, ZhihongX
3 siblings, 0 replies; 117+ messages in thread
From: Peng, ZhihongX @ 2021-10-19 5:52 UTC (permalink / raw)
To: david.marchand, Burakov, Anatoly, Ananyev, Konstantin, stephen,
Dumitrescu, Cristian, Mcnamara, John
Cc: dev, Lin, Xueqin
> -----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>
> Subject: [PATCH v10 1/4] Enable ASan for memory detector on DPDK
>
> 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
Hi, David
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.
Thanks!
^ 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
* 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 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 1/4] Enable ASan for memory detector on DPDK
2021-10-19 10:12 ` [dpdk-dev] [PATCH v11 1/4] Enable ASan for memory detector on DPDK zhihongx.peng
` (2 preceding siblings ...)
2021-10-19 10:12 ` [dpdk-dev] [PATCH v11 4/4] performance-thread: Fix cross compilation failed zhihongx.peng
@ 2021-10-19 10:24 ` Peng, ZhihongX
3 siblings, 0 replies; 117+ messages in thread
From: Peng, ZhihongX @ 2021-10-19 10:24 UTC (permalink / raw)
To: david.marchand, 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: Tuesday, October 19, 2021 6:12 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 v11 1/4] Enable ASan for memory detector on DPDK
>
> 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
Hi, David
I have submitted the v11 version and modified the document and string copy function. The string copy function has been compiled passed on the arm platform.
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.
Thanks!
^ 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
* 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 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
* [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 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
* 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
* [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-