DPDK patches and discussions
 help / color / mirror / Atom feed
* [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; 71+ 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] 71+ 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; 71+ 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] 71+ 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; 71+ 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] 71+ 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; 71+ 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] 71+ 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; 71+ 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] 71+ 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; 71+ 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] 71+ 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; 71+ 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] 71+ 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; 71+ 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] 71+ 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; 71+ 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] 71+ 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; 71+ 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] 71+ 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; 71+ 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] 71+ 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; 71+ 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] 71+ 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; 71+ 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] 71+ 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; 71+ 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] 71+ 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; 71+ 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] 71+ 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; 71+ 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] 71+ 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; 71+ 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] 71+ 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; 71+ 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] 71+ 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; 71+ 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] 71+ 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; 71+ 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] 71+ 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; 71+ 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] 71+ 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; 71+ 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] 71+ 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; 71+ 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] 71+ 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; 71+ 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] 71+ 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; 71+ 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] 71+ 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; 71+ 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] 71+ 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; 71+ 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] 71+ 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; 71+ 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] 71+ 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; 71+ 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] 71+ 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; 71+ 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] 71+ 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; 71+ 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] 71+ 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; 71+ 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] 71+ 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; 71+ 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] 71+ 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; 71+ 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] 71+ 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; 71+ 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] 71+ 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; 71+ 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] 71+ 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; 71+ 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] 71+ 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; 71+ 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] 71+ 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; 71+ 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] 71+ 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; 71+ 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] 71+ 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; 71+ 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] 71+ 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; 71+ 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] 71+ 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; 71+ 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] 71+ 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; 71+ 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] 71+ 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; 71+ 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] 71+ 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; 71+ 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] 71+ 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; 71+ 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] 71+ 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; 71+ 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] 71+ 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; 71+ 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] 71+ 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; 71+ 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] 71+ 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; 71+ 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] 71+ 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; 71+ 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] 71+ 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] Enable ASan for memory detector on DPDK David Marchand
  2 siblings, 1 reply; 71+ 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] 71+ 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                   ` [dpdk-dev] [PATCH v10 1/4] Enable ASan for memory detector on DPDK zhihongx.peng
  2021-10-13 16:44                 ` [dpdk-dev] [PATCH v9 1/3] Enable ASan for memory detector on DPDK David Marchand
  2 siblings, 2 replies; 71+ 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] 71+ 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; 71+ 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] 71+ 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; 71+ 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] 71+ 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; 71+ 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] 71+ 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; 71+ 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] 71+ 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] Enable ASan for memory detector on DPDK 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; 71+ 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] 71+ 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; 71+ 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] 71+ 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; 71+ 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] 71+ 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] Enable ASan for memory detector on DPDK David Marchand
  2021-10-14  6:46                   ` Peng, ZhihongX
@ 2021-10-14 11:42                   ` Peng, ZhihongX
  1 sibling, 0 replies; 71+ 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] 71+ 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; 71+ 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] 71+ 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
                                       ` (2 more replies)
  2021-10-15 15:11                   ` [dpdk-dev] [PATCH v10 1/4] Enable ASan for memory detector on DPDK zhihongx.peng
  1 sibling, 3 replies; 71+ 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] 71+ 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
  2021-10-15 14:27                     ` [dpdk-dev] [PATCH v10 4/4] performance-thread: Fix cross compilation failed zhihongx.peng
  2 siblings, 0 replies; 71+ 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] 71+ 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
  2 siblings, 0 replies; 71+ 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] 71+ 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
  2 siblings, 0 replies; 71+ 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] 71+ 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
                                       ` (2 more replies)
  1 sibling, 3 replies; 71+ 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] 71+ messages in thread

* [dpdk-dev] [PATCH v10 2/4] DPDK code adapts to ASan
  2021-10-15 15:11                   ` [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 3/4] pipeline: Fix compilation error with gcc ASan zhihongx.peng
  2021-10-15 15:11                     ` [dpdk-dev] [PATCH v10 4/4] performance-thread: Fix cross compilation failed zhihongx.peng
  2 siblings, 0 replies; 71+ 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] 71+ messages in thread

* [dpdk-dev] [PATCH v10 3/4] pipeline: Fix compilation error with gcc ASan
  2021-10-15 15:11                   ` [dpdk-dev] [PATCH v10 1/4] Enable ASan for memory detector on DPDK 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-15 15:11                     ` [dpdk-dev] [PATCH v10 4/4] performance-thread: Fix cross compilation failed zhihongx.peng
  2 siblings, 0 replies; 71+ 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] 71+ messages in thread

* [dpdk-dev] [PATCH v10 4/4] performance-thread: Fix cross compilation failed
  2021-10-15 15:11                   ` [dpdk-dev] [PATCH v10 1/4] Enable ASan for memory detector on DPDK 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
  2 siblings, 0 replies; 71+ 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] 71+ messages in thread

end of thread, other threads:[~2021-10-15 15:12 UTC | newest]

Thread overview: 71+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
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-13 15:05     ` Stephen Hemminger
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
2021-09-10 17:58 ` David Christensen
2021-09-13  5:35   ` Peng, ZhihongX
2021-09-17 20:50     ` David Christensen
2021-09-18  7:21       ` Peng, ZhihongX
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
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-17 14:58       ` Stephen Hemminger
2021-09-18  6:36         ` Peng, ZhihongX
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-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  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
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  5:27           ` [dpdk-dev] [PATCH v6 2/2] lib/pipeline: Fix gcc compilation error using ASan zhihongx.peng
2021-09-30  8:29             ` [dpdk-dev] [dpdk-stable] " David Marchand
2021-10-12  2:41               ` Peng, ZhihongX
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  5:41                 ` Peng, ZhihongX
2021-10-12  7:17             ` Peng, ZhihongX
2021-10-13  7:59             ` Bruce Richardson
2021-10-14  6:33               ` Peng, ZhihongX
2021-10-14  6:53                 ` Peng, ZhihongX
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
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             ` [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               ` [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-13 16:45                   ` David Marchand
2021-10-14 11:45                     ` Peng, ZhihongX
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 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                     ` [dpdk-dev] [PATCH v10 4/4] performance-thread: Fix cross compilation failed zhihongx.peng
2021-10-15 15:11                   ` [dpdk-dev] [PATCH v10 1/4] Enable ASan for memory detector on DPDK 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                     ` [dpdk-dev] [PATCH v10 4/4] performance-thread: Fix cross compilation failed zhihongx.peng
2021-10-13 16:44                 ` [dpdk-dev] [PATCH v9 1/3] Enable ASan for memory detector on DPDK David Marchand
2021-10-14  6:46                   ` Peng, ZhihongX
2021-10-14  7:04                     ` Thomas Monjalon
2021-10-14 11:42                   ` Peng, ZhihongX
2021-09-27 12:02     ` [dpdk-dev] [PATCH v3] Enable AddressSanitizer feature " Burakov, Anatoly
2021-09-30  5:18       ` Peng, ZhihongX

DPDK patches and discussions

This inbox may be cloned and mirrored by anyone:

	git clone --mirror http://inbox.dpdk.org/dev/0 dev/git/0.git

	# If you have public-inbox 1.1+ installed, you may
	# initialize and index your mirror using the following commands:
	public-inbox-init -V2 dev dev/ http://inbox.dpdk.org/dev \
		dev@dpdk.org
	public-inbox-index dev

Example config snippet for mirrors.
Newsgroup available over NNTP:
	nntp://inbox.dpdk.org/inbox.dpdk.dev


AGPL code for this site: git clone https://public-inbox.org/public-inbox.git