about summary refs log tree commit diff
path: root/elf/dl-tls.c
diff options
context:
space:
mode:
authorFlorian Weimer <fweimer@redhat.com>2016-08-03 16:15:38 +0200
committerFlorian Weimer <fweimer@redhat.com>2016-08-03 16:15:38 +0200
commita2ff21f825adb8821eeb6145197fa8b9a8a60a58 (patch)
tree65f74b90eb0a005a278f80586b0ee001d358021b /elf/dl-tls.c
parent5bc17330eb7667b96fee8baf3729c3310fa28b40 (diff)
downloadglibc-a2ff21f825adb8821eeb6145197fa8b9a8a60a58.tar.gz
glibc-a2ff21f825adb8821eeb6145197fa8b9a8a60a58.tar.xz
glibc-a2ff21f825adb8821eeb6145197fa8b9a8a60a58.zip
elf: Avoid using memalign for TLS allocations [BZ #17730]
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.
Diffstat (limited to 'elf/dl-tls.c')
-rw-r--r--elf/dl-tls.c75
1 files changed, 46 insertions, 29 deletions
diff --git a/elf/dl-tls.c b/elf/dl-tls.c
index ed13fd950b..be6a3c7ef5 100644
--- a/elf/dl-tls.c
+++ b/elf/dl-tls.c
@@ -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;
 }