diff mbox series

[v3,2/2] xmalloc: add a Kconfig option to poison free pool memory

Message ID 20190705144855.15259-3-paul.durrant@citrix.com (mailing list archive)
State New, archived
Headers show
Series xmalloc patches | expand

Commit Message

Paul Durrant July 5, 2019, 2:48 p.m. UTC
This patch adds XMEM_POOL_POISON to the Kconfig DEBUG options. If set,
free blocks (greater than MIN_BLOCK_SIZE) will be poisoned with 0xAA
bytes which will then be verified when memory is subsequently allocated.
This can help in spotting heap corruption, particularly use-after-free.

Signed-off-by: Paul Durrant <paul.durrant@citrix.com>
---
Cc: Andrew Cooper <andrew.cooper3@citrix.com>
Cc: George Dunlap <George.Dunlap@eu.citrix.com>
Cc: Ian Jackson <ian.jackson@eu.citrix.com>
Cc: Jan Beulich <jbeulich@suse.com>
Cc: Julien Grall <julien.grall@arm.com>
Cc: Konrad Rzeszutek Wilk <konrad.wilk@oracle.com>
Cc: Stefano Stabellini <sstabellini@kernel.org>
Cc: Tim Deegan <tim@xen.org>
Cc: Wei Liu <wl@xen.org>

v3:
 - #define the POISON_BYTE
 - Fix whitespace in Kconfig.debug

v2:
 - Change Kconfig option name to XMEM_POOL_POISON
 - Add an implementation of memchr_inv() and use that
---
 xen/Kconfig.debug         |  7 +++++++
 xen/common/string.c       | 20 ++++++++++++++++++++
 xen/common/xmalloc_tlsf.c | 12 ++++++++++++
 xen/include/xen/string.h  |  2 ++
 4 files changed, 41 insertions(+)

Comments

Jan Beulich July 5, 2019, 2:52 p.m. UTC | #1
On 05.07.2019 16:48, Paul Durrant wrote:
> This patch adds XMEM_POOL_POISON to the Kconfig DEBUG options. If set,
> free blocks (greater than MIN_BLOCK_SIZE) will be poisoned with 0xAA
> bytes which will then be verified when memory is subsequently allocated.
> This can help in spotting heap corruption, particularly use-after-free.
> 
> Signed-off-by: Paul Durrant <paul.durrant@citrix.com>

Reviewed-by: Jan Beulich <jbeulich@suse.com>

(I'm pondering whether to add the suggested blank lines while
committing.)
Paul Durrant July 5, 2019, 3:20 p.m. UTC | #2
> -----Original Message-----
> From: Jan Beulich <JBeulich@suse.com>
> Sent: 05 July 2019 15:52
> To: Paul Durrant <Paul.Durrant@citrix.com>
> Cc: xen-devel@lists.xenproject.org; Julien Grall <julien.grall@arm.com>; Andrew Cooper
> <Andrew.Cooper3@citrix.com>; George Dunlap <George.Dunlap@citrix.com>; Ian Jackson
> <Ian.Jackson@citrix.com>; Stefano Stabellini <sstabellini@kernel.org>; Konrad Rzeszutek Wilk
> <konrad.wilk@oracle.com>; Tim (Xen.org) <tim@xen.org>; Wei Liu <wl@xen.org>
> Subject: Re: [PATCH v3 2/2] xmalloc: add a Kconfig option to poison free pool memory
> 
> On 05.07.2019 16:48, Paul Durrant wrote:
> > This patch adds XMEM_POOL_POISON to the Kconfig DEBUG options. If set,
> > free blocks (greater than MIN_BLOCK_SIZE) will be poisoned with 0xAA
> > bytes which will then be verified when memory is subsequently allocated.
> > This can help in spotting heap corruption, particularly use-after-free.
> >
> > Signed-off-by: Paul Durrant <paul.durrant@citrix.com>
> 
> Reviewed-by: Jan Beulich <jbeulich@suse.com>
> 
> (I'm pondering whether to add the suggested blank lines while
> committing.)

Oh, sorry. I forgot about that.

  Paul
diff mbox series

Patch

diff --git a/xen/Kconfig.debug b/xen/Kconfig.debug
index daacf85141..e10e314e25 100644
--- a/xen/Kconfig.debug
+++ b/xen/Kconfig.debug
@@ -105,6 +105,13 @@  config DEBUG_TRACE
 	  either directly to the console or are printed to console in case of
 	  a system crash.
 
