diff mbox series

[v3,4/8] malloc: Clean up commentary

Message ID 20201123154236.25809-5-rearnsha@arm.com
State Superseded
Headers show
Series Memory tagging support | expand

Commit Message

Richard Earnshaw Nov. 23, 2020, 3:42 p.m. UTC
This patch will be merged with its predecessor before being committed, it is
kept separate for now to ease reviewing.
---
 malloc/arena.c              |   8 +++
 malloc/malloc.c             | 112 +++++++++++++++++++++++++++++-------
 sysdeps/generic/libc-mtag.h |   2 +-
 3 files changed, 101 insertions(+), 21 deletions(-)
diff mbox series

Patch

diff --git a/malloc/arena.c b/malloc/arena.c
index 835fcc0fb3..e348b10978 100644
--- a/malloc/arena.c
+++ b/malloc/arena.c
@@ -288,6 +288,10 @@  libc_hidden_proto (_dl_open_hook);
 #endif
 
 #ifdef _LIBC_MTAG
+
+/* Generate a new (random) tag value for PTR and tag the memory it
+   points to upto __malloc_usable_size (PTR).  Return the newly tagged
+   pointer.  */
 static void *
 __mtag_tag_new_usable (void *ptr)
 {
@@ -297,6 +301,10 @@  __mtag_tag_new_usable (void *ptr)
   return ptr;
 }
 
