[PATCH v1 1/1] rtld: add tunable for audit-specific surplus TLS

DJ Delorie <[email protected]>
Newsgroups gmane.comp.lib.glibc.alpha
Message-ID <[email protected]>
Add glibc.rtld.audit_static_tls which specifies additional
surplus TLS just for audit modules, as when audit modules are
used the loader can't compute TLS space before the auditors
run.  This surplus exists until the loader can recompute the
TLS needs, and is reduced for future threads.

---
Note: this patch is built on the optional_static_tls_alignment patch,
so CI/CD might complain about the missing context.
https://inbox.sourceware.org/libc-alpha/587b3a82d77ed767a78068dd67ab529e2a9e1499.1786567326.git.dj@redhat.com/

 elf/dl-tls.c               | 43 ++++++++++++++++++++++++++++++++++++--
 elf/dl-tunables.list       |  5 +++++
 elf/rtld.c                 |  2 ++
 manual/tunables.texi       | 22 +++++++++++++++++++
 sysdeps/generic/ldsodefs.h |  5 +++++
 5 files changed, 75 insertions(+), 2 deletions(-)

diff --git a/elf/dl-tls.c b/elf/dl-tls.c
index e4c93bace0..b085ca16f2 100644
--- a/elf/dl-tls.c
+++ b/elf/dl-tls.c
@@ -127,6 +127,10 @@ tls_static_surplus (int nns, int opt_tls)
 #define LEGACY_TLS (1664 - tls_static_surplus (DEFAULT_NNS, OPTIONAL_TLS))
 
 static size_t optional_static_tls_alignment = 0;
+static size_t dl_audit_tls = 0;
+static size_t dl_original_optional = 0;
+static size_t dl_original_surplus = 0;
+static size_t dl_original_align = 1;
 
 /* Calculate the size of the static TLS surplus, when the given
    number of audit modules are loaded.  Must be called after the
@@ -139,6 +143,8 @@ _dl_tls_static_surplus_init (size_t naudit)
   nns = TUNABLE_GET (nns, size_t, NULL);
   opt_tls = TUNABLE_GET (optional_static_tls, size_t, NULL);
   optional_static_tls_alignment = TUNABLE_GET (optional_static_tls_alignment, size_t, NULL);
+  if (naudit)
+    dl_audit_tls = TUNABLE_GET (audit_static_tls, size_t, NULL);
   if (nns > DL_NNS)
     nns = DL_NNS;
   if (DL_NNS - nns < naudit)
@@ -149,6 +155,33 @@ _dl_tls_static_surplus_init (size_t naudit)
   GL(dl_tls_static_optional) = opt_tls;
   assert (LEGACY_TLS >= 0);
   GLRO(dl_tls_static_surplus) = tls_static_surplus (nns, opt_tls) + LEGACY_TLS;
+
+  if (dl_audit_tls > 0)
+    {
+      if (dl_audit_tls >= SIZE_MAX / 2)
+	_dl_fatal_printf ("glibc.rtld.audit_static_tls exceeds SIZE_MAX/2\n");
+      dl_original_optional = GL(dl_tls_static_optional);
+      dl_original_surplus = GLRO(dl_tls_static_surplus);
+    }
+}
+
+/* Undo as much of the audit-specific extra TLS space accounted for
+   above, as possible.  After this function, the unused amount of TLS
+   must be at least the dl_tls_static_surplus amount.  */
+void
+_dl_tls_prune_audit_storage (void)
+{
+  if (dl_audit_tls > 0)
+    {
+      GL(dl_tls_static_optional) = dl_original_optional;
+      GLRO(dl_tls_static_surplus) = dl_original_surplus;
+      GLRO(dl_tls_static_size) = roundup (GL(dl_tls_static_used)
+					  + dl_original_surplus,
+					  dl_original_align);
+#ifdef TLS_TCB_AT_TP
+      GLRO(dl_tls_static_size) += TLS_TCB_SIZE;
+#endif
+    }
 }
 
 /* Out-of-memory handler.  */
@@ -374,9 +407,12 @@ _dl_determine_tlsoffset (void)
   _dl_extra_tls_set_offset (-offset);
 
   GL(dl_tls_static_used) = offset;
