[glib/rc-align: 25/26] Align the reference counted allocations



commit 35b5699467be136360d0da60f17568fda4563a39
Author: Emmanuele Bassi <ebassi gnome org>
Date:   Wed Nov 14 14:43:23 2018 +0000

    Align the reference counted allocations
    
    We need stronger alignment guarantees for the memory allocations done
    through g_rc_box_alloc_full(): while the passed block size may be
    aligned, we're not aligning the private data size; this means the
    overall allocation may become unaligned, and this could raise issues
    when we use the private data size as an offset to access the reference
    count.
    
    Fixes: #1581

 glib/garcbox.c       | 10 ++++----
 glib/grcbox.c        | 68 +++++++++++++++++++++++++++++++++++++++++++---------
 glib/grcboxprivate.h | 10 ++++++++
 3 files changed, 73 insertions(+), 15 deletions(-)
---
diff --git a/glib/garcbox.c b/glib/garcbox.c
index 9c1bd8fe5..02c9266aa 100644
--- a/glib/garcbox.c
+++ b/glib/garcbox.c
@@ -177,7 +177,7 @@ g_atomic_rc_box_alloc (gsize block_size)
 {
   g_return_val_if_fail (block_size > 0, NULL);
 
-  return g_rc_box_alloc_full (block_size, TRUE, FALSE);
+  return g_rc_box_alloc_full (block_size, STRUCT_ALIGNMENT, TRUE, FALSE);
 }
 
 /**
@@ -201,7 +201,7 @@ g_atomic_rc_box_alloc0 (gsize block_size)
 {
   g_return_val_if_fail (block_size > 0, NULL);
 
-  return g_rc_box_alloc_full (block_size, TRUE, TRUE);
+  return g_rc_box_alloc_full (block_size, STRUCT_ALIGNMENT, TRUE, TRUE);
 }
 
 /**
@@ -262,7 +262,7 @@ gpointer
   g_return_val_if_fail (block_size > 0, NULL);
   g_return_val_if_fail (mem_block != NULL, NULL);
 
-  res = g_rc_box_alloc_full (block_size, TRUE, FALSE);
+  res = g_rc_box_alloc_full (block_size, STRUCT_ALIGNMENT, TRUE, FALSE);
   memcpy (res, mem_block, block_size);
 
   return res;
@@ -339,13 +339,15 @@ g_atomic_rc_box_release_full (gpointer       mem_block,
 
   if (g_atomic_ref_count_dec (&real_box->ref_count))
     {
+      char *real_mem = (char *) real_box - real_box->private_offset;
+
       TRACE (GLIB_RCBOX_RELEASE (mem_block, 1));
 
       if (clear_func != NULL)
         clear_func (mem_block);
 
       TRACE (GLIB_RCBOX_FREE (mem_block));
-      g_free (real_box);
+      g_free (real_mem);
     }
 }
 
diff --git a/glib/grcbox.c b/glib/grcbox.c
index f31db78ab..2d8239ffc 100644
--- a/glib/grcbox.c
+++ b/glib/grcbox.c
@@ -162,25 +162,49 @@
  * Since: 2.58.
  */
 
-#define G_RC_BOX(p)             (GRcBox *) (((char *) (p)) - G_RC_BOX_SIZE)
-
 /* We use the same alignment as GTypeInstance and GNU libc's malloc */
-#define STRUCT_ALIGNMENT        (2 * sizeof (gsize))
 #define ALIGN_STRUCT(offset)    ((offset + (STRUCT_ALIGNMENT - 1)) & -STRUCT_ALIGNMENT)
 
