@@ -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
@@ -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
@@ -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;
@@ -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. */
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(+)