-  GLRO (dl_tls_static_size) = (roundup (offset + GLRO(dl_tls_static_surplus),
+  GLRO (dl_tls_static_size) = (roundup (offset
+					+ GLRO(dl_tls_static_surplus)
+					+ dl_audit_tls,
 					max_align)
 			       + TLS_TCB_SIZE);
+  dl_original_align = max_align;
 #elif TLS_DTV_AT_TP
   /* The TLS blocks start right after the TCB.  */
   size_t offset = TLS_TCB_SIZE;
@@ -461,8 +497,11 @@ _dl_determine_tlsoffset (void)
   offset += extra_tls_size;
 
   GL(dl_tls_static_used) = offset;
-  GLRO (dl_tls_static_size) = roundup (offset + GLRO(dl_tls_static_surplus),
+  GLRO (dl_tls_static_size) = roundup (offset
+				       + GLRO(dl_tls_static_surplus)
+				       + dl_audit_tls,
 				       TCB_ALIGNMENT);
+  dl_original_align = TCP_ALIGNMENT;
 #else
 # error "Either TLS_TCB_AT_TP or TLS_DTV_AT_TP must be defined"
 #endif
diff --git a/elf/dl-tunables.list b/elf/dl-tunables.list
index 4c78444542..d8daf30ab0 100644
--- a/elf/dl-tunables.list
+++ b/elf/dl-tunables.list
@@ -106,6 +106,11 @@ glibc {
       minval: 0
       default: 0
     }
+    audit_static_tls {
+      type: SIZE_T
+      minval: 0
+      default: 0
+    }
     enable_secure {
       type: INT_32
       minval: 0
diff --git a/elf/rtld.c b/elf/rtld.c
index b37c650631..e4f4dcf4b8 100644
--- a/elf/rtld.c
+++ b/elf/rtld.c
@@ -2353,6 +2353,8 @@ dl_main (const ElfW(Phdr) *phdr,
       _dl_debug_post_relocate (main_map);
     }
 
+  _dl_tls_prune_audit_storage ();
+
   /* All ld.so initialization is complete.  Apply RELRO.  */
   _dl_protect_relro (&_dl_rtld_map);
 
diff --git a/manual/tunables.texi b/manual/tunables.texi
index b6bf0a3964..8e1f905081 100644
--- a/manual/tunables.texi
+++ b/manual/tunables.texi
@@ -430,6 +430,28 @@ requirement, as an alternative to requesting excessive
 optional_static_tls (above).
 @end deftp
 
+@deftp Tunable glibc.rtld.audit_static_tls
+Sets an additional amount of memory to be allocated in the initial
+thread's TLS when one or more auditing modules are to be loaded.
+Since auditing modules must run before the loader is done loading
+dynamic objects, the loader cannot know how much TLS space is needed
+before it must create the first TLS area.  If no auditing modules are
+loaded, the initial TLS size can be computed after the dynamic objects
+are loaded.  When this tunable is used, the initial TLS size can be
+very large, and the size computation is adjusted after loading is
+complete, so that any additional threads created will have a more
+suitable TLS size (including any optional space as specified by
+@code{glibc.rtld.optional_static_tls}).  The default is to not
+allocate any additional TLS for audit modules.
+
+As an example, if a program required 1MB of TLS space and specified an
+optional 1MB surplus, normally every thread would have 2MB of TLS
+space.  If an auditing module requires 3MB of TLS space, and 5MB of
+audit TLS was specified by this tunable, the main thread would have
+7MB of TLS space (2MB plus the specified 5MB) and every other thread
+would have 5MB (2MB plus the actual 3MB).
+@end deftp
+
 @deftp Tunable glibc.rtld.dynamic_sort
 Sets the algorithm to use for DSO sorting, valid values are @samp{1} and
 @samp{2}.  For value of @samp{1}, an older O(n^3) algorithm is used, which is
diff --git a/sysdeps/generic/ldsodefs.h b/sysdeps/generic/ldsodefs.h
index 305ca6e0df..54535452b3 100644
--- a/sysdeps/generic/ldsodefs.h
+++ b/sysdeps/generic/ldsodefs.h
@@ -1184,6 +1184,11 @@ extern void _dl_determine_tlsoffset (void) attribute_hidden;
    number of audit modules are loaded.  */
 void _dl_tls_static_surplus_init (size_t naudit) attribute_hidden;
 
+/* After startup is complete but before user entry, the size of the
+   audit modules is more knowable so we can reduce the TLS size
+   accordingly.  */
+void _dl_tls_prune_audit_storage (void) attribute_hidden;
+
 /* This function is called very early from dl_main to set up TLS and
    other thread-related data structures.  */
 void __tls_pre_init_tp (void) attribute_hidden;
-- 
2.47.3
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.