+#define G_RC_BOX(p)             (GRcBox *) (((char *) (p)) - G_RC_BOX_SIZE)
+
 gpointer
 g_rc_box_alloc_full (gsize    block_size,
+                     gsize    alignment,
                      gboolean atomic,
                      gboolean clear)
 {
-  /* sizeof GArcBox == sizeof GRcBox */
+  /* We don't do an (atomic ? G_ARC_BOX_SIZE : G_RC_BOX_SIZE) check, here
+   * because we have a static assertion that sizeof(GArcBox) == sizeof(GRcBox)
+   * inside grcboxprivate.h, and we don't want the compiler to unnecessarily
+   * warn about both branches of the conditional yielding identical results
+   */
   gsize private_size = G_ARC_BOX_SIZE;
+  gsize private_offset = 0;
   gsize real_size;
   char *allocated;
 
-  g_assert (block_size < (G_MAXSIZE - G_ARC_BOX_SIZE));
+  g_assert (alignment != 0);
+
+  /* We need to ensure that the private data is aligned */
+  if (private_size % alignment != 0)
+    {
+      private_offset = private_size % alignment;
+      g_assert (private_size < (G_MAXSIZE - (alignment - private_offset)));
+      private_size += (alignment - private_offset);
+    }
+
+  g_assert (block_size < (G_MAXSIZE - private_size));
   real_size = private_size + block_size;
 
+  /* The real allocated size must be a multiple of @alignment, to
+   * maintain the alignment of block_size
+   */
+  if (real_size % alignment != 0)
+    {
+      gsize offset = real_size % alignment;
+      real_size += (alignment - offset);
+    }
+
 #ifdef ENABLE_VALGRIND
   if (RUNNING_ON_VALGRIND)
     {
@@ -214,8 +238,18 @@ g_rc_box_alloc_full (gsize    block_size,
 
   if (atomic)
     {
-      GArcBox *real_box = (GArcBox *) allocated;
+      /* We leave the alignment padding at the top of the allocation,
+       * so we have an in memory layout of:
+       *
+       *  |[ offset ][ sizeof(GArcBox) ]||[ block_size ]|
+       */
+      GArcBox *real_box = (GArcBox *) (allocated + private_offset);
+      /* Store the real size */
       real_box->mem_size = block_size;
+      /* Store the alignment offset, to be used when freeing the
+       * allocated block
+       */
+      real_box->private_offset = private_offset;
 #ifndef G_DISABLE_ASSERT
       real_box->magic = G_BOX_MAGIC;
 #endif
@@ -223,8 +257,18 @@ g_rc_box_alloc_full (gsize    block_size,
     }
   else
     {
-      GRcBox *real_box = (GRcBox *) allocated;
+      /* We leave the alignment padding at the top of the allocation,
+       * so we have an in memory layout of:
+       *
+       *  |[ offset ][ sizeof(GRcBox) ]||[ block_size ]|
+       */
+      GRcBox *real_box = (GRcBox *) (allocated + private_offset);
+      /* Store the real size */
       real_box->mem_size = block_size;
+      /* Store the alignment offset, to be used when freeing the
+       * allocated block
+       */
+      real_box->private_offset = private_offset;
 #ifndef G_DISABLE_ASSERT
       real_box->magic = G_BOX_MAGIC;
 #endif
@@ -255,7 +299,7 @@ g_rc_box_alloc (gsize block_size)
 {
   g_return_val_if_fail (block_size > 0, NULL);
 
-  return g_rc_box_alloc_full (block_size, FALSE, FALSE);
+  return g_rc_box_alloc_full (block_size, STRUCT_ALIGNMENT, FALSE, FALSE);
 }
 
 /**
@@ -279,7 +323,7 @@ g_rc_box_alloc0 (gsize block_size)
 {
   g_return_val_if_fail (block_size > 0, NULL);
 
-  return g_rc_box_alloc_full (block_size, FALSE, TRUE);
+  return g_rc_box_alloc_full (block_size, STRUCT_ALIGNMENT, FALSE, TRUE);
 }
 
 /**
@@ -339,7 +383,7 @@ gpointer
   g_return_val_if_fail (block_size > 0, NULL);
   g_return_val_if_fail (mem_block != NULL, NULL);
 
-  res = g_rc_box_alloc_full (block_size, FALSE, FALSE);
+  res = g_rc_box_alloc_full (block_size, STRUCT_ALIGNMENT, FALSE, FALSE);
   memcpy (res, mem_block, block_size);
 
   return res;
@@ -416,13 +460,15 @@ g_rc_box_release_full (gpointer       mem_block,
 
   if (g_ref_count_dec (&real_box->ref_count))
     {
+      char *real_mem = (char *) real_box - real_box->private_offset;
+
       TRACE (GLIB_RCBOX_RELEASE (mem_block, 0));
 
       if (clear_func != NULL)
         clear_func (mem_block);
 
       TRACE (GLIB_RCBOX_FREE (mem_block));
-      g_free (real_box);
+      g_free (real_mem);
     }
 }
 
diff --git a/glib/grcboxprivate.h b/glib/grcboxprivate.h
index 8b0d8dd4e..73b578d40 100644
--- a/glib/grcboxprivate.h
+++ b/glib/grcboxprivate.h
@@ -27,6 +27,7 @@ typedef struct {
   grefcount ref_count;
 
   gsize mem_size;
+  gsize private_offset;
 
 #ifndef G_DISABLE_ASSERT
   /* A "magic" number, used to perform additional integrity
@@ -40,6 +41,7 @@ typedef struct {
   gatomicrefcount ref_count;
 
   gsize mem_size;
+  gsize private_offset;
 
 #ifndef G_DISABLE_ASSERT
   guint32 magic;
@@ -51,10 +53,18 @@ typedef struct {
 /* Keep the two refcounted boxes identical in size */
 G_STATIC_ASSERT (sizeof (GRcBox) == sizeof (GArcBox));
 
+/* This is the default alignment we use when allocating the
+ * refcounted memory blocks; it's similar to the alignment
+ * guaranteed by the malloc() in GNU's libc and by the GSlice
+ * allocator
+ */
+#define STRUCT_ALIGNMENT (2 * sizeof (gsize))
+
 #define G_RC_BOX_SIZE sizeof (GRcBox)
 #define G_ARC_BOX_SIZE sizeof (GArcBox)
 
 gpointer        g_rc_box_alloc_full     (gsize    block_size,
+                                         gsize    alignment,
                                          gboolean atomic,
                                          gboolean clear);
 


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]