]> git.ipfire.org Git - thirdparty/glibc.git/commitdiff
elf: Avoid using memalign for TLS allocations [BZ #17730]
authorFlorian Weimer <fweimer@redhat.com>
Wed, 3 Aug 2016 14:15:38 +0000 (16:15 +0200)
committerFlorian Weimer <fweimer@redhat.com>
Wed, 3 Aug 2016 14:15:38 +0000 (16:15 +0200)
Instead of a flag which indicates the pointer can be freed, dtv_t
now includes the pointer which should be freed.  Due to padding,
the size of dtv_t does not increase.

To avoid using memalign, the new allocate_dtv_entry function
allocates a sufficiently large buffer so that a sub-buffer
can be found in it which starts with an aligned pointer.  Both
the aligned and original pointers are kept, the latter for calling
free later.

ChangeLog
csu/libc-tls.c
elf/dl-tls.c
nptl/allocatestack.c
sysdeps/generic/dl-dtv.h

index f222c13cebc4b83a9125265526bb2cf9c6bd7b27..5bc45b5e910442b01a79f9804012e2885021722f 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,25 @@
+2016-08-03  Florian Weimer  <fweimer@redhat.com>
+
+       [BZ #17730]
+       Avoid using memalign for TLS allocations.
+       * sysdeps/generic/dl-dtv.h (struct dtv_pointer): New.  Replaces
+       is_static member with to_free member.
+       (union dtv): Use struct dtv_pointer.
+       * csu/libc-tls.c (__libc_setup_tls): Set to_free member of struct
+       dtv_pointer instead of is_static.
+       * elf/dl-tls.c (_dl_allocate_tls_init): Likewise.
+       (_dl_deallocate_tls): Free to_free member of struct dtv_pointer
+       instead of val.
+       (allocate_dtv_entry): New function.
+       (allocate_and_init): Return struct dtv_pointer.  Call
+       allocate_dtv_entry instead of __libc_memalign.
+       (_dl_update_slotinfo): Free to_free member of struct dtv_pointer
+       instead of val.
+       (tls_get_addr_tail): Set to_free member of struct dtv_pointer
+       instead of is_static.  Adjust call to allocate_and_init.
+       * nptl/allocatestack.c (get_cached_stack): Free to_free member of
+       struct dtv_pointer instead of val.
+
 2016-08-03  Florian Weimer  <fweimer@redhat.com>
 
        * malloc/malloc.c (INTERNAL_SIZE_T, SIZE_SZ, MALLOC_ALIGNMENT)
index d6425e0189216e619421409a380b1f55cf7f4c60..235ac798edb9609a34a6933addf2f93d3e381eca 100644 (file)
@@ -175,7 +175,7 @@ __libc_setup_tls (size_t tcbsize, size_t tcbalign)
 #else
 # error "Either TLS_TCB_AT_TP or TLS_DTV_AT_TP must be defined"
 #endif
-  _dl_static_dtv[2].pointer.is_static = true;
+  _dl_static_dtv[2].pointer.to_free = NULL;
   /* sbrk gives us zero'd memory, so we don't need to clear the remainder.  */
   memcpy (_dl_static_dtv[2].pointer.val, initimage, filesz);
 
index ed13fd950bac00702776464ae975f49d3937f031..be6a3c7ef5fa281e71150972b8df98a5c3987179 100644 (file)
@@ -494,7 +494,7 @@ _dl_allocate_tls_init (void *result)
          maxgen = MAX (maxgen, listp->slotinfo[cnt].gen);
 
          dtv[map->l_tls_modid].pointer.val = TLS_DTV_UNALLOCATED;
-         dtv[map->l_tls_modid].pointer.is_static = false;
+         dtv[map->l_tls_modid].pointer.to_free = NULL;
 
          if (map->l_tls_offset == NO_TLS_OFFSET
              || map->l_tls_offset == FORCED_DYNAMIC_TLS_OFFSET)
@@ -551,9 +551,7 @@ _dl_deallocate_tls (void *tcb, bool dealloc_tcb)
 
   /* We need to free the memory allocated for non-static TLS.  */
   for (size_t cnt = 0; cnt < dtv[-1].counter; ++cnt)
-    if (! dtv[1 + cnt].pointer.is_static
-       && dtv[1 + cnt].pointer.val != TLS_DTV_UNALLOCATED)
-      free (dtv[1 + cnt].pointer.val);
+    free (dtv[1 + cnt].pointer.to_free);
 
   /* The array starts with dtv[-1].  */
   if (dtv != GL(dl_initial_dtv))
@@ -594,21 +592,49 @@ rtld_hidden_def (_dl_deallocate_tls)
 #  define GET_ADDR_OFFSET ti->ti_offset
 # endif
 
+/* Allocate one DTV entry.  */
+static struct dtv_pointer
+allocate_dtv_entry (size_t alignment, size_t size)
+{
+  if (powerof2 (alignment) && alignment <= _Alignof (max_align_t))
+    {
+      /* The alignment is supported by malloc.  */
+      void *ptr = malloc (size);
+      return (struct dtv_pointer) { ptr, ptr };
+    }
 
-static void *
+  /* Emulate memalign to by manually aligning a pointer returned by
+     malloc.  First compute the size with an overflow check.  */
+  size_t alloc_size = size + alignment;
+  if (alloc_size < size)
+    return (struct dtv_pointer) {};
+
+  /* Perform the allocation.  This is the pointer we need to free
+     later.  */
+  void *start = malloc (alloc_size);
+  if (start == NULL)
+    return (struct dtv_pointer) {};
+
+  /* Find the aligned position within the larger allocation.  */
+  void *aligned = (void *) roundup ((uintptr_t) start, alignment);
+
+  return (struct dtv_pointer) { .val = aligned, .to_free = start };
+}
+
+static struct dtv_pointer
 allocate_and_init (struct link_map *map)
 {
-  void *newp;
-
-  newp = __libc_memalign (map->l_tls_align, map->l_tls_blocksize);
-  if (newp == NULL)
+  struct dtv_pointer result = allocate_dtv_entry
+    (map->l_tls_align, map->l_tls_blocksize);
+  if (result.val == NULL)
     oom ();
 
   /* Initialize the memory.  */
-  memset (__mempcpy (newp, map->l_tls_initimage, map->l_tls_initimage_size),
+  memset (__mempcpy (result.val, map->l_tls_initimage,
+                    map->l_tls_initimage_size),
          '\0', map->l_tls_blocksize - map->l_tls_initimage_size);
 
-  return newp;
+  return result;
 }
 
 
@@ -678,12 +704,9 @@ _dl_update_slotinfo (unsigned long int req_modid)
                    {
                      /* If this modid was used at some point the memory
                         might still be allocated.  */
-                     if (! dtv[total + cnt].pointer.is_static
-                         && (dtv[total + cnt].pointer.val
-                             != TLS_DTV_UNALLOCATED))
-                       free (dtv[total + cnt].pointer.val);
+                     free (dtv[total + cnt].pointer.to_free);
                      dtv[total + cnt].pointer.val = TLS_DTV_UNALLOCATED;
-                     dtv[total + cnt].pointer.is_static = false;
+                     dtv[total + cnt].pointer.to_free = NULL;
                    }
 
                  continue;
@@ -708,16 +731,9 @@ _dl_update_slotinfo (unsigned long int req_modid)
                 dtv entry free it.  */
              /* XXX Ideally we will at some point create a memory
                 pool.  */
-             if (! dtv[modid].pointer.is_static
-                 && dtv[modid].pointer.val != TLS_DTV_UNALLOCATED)
-               /* Note that free is called for NULL is well.  We
-                  deallocate even if it is this dtv entry we are
-                  supposed to load.  The reason is that we call
-                  memalign and not malloc.  */
-               free (dtv[modid].pointer.val);
-
+             free (dtv[modid].pointer.to_free);
              dtv[modid].pointer.val = TLS_DTV_UNALLOCATED;
-             dtv[modid].pointer.is_static = false;
+             dtv[modid].pointer.to_free = NULL;
 
              if (modid == req_modid)
                the_map = map;
@@ -780,7 +796,7 @@ tls_get_addr_tail (GET_ADDR_ARGS, dtv_t *dtv, struct link_map *the_map)
 #endif
          __rtld_lock_unlock_recursive (GL(dl_load_lock));
 
-         dtv[GET_ADDR_MODULE].pointer.is_static = true;
+         dtv[GET_ADDR_MODULE].pointer.to_free = NULL;
          dtv[GET_ADDR_MODULE].pointer.val = p;
 
          return (char *) p + GET_ADDR_OFFSET;
@@ -788,10 +804,11 @@ tls_get_addr_tail (GET_ADDR_ARGS, dtv_t *dtv, struct link_map *the_map)
       else
        __rtld_lock_unlock_recursive (GL(dl_load_lock));
     }
-  void *p = dtv[GET_ADDR_MODULE].pointer.val = allocate_and_init (the_map);
-  assert (!dtv[GET_ADDR_MODULE].pointer.is_static);
+  struct dtv_pointer result = allocate_and_init (the_map);
+  dtv[GET_ADDR_MODULE].pointer = result;
+  assert (result.to_free != NULL);
 
-  return (char *) p + GET_ADDR_OFFSET;
+  return (char *) result.val + GET_ADDR_OFFSET;
 }
 
 
index 6b42b11d5a77848a15a557efddddf2205d95c2ca..60b34dc6fcb6d18e3db59b9905423cdc73e94d99 100644 (file)
@@ -245,9 +245,7 @@ get_cached_stack (size_t *sizep, void **memp)
   /* Clear the DTV.  */
   dtv_t *dtv = GET_DTV (TLS_TPADJ (result));
   for (size_t cnt = 0; cnt < dtv[-1].counter; ++cnt)
-    if (! dtv[1 + cnt].pointer.is_static
-       && dtv[1 + cnt].pointer.val != TLS_DTV_UNALLOCATED)
-      free (dtv[1 + cnt].pointer.val);
+    free (dtv[1 + cnt].pointer.to_free);
   memset (dtv, '\0', (dtv[-1].counter + 1) * sizeof (dtv_t));
 
   /* Re-initialize the TLS.  */
index 36c5c58cddb16dd5816db8d74c8eab17e7a29bbb..39d8fe27def2f039c3af5736659d2831d85e1672 100644 (file)
 #ifndef _DL_DTV_H
 #define _DL_DTV_H
 
+struct dtv_pointer
+{
+  void *val;                    /* Pointer to data, or TLS_DTV_UNALLOCATED.  */
+  void *to_free;                /* Unaligned pointer, for deallocation.  */
+};
+
 /* Type for the dtv.  */
 typedef union dtv
 {
   size_t counter;
-  struct
-  {
-    void *val;
-    bool is_static;
-  } pointer;
+  struct dtv_pointer pointer;
 } dtv_t;
 
 /* Value used for dtv entries for which the allocation is delayed.  */