mirror of
https://gitlab.gnome.org/GNOME/glib.git
synced 2024-12-25 15:06:14 +01:00
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
This commit is contained in:
parent
2a64176b83
commit
87f0a5a219
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
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;
|
||||
g_assert (real_size < (G_MAXSIZE - (alignment - offset)));
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user