+config XMEM_POOL_POISON
+	bool "Poison free xenpool blocks"
+	default DEBUG
+	---help---
+	  Poison free blocks with 0xAA bytes and verify them when a block is
+	  allocated in order to spot use-after-free issues.
+
 endif # DEBUG || EXPERT
 
 endmenu
diff --git a/xen/common/string.c b/xen/common/string.c
index a2bbe7dc97..af3d96ad0f 100644
--- a/xen/common/string.c
+++ b/xen/common/string.c
@@ -423,6 +423,26 @@  void *(memchr)(const void *s, int c, size_t n)
 }
 #endif
 
+/**
+ * memchr_inv - Find an unmatching character in an area of memory.
+ * @s: The memory area
+ * @c: The byte that is expected
+ * @n: The size of the area.
+ *
+ * returns the address of the first occurrence of a character other than @c,
+ * or %NULL if the whole buffer contains just @c.
+ */
+void *memchr_inv(const void *s, int c, size_t n)
+{
+	const unsigned char *p = s;
+
+	while (n--)
+		if ((unsigned char)c != *p++)
+			return (void *)(p - 1);
+
+	return NULL;
+}
+
 /*
  * Local variables:
  * mode: C
diff --git a/xen/common/xmalloc_tlsf.c b/xen/common/xmalloc_tlsf.c
index e4e476a27c..a1acfceaf6 100644
--- a/xen/common/xmalloc_tlsf.c
+++ b/xen/common/xmalloc_tlsf.c
@@ -215,6 +215,8 @@  static inline void EXTRACT_BLOCK_HDR(struct bhdr *b, struct xmem_pool *p, int fl
     b->ptr.free_ptr = (struct free_ptr) {NULL, NULL};
 }
 
+#define POISON_BYTE 0xAA
+
 /**
  * Removes block(b) from free list with indexes (fl, sl)
  */
@@ -238,6 +240,11 @@  static inline void EXTRACT_BLOCK(struct bhdr *b, struct xmem_pool *p, int fl,
         }
     }
     b->ptr.free_ptr = (struct free_ptr) {NULL, NULL};
+#ifdef CONFIG_XMEM_POOL_POISON
+    if ( (b->size & BLOCK_SIZE_MASK) > MIN_BLOCK_SIZE )
+        ASSERT(!memchr_inv(b->ptr.buffer + MIN_BLOCK_SIZE, POISON_BYTE,
+                           (b->size & BLOCK_SIZE_MASK) - MIN_BLOCK_SIZE));
+#endif /* CONFIG_XMEM_POOL_POISON */
 }
 
 /**
@@ -245,6 +252,11 @@  static inline void EXTRACT_BLOCK(struct bhdr *b, struct xmem_pool *p, int fl,
  */
 static inline void INSERT_BLOCK(struct bhdr *b, struct xmem_pool *p, int fl, int sl)
 {
+#ifdef CONFIG_XMEM_POOL_POISON
+    if ( (b->size & BLOCK_SIZE_MASK) > MIN_BLOCK_SIZE )
+        memset(b->ptr.buffer + MIN_BLOCK_SIZE, POISON_BYTE,
+               (b->size & BLOCK_SIZE_MASK) - MIN_BLOCK_SIZE);
+#endif /* CONFIG_XMEM_POOL_POISON */
     b->ptr.free_ptr = (struct free_ptr) {NULL, p->matrix[fl][sl]};
     if ( p->matrix[fl][sl] )
         p->matrix[fl][sl]->ptr.free_ptr.prev = b;
diff --git a/xen/include/xen/string.h b/xen/include/xen/string.h
index 711cb60a7d..4b3b57e74f 100644
--- a/xen/include/xen/string.h
+++ b/xen/include/xen/string.h
@@ -106,6 +106,8 @@  void *memchr(const void *, int, size_t);
 #define memchr(s, c, n) __builtin_memchr(s, c, n)
 #endif
 
+void *memchr_inv(const void *, int, size_t);
+
 #define is_char_array(x) __builtin_types_compatible_p(typeof(x), char[])
 
 /* safe_xxx always NUL-terminates and returns !=0 if result is truncated. */