+/* Generate a new (random) tag value for PTR, set the tags for the
+   memory to the new tag and initialize the memory contents to VAL.
+   In practice this function will only be called with VAL=0, but we
+   keep this parameter to maintain the same prototype as memset.  */
 static void *
 __mtag_tag_new_memset (void *ptr, int val, size_t size)
 {
diff --git a/malloc/malloc.c b/malloc/malloc.c
index b866e87bc3..deabeb010b 100644
--- a/malloc/malloc.c
+++ b/malloc/malloc.c
@@ -381,7 +381,65 @@  __malloc_assert (const char *assertion, const char *file, unsigned int line,
 void * __default_morecore (ptrdiff_t);
 void *(*__morecore)(ptrdiff_t) = __default_morecore;
 
+/* Memory tagging.  */
+
+/* Some systems support the concept of tagging (sometimes known as
+   coloring) memory locations on a fine grained basis.  Each memory
+   location is given a color (normally allocated randomly) and
+   pointers are also colored.  When the pointer is dereferenced, the
+   pointer's color is checked against the memory's color and if they
+   differ the access is faulted (sometimes lazily).
+
+   We use this in glibc by maintaining a single color for the malloc
+   data structures that are interleaved with the user data and then
+   assigning separate colors for each block allocation handed out.  In
+   this way simple buffer overruns will be rapidly detected.  When
+   memory is freed, the memory is recolored back to the glibc default
+   so that simple use-after-free errors can also be detected.
+
+   If memory is reallocated the buffer is recolored even if the
+   address remains the same.  This has a performance impact, but
+   guarantees that the old pointer cannot mistakenly be reused (code
+   that compares old against new will see a mismatch and will then
+   need to behave as though realloc moved the data to a new location).
+
+   Internal API for memory tagging support.
+
+   The aim is to keep the code for memory tagging support as close to
+   the normal APIs in glibc as possible, so that if tagging is not
+   enabled in the library, or is disabled at runtime then standard
+   operations can continue to be used.  Support macros are used to do
+   this:
+
+   void *TAG_NEW_MEMSET (void *ptr, int, val, size_t size)
+
+   Has the same interface as memset(), but additionally allocates a
+   new tag, colors the memory with that tag and returns a pointer that
+   is correctly colored for that location.  The non-tagging version
+   will simply call memset.
+
+   void *TAG_REGION (void *ptr, size_t size)
+
+   Color the region of memory pointed to by PTR and size SIZE with
+   the color of PTR.  Returns the original pointer.
+
+   void *TAG_NEW_USABLE (void *ptr)
+
+   Allocate a new random color and use it to color the region of
+   memory starting at PTR and of size __malloc_usable_size() with that
+   color.  Returns PTR suitably recolored for accessing the memory there.
+
+   void *TAG_AT (void *ptr)
+
+   Read the current color of the memory at the address pointed to by
+   PTR (ignoring it's current color) and return PTR recolored to that
+   color.  PTR must be valid address in all other respects.  When
+   tagging is not enabled, it simply returns the original pointer.
+*/
+
 #ifdef _LIBC_MTAG
+
+/* Default implementaions when memory tagging is supported, but disabled.  */
 static void *
 __default_tag_region (void *ptr, size_t size)
 {
@@ -413,21 +471,6 @@  static void *(*__tag_at)(void *) = __default_tag_nop;
 # define TAG_AT(ptr) (ptr)
 #endif
 
-/* When using tagged memory, we cannot share the end of the user block
-   with the header for the next chunk, so ensure that we allocate
-   blocks that are rounded up to the granule size.  Take care not to
-   overflow from close to MAX_SIZE_T to a small number.  */
-static inline size_t
-ROUND_UP_ALLOCATION_SIZE(size_t bytes)
-{
-#ifdef _LIBC_MTAG
-  return (bytes + ~__mtag_granule_mask) & __mtag_granule_mask;
-#else
-  return bytes;
-#endif
-}
-
-
 #include <string.h>
 
 /*
@@ -1234,10 +1277,26 @@  nextchunk-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   ---------- Size and alignment checks and conversions ----------
 */
 
-/* conversion from malloc headers to user pointers, and back */
+/* Conversion from malloc headers to user pointers, and back.  When
+   using memory tagging the user data and the malloc data structure
+   headers have distinct tags.  Converting fully from one to the other
+   involves extracting the tag at the other address and creating a
+   suitable pointer using it.  That can be quite expensive.  There are
+   many occasions, though when the pointer will not be dereferenced
+   (for example, because we only want to assert that the pointer is
+   correctly aligned).  In these cases it is more efficient not
+   to extract the tag, since the answer will be the same either way.
+   chunk2rawmem() can be used in these cases.
+ */
 
-#define chunk2mem(p) ((void*)TAG_AT (((char*)(p) + 2*SIZE_SZ)))
+/* Convert a user mem pointer to a chunk address without correcting
+   the tag.  */
 #define chunk2rawmem(p) ((void*)((char*)(p) + 2*SIZE_SZ))
+
+/* Convert between user mem pointers and chunk pointers, updating any
+   memory tags on the pointer to respect the tag value at that
+   location.  */
+#define chunk2mem(p) ((void*)TAG_AT (((char*)(p) + 2*SIZE_SZ)))
 #define mem2chunk(mem) ((mchunkptr)TAG_AT (((char*)(mem) - 2*SIZE_SZ)))
 
 /* The smallest possible chunk */
@@ -1257,7 +1316,8 @@  nextchunk-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
    & MALLOC_ALIGN_MASK)
 
 /* pad request bytes into a usable size -- internal version */
-
+/* Note: This must be a macro that evaluates to a compile time constant
+   if passed a literal constant.  */
 #define request2size(req)                                         \
   (((req) + SIZE_SZ + MALLOC_ALIGN_MASK < MINSIZE)  ?             \
    MINSIZE :                                                      \
@@ -1272,7 +1332,18 @@  checked_request2size (size_t req, size_t *sz) __nonnull (1)
 {
   if (__glibc_unlikely (req > PTRDIFF_MAX))
     return false;
-  req = ROUND_UP_ALLOCATION_SIZE (req);
+
+#ifdef _LIBC_MTAG
+  /* When using tagged memory, we cannot share the end of the user
+     block with the header for the next chunk, so ensure that we
+     allocate blocks that are rounded up to the granule size.  Take
+     care not to overflow from close to MAX_SIZE_T to a small
+     number.  Ideally, this would be part of request2size(), but that
+     must be a macro that produces a compile time constant if passed
+     a constant literal.  */
+  req = (req + ~__mtag_granule_mask) & __mtag_granule_mask;
+#endif
+
   *sz = request2size (req);
   return true;
 }
@@ -3391,6 +3462,7 @@  _mid_memalign (size_t alignment, size_t bytes, void *address)
       return 0;
     }
 
+
   /* Make sure alignment is power of 2.  */
   if (!powerof2 (alignment))
     {
@@ -4784,7 +4856,7 @@  _int_realloc(mstate av, mchunkptr oldp, INTERNAL_SIZE_T oldsize,
 
   check_inuse_chunk (av, newp);
   return TAG_NEW_USABLE (chunk2rawmem (newp));
-    }
+}
 
 /*
    ------------------------------ memalign ------------------------------
diff --git a/sysdeps/generic/libc-mtag.h b/sysdeps/generic/libc-mtag.h
index 3e9885451c..07f0203253 100644
--- a/sysdeps/generic/libc-mtag.h
+++ b/sysdeps/generic/libc-mtag.h
@@ -1,5 +1,5 @@ 
 /* libc-internal interface for tagged (colored) memory support.
-   Copyright (C) 2019 Free Software Foundation, Inc.
+   Copyright (C) 2020 Free Software Foundation, Inc.
    This file is part of the GNU C Library.
 
    The GNU C Library is free software; you can redistribute it and/or