public inbox for libc-alpha@sourceware.org
 help / color / mirror / Atom feed
* [PATCH v2 00/19] Improve loader environment variable handling
@ 2023-10-17 13:05 Adhemerval Zanella
  2023-10-17 13:05 ` [PATCH v2 01/19] elf: Remove /etc/suid-debug support Adhemerval Zanella
                   ` (18 more replies)
  0 siblings, 19 replies; 45+ messages in thread
From: Adhemerval Zanella @ 2023-10-17 13:05 UTC (permalink / raw)
  To: libc-alpha, Siddhesh Poyarekar

The recent CVE-2023-4911 fix [1] and tunable change to SXID_ERASE
discussion [2] brought some issues with the current environment handling
by the loader. Besides the bugs in tuning parsing, some other questions
are:

  * What should be the security boundaries for tunable and other tuning
    environment variables?

  * Should tunables be filtered out or be disabled altogether in setuid
    binaries [3]?

  * How should ld.so handle security-sensitive tunable (like malloc
    options)?

  * How to handle ill-formatted tunable definition [4]?

  * Is tunable copy/parsing (through tunable_strdup) required [5]?

  * Which other environment variables the loader should ignore and/or
    filter in security-sensitive context.

On this patchset, I followed the idea laid out in the discussion on
whether to apply SXID_ERASE to all tunables [6]:

  1. ignore any tunable on AT_SECURE binaries (as some Linux distributions
      do already [7]);

  2. Add malloc tunables along with GLIBC_TUNABLES to unsecvars;

  3. Do not parse ill-formatted GLIBC_TUNABLES strings;

  4. Remove the requirement of duplicating the GLIBC_TUNABLES string for
     parsing.

  5. Ignore most of the environment variables on security-sensitive
     mode (AT_SECURE/setuid/setgid).

Patch #1 removes '/etc/suid-debug', which has not been working since
malloc debugging supported moved to libc_malloc_debug.so. It is one
thing less that might change AT_SECURE binaries' behavior
due to environment configurations.

Patch #2 removed tunables parsing and applying for setuid/setgid
binaries (similar to Alt Linux patch).

Patch #3 and #4 add all malloc tunable and GLIBC_TUNABLES to unsecvars
and improve tst-env-setuid.c to test all possible environment variables.

Patch #5 and #6 improved the GLIBC_TUNABLES handling to avoid handling
ill-formatted inputs.

Patch #7 makes _dl_debug_vdprintf usable before self-relocation so patch
#8 can add a loader warning that ill-formatted GLIBC_TUNABLES inputs are
ignored (it also fixes the issue where the GLIBC_TUNABLE allocation
failure will trigger a SEGFAULT on some architecture for PIE).

Patch #9, #10, and #11 remove the tunable_strdup and make the
GLIBC_TUNABLE parsing in place (no more possible allocation failure).
The parsing now tracks the tunable start and its size. The
dl-tunable-parse.h adds helper functions to help to parse, like an
strcmp that also checks for size and an iterator for suboptions that are
comma-separated (used on hwcap parsing by x86, powerpc, and s390x).

Patch #12, #13, #14, #16, and #18 make loader ignore all but just
LD_PRELOAD and LD_AUDIT for setuid binaries.  For both options, loader
ensures that pathnames containing slashes are ignored and shared
libraries are loaded only from the standard search directories and only
if they have set-user-ID mode bit enabled.

[1] https://sourceware.org/pipermail/libc-alpha/2023-October/151921.html
[2] https://sourceware.org/pipermail/libc-alpha/2023-October/151936.html
[3] https://www.openwall.com/lists/oss-security/2023/10/03/3
[4] https://sourceware.org/pipermail/libc-alpha/2023-October/151927.html
[5] https://sourceware.org/pipermail/libc-alpha/2023-October/151959.html
[6] https://sourceware.org/pipermail/libc-alpha/2023-October/152011.html
[7] https://git.altlinux.org/gears/g/glibc.git?p=glibc.git;a=commitdiff;h=5d1686416ab766f3dd0780ab730650c4c0f76ca9

Changes from v1:
* Ignore most of the environment variables on security-sensitive mode.
* Extend tests.

Adhemerval Zanella (19):
  elf: Remove /etc/suid-debug support
  elf: Add GLIBC_TUNABLES to unsecvars
  elf: Ignore GLIBC_TUNABLES for setuid/setgid binaries
  elf: Add all malloc tunable to unsecvars
  elf: Do not process invalid tunable format
  elf: Do not parse ill-formatted strings
  elf: Fix _dl_debug_vdprintf to work before self-relocation
  elf: Emit warning if tunable is ill-formatted
  x86: Use dl-symbol-redir-ifunc.h on cpu-tunables
  s390: Use dl-symbol-redir-ifunc.h on cpu-tunables
  elf: Do not duplicate the GLIBC_TUNABLES string
  elf: Ignore LD_PROFILE for setuid binaries
  elf: Remove LD_PROFILE for static binaries
  elf: Ignore loader debug env vars for setuid
  elf: Remove any_debug from dl_main_state
  elf: Ignore LD_LIBRARY_PATH and debug env var for setuid for static
  elf: Add comments on how LD_AUDIT and LD_PRELOAD handle
    __libc_enable_secure
  elf: Ignore LD_BIND_NOW and LD_BIND_NOT for setuid binaries
  elf: Refactor process_envvars

 elf/Makefile                                  |  26 +-
 elf/dl-load.c                                 |  10 +-
 elf/dl-main.h                                 |   3 -
 elf/dl-printf.c                               |  16 +-
 elf/dl-runtime.c                              |  12 +-
 elf/dl-support.c                              |  41 ++-
 elf/dl-tunable-types.h                        |  10 -
 elf/dl-tunables.c                             | 219 +++++----------
 elf/dl-tunables.h                             |   6 +-
 elf/dl-tunables.list                          |   9 -
 elf/{dl-profstub.c => libc-dl-profstub.c}     |   0
 elf/rtld.c                                    | 122 +++++---
 elf/tst-env-setuid-static.c                   |   1 +
 elf/tst-env-setuid-tunables.c                 |  59 ++--
 elf/tst-env-setuid.c                          | 140 ++++++----
 elf/tst-tunables.c                            | 261 ++++++++++++++++++
 include/dlfcn.h                               |   5 +
 manual/README.tunables                        |   9 -
 manual/memory.texi                            |   4 +-
 manual/tunables.texi                          |   4 +-
 scripts/gen-tunables.awk                      |  18 +-
 stdio-common/Makefile                         |   5 +
 stdio-common/_itoa.c                          |   5 +
 sysdeps/aarch64/dl-machine.h                  |   4 +-
 sysdeps/aarch64/dl-trampoline.S               |   3 +-
 sysdeps/alpha/dl-machine.h                    |   6 +-
 sysdeps/alpha/dl-trampoline.S                 |   6 +
 sysdeps/arm/dl-machine.h                      |   4 +-
 sysdeps/arm/dl-trampoline.S                   |   2 +-
 sysdeps/generic/dl-tunables-parse.h           | 128 +++++++++
 sysdeps/generic/unsecvars.h                   |  10 +
 sysdeps/hppa/dl-machine.h                     |  36 +--
 sysdeps/hppa/dl-trampoline.S                  |   2 +
 sysdeps/i386/dl-machine.h                     |   2 +
 sysdeps/i386/dl-trampoline.S                  |   2 +-
 .../i686/multiarch/dl-symbol-redir-ifunc.h    |   5 +
 sysdeps/ia64/dl-machine.h                     |  10 +-
 sysdeps/ia64/dl-trampoline.S                  |   2 +-
 sysdeps/loongarch/dl-machine.h                |   6 +-
 sysdeps/loongarch/dl-trampoline.h             |   2 +
 sysdeps/m68k/dl-machine.h                     |   4 +-
 sysdeps/m68k/dl-trampoline.S                  |   2 +
 sysdeps/powerpc/powerpc32/dl-machine.c        |   2 +-
 sysdeps/powerpc/powerpc32/dl-machine.h        |  10 +-
 sysdeps/powerpc/powerpc32/dl-trampoline.S     |   2 +-
 sysdeps/powerpc/powerpc64/dl-machine.h        |  20 +-
 sysdeps/powerpc/powerpc64/dl-trampoline.S     |   2 +-
 sysdeps/s390/cpu-features.c                   | 169 +++++-------
 .../s390/multiarch/dl-symbol-redir-ifunc.h    |   2 +
 sysdeps/s390/s390-32/dl-machine.h             |   8 +-
 sysdeps/s390/s390-32/dl-trampoline.h          |   2 +-
 sysdeps/s390/s390-64/dl-machine.h             |   8 +-
 sysdeps/s390/s390-64/dl-trampoline.h          |   2 +-
 sysdeps/sh/dl-machine.h                       |   2 +
 sysdeps/sh/dl-trampoline.S                    |   2 +
 sysdeps/sparc/sparc32/dl-machine.h            |   4 +-
 sysdeps/sparc/sparc32/dl-trampoline.S         |   2 +
 sysdeps/sparc/sparc64/dl-machine.h            |   4 +-
 sysdeps/sparc/sparc64/dl-trampoline.S         |   2 +
 .../unix/sysv/linux/aarch64/cpu-features.c    |  38 ++-
 .../sysv/linux/i386/dl-writev.h}              |  18 +-
 .../unix/sysv/linux/powerpc/cpu-features.c    |  45 +--
 .../sysv/linux/powerpc/tst-hwcap-tunables.c   |   6 +-
 sysdeps/x86/Makefile                          |   4 +-
 sysdeps/x86/cpu-tunables.c                    | 135 +++------
 sysdeps/x86/tst-hwcap-tunables.c              | 150 ++++++++++
 sysdeps/x86_64/64/dl-tunables.list            |   1 -
 sysdeps/x86_64/dl-machine.h                   |   2 +
 sysdeps/x86_64/dl-trampoline.S                |  64 +++--
 .../x86_64/multiarch/dl-symbol-redir-ifunc.h  |  15 +
 70 files changed, 1225 insertions(+), 717 deletions(-)
 rename elf/{dl-profstub.c => libc-dl-profstub.c} (100%)
 create mode 100644 elf/tst-env-setuid-static.c
 create mode 100644 elf/tst-tunables.c
 create mode 100644 sysdeps/generic/dl-tunables-parse.h
 rename sysdeps/{x86_64/memcmp-isa-default-impl.h => unix/sysv/linux/i386/dl-writev.h} (62%)
 create mode 100644 sysdeps/x86/tst-hwcap-tunables.c

-- 
2.34.1


^ permalink raw reply	[flat|nested] 45+ messages in thread

* [PATCH v2 01/19] elf: Remove /etc/suid-debug support
  2023-10-17 13:05 [PATCH v2 00/19] Improve loader environment variable handling Adhemerval Zanella
@ 2023-10-17 13:05 ` Adhemerval Zanella
  2023-10-18 12:31   ` Siddhesh Poyarekar
  2023-10-17 13:05 ` [PATCH v2 02/19] elf: Add GLIBC_TUNABLES to unsecvars Adhemerval Zanella
                   ` (17 subsequent siblings)
  18 siblings, 1 reply; 45+ messages in thread
From: Adhemerval Zanella @ 2023-10-17 13:05 UTC (permalink / raw)
  To: libc-alpha, Siddhesh Poyarekar

Since malloc debug support moved to a different library
(libc_malloc_debug.so), the glibc.malloc.check requires preloading the
debug library to enable it.  It means that suid-debug support has not
been working since 2.34.

To restore its support, it would require to add additional information
and parsing to where to find libc_malloc_debug.so.

It is one thing less that might change AT_SECURE binaries' behavior
due to environment configurations.

Checked on x86_64-linux-gnu.
---
 elf/dl-tunables.c    | 16 ----------------
 elf/rtld.c           |  3 +--
 manual/memory.texi   |  4 +---
 manual/tunables.texi |  4 +---
 4 files changed, 3 insertions(+), 24 deletions(-)

diff --git a/elf/dl-tunables.c b/elf/dl-tunables.c
index cae67efa0a..24252af22c 100644
--- a/elf/dl-tunables.c
+++ b/elf/dl-tunables.c
@@ -252,20 +252,6 @@ parse_tunables (char *tunestr, char *valstring)
     tunestr[off] = '\0';
 }
 
-/* Enable the glibc.malloc.check tunable in SETUID/SETGID programs only when
-   the system administrator has created the /etc/suid-debug file.  This is a
-   special case where we want to conditionally enable/disable a tunable even
-   for setuid binaries.  We use the special version of access() to avoid
-   setting ERRNO, which is a TLS variable since TLS has not yet been set
-   up.  */
-static __always_inline void
-maybe_enable_malloc_check (void)
-{
-  tunable_id_t id = TUNABLE_ENUM_NAME (glibc, malloc, check);
-  if (__libc_enable_secure && __access_noerrno ("/etc/suid-debug", F_OK) == 0)
-    tunable_list[id].security_level = TUNABLE_SECLEVEL_NONE;
-}
-
 /* Initialize the tunables list from the environment.  For now we only use the
    ENV_ALIAS to find values.  Later we will also use the tunable names to find
    values.  */
@@ -277,8 +263,6 @@ __tunables_init (char **envp)
   size_t len = 0;
   char **prev_envp = envp;
 
-  maybe_enable_malloc_check ();
-
   while ((envp = get_next_env (envp, &envname, &len, &envval,
 			       &prev_envp)) != NULL)
     {
diff --git a/elf/rtld.c b/elf/rtld.c
index 5107d16fe3..51b6d9f326 100644
--- a/elf/rtld.c
+++ b/elf/rtld.c
@@ -2670,8 +2670,7 @@ process_envvars (struct dl_main_state *state)
 	}
       while (*nextp != '\0');
 
-      if (__access ("/etc/suid-debug", F_OK) != 0)
-	GLRO(dl_debug_mask) = 0;
+      GLRO(dl_debug_mask) = 0;
 
       if (state->mode != rtld_mode_normal)
 	_exit (5);
diff --git a/manual/memory.texi b/manual/memory.texi
index 5781a64f35..258fdbd3a0 100644
--- a/manual/memory.texi
+++ b/manual/memory.texi
@@ -1379,9 +1379,7 @@ There is one problem with @code{MALLOC_CHECK_}: in SUID or SGID binaries
 it could possibly be exploited since diverging from the normal programs
 behavior it now writes something to the standard error descriptor.
 Therefore the use of @code{MALLOC_CHECK_} is disabled by default for
-SUID and SGID binaries.  It can be enabled again by the system
-administrator by adding a file @file{/etc/suid-debug} (the content is
-not important it could be empty).
+SUID and SGID binaries.
 
 So, what's the difference between using @code{MALLOC_CHECK_} and linking
 with @samp{-lmcheck}?  @code{MALLOC_CHECK_} is orthogonal with respect to
diff --git a/manual/tunables.texi b/manual/tunables.texi
index 776fd93fd9..347b5698b5 100644
--- a/manual/tunables.texi
+++ b/manual/tunables.texi
@@ -136,9 +136,7 @@ termination of the process.
 Like @env{MALLOC_CHECK_}, @code{glibc.malloc.check} has a problem in that it
 diverges from normal program behavior by writing to @code{stderr}, which could
 by exploited in SUID and SGID binaries.  Therefore, @code{glibc.malloc.check}
-is disabled by default for SUID and SGID binaries.  This can be enabled again
-by the system administrator by adding a file @file{/etc/suid-debug}; the
-content of the file could be anything or even empty.
+is disabled by default for SUID and SGID binaries.
 @end deftp
 
 @deftp Tunable glibc.malloc.top_pad
-- 
2.34.1


^ permalink raw reply	[flat|nested] 45+ messages in thread

* [PATCH v2 02/19] elf: Add GLIBC_TUNABLES to unsecvars
  2023-10-17 13:05 [PATCH v2 00/19] Improve loader environment variable handling Adhemerval Zanella
  2023-10-17 13:05 ` [PATCH v2 01/19] elf: Remove /etc/suid-debug support Adhemerval Zanella
@ 2023-10-17 13:05 ` Adhemerval Zanella
  2023-10-18 12:52   ` Siddhesh Poyarekar
  2023-10-17 13:05 ` [PATCH v2 03/19] elf: Ignore GLIBC_TUNABLES for setuid/setgid binaries Adhemerval Zanella
                   ` (16 subsequent siblings)
  18 siblings, 1 reply; 45+ messages in thread
From: Adhemerval Zanella @ 2023-10-17 13:05 UTC (permalink / raw)
  To: libc-alpha, Siddhesh Poyarekar; +Cc: Florian Weimer

setuid/setgid process now ignores any glibc tunables, and filters out
all environment variables that might changes its behavior. This patch
also adds GLIBC_TUNABLES, so any spawned process by setuid/setgid
processes should set tunable explicitly.

Checked on x86_64-linux-gnu.

Reviewed-by: Florian Weimer <fweimer@redhat.com>
---
 elf/tst-env-setuid-tunables.c | 32 ++++----------------------------
 sysdeps/generic/unsecvars.h   |  1 +
 2 files changed, 5 insertions(+), 28 deletions(-)

diff --git a/elf/tst-env-setuid-tunables.c b/elf/tst-env-setuid-tunables.c
index f0b92c97e7..2603007b7b 100644
--- a/elf/tst-env-setuid-tunables.c
+++ b/elf/tst-env-setuid-tunables.c
@@ -60,45 +60,21 @@ const char *teststrings[] =
   "glibc.not_valid.check=2",
 };
 
-const char *resultstrings[] =
-{
-  "glibc.malloc.mmap_threshold=4096",
-  "glibc.malloc.mmap_threshold=4096",
-  "glibc.malloc.mmap_threshold=4096",
-  "glibc.malloc.perturb=0x800",
-  "glibc.malloc.perturb=0x800:glibc.malloc.mmap_threshold=4096",
-  "glibc.malloc.perturb=0x800:glibc.malloc.mmap_threshold=4096",
-  "glibc.malloc.mmap_threshold=4096",
-  "glibc.malloc.mmap_threshold=4096",
-  "glibc.malloc.mmap_threshold=glibc.malloc.mmap_threshold=4096",
-  "",
-  "",
-  "",
-  "",
-  "",
-  "",
-  "",
-};
-
 static int
 test_child (int off)
 {
   const char *val = getenv ("GLIBC_TUNABLES");
+  int ret = 1;
 
   printf ("    [%d] GLIBC_TUNABLES is %s\n", off, val);
   fflush (stdout);
-  if (val != NULL && strcmp (val, resultstrings[off]) == 0)
-    return 0;
-
   if (val != NULL)
-    printf ("    [%d] Unexpected GLIBC_TUNABLES VALUE %s, expected %s\n",
-	    off, val, resultstrings[off]);
+    printf ("    [%d] Unexpected GLIBC_TUNABLES VALUE %s\n", off, val);
   else
-    printf ("    [%d] GLIBC_TUNABLES environment variable absent\n", off);
-
+    ret = 0;
   fflush (stdout);
 
-  return 1;
+  return ret;
 }
 
 static int
diff --git a/sysdeps/generic/unsecvars.h b/sysdeps/generic/unsecvars.h
index 8278c50a84..81397fb90b 100644
--- a/sysdeps/generic/unsecvars.h
+++ b/sysdeps/generic/unsecvars.h
@@ -4,6 +4,7 @@
 #define UNSECURE_ENVVARS \
   "GCONV_PATH\0"							      \
   "GETCONF_DIR\0"							      \
+  "GLIBC_TUNABLES\0"							      \
   "HOSTALIASES\0"							      \
   "LD_AUDIT\0"								      \
   "LD_DEBUG\0"								      \
-- 
2.34.1


^ permalink raw reply	[flat|nested] 45+ messages in thread

* [PATCH v2 03/19] elf: Ignore GLIBC_TUNABLES for setuid/setgid binaries
  2023-10-17 13:05 [PATCH v2 00/19] Improve loader environment variable handling Adhemerval Zanella
  2023-10-17 13:05 ` [PATCH v2 01/19] elf: Remove /etc/suid-debug support Adhemerval Zanella
  2023-10-17 13:05 ` [PATCH v2 02/19] elf: Add GLIBC_TUNABLES to unsecvars Adhemerval Zanella
@ 2023-10-17 13:05 ` Adhemerval Zanella
  2023-10-18 13:04   ` Siddhesh Poyarekar
  2023-10-28  2:14   ` DJ Delorie
  2023-10-17 13:05 ` [PATCH v2 04/19] elf: Add all malloc tunable to unsecvars Adhemerval Zanella
                   ` (15 subsequent siblings)
  18 siblings, 2 replies; 45+ messages in thread
From: Adhemerval Zanella @ 2023-10-17 13:05 UTC (permalink / raw)
  To: libc-alpha, Siddhesh Poyarekar

The tunable privilege levels were a retrofit to try and keep the malloc
tunable environment variables' behavior unchanged across security
boundaries.  However, CVE-2023-4911 shows how tricky can be
tunable parsing in a security-sensitive environment.

Not only parsing, but the malloc tunable essentially changes some
semantics on setuid/setgid processes.  Although it is not a direct
security issue, allowing users to change setuid/setgid semantics is not
a good security practice, and requires extra code and analysis to check
if each tunable is safe to use on all security boundaries.

It also means that security opt-in features, like aarch64 MTE, would
need to be explicit enabled by an administrator with a wrapper script
or with a possible future system-wide tunable setting.

Co-authored-by: Siddhesh Poyarekar  <siddhesh@sourceware.org>
---
 elf/Makefile                       |   5 +-
 elf/dl-tunable-types.h             |  10 --
 elf/dl-tunables.c                  | 127 ++++-----------
 elf/dl-tunables.list               |   9 --
 elf/tst-env-setuid-tunables.c      |  29 +++-
 elf/tst-tunables.c                 | 244 +++++++++++++++++++++++++++++
 manual/README.tunables             |   9 --
 scripts/gen-tunables.awk           |  18 +--
 sysdeps/x86_64/64/dl-tunables.list |   1 -
 9 files changed, 299 insertions(+), 153 deletions(-)
 create mode 100644 elf/tst-tunables.c

diff --git a/elf/Makefile b/elf/Makefile
index 9176cbf1e3..c824f7dab7 100644
--- a/elf/Makefile
+++ b/elf/Makefile
@@ -263,7 +263,6 @@ tests-static-normal := \
   tst-dl-iter-static \
   tst-dst-static \
   tst-env-setuid \
-  tst-env-setuid-tunables \
   tst-getauxval-static \
   tst-linkall-static \
   tst-single_threaded-pthread-static \
@@ -276,10 +275,12 @@ tests-static-normal := \
 tests-static-internal := \
   tst-dl-printf-static \
   tst-dl_find_object-static \
+  tst-env-setuid-tunables \
   tst-ptrguard1-static \
   tst-stackguard1-static \
   tst-tls1-static \
   tst-tls1-static-non-pie \
+  tst-tunables \
   # tests-static-internal
 
 CRT-tst-tls1-static-non-pie := $(csu-objpfx)crt1.o
@@ -2696,6 +2697,8 @@ $(objpfx)tst-glibc-hwcaps-mask.out: \
 # tst-glibc-hwcaps-cache.
 $(objpfx)tst-glibc-hwcaps-cache.out: $(objpfx)tst-glibc-hwcaps
 
+tst-tunables-ARGS = -- $(host-test-program-cmd)
+
 $(objpfx)list-tunables.out: tst-rtld-list-tunables.sh $(objpfx)ld.so
 	$(SHELL) $< $(objpfx)ld.so '$(test-wrapper-env)' \
 	    '$(run_program_env)' > $(objpfx)/tst-rtld-list-tunables.out
diff --git a/elf/dl-tunable-types.h b/elf/dl-tunable-types.h
index c88332657e..62d6d9e629 100644
--- a/elf/dl-tunable-types.h
+++ b/elf/dl-tunable-types.h
@@ -64,16 +64,6 @@ struct _tunable
   tunable_val_t val;			/* The value.  */
   bool initialized;			/* Flag to indicate that the tunable is
 					   initialized.  */
-  tunable_seclevel_t security_level;	/* Specify the security level for the
-					   tunable with respect to AT_SECURE
-					   programs.  See description of
-					   tunable_seclevel_t to see a
-					   description of the values.
-
-					   Note that even if the tunable is
-					   read, it may not get used by the
-					   target module if the value is
-					   considered unsafe.  */
   /* Compatibility elements.  */
   const char env_alias[TUNABLE_ALIAS_MAX]; /* The compatibility environment
 					   variable name.  */
diff --git a/elf/dl-tunables.c b/elf/dl-tunables.c
index 24252af22c..a83bd2b8bc 100644
--- a/elf/dl-tunables.c
+++ b/elf/dl-tunables.c
@@ -154,50 +154,51 @@ __tunable_set_val (tunable_id_t id, tunable_val_t *valp, tunable_num_t *minp,
   do_tunable_update_val (cur, valp, minp, maxp);
 }
 
-/* Parse the tunable string TUNESTR and adjust it to drop any tunables that may
-   be unsafe for AT_SECURE processes so that it can be used as the new
-   environment variable value for GLIBC_TUNABLES.  VALSTRING is the original
-   environment variable string which we use to make NULL terminated values so
-   that we don't have to allocate memory again for it.  */
+/* Parse the tunable string VALSTRING.  VALSTRING is a duplicated values,
+   where delimiters ':' are replaced with '\0', so string tunables are null
+   terminated.  */
 static void
-parse_tunables (char *tunestr, char *valstring)
+parse_tunables (char *valstring)
 {
-  if (tunestr == NULL || *tunestr == '\0')
+  if (valstring == NULL || *valstring == '\0')
     return;
 
-  char *p = tunestr;
-  size_t off = 0;
+  char *p = valstring;
+  bool done = false;
 
-  while (true)
+  while (!done)
     {
       char *name = p;
-      size_t len = 0;
 
       /* First, find where the name ends.  */
-      while (p[len] != '=' && p[len] != ':' && p[len] != '\0')
-	len++;
+      while (*p != '=' && *p != ':' && *p != '\0')
+	p++;
 
       /* If we reach the end of the string before getting a valid name-value
 	 pair, bail out.  */
-      if (p[len] == '\0')
+      if (*p == '\0')
 	break;
 
       /* We did not find a valid name-value pair before encountering the
 	 colon.  */
-      if (p[len]== ':')
+      if (*p == ':')
 	{
-	  p += len + 1;
+	  p++;
 	  continue;
 	}
 
-      p += len + 1;
+      /* Skip the ':' or '='.  */
+      p++;
 
-      /* Take the value from the valstring since we need to NULL terminate it.  */
-      char *value = &valstring[p - tunestr];
-      len = 0;
+      const char *value = p;
 
-      while (p[len] != ':' && p[len] != '\0')
-	len++;
+      while (*p != ':' && *p != '\0')
+	p++;
+
+      if (*p == '\0')
+	done = true;
+      else
+	*p++ = '\0';
 
       /* Add the tunable if it exists.  */
       for (size_t i = 0; i < sizeof (tunable_list) / sizeof (tunable_t); i++)
@@ -206,50 +207,11 @@ parse_tunables (char *tunestr, char *valstring)
 
 	  if (tunable_is_name (cur->name, name))
 	    {
-	      /* If we are in a secure context (AT_SECURE) then ignore the
-		 tunable unless it is explicitly marked as secure.  Tunable
-		 values take precedence over their envvar aliases.  We write
-		 the tunables that are not SXID_ERASE back to TUNESTR, thus
-		 dropping all SXID_ERASE tunables and any invalid or
-		 unrecognized tunables.  */
-	      if (__libc_enable_secure)
-		{
-		  if (cur->security_level != TUNABLE_SECLEVEL_SXID_ERASE)
-		    {
-		      if (off > 0)
-			tunestr[off++] = ':';
-
-		      const char *n = cur->name;
-
-		      while (*n != '\0')
-			tunestr[off++] = *n++;
-
-		      tunestr[off++] = '=';
-
-		      for (size_t j = 0; j < len; j++)
-			tunestr[off++] = value[j];
-		    }
-
-		  if (cur->security_level != TUNABLE_SECLEVEL_NONE)
-		    break;
-		}
-
-	      value[len] = '\0';
 	      tunable_initialize (cur, value);
 	      break;
 	    }
 	}
-
-      /* We reached the end while processing the tunable string.  */
-      if (p[len] == '\0')
-	break;
-
-      p += len + 1;
     }
-
-  /* Terminate tunestr before we leave.  */
-  if (__libc_enable_secure)
-    tunestr[off] = '\0';
 }
 
 /* Initialize the tunables list from the environment.  For now we only use the
@@ -263,16 +225,16 @@ __tunables_init (char **envp)
   size_t len = 0;
   char **prev_envp = envp;
 
+  /* Ignore tunables for AT_SECURE programs.  */
+  if (__libc_enable_secure)
+    return;
+
   while ((envp = get_next_env (envp, &envname, &len, &envval,
 			       &prev_envp)) != NULL)
     {
       if (tunable_is_name ("GLIBC_TUNABLES", envname))
 	{
-	  char *new_env = tunables_strdup (envname);
-	  if (new_env != NULL)
-	    parse_tunables (new_env + len + 1, envval);
-	  /* Put in the updated envval.  */
-	  *prev_envp = new_env;
+	  parse_tunables (tunables_strdup (envval));
 	  continue;
 	}
 
@@ -290,39 +252,6 @@ __tunables_init (char **envp)
 	  /* We have a match.  Initialize and move on to the next line.  */
 	  if (tunable_is_name (name, envname))
 	    {
-	      /* For AT_SECURE binaries, we need to check the security settings of
-		 the tunable and decide whether we read the value and also whether
-		 we erase the value so that child processes don't inherit them in
-		 the environment.  */
-	      if (__libc_enable_secure)
-		{
-		  if (cur->security_level == TUNABLE_SECLEVEL_SXID_ERASE)
-		    {
-		      /* Erase the environment variable.  */
-		      char **ep = prev_envp;
-
-		      while (*ep != NULL)
-			{
-			  if (tunable_is_name (name, *ep))
-			    {
-			      char **dp = ep;
-
-			      do
-				dp[0] = dp[1];
-			      while (*dp++);
-			    }
-			  else
-			    ++ep;
-			}
-		      /* Reset the iterator so that we read the environment again
-			 from the point we erased.  */
-		      envp = prev_envp;
-		    }
-
-		  if (cur->security_level != TUNABLE_SECLEVEL_NONE)
-		    continue;
-		}
-
 	      tunable_initialize (cur, envval);
 	      break;
 	    }
diff --git a/elf/dl-tunables.list b/elf/dl-tunables.list
index 695ba7192e..471895af7b 100644
--- a/elf/dl-tunables.list
+++ b/elf/dl-tunables.list
@@ -21,14 +21,6 @@
 # minval: Optional minimum acceptable value
 # maxval: Optional maximum acceptable value
 # env_alias: An alias environment variable
-# security_level: Specify security level of the tunable for AT_SECURE binaries.
-# 		  Valid values are:
-#
-# 	     SXID_ERASE: (default) Do not read and do not pass on to
-# 	     child processes.
-# 	     SXID_IGNORE: Do not read, but retain for non-AT_SECURE
-# 	     subprocesses.
-# 	     NONE: Read all the time.
 
 glibc {
   malloc {
@@ -158,7 +150,6 @@ glibc {
       type: INT_32
       minval: 0
       maxval: 255
-      security_level: SXID_IGNORE
     }
   }
 
diff --git a/elf/tst-env-setuid-tunables.c b/elf/tst-env-setuid-tunables.c
index 2603007b7b..ca02dbba58 100644
--- a/elf/tst-env-setuid-tunables.c
+++ b/elf/tst-env-setuid-tunables.c
@@ -15,14 +15,10 @@
    License along with the GNU C Library; if not, see
    <https://www.gnu.org/licenses/>.  */
 
-/* Verify that tunables correctly filter out unsafe tunables like
-   glibc.malloc.check and glibc.malloc.mmap_threshold but also retain
-   glibc.malloc.mmap_threshold in an unprivileged child.  */
-
-#define _LIBC 1
-#include "config.h"
-#undef _LIBC
+/* Verify that GLIBC_TUNABLES is kept unchanged but no tunable is actually
+   enabled for AT_SECURE processes.  */
 
+#include <dl-tunables.h>
 #include <errno.h>
 #include <fcntl.h>
 #include <stdlib.h>
@@ -40,7 +36,7 @@
 #include <support/test-driver.h>
 #include <support/capture_subprocess.h>
 
-const char *teststrings[] =
+static const char *teststrings[] =
 {
   "glibc.malloc.check=2:glibc.malloc.mmap_threshold=4096",
   "glibc.malloc.check=2:glibc.malloc.check=2:glibc.malloc.mmap_threshold=4096",
@@ -74,6 +70,23 @@ test_child (int off)
     ret = 0;
   fflush (stdout);
 
+  /* Also check if the set tunables are effectively unchanged.  */
+  int32_t check = TUNABLE_GET_FULL (glibc, malloc, check, int32_t, NULL);
+  size_t mmap_threshold = TUNABLE_GET_FULL (glibc, malloc, mmap_threshold,
+					    size_t, NULL);
+  int32_t perturb = TUNABLE_GET_FULL (glibc, malloc, perturb, int32_t, NULL);
+
+  printf ("    [%d] glibc.malloc.check=%d\n", off, check);
+  fflush (stdout);
+  printf ("    [%d] glibc.malloc.mmap_threshold=%zu\n", off, mmap_threshold);
+  fflush (stdout);
+  printf ("    [%d] glibc.malloc.perturb=%d\n", off, perturb);
+  fflush (stdout);
+
+  ret |= check != 0;
+  ret |= mmap_threshold != 0;
+  ret |= perturb != 0;
+
   return ret;
 }
 
diff --git a/elf/tst-tunables.c b/elf/tst-tunables.c
new file mode 100644
index 0000000000..57cf532fc4
--- /dev/null
+++ b/elf/tst-tunables.c
@@ -0,0 +1,244 @@
+/* Check GLIBC_TUNABLES parsing.
+   Copyright (C) 2023 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <https://www.gnu.org/licenses/>.  */
+
+#include <array_length.h>
+#include <dl-tunables.h>
+#include <getopt.h>
+#include <intprops.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <support/capture_subprocess.h>
+#include <support/check.h>
+
+static int restart;
+#define CMDLINE_OPTIONS \
+  { "restart", no_argument, &restart, 1 },
+
+static const struct test_t
+{
+  const char *env;
+  int32_t expected_malloc_check;
+  size_t expected_mmap_threshold;
+  int32_t expected_perturb;
+} tests[] =
+{
+  /* Expected tunable format.  */
+  {
+    "glibc.malloc.check=2",
+    2,
+    0,
+    0,
+  },
+  {
+    "glibc.malloc.check=2:glibc.malloc.mmap_threshold=4096",
+    2,
+    4096,
+    0,
+  },
+  /* Empty tunable are ignored.  */
+  {
+    "glibc.malloc.check=2::glibc.malloc.mmap_threshold=4096",
+    2,
+    4096,
+    0,
+  },
+  /* As well empty values.  */
+  {
+    "glibc.malloc.check=:glibc.malloc.mmap_threshold=4096",
+    0,
+    4096,
+    0,
+  },
+  /* Tunable are processed from left to right, so last one is the one set.  */
+  {
+    "glibc.malloc.check=1:glibc.malloc.check=2",
+    2,
+    0,
+    0,
+  },
+  {
+    "glibc.malloc.check=2:glibc.malloc.check=2:glibc.malloc.mmap_threshold=4096",
+    2,
+    4096,
+    0,
+  },
+  {
+    "glibc.malloc.check=2:glibc.malloc.mmap_threshold=4096:glibc.malloc.check=2",
+    2,
+    4096,
+    0,
+  },
+  /* 0x800 is larger than tunable maxval (0xff), so the tunable is unchanged.  */
+  {
+    "glibc.malloc.perturb=0x800",
+    0,
+    0,
+    0,
+  },
+  {
+    "glibc.malloc.perturb=0x55",
+    0,
+    0,
+    0x55,
+  },
+  /* Out of range values are just ignored.  */
+  {
+    "glibc.malloc.perturb=0x800:glibc.malloc.mmap_threshold=4096",
+    0,
+    4096,
+    0,
+  },
+  /* Invalid keys are ignored.  */
+  {
+    ":glibc.malloc.garbage=2:glibc.malloc.check=1",
+    1,
+    0,
+    0,
+  },
+  {
+    "glibc.malloc.perturb=0x800:not_valid.malloc.check=2:glibc.malloc.mmap_threshold=4096",
+    0,
+    4096,
+    0,
+  },
+  {
+    "glibc.not_valid.check=2:glibc.malloc.mmap_threshold=4096",
+    0,
+    4096,
+    0,
+  },
+  {
+    "not_valid.malloc.check=2:glibc.malloc.mmap_threshold=4096",
+    0,
+    4096,
+    0,
+  },
+  /* Invalid subkeys are ignored.  */
+  {
+    "glibc.malloc.garbage=2:glibc.maoc.mmap_threshold=4096:glibc.malloc.check=2",
+    2,
+    0,
+    0,
+  },
+  {
+    "glibc.malloc.check=4:glibc.malloc.garbage=2:glibc.maoc.mmap_threshold=4096",
+    0,
+    0,
+    0,
+  },
+  {
+    "not_valid.malloc.check=2",
+    0,
+    0,
+    0,
+  },
+  {
+    "glibc.not_valid.check=2",
+    0,
+    0,
+    0,
+  },
+  /* An ill-formatted tunable in the for key=key=value will considere the
+     value as 'key=value' (which can not be parsed as an integer).  */
+  {
+    "glibc.malloc.mmap_threshold=glibc.malloc.mmap_threshold=4096",
+    0,
+    0,
+    0,
+  },
+  /* The ill-formatted tunable is also skipped.  */
+  {
+    "glibc.malloc.mmap_threshold=glibc.malloc.mmap_threshold=4096:glibc.malloc.check=2",
+    2,
+    0,
+    0,
+  },
+  /* For an integer tunable, parse will stop on non number character.  */
+  {
+    "glibc.malloc.check=2=2",
+    2,
+    0,
+    0,
+  },
+  {
+    "glibc.malloc.check=2=2:glibc.malloc.mmap_threshold=4096",
+    2,
+    4096,
+    0,
+  }
+};
+
+static int
+handle_restart (int i)
+{
+  TEST_COMPARE (tests[i].expected_malloc_check,
+		TUNABLE_GET_FULL (glibc, malloc, check, int32_t, NULL));
+  TEST_COMPARE (tests[i].expected_mmap_threshold,
+		TUNABLE_GET_FULL (glibc, malloc, mmap_threshold, size_t, NULL));
+  TEST_COMPARE (tests[i].expected_perturb,
+		TUNABLE_GET_FULL (glibc, malloc, perturb, int32_t, NULL));
+  return 0;
+}
+
+static int
+do_test (int argc, char *argv[])
+{
+  /* We must have either:
+     - One our fource parameters left if called initially:
+       + path to ld.so         optional
+       + "--library-path"      optional
+       + the library path      optional
+       + the application name
+       + the test to check  */
+
+  TEST_VERIFY_EXIT (argc == 2 || argc == 5);
+
+  if (restart)
+    return handle_restart (atoi (argv[1]));
+
+  char nteststr[INT_BUFSIZE_BOUND (int)];
+
+  char *spargv[10];
+  {
+    int i = 0;
+    for (; i < argc - 1; i++)
+      spargv[i] = argv[i + 1];
+    spargv[i++] = (char *) "--direct";
+    spargv[i++] = (char *) "--restart";
+    spargv[i++] = nteststr;
+    spargv[i] = NULL;
+  }
+
+  for (int i = 0; i < array_length (tests); i++)
+    {
+      snprintf (nteststr, sizeof nteststr, "%d", i);
+
+      printf ("[%d] Spawned test for %s\n", i, tests[i].env);
+      setenv ("GLIBC_TUNABLES", tests[i].env, 1);
+      struct support_capture_subprocess result
+	= support_capture_subprogram (spargv[0], spargv);
+      support_capture_subprocess_check (&result, "tst-tunables", 0,
+					sc_allow_stderr);
+      support_capture_subprocess_free (&result);
+    }
+
+  return 0;
+}
+
+#define TEST_FUNCTION_ARGV do_test
+#include <support/test-driver.c>
diff --git a/manual/README.tunables b/manual/README.tunables
index 605ddd78cd..72ae00dc02 100644
--- a/manual/README.tunables
+++ b/manual/README.tunables
@@ -59,15 +59,6 @@ The list of allowed attributes are:
 
 - env_alias:		An alias environment variable
 
-- security_level:	Specify security level of the tunable for AT_SECURE
-			binaries.  Valid values are:
-
-			SXID_ERASE: (default) Do not read and do not pass on to
-			child processes.
-			SXID_IGNORE: Do not read, but retain for non-AT_SECURE
-			child processes.
-			NONE: Read all the time.
-
 2. Use TUNABLE_GET/TUNABLE_SET/TUNABLE_SET_WITH_BOUNDS to get and set tunables.
 
 3. OPTIONAL: If tunables in a namespace are being used multiple times within a
diff --git a/scripts/gen-tunables.awk b/scripts/gen-tunables.awk
index d6de100df0..1e9d6b534e 100644
--- a/scripts/gen-tunables.awk
+++ b/scripts/gen-tunables.awk
@@ -61,9 +61,6 @@ $1 == "}" {
     if (!env_alias[top_ns,ns,tunable]) {
       env_alias[top_ns,ns,tunable] = "{0}"
     }
-    if (!security_level[top_ns,ns,tunable]) {
-      security_level[top_ns,ns,tunable] = "SXID_ERASE"
-    }
     len = length(top_ns"."ns"."tunable)
     if (len > max_name_len)
       max_name_len = len
@@ -118,17 +115,6 @@ $1 == "}" {
     if (len > max_alias_len)
       max_alias_len = len
   }
-  else if (attr == "security_level") {
-    if (val == "SXID_ERASE" || val == "SXID_IGNORE" || val == "NONE") {
-      security_level[top_ns,ns,tunable] = val
-    }
-    else {
-      printf("Line %d: Invalid value (%s) for security_level: %s, ", NR, val,
-	     $0)
-      print("Allowed values are 'SXID_ERASE', 'SXID_IGNORE', or 'NONE'")
-      exit 1
-    }
-  }
   else if (attr == "default") {
     if (types[top_ns,ns,tunable] == "STRING") {
       default_val[top_ns,ns,tunable] = sprintf(".strval = \"%s\"", val);
@@ -177,9 +163,9 @@ END {
     n = indices[2];
     m = indices[3];
     printf ("  {TUNABLE_NAME_S(%s, %s, %s)", t, n, m)
-    printf (", {TUNABLE_TYPE_%s, %s, %s}, {%s}, false, TUNABLE_SECLEVEL_%s, %s},\n",
+    printf (", {TUNABLE_TYPE_%s, %s, %s}, {%s}, false, %s},\n",
 	    types[t,n,m], minvals[t,n,m], maxvals[t,n,m],
-	    default_val[t,n,m], security_level[t,n,m], env_alias[t,n,m]);
+	    default_val[t,n,m], env_alias[t,n,m]);
   }
   print "};"
   print "#endif"
diff --git a/sysdeps/x86_64/64/dl-tunables.list b/sysdeps/x86_64/64/dl-tunables.list
index 0aab52e662..54a216a461 100644
--- a/sysdeps/x86_64/64/dl-tunables.list
+++ b/sysdeps/x86_64/64/dl-tunables.list
@@ -23,7 +23,6 @@ glibc {
       minval: 0
       maxval: 1
       env_alias: LD_PREFER_MAP_32BIT_EXEC
-      security_level: SXID_IGNORE
     }
   }
 }
-- 
2.34.1


^ permalink raw reply	[flat|nested] 45+ messages in thread

* [PATCH v2 04/19] elf: Add all malloc tunable to unsecvars
  2023-10-17 13:05 [PATCH v2 00/19] Improve loader environment variable handling Adhemerval Zanella
                   ` (2 preceding siblings ...)
  2023-10-17 13:05 ` [PATCH v2 03/19] elf: Ignore GLIBC_TUNABLES for setuid/setgid binaries Adhemerval Zanella
@ 2023-10-17 13:05 ` Adhemerval Zanella
  2023-10-18 13:04   ` Siddhesh Poyarekar
  2023-10-28  3:45   ` DJ Delorie
  2023-10-17 13:05 ` [PATCH v2 05/19] elf: Do not process invalid tunable format Adhemerval Zanella
                   ` (14 subsequent siblings)
  18 siblings, 2 replies; 45+ messages in thread
From: Adhemerval Zanella @ 2023-10-17 13:05 UTC (permalink / raw)
  To: libc-alpha, Siddhesh Poyarekar

Some environment variables allow alteration of allocator behavior
across setuid boundaries, where a setuid program may ignore the
tunable, but its non-setuid child can read it and adjust the memory
allocator behavior accordingly.

Most library behavior tunings is limited to the current process and does
not bleed in scope; so it is unclear how pratical this misfeature is.
If behavior change across privilege boundaries is desirable, it would be
better done with a wrapper program around the non-setuid child that sets
these envvars, instead of using the setuid process as the messenger.

The patch as fixes tst-env-setuid, where it fail if any unsecvars is
set.  It also adds a dynamic test, although it requires
--enable-hardcoded-path-in-tests so kernel correctly sets the setuid
bit (using the loader command directly would require to set the
setuid bit on the loader itself, which is not a usual deployment).

Co-authored-by: Siddhesh Poyarekar <siddhesh@sourceware.org>

Checked on x86_64-linux-gnu.
---
 elf/Makefile                |   8 +--
 elf/tst-env-setuid-static.c |   1 +
 elf/tst-env-setuid.c        | 128 +++++++++++++++++++++---------------
 sysdeps/generic/unsecvars.h |   7 ++
 4 files changed, 86 insertions(+), 58 deletions(-)
 create mode 100644 elf/tst-env-setuid-static.c

diff --git a/elf/Makefile b/elf/Makefile
index c824f7dab7..f1cd6e13fa 100644
--- a/elf/Makefile
+++ b/elf/Makefile
@@ -262,7 +262,7 @@ tests-static-normal := \
   tst-array5-static \
   tst-dl-iter-static \
   tst-dst-static \
-  tst-env-setuid \
+  tst-env-setuid-static \
   tst-getauxval-static \
   tst-linkall-static \
   tst-single_threaded-pthread-static \
@@ -305,6 +305,7 @@ tests := \
   tst-array5 \
   tst-auxv \
   tst-dl-hash \
+  tst-env-setuid \
   tst-leaks1 \
   tst-stringtable \
   tst-tls9 \
@@ -2467,9 +2468,6 @@ $(objpfx)tst-nodelete-dlclose: $(objpfx)tst-nodelete-dlclose-dso.so
 $(objpfx)tst-nodelete-dlclose.out: $(objpfx)tst-nodelete-dlclose-dso.so \
 				   $(objpfx)tst-nodelete-dlclose-plugin.so
 
-tst-env-setuid-ENV = MALLOC_CHECK_=2 MALLOC_MMAP_THRESHOLD_=4096 \
-		     LD_HWCAP_MASK=0x1
-
 $(objpfx)tst-debug1.out: $(objpfx)tst-debug1mod1.so
 
 $(objpfx)tst-debug1mod1.so: $(objpfx)testobj1.so
@@ -3021,3 +3019,5 @@ LDFLAGS-tst-dlclose-lazy-mod1.so = -Wl,-z,lazy,--no-as-needed
 $(objpfx)tst-dlclose-lazy-mod1.so: $(objpfx)tst-dlclose-lazy-mod2.so
 $(objpfx)tst-dlclose-lazy.out: \
   $(objpfx)tst-dlclose-lazy-mod1.so $(objpfx)tst-dlclose-lazy-mod2.so
+
+tst-env-setuid-ARGS = -- $(host-test-program-cmd)
diff --git a/elf/tst-env-setuid-static.c b/elf/tst-env-setuid-static.c
new file mode 100644
index 0000000000..0d88ae88b9
--- /dev/null
+++ b/elf/tst-env-setuid-static.c
@@ -0,0 +1 @@
+#include "tst-env-setuid.c"
diff --git a/elf/tst-env-setuid.c b/elf/tst-env-setuid.c
index 032ab44be2..ba295a6a14 100644
--- a/elf/tst-env-setuid.c
+++ b/elf/tst-env-setuid.c
@@ -15,18 +15,14 @@
    License along with the GNU C Library; if not, see
    <https://www.gnu.org/licenses/>.  */
 
-/* Verify that tunables correctly filter out unsafe environment variables like
-   MALLOC_CHECK_ and MALLOC_MMAP_THRESHOLD_ but also retain
-   MALLOC_MMAP_THRESHOLD_ in an unprivileged child.  */
+/* Verify that correctly filter out unsafe environment variables defined
+   in unsecvars.h.  */
 
-#include <errno.h>
-#include <fcntl.h>
-#include <stdlib.h>
-#include <stdint.h>
+#include <array_length.h>
+#include <gnu/lib-names.h>
 #include <stdio.h>
+#include <stdlib.h>
 #include <string.h>
-#include <sys/stat.h>
-#include <sys/wait.h>
 #include <unistd.h>
 
 #include <support/check.h>
@@ -36,61 +32,72 @@
 
 static char SETGID_CHILD[] = "setgid-child";
 
-#ifndef test_child
-static int
-test_child (void)
-{
-  if (getenv ("MALLOC_CHECK_") != NULL)
-    {
-      printf ("MALLOC_CHECK_ is still set\n");
-      return 1;
-    }
-
-  if (getenv ("MALLOC_MMAP_THRESHOLD_") == NULL)
-    {
-      printf ("MALLOC_MMAP_THRESHOLD_ lost\n");
-      return 1;
-    }
+#define FILTERED_VALUE   "some-filtered-value"
+#define UNFILTERED_VALUE "some-unfiltered-value"
 
-  if (getenv ("LD_HWCAP_MASK") != NULL)
-    {
-      printf ("LD_HWCAP_MASK still set\n");
-      return 1;
-    }
+struct envvar_t
+{
+  const char *env;
+  const char *value;
+};
 
-  return 0;
-}
-#endif
+/* That is not an extensible list of all filtered out environment
+   variables.  */
+static const struct envvar_t filtered_envvars[] =
+{
+  { "GLIBC_TUNABLES",          FILTERED_VALUE },
+  { "LD_AUDIT",                FILTERED_VALUE },
+  { "LD_HWCAP_MASK",           FILTERED_VALUE },
+  { "LD_LIBRARY_PATH",         FILTERED_VALUE },
+  { "LD_PRELOAD",              FILTERED_VALUE },
+  { "LD_PROFILE",              FILTERED_VALUE },
+  { "MALLOC_ARENA_MAX",        FILTERED_VALUE },
+  { "MALLOC_PERTURB_",         FILTERED_VALUE },
+  { "MALLOC_TRACE",            FILTERED_VALUE },
+  { "MALLOC_TRIM_THRESHOLD_",  FILTERED_VALUE },
+  { "RES_OPTIONS",             FILTERED_VALUE },
+};
+
+static const struct envvar_t unfiltered_envvars[] =
+{
+  { "LD_BIND_NOW",             "0" },
+  { "LD_BIND_NOT",             "1" },
+  /* Non longer supported option.  */
+  { "LD_ASSUME_KERNEL",        UNFILTERED_VALUE },
+};
 
-#ifndef test_parent
 static int
-test_parent (void)
+test_child (void)
 {
-  if (getenv ("MALLOC_CHECK_") == NULL)
-    {
-      printf ("MALLOC_CHECK_ lost\n");
-      return 1;
-    }
+  int ret = 0;
 
-  if (getenv ("MALLOC_MMAP_THRESHOLD_") == NULL)
+  for (const struct envvar_t *e = filtered_envvars;
+       e != array_end (filtered_envvars);
+       e++)
     {
-      printf ("MALLOC_MMAP_THRESHOLD_ lost\n");
-      return 1;
+      const char *env = getenv (e->env);
+      ret |= env != NULL;
     }
 
-  if (getenv ("LD_HWCAP_MASK") == NULL)
+  for (const struct envvar_t *e = unfiltered_envvars;
+       e != array_end (unfiltered_envvars);
+       e++)
     {
-      printf ("LD_HWCAP_MASK lost\n");
-      return 1;
+      const char *env = getenv (e->env);
+      ret |= !(env != NULL && strcmp (env, e->value) == 0);
     }
 
-  return 0;
+  return ret;
 }
-#endif
 
 static int
 do_test (int argc, char **argv)
 {
+  /* For dynamic loader, the test requires --enable-hardcoded-path-in-tests so
+     the kernel sets the AT_SECURE on process initialization.  */
+  if (argc >= 2 && strstr (argv[1], LD_SO) != 0)
+    FAIL_UNSUPPORTED ("dynamic test requires --enable-hardcoded-path-in-tests");
+
   /* Setgid child process.  */
   if (argc == 2 && strcmp (argv[1], SETGID_CHILD) == 0)
     {
@@ -104,20 +111,33 @@ do_test (int argc, char **argv)
       if (ret != 0)
 	exit (1);
 
-      exit (EXIT_SUCCESS);
+      /* Special return code to make sure that the child executed all the way
+	 through.  */
+      exit (42);
     }
   else
     {
-      if (test_parent () != 0)
-	exit (1);
+      for (const struct envvar_t *e = filtered_envvars;
+	   e != array_end (filtered_envvars);
+	   e++)
+	setenv (e->env, e->value, 1);
+
+      for (const struct envvar_t *e = unfiltered_envvars;
+	   e != array_end (unfiltered_envvars);
+	   e++)
+	setenv (e->env, e->value, 1);
 
       int status = support_capture_subprogram_self_sgid (SETGID_CHILD);
 
       if (WEXITSTATUS (status) == EXIT_UNSUPPORTED)
-	return EXIT_UNSUPPORTED;
-
-      if (!WIFEXITED (status))
-	FAIL_EXIT1 ("Unexpected exit status %d from child process\n", status);
+	exit (EXIT_UNSUPPORTED);
+
+      if (WEXITSTATUS (status) != 42)
+	{
+	  printf ("    child failed with status %d\n",
+		  WEXITSTATUS (status));
+	  support_record_failure ();
+	}
 
       return 0;
     }
diff --git a/sysdeps/generic/unsecvars.h b/sysdeps/generic/unsecvars.h
index 81397fb90b..f7ebed60e5 100644
--- a/sysdeps/generic/unsecvars.h
+++ b/sysdeps/generic/unsecvars.h
@@ -18,7 +18,14 @@
   "LD_SHOW_AUXV\0"							      \
   "LOCALDOMAIN\0"							      \
   "LOCPATH\0"								      \
+  "MALLOC_ARENA_MAX\0"							      \
+  "MALLOC_ARENA_TEST\0"							      \
+  "MALLOC_MMAP_MAX_\0"							      \
+  "MALLOC_MMAP_THRESHOLD_\0"						      \
+  "MALLOC_PERTURB_\0"							      \
+  "MALLOC_TOP_PAD_\0"							      \
   "MALLOC_TRACE\0"							      \
+  "MALLOC_TRIM_THRESHOLD_\0"						      \
   "NIS_PATH\0"								      \
   "NLSPATH\0"								      \
   "RESOLV_HOST_CONF\0"							      \
-- 
2.34.1


^ permalink raw reply	[flat|nested] 45+ messages in thread

* [PATCH v2 05/19] elf: Do not process invalid tunable format
  2023-10-17 13:05 [PATCH v2 00/19] Improve loader environment variable handling Adhemerval Zanella
                   ` (3 preceding siblings ...)
  2023-10-17 13:05 ` [PATCH v2 04/19] elf: Add all malloc tunable to unsecvars Adhemerval Zanella
@ 2023-10-17 13:05 ` Adhemerval Zanella
  2023-10-18 13:42   ` Siddhesh Poyarekar
  2023-10-17 13:05 ` [PATCH v2 06/19] elf: Do not parse ill-formatted strings Adhemerval Zanella
                   ` (13 subsequent siblings)
  18 siblings, 1 reply; 45+ messages in thread
From: Adhemerval Zanella @ 2023-10-17 13:05 UTC (permalink / raw)
  To: libc-alpha, Siddhesh Poyarekar

Tunable definitions with more than one '=' on are parsed and enabled,
and any subsequent '=' are ignored.  It means that tunables in the form
'tunable=tunable=value' or 'tunable=value=value' are handled as
'tunable=value'.  These inputs are likely user input errors, which
should not be accepted.

Checked on x86_64-linux-gnu.
---
 elf/dl-tunables.c  |  6 ++++--
 elf/tst-tunables.c | 22 +++++++++++++++++-----
 2 files changed, 21 insertions(+), 7 deletions(-)

diff --git a/elf/dl-tunables.c b/elf/dl-tunables.c
index a83bd2b8bc..59bee61124 100644
--- a/elf/dl-tunables.c
+++ b/elf/dl-tunables.c
@@ -192,10 +192,12 @@ parse_tunables (char *valstring)
 
       const char *value = p;
 
-      while (*p != ':' && *p != '\0')
+      while (*p != '=' && *p != ':' && *p != '\0')
 	p++;
 
-      if (*p == '\0')
+      if (*p == '=')
+	break;
+      else if (*p == '\0')
 	done = true;
       else
 	*p++ = '\0';
diff --git a/elf/tst-tunables.c b/elf/tst-tunables.c
index 57cf532fc4..03039b5260 100644
--- a/elf/tst-tunables.c
+++ b/elf/tst-tunables.c
@@ -161,24 +161,36 @@ static const struct test_t
     0,
     0,
   },
-  /* The ill-formatted tunable is also skipped.  */
+  /* If there is a ill-formatted key=value, everything after is also ignored.  */
   {
     "glibc.malloc.mmap_threshold=glibc.malloc.mmap_threshold=4096:glibc.malloc.check=2",
-    2,
+    0,
     0,
     0,
   },
-  /* For an integer tunable, parse will stop on non number character.  */
   {
     "glibc.malloc.check=2=2",
-    2,
+    0,
     0,
     0,
   },
   {
     "glibc.malloc.check=2=2:glibc.malloc.mmap_threshold=4096",
+    0,
+    0,
+    0,
+  },
+  {
+    "glibc.malloc.check=2=2:glibc.malloc.check=2",
+    0,
+    0,
+    0,
+  },
+  /* Valid tunables set before ill-formatted ones are set.  */
+  {
+    "glibc.malloc.check=2:glibc.malloc.mmap_threshold=4096=4096",
     2,
-    4096,
+    0,
     0,
   }
 };
-- 
2.34.1


^ permalink raw reply	[flat|nested] 45+ messages in thread

* [PATCH v2 06/19] elf: Do not parse ill-formatted strings
  2023-10-17 13:05 [PATCH v2 00/19] Improve loader environment variable handling Adhemerval Zanella
                   ` (4 preceding siblings ...)
  2023-10-17 13:05 ` [PATCH v2 05/19] elf: Do not process invalid tunable format Adhemerval Zanella
@ 2023-10-17 13:05 ` Adhemerval Zanella
  2023-10-27 10:25   ` Siddhesh Poyarekar
  2023-10-17 13:05 ` [PATCH v2 07/19] elf: Fix _dl_debug_vdprintf to work before self-relocation Adhemerval Zanella
                   ` (12 subsequent siblings)
  18 siblings, 1 reply; 45+ messages in thread
From: Adhemerval Zanella @ 2023-10-17 13:05 UTC (permalink / raw)
  To: libc-alpha, Siddhesh Poyarekar

Instead of ignoring ill-formatted tunable strings, first, check all the
tunable definitions are correct and then set each tunable value. It
means that partially invalid strings, like "key1=value1:key2=key2=value'
or 'key1=value':key2=value2=value2' do not enable 'key1=value1'. It
avoids possible user-defined errors in tunable definitions.

Checked on x86_64-linux-gnu.
---
 elf/dl-tunables.c  | 50 +++++++++++++++++++++++++++++++++++-----------
 elf/tst-tunables.c | 13 ++++++++----
 2 files changed, 47 insertions(+), 16 deletions(-)

diff --git a/elf/dl-tunables.c b/elf/dl-tunables.c
index 59bee61124..5d4b8c5bc0 100644
--- a/elf/dl-tunables.c
+++ b/elf/dl-tunables.c
@@ -154,17 +154,29 @@ __tunable_set_val (tunable_id_t id, tunable_val_t *valp, tunable_num_t *minp,
   do_tunable_update_val (cur, valp, minp, maxp);
 }
 
-/* Parse the tunable string VALSTRING.  VALSTRING is a duplicated values,
-   where delimiters ':' are replaced with '\0', so string tunables are null
-   terminated.  */
-static void
-parse_tunables (char *valstring)
+struct tunable_toset_t
+{
+  tunable_t *t;
+  const char *value;
+};
+
+enum { tunables_list_size = array_length (tunable_list) };
+
+/* Parse the tunable string VALSTRING and set TUNABLES with the found tunables
+   and their respectibles values.  VALSTRING is a duplicated values,  where
+   delimiters ':' are replaced with '\0', so string tunables are null
+   terminated.
+   Return the number of tunables found (including 0 if the string is empty)
+   or -1 if for a ill-formatted definition.  */
+static int
+parse_tunables_string (char *valstring, struct tunable_toset_t *tunables)
 {
   if (valstring == NULL || *valstring == '\0')
-    return;
+    return 0;
 
   char *p = valstring;
   bool done = false;
+  int ntunables = 0;
 
   while (!done)
     {
@@ -177,7 +189,7 @@ parse_tunables (char *valstring)
       /* If we reach the end of the string before getting a valid name-value
 	 pair, bail out.  */
       if (*p == '\0')
-	break;
+	return -1;
 
       /* We did not find a valid name-value pair before encountering the
 	 colon.  */
@@ -190,30 +202,44 @@ parse_tunables (char *valstring)
       /* Skip the ':' or '='.  */
       p++;
 
-      const char *value = p;
+      char *value = p;
 
       while (*p != '=' && *p != ':' && *p != '\0')
 	p++;
 
       if (*p == '=')
-	break;
+	return -1;
       else if (*p == '\0')
 	done = true;
       else
 	*p++ = '\0';
 
       /* Add the tunable if it exists.  */
-      for (size_t i = 0; i < sizeof (tunable_list) / sizeof (tunable_t); i++)
+      for (size_t i = 0; i < tunables_list_size; i++)
 	{
 	  tunable_t *cur = &tunable_list[i];
 
 	  if (tunable_is_name (cur->name, name))
 	    {
-	      tunable_initialize (cur, value);
+	      tunables[ntunables++] = (struct tunable_toset_t) { cur, value };
 	      break;
 	    }
 	}
     }
+
+  return ntunables;
+}
+
+static void
+parse_tunables (char *valstring)
+{
+  struct tunable_toset_t tunables[tunables_list_size];
+  int ntunables = parse_tunables_string (valstring, tunables);
+  if (ntunables == -1)
+    return;
+
+  for (int i = 0; i < ntunables; i++)
+    tunable_initialize (tunables[i].t, tunables[i].value);
 }
 
 /* Initialize the tunables list from the environment.  For now we only use the
@@ -240,7 +266,7 @@ __tunables_init (char **envp)
 	  continue;
 	}
 
-      for (int i = 0; i < sizeof (tunable_list) / sizeof (tunable_t); i++)
+      for (int i = 0; i < tunables_list_size; i++)
 	{
 	  tunable_t *cur = &tunable_list[i];
 
diff --git a/elf/tst-tunables.c b/elf/tst-tunables.c
index 03039b5260..e124fa4c6d 100644
--- a/elf/tst-tunables.c
+++ b/elf/tst-tunables.c
@@ -161,7 +161,7 @@ static const struct test_t
     0,
     0,
   },
-  /* If there is a ill-formatted key=value, everything after is also ignored.  */
+  /* Ill-formatted tunables string is not parsed.  */
   {
     "glibc.malloc.mmap_threshold=glibc.malloc.mmap_threshold=4096:glibc.malloc.check=2",
     0,
@@ -186,13 +186,18 @@ static const struct test_t
     0,
     0,
   },
-  /* Valid tunables set before ill-formatted ones are set.  */
   {
     "glibc.malloc.check=2:glibc.malloc.mmap_threshold=4096=4096",
-    2,
     0,
     0,
-  }
+    0,
+  },
+  {
+    "glibc.malloc.check=2:glibc.malloc.mmap_threshold=4096=4096",
+    0,
+    0,
+    0,
+  },
 };
 
 static int
-- 
2.34.1


^ permalink raw reply	[flat|nested] 45+ messages in thread

* [PATCH v2 07/19] elf: Fix _dl_debug_vdprintf to work before self-relocation
  2023-10-17 13:05 [PATCH v2 00/19] Improve loader environment variable handling Adhemerval Zanella
                   ` (5 preceding siblings ...)
  2023-10-17 13:05 ` [PATCH v2 06/19] elf: Do not parse ill-formatted strings Adhemerval Zanella
@ 2023-10-17 13:05 ` Adhemerval Zanella
  2023-10-27 10:27   ` Siddhesh Poyarekar
  2023-10-17 13:05 ` [PATCH v2 08/19] elf: Emit warning if tunable is ill-formatted Adhemerval Zanella
                   ` (11 subsequent siblings)
  18 siblings, 1 reply; 45+ messages in thread
From: Adhemerval Zanella @ 2023-10-17 13:05 UTC (permalink / raw)
  To: libc-alpha, Siddhesh Poyarekar

The strlen might trigger and invalid GOT entry if it used before
the process is self-relocated (for instance on dl-tunables if any
error occurs).

Checked on x86_64-linux-gnu.
---
 elf/dl-printf.c       | 16 ++++++++++++++--
 stdio-common/Makefile |  5 +++++
 stdio-common/_itoa.c  |  5 +++++
 3 files changed, 24 insertions(+), 2 deletions(-)

diff --git a/elf/dl-printf.c b/elf/dl-printf.c
index 6efb4c019a..5e93208535 100644
--- a/elf/dl-printf.c
+++ b/elf/dl-printf.c
@@ -17,6 +17,10 @@
    License along with the GNU C Library; if not, see
    <https://www.gnu.org/licenses/>.  */
 
+#include <string.h>
+#if BUILD_PIE_DEFAULT
+# pragma GCC visibility push(hidden)
+#endif
 #include <_itoa.h>
 #include <assert.h>
 #include <dl-writev.h>
@@ -25,11 +29,19 @@
 #include <stdarg.h>
 #include <stdint.h>
 #include <stdlib.h>
-#include <string.h>
 #include <sys/uio.h>
 #include <unistd.h>
 #include <intprops.h>
 
+/* The function might be called before the process is self-relocated.  */
+static size_t
+_dl_debug_strlen (const char *s)
+{
+  const char *p = s;
+  for (; *s != '\0'; s++);
+  return s - p;
+}
+
 /* Bare-bones printf implementation.  This function only knows about
    the formats and flags needed and can handle only up to 64 stripes in
    the output.  */
@@ -193,7 +205,7 @@ _dl_debug_vdprintf (int fd, int tag_p, const char *fmt, va_list arg)
 	    case 's':
 	      /* Get the string argument.  */
 	      iov[niov].iov_base = va_arg (arg, char *);
-	      iov[niov].iov_len = strlen (iov[niov].iov_base);
+	      iov[niov].iov_len = _dl_debug_strlen (iov[niov].iov_base);
 	      if (prec != -1)
 		iov[niov].iov_len = MIN ((size_t) prec, iov[niov].iov_len);
 	      ++niov;
diff --git a/stdio-common/Makefile b/stdio-common/Makefile
index bacb795fed..e88a9cea29 100644
--- a/stdio-common/Makefile
+++ b/stdio-common/Makefile
@@ -460,6 +460,11 @@ CFLAGS-isoc23_scanf.c += -fexceptions
 
 CFLAGS-dprintf.c += $(config-cflags-wno-ignored-attributes)
 
+# Called during static library initialization, so turn stack-protection
+# off for non-shared builds.
+CFLAGS-_itoa.o = $(no-stack-protector)
+CFLAGS-_itoa.op = $(no-stack-protector)
+
 # scanf18.c and scanf19.c test a deprecated extension which is no
 # longer visible under most conformance levels; see the source files
 # for more detail.
diff --git a/stdio-common/_itoa.c b/stdio-common/_itoa.c
index 3037b0f529..48f2903ecb 100644
--- a/stdio-common/_itoa.c
+++ b/stdio-common/_itoa.c
@@ -16,6 +16,11 @@
    License along with the GNU C Library; if not, see
    <https://www.gnu.org/licenses/>.  */
 
+/* Mark symbols hidden in static PIE for early self relocation to work.
+   Note: string.h may have ifuncs which cannot be hidden on i686.  */
+#if BUILD_PIE_DEFAULT
+# pragma GCC visibility push(hidden)
+#endif
 #include <gmp-mparam.h>
 #include <gmp.h>
 #include <limits.h>
-- 
2.34.1


^ permalink raw reply	[flat|nested] 45+ messages in thread

* [PATCH v2 08/19] elf: Emit warning if tunable is ill-formatted
  2023-10-17 13:05 [PATCH v2 00/19] Improve loader environment variable handling Adhemerval Zanella
                   ` (6 preceding siblings ...)
  2023-10-17 13:05 ` [PATCH v2 07/19] elf: Fix _dl_debug_vdprintf to work before self-relocation Adhemerval Zanella
@ 2023-10-17 13:05 ` Adhemerval Zanella
  2023-10-27 10:28   ` Siddhesh Poyarekar
  2023-10-17 13:05 ` [PATCH v2 09/19] x86: Use dl-symbol-redir-ifunc.h on cpu-tunables Adhemerval Zanella
                   ` (10 subsequent siblings)
  18 siblings, 1 reply; 45+ messages in thread
From: Adhemerval Zanella @ 2023-10-17 13:05 UTC (permalink / raw)
  To: libc-alpha, Siddhesh Poyarekar

So caller knows that the tunable will be ignored.

Checked on x86_64-linux-gnu.
---
 elf/dl-tunables.c | 6 +++++-
 1 file changed, 5 insertions(+), 1 deletion(-)

diff --git a/elf/dl-tunables.c b/elf/dl-tunables.c
index 5d4b8c5bc0..b39c14694c 100644
--- a/elf/dl-tunables.c
+++ b/elf/dl-tunables.c
@@ -236,7 +236,11 @@ parse_tunables (char *valstring)
   struct tunable_toset_t tunables[tunables_list_size];
   int ntunables = parse_tunables_string (valstring, tunables);
   if (ntunables == -1)
-    return;
+    {
+      _dl_error_printf (
+        "ERROR: ld.so: invalid GLIBC_TUNABLES `%s': ignored.\n", valstring);
+      return;
+    }
 
   for (int i = 0; i < ntunables; i++)
     tunable_initialize (tunables[i].t, tunables[i].value);
-- 
2.34.1


^ permalink raw reply	[flat|nested] 45+ messages in thread

* [PATCH v2 09/19] x86: Use dl-symbol-redir-ifunc.h on cpu-tunables
  2023-10-17 13:05 [PATCH v2 00/19] Improve loader environment variable handling Adhemerval Zanella
                   ` (7 preceding siblings ...)
  2023-10-17 13:05 ` [PATCH v2 08/19] elf: Emit warning if tunable is ill-formatted Adhemerval Zanella
@ 2023-10-17 13:05 ` Adhemerval Zanella
  2023-10-27 10:32   ` Siddhesh Poyarekar
  2023-10-17 13:05 ` [PATCH v2 10/19] s390: " Adhemerval Zanella
                   ` (9 subsequent siblings)
  18 siblings, 1 reply; 45+ messages in thread
From: Adhemerval Zanella @ 2023-10-17 13:05 UTC (permalink / raw)
  To: libc-alpha, Siddhesh Poyarekar; +Cc: Noah Goldstein

The dl-symbol-redir-ifunc.h redirects compiler-generated libcalls to
arch-specific memory implementations to avoid ifun calls where it is not
yet possible. The memcmp-isa-default-impl.h aims to fix the same issue
by calling the specific memset implementation directly.

Using the memcmp symbol directly allows the compile to inline the memset
calls (especially because _dl_tunable_set_hwcaps uses constants values),
generating better code.

For i386, _dl_writev with PIE requires to use the old 'int $0x80'
syscall mode because the calling the TLS register (gs) is not yet
initialized.

Checked on x86_64-linux-gnu.

Reviewed-by: Noah Goldstein <goldstein.w.n@gmail.com>
---
 .../i686/multiarch/dl-symbol-redir-ifunc.h    |  5 +++
 .../sysv/linux/i386/dl-writev.h}              | 18 ++++-----
 sysdeps/x86/cpu-tunables.c                    | 39 ++++++-------------
 .../x86_64/multiarch/dl-symbol-redir-ifunc.h  | 15 +++++++
 4 files changed, 39 insertions(+), 38 deletions(-)
 rename sysdeps/{x86_64/memcmp-isa-default-impl.h => unix/sysv/linux/i386/dl-writev.h} (62%)

diff --git a/sysdeps/i386/i686/multiarch/dl-symbol-redir-ifunc.h b/sysdeps/i386/i686/multiarch/dl-symbol-redir-ifunc.h
index dee69d19db..220c586bd2 100644
--- a/sysdeps/i386/i686/multiarch/dl-symbol-redir-ifunc.h
+++ b/sysdeps/i386/i686/multiarch/dl-symbol-redir-ifunc.h
@@ -19,6 +19,11 @@
 #ifndef _DL_IFUNC_GENERIC_H
 #define _DL_IFUNC_GENERIC_H
 
+#ifndef SHARED
+
 asm ("memset = __memset_ia32");
+asm ("memcmp = __memcmp_ia32");
+
+#endif /* SHARED */
 
 #endif
diff --git a/sysdeps/x86_64/memcmp-isa-default-impl.h b/sysdeps/unix/sysv/linux/i386/dl-writev.h
similarity index 62%
rename from sysdeps/x86_64/memcmp-isa-default-impl.h
rename to sysdeps/unix/sysv/linux/i386/dl-writev.h
index 0962e83c3d..624d0e46b0 100644
--- a/sysdeps/x86_64/memcmp-isa-default-impl.h
+++ b/sysdeps/unix/sysv/linux/i386/dl-writev.h
@@ -1,5 +1,5 @@
-/* Set default memcmp impl based on ISA level.
-   Copyright (C) 2022-2023 Free Software Foundation, Inc.
+/* Message-writing for the dynamic linker.  Linux/i386 version.
+   Copyright (C) 2013-2023 Free Software Foundation, Inc.
    This file is part of the GNU C Library.
 
    The GNU C Library is free software; you can redistribute it and/or
@@ -16,13 +16,9 @@
    License along with the GNU C Library; if not, see
    <https://www.gnu.org/licenses/>.  */
 
-#include <isa-level.h>
-#if MINIMUM_X86_ISA_LEVEL == 1 || MINIMUM_X86_ISA_LEVEL == 2
-# define DEFAULT_MEMCMP	__memcmp_sse2
-#elif MINIMUM_X86_ISA_LEVEL == 3
-# define DEFAULT_MEMCMP	__memcmp_avx2_movbe
-#elif MINIMUM_X86_ISA_LEVEL == 4
-# define DEFAULT_MEMCMP	__memcmp_evex_movbe
-#else
-# error "Unknown default memcmp implementation"
+#if BUILD_PIE_DEFAULT
+/* Can't use "call *%gs:SYSINFO_OFFSET" during startup in static PIE.  */
+# define I386_USE_SYSENTER 0
 #endif
+
+#include <sysdeps/unix/sysv/linux/dl-writev.h>
diff --git a/sysdeps/x86/cpu-tunables.c b/sysdeps/x86/cpu-tunables.c
index 0d4f328585..5697885226 100644
--- a/sysdeps/x86/cpu-tunables.c
+++ b/sysdeps/x86/cpu-tunables.c
@@ -24,24 +24,11 @@
 #include <string.h>
 #include <cpu-features.h>
 #include <ldsodefs.h>
-
-/* We can't use IFUNC memcmp nor strlen in init_cpu_features from libc.a
-   since IFUNC must be set up by init_cpu_features.  */
-#if defined USE_MULTIARCH && !defined SHARED
-# ifdef __x86_64__
-/* DEFAULT_MEMCMP by sysdeps/x86_64/memcmp-isa-default-impl.h.  */
-#  include <sysdeps/x86_64/memcmp-isa-default-impl.h>
-# else
-#  define DEFAULT_MEMCMP	__memcmp_ia32
-# endif
-extern __typeof (memcmp) DEFAULT_MEMCMP;
-#else
-# define DEFAULT_MEMCMP	memcmp
-#endif
+#include <dl-symbol-redir-ifunc.h>
 
 #define CHECK_GLIBC_IFUNC_CPU_OFF(f, cpu_features, name, len)		\
   _Static_assert (sizeof (#name) - 1 == len, #name " != " #len);	\
-  if (!DEFAULT_MEMCMP (f, #name, len))					\
+  if (memcmp (f, #name, len) == 0)					\
     {									\
       CPU_FEATURE_UNSET (cpu_features, name)				\
       break;								\
@@ -51,7 +38,7 @@ extern __typeof (memcmp) DEFAULT_MEMCMP;
    which isn't available.  */
 #define CHECK_GLIBC_IFUNC_PREFERRED_OFF(f, cpu_features, name, len)	\
   _Static_assert (sizeof (#name) - 1 == len, #name " != " #len);	\
-  if (!DEFAULT_MEMCMP (f, #name, len))					\
+  if (memcmp (f, #name, len) == 0)					\
     {									\
       cpu_features->preferred[index_arch_##name]			\
 	&= ~bit_arch_##name;						\
@@ -62,7 +49,7 @@ extern __typeof (memcmp) DEFAULT_MEMCMP;
 #define CHECK_GLIBC_IFUNC_PREFERRED_BOTH(f, cpu_features, name,	\
 					  disable, len)			\
   _Static_assert (sizeof (#name) - 1 == len, #name " != " #len);	\
-  if (!DEFAULT_MEMCMP (f, #name, len))					\
+  if (memcmp (f, #name, len) == 0)					\
     {									\
       if (disable)							\
 	cpu_features->preferred[index_arch_##name] &= ~bit_arch_##name;	\
@@ -76,7 +63,7 @@ extern __typeof (memcmp) DEFAULT_MEMCMP;
 #define CHECK_GLIBC_IFUNC_PREFERRED_NEED_BOTH(f, cpu_features, name,	\
 					       need, disable, len)	\
   _Static_assert (sizeof (#name) - 1 == len, #name " != " #len);	\
-  if (!DEFAULT_MEMCMP (f, #name, len))					\
+  if (memcmp (f, #name, len) == 0)					\
     {									\
       if (disable)							\
 	cpu_features->preferred[index_arch_##name] &= ~bit_arch_##name;	\
@@ -177,7 +164,7 @@ TUNABLE_CALLBACK (set_hwcaps) (tunable_val_t *valp)
 	      CHECK_GLIBC_IFUNC_CPU_OFF (n, cpu_features, POPCNT, 6);
 	      CHECK_GLIBC_IFUNC_CPU_OFF (n, cpu_features, SSE4_1, 6);
 	      CHECK_GLIBC_IFUNC_CPU_OFF (n, cpu_features, SSE4_2, 6);
-	      if (!DEFAULT_MEMCMP (n, "XSAVEC", 6))
+	      if (memcmp (n, "XSAVEC", 6) == 0)
 		{
 		  /* Update xsave_state_size to XSAVE state size.  */
 		  cpu_features->xsave_state_size
@@ -290,12 +277,11 @@ attribute_hidden
 void
 TUNABLE_CALLBACK (set_x86_ibt) (tunable_val_t *valp)
 {
-  if (DEFAULT_MEMCMP (valp->strval, "on", sizeof ("on")) == 0)
+  if (memcmp (valp->strval, "on", sizeof ("on")) == 0)
     GL(dl_x86_feature_control).ibt = cet_always_on;
-  else if (DEFAULT_MEMCMP (valp->strval, "off", sizeof ("off")) == 0)
+  else if (memcmp (valp->strval, "off", sizeof ("off")) == 0)
     GL(dl_x86_feature_control).ibt = cet_always_off;
-  else if (DEFAULT_MEMCMP (valp->strval, "permissive",
-			   sizeof ("permissive")) == 0)
+  else if (memcmp (valp->strval, "permissive", sizeof ("permissive")) == 0)
     GL(dl_x86_feature_control).ibt = cet_permissive;
 }
 
@@ -303,12 +289,11 @@ attribute_hidden
 void
 TUNABLE_CALLBACK (set_x86_shstk) (tunable_val_t *valp)
 {
-  if (DEFAULT_MEMCMP (valp->strval, "on", sizeof ("on")) == 0)
+  if (memcmp (valp->strval, "on", sizeof ("on")) == 0)
     GL(dl_x86_feature_control).shstk = cet_always_on;
-  else if (DEFAULT_MEMCMP (valp->strval, "off", sizeof ("off")) == 0)
+  else if (memcmp (valp->strval, "off", sizeof ("off")) == 0)
     GL(dl_x86_feature_control).shstk = cet_always_off;
-  else if (DEFAULT_MEMCMP (valp->strval, "permissive",
-			   sizeof ("permissive")) == 0)
+  else if (memcmp (valp->strval, "permissive", sizeof ("permissive")) == 0)
     GL(dl_x86_feature_control).shstk = cet_permissive;
 }
 #endif
diff --git a/sysdeps/x86_64/multiarch/dl-symbol-redir-ifunc.h b/sysdeps/x86_64/multiarch/dl-symbol-redir-ifunc.h
index 3fe73ca1c3..c7d8961bb6 100644
--- a/sysdeps/x86_64/multiarch/dl-symbol-redir-ifunc.h
+++ b/sysdeps/x86_64/multiarch/dl-symbol-redir-ifunc.h
@@ -19,6 +19,8 @@
 #ifndef _DL_IFUNC_GENERIC_H
 #define _DL_IFUNC_GENERIC_H
 
+#ifndef SHARED
+
 #include <isa-level.h>
 
 #if MINIMUM_X86_ISA_LEVEL >= 4
@@ -31,4 +33,17 @@
 
 asm ("memset = " HAVE_MEMSET_IFUNC_GENERIC);
 
+
+#if MINIMUM_X86_ISA_LEVEL >= 4
+# define HAVE_MEMCMP_IFUNC_GENERIC "__memcmp_evex_movbe"
+#elif MINIMUM_X86_ISA_LEVEL == 3
+# define HAVE_MEMCMP_IFUNC_GENERIC "__memcmp_avx2_movbe"
+#else
+# define HAVE_MEMCMP_IFUNC_GENERIC "__memcmp_sse2"
+#endif
+
+asm ("memcmp = " HAVE_MEMCMP_IFUNC_GENERIC);
+
+#endif /* SHARED */
+
 #endif
-- 
2.34.1


^ permalink raw reply	[flat|nested] 45+ messages in thread

* [PATCH v2 10/19] s390: Use dl-symbol-redir-ifunc.h on cpu-tunables
  2023-10-17 13:05 [PATCH v2 00/19] Improve loader environment variable handling Adhemerval Zanella
                   ` (8 preceding siblings ...)
  2023-10-17 13:05 ` [PATCH v2 09/19] x86: Use dl-symbol-redir-ifunc.h on cpu-tunables Adhemerval Zanella
@ 2023-10-17 13:05 ` Adhemerval Zanella
  2023-10-27 10:34   ` Siddhesh Poyarekar
  2023-10-17 13:05 ` [PATCH v2 11/19] elf: Do not duplicate the GLIBC_TUNABLES string Adhemerval Zanella
                   ` (8 subsequent siblings)
  18 siblings, 1 reply; 45+ messages in thread
From: Adhemerval Zanella @ 2023-10-17 13:05 UTC (permalink / raw)
  To: libc-alpha, Siddhesh Poyarekar

Using the memcmp symbol directly allows the compile to inline the
memcmp calls (especially because _dl_tunable_set_hwcaps uses constants
values), generating better code.

Checked with tst-tunables on s390x-linux-gnu (qemu system).
---
 sysdeps/s390/cpu-features.c                   | 30 +++++++++----------
 .../s390/multiarch/dl-symbol-redir-ifunc.h    |  2 ++
 2 files changed, 17 insertions(+), 15 deletions(-)

diff --git a/sysdeps/s390/cpu-features.c b/sysdeps/s390/cpu-features.c
index 39f8c23a60..55449ba07f 100644
--- a/sysdeps/s390/cpu-features.c
+++ b/sysdeps/s390/cpu-features.c
@@ -21,7 +21,7 @@
 #include <elf/dl-tunables.h>
 #include <ifunc-memcmp.h>
 #include <string.h>
-extern __typeof (memcmp) MEMCMP_DEFAULT;
+#include <dl-symbol-redir-ifunc.h>
 
 #define S390_COPY_CPU_FEATURES(SRC_PTR, DEST_PTR)	\
   (DEST_PTR)->hwcap = (SRC_PTR)->hwcap;			\
@@ -89,9 +89,9 @@ TUNABLE_CALLBACK (set_hwcaps) (tunable_val_t *valp)
       if ((*feature == 'z' || *feature == 'a'))
 	{
 	  if ((feature_len == 5 && *feature == 'z'
-	       && MEMCMP_DEFAULT (feature, "zEC12", 5) == 0)
+	       && memcmp (feature, "zEC12", 5) == 0)
 	      || (feature_len == 6 && *feature == 'a'
-		  && MEMCMP_DEFAULT (feature, "arch10", 6) == 0))
+		  && memcmp (feature, "arch10", 6) == 0))
 	    {
 	      reset_features = true;
 	      disable = true;
@@ -100,9 +100,9 @@ TUNABLE_CALLBACK (set_hwcaps) (tunable_val_t *valp)
 	      stfle_bits0_mask = S390_STFLE_MASK_ARCH13_MIE3;
 	    }
 	  else if ((feature_len == 3 && *feature == 'z'
-		    && MEMCMP_DEFAULT (feature, "z13", 3) == 0)
+		    && memcmp (feature, "z13", 3) == 0)
 		   || (feature_len == 6 && *feature == 'a'
-		       && MEMCMP_DEFAULT (feature, "arch11", 6) == 0))
+		       && memcmp (feature, "arch11", 6) == 0))
 	    {
 	      reset_features = true;
 	      disable = true;
@@ -110,9 +110,9 @@ TUNABLE_CALLBACK (set_hwcaps) (tunable_val_t *valp)
 	      stfle_bits0_mask = S390_STFLE_MASK_ARCH13_MIE3;
 	    }
 	  else if ((feature_len == 3 && *feature == 'z'
-		    && MEMCMP_DEFAULT (feature, "z14", 3) == 0)
+		    && memcmp (feature, "z14", 3) == 0)
 		   || (feature_len == 6 && *feature == 'a'
-		       && MEMCMP_DEFAULT (feature, "arch12", 6) == 0))
+		       && memcmp (feature, "arch12", 6) == 0))
 	    {
 	      reset_features = true;
 	      disable = true;
@@ -120,11 +120,11 @@ TUNABLE_CALLBACK (set_hwcaps) (tunable_val_t *valp)
 	      stfle_bits0_mask = S390_STFLE_MASK_ARCH13_MIE3;
 	    }
 	  else if ((feature_len == 3 && *feature == 'z'
-		    && (MEMCMP_DEFAULT (feature, "z15", 3) == 0
-			|| MEMCMP_DEFAULT (feature, "z16", 3) == 0))
+		    && (memcmp (feature, "z15", 3) == 0
+			|| memcmp (feature, "z16", 3) == 0))
 		   || (feature_len == 6
-		       && (MEMCMP_DEFAULT (feature, "arch13", 6) == 0
-			   || MEMCMP_DEFAULT (feature, "arch14", 6) == 0)))
+		       && (memcmp (feature, "arch13", 6) == 0
+			   || memcmp (feature, "arch14", 6) == 0)))
 	    {
 	      /* For z15 or newer we don't have to disable something,
 		 but we have to reset to the original values.  */
@@ -134,14 +134,14 @@ TUNABLE_CALLBACK (set_hwcaps) (tunable_val_t *valp)
       else if (*feature == 'H')
 	{
 	  if (feature_len == 15
-	      && MEMCMP_DEFAULT (feature, "HWCAP_S390_VXRS", 15) == 0)
+	      && memcmp (feature, "HWCAP_S390_VXRS", 15) == 0)
 	    {
 	      hwcap_mask = HWCAP_S390_VXRS;
 	      if (disable)
 		hwcap_mask |= HWCAP_S390_VXRS_EXT | HWCAP_S390_VXRS_EXT2;
 	    }
 	  else if (feature_len == 19
-		   && MEMCMP_DEFAULT (feature, "HWCAP_S390_VXRS_EXT", 19) == 0)
+		   && memcmp (feature, "HWCAP_S390_VXRS_EXT", 19) == 0)
 	    {
 	      hwcap_mask = HWCAP_S390_VXRS_EXT;
 	      if (disable)
@@ -150,7 +150,7 @@ TUNABLE_CALLBACK (set_hwcaps) (tunable_val_t *valp)
 		hwcap_mask |= HWCAP_S390_VXRS;
 	    }
 	  else if (feature_len == 20
-		   && MEMCMP_DEFAULT (feature, "HWCAP_S390_VXRS_EXT2", 20) == 0)
+		   && memcmp (feature, "HWCAP_S390_VXRS_EXT2", 20) == 0)
 	    {
 	      hwcap_mask = HWCAP_S390_VXRS_EXT2;
 	      if (!disable)
@@ -160,7 +160,7 @@ TUNABLE_CALLBACK (set_hwcaps) (tunable_val_t *valp)
       else if (*feature == 'S')
 	{
 	  if (feature_len == 10
-	      && MEMCMP_DEFAULT (feature, "STFLE_MIE3", 10) == 0)
+	      && memcmp (feature, "STFLE_MIE3", 10) == 0)
 	    {
 	      stfle_bits0_mask = S390_STFLE_MASK_ARCH13_MIE3;
 	    }
diff --git a/sysdeps/s390/multiarch/dl-symbol-redir-ifunc.h b/sysdeps/s390/multiarch/dl-symbol-redir-ifunc.h
index aa084fdcde..0f58897c48 100644
--- a/sysdeps/s390/multiarch/dl-symbol-redir-ifunc.h
+++ b/sysdeps/s390/multiarch/dl-symbol-redir-ifunc.h
@@ -20,10 +20,12 @@
 #define _DL_IFUNC_GENERIC_H
 
 #include <ifunc-memset.h>
+#include <ifunc-memcmp.h>
 
 #define IFUNC_SYMBOL_STR1(s)	#s
 #define IFUNC_SYMBOL_STR(s)	IFUNC_SYMBOL_STR1(s)
 
 asm ("memset = " IFUNC_SYMBOL_STR(MEMSET_DEFAULT));
+asm ("memcmp = " IFUNC_SYMBOL_STR(MEMCMP_DEFAULT));
 
 #endif
-- 
2.34.1


^ permalink raw reply	[flat|nested] 45+ messages in thread

* [PATCH v2 11/19] elf: Do not duplicate the GLIBC_TUNABLES string
  2023-10-17 13:05 [PATCH v2 00/19] Improve loader environment variable handling Adhemerval Zanella
                   ` (9 preceding siblings ...)
  2023-10-17 13:05 ` [PATCH v2 10/19] s390: " Adhemerval Zanella
@ 2023-10-17 13:05 ` Adhemerval Zanella
  2023-10-27 11:54   ` Siddhesh Poyarekar
  2023-10-17 13:05 ` [PATCH v2 12/19] elf: Ignore LD_PROFILE for setuid binaries Adhemerval Zanella
                   ` (7 subsequent siblings)
  18 siblings, 1 reply; 45+ messages in thread
From: Adhemerval Zanella @ 2023-10-17 13:05 UTC (permalink / raw)
  To: libc-alpha, Siddhesh Poyarekar

The tunable parsing duplicates the tunable environment variable so it
null-terminates each one since it simplifies the later parsing. It has
the drawback of adding another point of failure (__minimal_malloc
failing), and the memory copy requires tuning the compiler to avoid mem
operations calls.

The parsing now tracks the tunable start and its size. The
dl-tunable-parse.h adds helper functions to help parsing, like a strcmp
that also checks for size and an iterator for suboptions that are
comma-separated (used on hwcap parsing by x86, powerpc, and s390x).

Since the environment variable is allocated on the stack by the kernel,
it is safe to keep the references to the suboptions for later parsing
of string tunables (as done by set_hwcaps by multiple architectures).

Checked on x86_64-linux-gnu, powerpc64le-linux-gnu, and
aarch64-linux-gnu.
---
 elf/dl-tunables.c                             |  66 +++----
 elf/dl-tunables.h                             |   6 +-
 sysdeps/generic/dl-tunables-parse.h           | 128 ++++++++++++++
 sysdeps/s390/cpu-features.c                   | 167 +++++++-----------
 .../unix/sysv/linux/aarch64/cpu-features.c    |  38 ++--
 .../unix/sysv/linux/powerpc/cpu-features.c    |  45 ++---
 .../sysv/linux/powerpc/tst-hwcap-tunables.c   |   6 +-
 sysdeps/x86/Makefile                          |   4 +-
 sysdeps/x86/cpu-tunables.c                    | 118 +++++--------
 sysdeps/x86/tst-hwcap-tunables.c              | 150 ++++++++++++++++
 10 files changed, 455 insertions(+), 273 deletions(-)
 create mode 100644 sysdeps/generic/dl-tunables-parse.h
 create mode 100644 sysdeps/x86/tst-hwcap-tunables.c

diff --git a/elf/dl-tunables.c b/elf/dl-tunables.c
index b39c14694c..869846f9da 100644
--- a/elf/dl-tunables.c
+++ b/elf/dl-tunables.c
@@ -36,28 +36,6 @@
 #define TUNABLES_INTERNAL 1
 #include "dl-tunables.h"
 
-#include <not-errno.h>
-
-static char *
-tunables_strdup (const char *in)
-{
-  size_t i = 0;
-
-  while (in[i++] != '\0');
-  char *out = __minimal_malloc (i + 1);
-
-  /* For most of the tunables code, we ignore user errors.  However,
-     this is a system error - and running out of memory at program
-     startup should be reported, so we do.  */
-  if (out == NULL)
-    _dl_fatal_printf ("failed to allocate memory to process tunables\n");
-
-  while (i-- > 0)
-    out[i] = in[i];
-
-  return out;
-}
-
 static char **
 get_next_env (char **envp, char **name, size_t *namelen, char **val,
 	      char ***prev_envp)
@@ -134,14 +112,14 @@ do_tunable_update_val (tunable_t *cur, const tunable_val_t *valp,
 /* Validate range of the input value and initialize the tunable CUR if it looks
    good.  */
 static void
-tunable_initialize (tunable_t *cur, const char *strval)
+tunable_initialize (tunable_t *cur, const char *strval, size_t len)
 {
-  tunable_val_t val;
+  tunable_val_t val = { 0 };
 
   if (cur->type.type_code != TUNABLE_TYPE_STRING)
     val.numval = (tunable_num_t) _dl_strtoul (strval, NULL);
   else
-    val.strval = strval;
+    val.strval = (struct tunable_str_t) { strval, len };
   do_tunable_update_val (cur, &val, NULL, NULL);
 }
 
@@ -158,29 +136,29 @@ struct tunable_toset_t
 {
   tunable_t *t;
   const char *value;
+  size_t len;
 };
 
 enum { tunables_list_size = array_length (tunable_list) };
 
 /* Parse the tunable string VALSTRING and set TUNABLES with the found tunables
-   and their respectibles values.  VALSTRING is a duplicated values,  where
-   delimiters ':' are replaced with '\0', so string tunables are null
-   terminated.
+   and their respectibles values.  The VALSTRING is parse in place, with the
+   tunable start and size recorded in TUNABLES.
    Return the number of tunables found (including 0 if the string is empty)
    or -1 if for a ill-formatted definition.  */
 static int
-parse_tunables_string (char *valstring, struct tunable_toset_t *tunables)
+parse_tunables_string (const char *valstring, struct tunable_toset_t *tunables)
 {
   if (valstring == NULL || *valstring == '\0')
     return 0;
 
-  char *p = valstring;
+  const char *p = valstring;
   bool done = false;
   int ntunables = 0;
 
   while (!done)
     {
-      char *name = p;
+      const char *name = p;
 
       /* First, find where the name ends.  */
       while (*p != '=' && *p != ':' && *p != '\0')
@@ -202,7 +180,7 @@ parse_tunables_string (char *valstring, struct tunable_toset_t *tunables)
       /* Skip the ':' or '='.  */
       p++;
 
-      char *value = p;
+      const char *value = p;
 
       while (*p != '=' && *p != ':' && *p != '\0')
 	p++;
@@ -211,8 +189,6 @@ parse_tunables_string (char *valstring, struct tunable_toset_t *tunables)
 	return -1;
       else if (*p == '\0')
 	done = true;
-      else
-	*p++ = '\0';
 
       /* Add the tunable if it exists.  */
       for (size_t i = 0; i < tunables_list_size; i++)
@@ -221,7 +197,8 @@ parse_tunables_string (char *valstring, struct tunable_toset_t *tunables)
 
 	  if (tunable_is_name (cur->name, name))
 	    {
-	      tunables[ntunables++] = (struct tunable_toset_t) { cur, value };
+	      tunables[ntunables++] =
+		(struct tunable_toset_t) { cur, value, p - value };
 	      break;
 	    }
 	}
@@ -231,7 +208,7 @@ parse_tunables_string (char *valstring, struct tunable_toset_t *tunables)
 }
 
 static void
-parse_tunables (char *valstring)
+parse_tunables (const char *valstring)
 {
   struct tunable_toset_t tunables[tunables_list_size];
   int ntunables = parse_tunables_string (valstring, tunables);
@@ -243,7 +220,7 @@ parse_tunables (char *valstring)
     }
 
   for (int i = 0; i < ntunables; i++)
-    tunable_initialize (tunables[i].t, tunables[i].value);
+    tunable_initialize (tunables[i].t, tunables[i].value, tunables[i].len);
 }
 
 /* Initialize the tunables list from the environment.  For now we only use the
@@ -264,9 +241,12 @@ __tunables_init (char **envp)
   while ((envp = get_next_env (envp, &envname, &len, &envval,
 			       &prev_envp)) != NULL)
     {
+      /* The environment variable is allocated on the stack by the kernel, so
+	 it is safe to keep the references to the suboptions for later parsing
+	 of string tunables.  */
       if (tunable_is_name ("GLIBC_TUNABLES", envname))
 	{
-	  parse_tunables (tunables_strdup (envval));
+	  parse_tunables (envval);
 	  continue;
 	}
 
@@ -284,7 +264,7 @@ __tunables_init (char **envp)
 	  /* We have a match.  Initialize and move on to the next line.  */
 	  if (tunable_is_name (name, envname))
 	    {
-	      tunable_initialize (cur, envval);
+	      tunable_initialize (cur, envval, 0);
 	      break;
 	    }
 	}
@@ -298,7 +278,7 @@ __tunables_print (void)
     {
       const tunable_t *cur = &tunable_list[i];
       if (cur->type.type_code == TUNABLE_TYPE_STRING
-	  && cur->val.strval == NULL)
+	  && cur->val.strval.str == NULL)
 	_dl_printf ("%s:\n", cur->name);
       else
 	{
@@ -324,7 +304,9 @@ __tunables_print (void)
 			  (size_t) cur->type.max);
 	      break;
 	    case TUNABLE_TYPE_STRING:
-	      _dl_printf ("%s\n", cur->val.strval);
+	      _dl_printf ("%.*s\n",
+			  (int) cur->val.strval.len,
+			  cur->val.strval.str);
 	      break;
 	    default:
 	      __builtin_unreachable ();
@@ -359,7 +341,7 @@ __tunable_get_val (tunable_id_t id, void *valp, tunable_callback_t callback)
 	}
     case TUNABLE_TYPE_STRING:
 	{
-	  *((const char **)valp) = cur->val.strval;
+	  *((struct tunable_str_t **) valp) = &cur->val.strval;
 	  break;
 	}
     default:
diff --git a/elf/dl-tunables.h b/elf/dl-tunables.h
index 45c191e021..0e777d7d37 100644
--- a/elf/dl-tunables.h
+++ b/elf/dl-tunables.h
@@ -30,7 +30,11 @@ typedef intmax_t tunable_num_t;
 typedef union
 {
   tunable_num_t numval;
-  const char *strval;
+  struct tunable_str_t
+  {
+    const char *str;
+    size_t len;
+  } strval;
 } tunable_val_t;
 
 typedef void (*tunable_callback_t) (tunable_val_t *);
diff --git a/sysdeps/generic/dl-tunables-parse.h b/sysdeps/generic/dl-tunables-parse.h
new file mode 100644
index 0000000000..a2420f9129
--- /dev/null
+++ b/sysdeps/generic/dl-tunables-parse.h
@@ -0,0 +1,128 @@
+/* Helper functions to handle tunable strings.
+   Copyright (C) 2023 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <https://www.gnu.org/licenses/>.  */
+
+#ifndef _DL_TUNABLES_PARSE_H
+#define _DL_TUNABLES_PARSE_H 1
+
+#include <string.h>
+
+/* Compare the contents of STRVAL with STR of size LEN.  The STR might not
+   be null-terminated.   */
+static __always_inline bool
+tunable_strcmp (const struct tunable_str_t *strval, const char *str,
+		size_t len)
+{
+  return strval->len == len && memcmp (strval->str, str, len) == 0;
+}
+#define tunable_strcmp_cte(__tunable, __str) \
+ tunable_strcmp (&__tunable->strval, __str, sizeof (__str) - 1)
+
+/*
+   Helper functions to iterate over a tunable string composed by multiple
+   suboptions separated by comma.  Each suboptions is return in the form
+   of { address, size } (no null terminated).  For instance:
+
+     struct tunable_str_comma_t ts;
+     tunable_str_comma_init (&ts, valp);
+
+     struct tunable_str_t t;
+     while (tunable_str_comma_next (&ts, &t))
+      {
+	_dl_printf ("[%s] %.*s (%d)\n",
+		    __func__,
+		    (int) tstr.len,
+		    tstr.str,
+		    (int) tstr.len);
+
+        if (tunable_str_comma_strcmp (&t, opt, opt1_len))
+	  {
+	    [...]
+	  }
+	else if (tunable_str_comma_strcmp_cte (&t, "opt2"))
+	  {
+	    [...]
+	  }
+      }
+*/
+
+struct tunable_str_comma_state_t
+{
+  const char *p;
+  size_t plen;
+  size_t maxplen;
+};
+
+struct tunable_str_comma_t
+{
+  const char *str;
+  size_t len;
+  bool disable;
+};
+
+static inline void
+tunable_str_comma_init (struct tunable_str_comma_state_t *state,
+			tunable_val_t *valp)
+{
+  state->p = valp->strval.str;
+  state->plen = 0;
+  state->maxplen = valp->strval.len;
+}
+
+static inline bool
+tunable_str_comma_next (struct tunable_str_comma_state_t *state,
+			struct tunable_str_comma_t *str)
+{
+  if (*state->p == '\0' || state->plen >= state->maxplen)
+    return false;
+
+  const char *c;
+  for (c = state->p; *c != ','; c++, state->plen++)
+    if (*c == '\0' || state->plen == state->maxplen)
+      break;
+
+  str->str = state->p;
+  str->len = c - state->p;
+
+  if (str->len > 0)
+    {
+      str->disable = *str->str == '-';
+      if (str->disable)
+	{
+	  str->str = str->str + 1;
+	  str->len = str->len - 1;
+	}
+    }
+
+  state->p = c + 1;
+  state->plen++;
+
+  return true;
+}
+
+/* Compare the contents of T with STR of size LEN.  The STR might not be
+   null-terminated.   */
+static __always_inline bool
+tunable_str_comma_strcmp (const struct tunable_str_comma_t *t, const char *str,
+			  size_t len)
+{
+  return t->len == len && memcmp (t->str, str, len) == 0;
+}
+#define tunable_str_comma_strcmp_cte(__t, __str) \
+  tunable_str_comma_strcmp (__t, __str, sizeof (__str) - 1)
+
+#endif
diff --git a/sysdeps/s390/cpu-features.c b/sysdeps/s390/cpu-features.c
index 55449ba07f..8b1466fa7b 100644
--- a/sysdeps/s390/cpu-features.c
+++ b/sysdeps/s390/cpu-features.c
@@ -22,6 +22,7 @@
 #include <ifunc-memcmp.h>
 #include <string.h>
 #include <dl-symbol-redir-ifunc.h>
+#include <dl-tunables-parse.h>
 
 #define S390_COPY_CPU_FEATURES(SRC_PTR, DEST_PTR)	\
   (DEST_PTR)->hwcap = (SRC_PTR)->hwcap;			\
@@ -51,33 +52,14 @@ TUNABLE_CALLBACK (set_hwcaps) (tunable_val_t *valp)
   struct cpu_features cpu_features_curr;
   S390_COPY_CPU_FEATURES (cpu_features, &cpu_features_curr);
 
-  const char *token = valp->strval;
-  do
+  struct tunable_str_comma_state_t ts;
+  tunable_str_comma_init (&ts, valp);
+
+  struct tunable_str_comma_t t;
+  while (tunable_str_comma_next (&ts, &t))
     {
-      const char *token_end, *feature;
-      bool disable;
-      size_t token_len;
-      size_t feature_len;
-
-      /* Find token separator or end of string.  */
-      for (token_end = token; *token_end != ','; token_end++)
-	if (*token_end == '\0')
-	  break;
-
-      /* Determine feature.  */
-      token_len = token_end - token;
-      if (*token == '-')
-	{
-	  disable = true;
-	  feature = token + 1;
-	  feature_len = token_len - 1;
-	}
-      else
-	{
-	  disable = false;
-	  feature = token;
-	  feature_len = token_len;
-	}
+      if (t.len == 0)
+	continue;
 
       /* Handle only the features here which are really used in the
 	 IFUNC-resolvers.  All others are ignored as the values are only used
@@ -85,85 +67,65 @@ TUNABLE_CALLBACK (set_hwcaps) (tunable_val_t *valp)
       bool reset_features = false;
       unsigned long int hwcap_mask = 0UL;
       unsigned long long stfle_bits0_mask = 0ULL;
+      bool disable = t.disable;
 
-      if ((*feature == 'z' || *feature == 'a'))
+      if (tunable_str_comma_strcmp_cte (&t, "zEC12")
+	  || tunable_str_comma_strcmp_cte (&t, "arch10"))
+	{
+	  reset_features = true;
+	  disable = true;
+	  hwcap_mask = HWCAP_S390_VXRS | HWCAP_S390_VXRS_EXT
+	    | HWCAP_S390_VXRS_EXT2;
+	  stfle_bits0_mask = S390_STFLE_MASK_ARCH13_MIE3;
+	}
+      else if (tunable_str_comma_strcmp_cte (&t, "z13")
+	       || tunable_str_comma_strcmp_cte (&t, "arch11"))
+	{
+	  reset_features = true;
+	  disable = true;
+	  hwcap_mask = HWCAP_S390_VXRS_EXT | HWCAP_S390_VXRS_EXT2;
+	  stfle_bits0_mask = S390_STFLE_MASK_ARCH13_MIE3;
+	}
+      else if (tunable_str_comma_strcmp_cte (&t, "z14")
+	       || tunable_str_comma_strcmp_cte (&t, "arch12"))
+	{
+	  reset_features = true;
+	  disable = true;
+	  hwcap_mask = HWCAP_S390_VXRS_EXT2;
+	  stfle_bits0_mask = S390_STFLE_MASK_ARCH13_MIE3;
+	}
+      else if (tunable_str_comma_strcmp_cte (&t, "z15")
+	       || tunable_str_comma_strcmp_cte (&t, "z16")
+	       || tunable_str_comma_strcmp_cte (&t, "arch13")
+	       || tunable_str_comma_strcmp_cte (&t, "arch14"))
 	{
-	  if ((feature_len == 5 && *feature == 'z'
-	       && memcmp (feature, "zEC12", 5) == 0)
-	      || (feature_len == 6 && *feature == 'a'
-		  && memcmp (feature, "arch10", 6) == 0))
-	    {
-	      reset_features = true;
-	      disable = true;
-	      hwcap_mask = HWCAP_S390_VXRS | HWCAP_S390_VXRS_EXT
-		| HWCAP_S390_VXRS_EXT2;
-	      stfle_bits0_mask = S390_STFLE_MASK_ARCH13_MIE3;
-	    }
-	  else if ((feature_len == 3 && *feature == 'z'
-		    && memcmp (feature, "z13", 3) == 0)
-		   || (feature_len == 6 && *feature == 'a'
-		       && memcmp (feature, "arch11", 6) == 0))
-	    {
-	      reset_features = true;
-	      disable = true;
-	      hwcap_mask = HWCAP_S390_VXRS_EXT | HWCAP_S390_VXRS_EXT2;
-	      stfle_bits0_mask = S390_STFLE_MASK_ARCH13_MIE3;
-	    }
-	  else if ((feature_len == 3 && *feature == 'z'
-		    && memcmp (feature, "z14", 3) == 0)
-		   || (feature_len == 6 && *feature == 'a'
-		       && memcmp (feature, "arch12", 6) == 0))
-	    {
-	      reset_features = true;
-	      disable = true;
-	      hwcap_mask = HWCAP_S390_VXRS_EXT2;
-	      stfle_bits0_mask = S390_STFLE_MASK_ARCH13_MIE3;
-	    }
-	  else if ((feature_len == 3 && *feature == 'z'
-		    && (memcmp (feature, "z15", 3) == 0
-			|| memcmp (feature, "z16", 3) == 0))
-		   || (feature_len == 6
-		       && (memcmp (feature, "arch13", 6) == 0
-			   || memcmp (feature, "arch14", 6) == 0)))
-	    {
-	      /* For z15 or newer we don't have to disable something,
-		 but we have to reset to the original values.  */
-	      reset_features = true;
-	    }
+	  /* For z15 or newer we don't have to disable something, but we have
+	     to reset to the original values.  */
+	  reset_features = true;
 	}
-      else if (*feature == 'H')
+      else if (tunable_str_comma_strcmp_cte (&t, "HWCAP_S390_VXRS"))
 	{
-	  if (feature_len == 15
-	      && memcmp (feature, "HWCAP_S390_VXRS", 15) == 0)
-	    {
-	      hwcap_mask = HWCAP_S390_VXRS;
-	      if (disable)
-		hwcap_mask |= HWCAP_S390_VXRS_EXT | HWCAP_S390_VXRS_EXT2;
-	    }
-	  else if (feature_len == 19
-		   && memcmp (feature, "HWCAP_S390_VXRS_EXT", 19) == 0)
-	    {
-	      hwcap_mask = HWCAP_S390_VXRS_EXT;
-	      if (disable)
-		hwcap_mask |= HWCAP_S390_VXRS_EXT2;
-	      else
-		hwcap_mask |= HWCAP_S390_VXRS;
-	    }
-	  else if (feature_len == 20
-		   && memcmp (feature, "HWCAP_S390_VXRS_EXT2", 20) == 0)
-	    {
-	      hwcap_mask = HWCAP_S390_VXRS_EXT2;
-	      if (!disable)
-		hwcap_mask |= HWCAP_S390_VXRS | HWCAP_S390_VXRS_EXT;
-	    }
+	  hwcap_mask = HWCAP_S390_VXRS;
+	  if (t.disable)
+	    hwcap_mask |= HWCAP_S390_VXRS_EXT | HWCAP_S390_VXRS_EXT2;
 	}
-      else if (*feature == 'S')
+      else if (tunable_str_comma_strcmp_cte (&t, "HWCAP_S390_VXRS_EXT"))
 	{
-	  if (feature_len == 10
-	      && memcmp (feature, "STFLE_MIE3", 10) == 0)
-	    {
-	      stfle_bits0_mask = S390_STFLE_MASK_ARCH13_MIE3;
-	    }
+	  hwcap_mask = HWCAP_S390_VXRS_EXT;
+	  if (t.disable)
+	    hwcap_mask |= HWCAP_S390_VXRS_EXT2;
+	  else
+	    hwcap_mask |= HWCAP_S390_VXRS;
+	}
+      else if (tunable_str_comma_strcmp_cte (&t, "HWCAP_S390_VXRS_EXT2"))
+	{
+	  hwcap_mask = HWCAP_S390_VXRS_EXT2;
+	  if (!t.disable)
+	    hwcap_mask |= HWCAP_S390_VXRS | HWCAP_S390_VXRS_EXT;
+	}
+      else if (tunable_str_comma_strcmp_cte (&t, "STFLE_MIE3"))
+	{
+	  stfle_bits0_mask = S390_STFLE_MASK_ARCH13_MIE3;
 	}
 
       /* Perform the actions determined above.  */
@@ -187,14 +149,7 @@ TUNABLE_CALLBACK (set_hwcaps) (tunable_val_t *valp)
 	  else
 	    cpu_features_curr.stfle_bits[0] |= stfle_bits0_mask;
 	}
-
-      /* Jump over current token ... */
-      token += token_len;
-
-      /* ... and skip token separator for next round.  */
-      if (*token == ',') token++;
     }
-  while (*token != '\0');
 
   /* Copy back the features after checking that no unsupported features were
      enabled by user.  */
diff --git a/sysdeps/unix/sysv/linux/aarch64/cpu-features.c b/sysdeps/unix/sysv/linux/aarch64/cpu-features.c
index dc09c1c827..3f1a6bcd62 100644
--- a/sysdeps/unix/sysv/linux/aarch64/cpu-features.c
+++ b/sysdeps/unix/sysv/linux/aarch64/cpu-features.c
@@ -16,10 +16,12 @@
    License along with the GNU C Library; if not, see
    <https://www.gnu.org/licenses/>.  */
 
+#include <array_length.h>
 #include <cpu-features.h>
 #include <sys/auxv.h>
 #include <elf/dl-hwcaps.h>
 #include <sys/prctl.h>
+#include <dl-tunables-parse.h>
 
 #define DCZID_DZP_MASK (1 << 4)
 #define DCZID_BS_MASK (0xf)
@@ -33,28 +35,32 @@
 struct cpu_list
 {
   const char *name;
+  size_t len;
   uint64_t midr;
 };
 
-static struct cpu_list cpu_list[] = {
-      {"falkor",	 0x510FC000},
-      {"thunderxt88",	 0x430F0A10},
-      {"thunderx2t99",   0x431F0AF0},
-      {"thunderx2t99p1", 0x420F5160},
-      {"phecda",	 0x680F0000},
-      {"ares",		 0x411FD0C0},
-      {"emag",		 0x503F0001},
-      {"kunpeng920", 	 0x481FD010},
-      {"a64fx",		 0x460F0010},
-      {"generic", 	 0x0}
+static const struct cpu_list cpu_list[] =
+{
+#define CPU_LIST_ENTRY(__str, __num) { __str, sizeof (__str) - 1, __num }
+  CPU_LIST_ENTRY ("falkor",         0x510FC000),
+  CPU_LIST_ENTRY ("thunderxt88",    0x430F0A10),
+  CPU_LIST_ENTRY ("thunderx2t99",   0x431F0AF0),
+  CPU_LIST_ENTRY ("thunderx2t99p1", 0x420F5160),
+  CPU_LIST_ENTRY ("phecda",         0x680F0000),
+  CPU_LIST_ENTRY ("ares",           0x411FD0C0),
+  CPU_LIST_ENTRY ("emag",           0x503F0001),
+  CPU_LIST_ENTRY ("kunpeng920",     0x481FD010),
+  CPU_LIST_ENTRY ("a64fx",          0x460F0010),
+  CPU_LIST_ENTRY ("generic",        0x0),
 };
 
 static uint64_t
-get_midr_from_mcpu (const char *mcpu)
+get_midr_from_mcpu (const struct tunable_str_t *mcpu)
 {
-  for (int i = 0; i < sizeof (cpu_list) / sizeof (struct cpu_list); i++)
-    if (strcmp (mcpu, cpu_list[i].name) == 0)
+  for (int i = 0; i < array_length (cpu_list); i++) {
+    if (tunable_strcmp (mcpu, cpu_list[i].name, cpu_list[i].len))
       return cpu_list[i].midr;
+  }
 
   return UINT64_MAX;
 }
@@ -65,7 +71,9 @@ init_cpu_features (struct cpu_features *cpu_features)
   register uint64_t midr = UINT64_MAX;
 
   /* Get the tunable override.  */
-  const char *mcpu = TUNABLE_GET (glibc, cpu, name, const char *, NULL);
+  const struct tunable_str_t *mcpu = TUNABLE_GET (glibc, cpu, name,
+						  struct tunable_str_t *,
+						  NULL);
   if (mcpu != NULL)
     midr = get_midr_from_mcpu (mcpu);
 
diff --git a/sysdeps/unix/sysv/linux/powerpc/cpu-features.c b/sysdeps/unix/sysv/linux/powerpc/cpu-features.c
index 7c6e20e702..390b3fd11a 100644
--- a/sysdeps/unix/sysv/linux/powerpc/cpu-features.c
+++ b/sysdeps/unix/sysv/linux/powerpc/cpu-features.c
@@ -20,6 +20,7 @@
 #include <stdint.h>
 #include <cpu-features.h>
 #include <elf/dl-tunables.h>
+#include <dl-tunables-parse.h>
 #include <unistd.h>
 #include <string.h>
 
@@ -43,41 +44,26 @@ TUNABLE_CALLBACK (set_hwcaps) (tunable_val_t *valp)
   struct cpu_features *cpu_features = &GLRO(dl_powerpc_cpu_features);
   unsigned long int tcbv_hwcap = cpu_features->hwcap;
   unsigned long int tcbv_hwcap2 = cpu_features->hwcap2;
-  const char *token = valp->strval;
-  do
+
+  struct tunable_str_comma_state_t ts;
+  tunable_str_comma_init (&ts, valp);
+
+  struct tunable_str_comma_t t;
+  while (tunable_str_comma_next (&ts, &t))
     {
-      const char *token_end, *feature;
-      bool disable;
-      size_t token_len, i, feature_len, offset = 0;
-      /* Find token separator or end of string.  */
-      for (token_end = token; *token_end != ','; token_end++)
-	if (*token_end == '\0')
-	  break;
+      if (t.len == 0)
+	continue;
 
-      /* Determine feature.  */
-      token_len = token_end - token;
-      if (*token == '-')
-	{
-	  disable = true;
-	  feature = token + 1;
-	  feature_len = token_len - 1;
-	}
-      else
-	{
-	  disable = false;
-	  feature = token;
-	  feature_len = token_len;
-	}
-      for (i = 0; i < array_length (hwcap_tunables); ++i)
+      size_t offset = 0;
+      for (int i = 0; i < array_length (hwcap_tunables); ++i)
 	{
 	  const char *hwcap_name = hwcap_names + offset;
 	  size_t hwcap_name_len = strlen (hwcap_name);
 	  /* Check the tunable name on the supported list.  */
-	  if (hwcap_name_len == feature_len
-	      && memcmp (feature, hwcap_name, feature_len) == 0)
+	  if (tunable_str_comma_strcmp (&t, hwcap_name, hwcap_name_len))
 	    {
 	      /* Update the hwcap and hwcap2 bits.  */
-	      if (disable)
+	      if (t.disable)
 		{
 		  /* Id is 1 for hwcap2 tunable.  */
 		  if (hwcap_tunables[i].id)
@@ -98,12 +84,7 @@ TUNABLE_CALLBACK (set_hwcaps) (tunable_val_t *valp)
 	    }
 	  offset += hwcap_name_len + 1;
 	}
-	token += token_len;
-	/* ... and skip token separator for next round.  */
-	if (*token == ',')
-	  token++;
     }
-  while (*token != '\0');
 }
 
 static inline void
diff --git a/sysdeps/unix/sysv/linux/powerpc/tst-hwcap-tunables.c b/sysdeps/unix/sysv/linux/powerpc/tst-hwcap-tunables.c
index 2631016a3a..049164f841 100644
--- a/sysdeps/unix/sysv/linux/powerpc/tst-hwcap-tunables.c
+++ b/sysdeps/unix/sysv/linux/powerpc/tst-hwcap-tunables.c
@@ -110,7 +110,11 @@ do_test (int argc, char *argv[])
 	run_test ("-arch_2_06", "__memcpy_power7");
       if (hwcap & PPC_FEATURE_ARCH_2_05)
 	run_test ("-arch_2_06,-arch_2_05","__memcpy_power6");
-      run_test ("-arch_2_06,-arch_2_05,-power5+,-power5,-power4", "__memcpy_power4");
+      run_test ("-arch_2_06,-arch_2_05,-power5+,-power5,-power4",
+		"__memcpy_power4");
+      /* Also run with valid, but empty settings.  */
+      run_test (",-,-arch_2_06,-arch_2_05,-power5+,-power5,,-power4,-",
+		"__memcpy_power4");
     }
   else
     {
diff --git a/sysdeps/x86/Makefile b/sysdeps/x86/Makefile
index 917c26f116..a64e5f002a 100644
--- a/sysdeps/x86/Makefile
+++ b/sysdeps/x86/Makefile
@@ -12,7 +12,8 @@ CFLAGS-get-cpuid-feature-leaf.o += $(no-stack-protector)
 
 tests += tst-get-cpu-features tst-get-cpu-features-static \
 	 tst-cpu-features-cpuinfo tst-cpu-features-cpuinfo-static \
-	 tst-cpu-features-supports tst-cpu-features-supports-static
+	 tst-cpu-features-supports tst-cpu-features-supports-static \
+	 tst-hwcap-tunables
 tests-static += tst-get-cpu-features-static \
 		tst-cpu-features-cpuinfo-static \
 		tst-cpu-features-supports-static
@@ -65,6 +66,7 @@ $(objpfx)tst-isa-level-1.out: $(objpfx)tst-isa-level-mod-1-baseline.so \
 endif
 tst-ifunc-isa-2-ENV = GLIBC_TUNABLES=glibc.cpu.hwcaps=-SSE4_2,-AVX,-AVX2,-AVX512F
 tst-ifunc-isa-2-static-ENV = $(tst-ifunc-isa-2-ENV)
+tst-hwcap-tunables-ARGS = -- $(host-test-program-cmd)
 endif
 
 ifeq ($(subdir),math)
diff --git a/sysdeps/x86/cpu-tunables.c b/sysdeps/x86/cpu-tunables.c
index 5697885226..0608209768 100644
--- a/sysdeps/x86/cpu-tunables.c
+++ b/sysdeps/x86/cpu-tunables.c
@@ -24,11 +24,12 @@
 #include <string.h>
 #include <cpu-features.h>
 #include <ldsodefs.h>
+#include <dl-tunables-parse.h>
 #include <dl-symbol-redir-ifunc.h>
 
 #define CHECK_GLIBC_IFUNC_CPU_OFF(f, cpu_features, name, len)		\
   _Static_assert (sizeof (#name) - 1 == len, #name " != " #len);	\
-  if (memcmp (f, #name, len) == 0)					\
+  if (tunable_str_comma_strcmp_cte (&f, #name))				\
     {									\
       CPU_FEATURE_UNSET (cpu_features, name)				\
       break;								\
@@ -38,7 +39,7 @@
    which isn't available.  */
 #define CHECK_GLIBC_IFUNC_PREFERRED_OFF(f, cpu_features, name, len)	\
   _Static_assert (sizeof (#name) - 1 == len, #name " != " #len);	\
-  if (memcmp (f, #name, len) == 0)					\
+  if (tunable_str_comma_strcmp_cte (&f, #name) == 0)			\
     {									\
       cpu_features->preferred[index_arch_##name]			\
 	&= ~bit_arch_##name;						\
@@ -46,12 +47,11 @@
     }
 
 /* Enable/disable a preferred feature NAME.  */
-#define CHECK_GLIBC_IFUNC_PREFERRED_BOTH(f, cpu_features, name,	\
-					  disable, len)			\
+#define CHECK_GLIBC_IFUNC_PREFERRED_BOTH(f, cpu_features, name,	len)	\
   _Static_assert (sizeof (#name) - 1 == len, #name " != " #len);	\
-  if (memcmp (f, #name, len) == 0)					\
+  if (tunable_str_comma_strcmp_cte (&f, #name))				\
     {									\
-      if (disable)							\
+      if (f.disable)							\
 	cpu_features->preferred[index_arch_##name] &= ~bit_arch_##name;	\
       else								\
 	cpu_features->preferred[index_arch_##name] |= bit_arch_##name;	\
@@ -61,11 +61,11 @@
 /* Enable/disable a preferred feature NAME.  Enable a preferred feature
    only if the feature NEED is usable.  */
 #define CHECK_GLIBC_IFUNC_PREFERRED_NEED_BOTH(f, cpu_features, name,	\
-					       need, disable, len)	\
+					      need, len)		\
   _Static_assert (sizeof (#name) - 1 == len, #name " != " #len);	\
-  if (memcmp (f, #name, len) == 0)					\
+  if (tunable_str_comma_strcmp_cte (&f, #name))				\
     {									\
-      if (disable)							\
+      if (f.disable)							\
 	cpu_features->preferred[index_arch_##name] &= ~bit_arch_##name;	\
       else if (CPU_FEATURE_USABLE_P (cpu_features, need))		\
 	cpu_features->preferred[index_arch_##name] |= bit_arch_##name;	\
@@ -93,38 +93,20 @@ TUNABLE_CALLBACK (set_hwcaps) (tunable_val_t *valp)
      NOTE: the IFUNC selection may change over time.  Please check all
      multiarch implementations when experimenting.  */
 
-  const char *p = valp->strval, *c;
   struct cpu_features *cpu_features = &GLRO(dl_x86_cpu_features);
-  size_t len;
 
-  do
-    {
-      const char *n;
-      bool disable;
-      size_t nl;
-
-      for (c = p; *c != ','; c++)
-	if (*c == '\0')
-	  break;
+  struct tunable_str_comma_state_t ts;
+  tunable_str_comma_init (&ts, valp);
 
-      len = c - p;
-      disable = *p == '-';
-      if (disable)
-	{
-	  n = p + 1;
-	  nl = len - 1;
-	}
-      else
-	{
-	  n = p;
-	  nl = len;
-	}
-      switch (nl)
+  struct tunable_str_comma_t n;
+  while (tunable_str_comma_next (&ts, &n))
+    {
+      switch (n.len)
 	{
 	default:
 	  break;
 	case 3:
-	  if (disable)
+	  if (n.disable)
 	    {
 	      CHECK_GLIBC_IFUNC_CPU_OFF (n, cpu_features, AVX, 3);
 	      CHECK_GLIBC_IFUNC_CPU_OFF (n, cpu_features, CX8, 3);
@@ -135,7 +117,7 @@ TUNABLE_CALLBACK (set_hwcaps) (tunable_val_t *valp)
 	    }
 	  break;
 	case 4:
-	  if (disable)
+	  if (n.disable)
 	    {
 	      CHECK_GLIBC_IFUNC_CPU_OFF (n, cpu_features, AVX2, 4);
 	      CHECK_GLIBC_IFUNC_CPU_OFF (n, cpu_features, BMI1, 4);
@@ -149,7 +131,7 @@ TUNABLE_CALLBACK (set_hwcaps) (tunable_val_t *valp)
 	    }
 	  break;
 	case 5:
-	  if (disable)
+	  if (n.disable)
 	    {
 	      CHECK_GLIBC_IFUNC_CPU_OFF (n, cpu_features, LZCNT, 5);
 	      CHECK_GLIBC_IFUNC_CPU_OFF (n, cpu_features, MOVBE, 5);
@@ -159,12 +141,12 @@ TUNABLE_CALLBACK (set_hwcaps) (tunable_val_t *valp)
 	    }
 	  break;
 	case 6:
-	  if (disable)
+	  if (n.disable)
 	    {
 	      CHECK_GLIBC_IFUNC_CPU_OFF (n, cpu_features, POPCNT, 6);
 	      CHECK_GLIBC_IFUNC_CPU_OFF (n, cpu_features, SSE4_1, 6);
 	      CHECK_GLIBC_IFUNC_CPU_OFF (n, cpu_features, SSE4_2, 6);
-	      if (memcmp (n, "XSAVEC", 6) == 0)
+	      if (memcmp (n.str, "XSAVEC", 6) == 0)
 		{
 		  /* Update xsave_state_size to XSAVE state size.  */
 		  cpu_features->xsave_state_size
@@ -174,14 +156,14 @@ TUNABLE_CALLBACK (set_hwcaps) (tunable_val_t *valp)
 	    }
 	  break;
 	case 7:
-	  if (disable)
+	  if (n.disable)
 	    {
 	      CHECK_GLIBC_IFUNC_CPU_OFF (n, cpu_features, AVX512F, 7);
 	      CHECK_GLIBC_IFUNC_CPU_OFF (n, cpu_features, OSXSAVE, 7);
 	    }
 	  break;
 	case 8:
-	  if (disable)
+	  if (n.disable)
 	    {
 	      CHECK_GLIBC_IFUNC_CPU_OFF (n, cpu_features, AVX512CD, 8);
 	      CHECK_GLIBC_IFUNC_CPU_OFF (n, cpu_features, AVX512BW, 8);
@@ -190,86 +172,72 @@ TUNABLE_CALLBACK (set_hwcaps) (tunable_val_t *valp)
 	      CHECK_GLIBC_IFUNC_CPU_OFF (n, cpu_features, AVX512PF, 8);
 	      CHECK_GLIBC_IFUNC_CPU_OFF (n, cpu_features, AVX512VL, 8);
 	    }
-	  CHECK_GLIBC_IFUNC_PREFERRED_BOTH (n, cpu_features, Slow_BSF,
-					    disable, 8);
+	  CHECK_GLIBC_IFUNC_PREFERRED_BOTH (n, cpu_features, Slow_BSF, 8);
 	  break;
 	case 11:
 	    {
-	      CHECK_GLIBC_IFUNC_PREFERRED_BOTH (n, cpu_features,
-						Prefer_ERMS,
-						disable, 11);
-	      CHECK_GLIBC_IFUNC_PREFERRED_BOTH (n, cpu_features,
-						Prefer_FSRM,
-						disable, 11);
+	      CHECK_GLIBC_IFUNC_PREFERRED_BOTH (n, cpu_features, Prefer_ERMS,
+						11);
+	      CHECK_GLIBC_IFUNC_PREFERRED_BOTH (n, cpu_features, Prefer_FSRM,
+						11);
 	      CHECK_GLIBC_IFUNC_PREFERRED_NEED_BOTH (n, cpu_features,
 						     Slow_SSE4_2,
 						     SSE4_2,
-						     disable, 11);
+						     11);
 	    }
 	  break;
 	case 15:
 	    {
 	      CHECK_GLIBC_IFUNC_PREFERRED_BOTH (n, cpu_features,
-						Fast_Rep_String,
-						disable, 15);
+						Fast_Rep_String, 15);
 	    }
 	  break;
 	case 16:
 	    {
 	      CHECK_GLIBC_IFUNC_PREFERRED_NEED_BOTH
-		(n, cpu_features, Prefer_No_AVX512, AVX512F,
-		 disable, 16);
+		(n, cpu_features, Prefer_No_AVX512, AVX512F, 16);
 	    }
 	  break;
 	case 18:
 	    {
 	      CHECK_GLIBC_IFUNC_PREFERRED_BOTH (n, cpu_features,
-						Fast_Copy_Backward,
-						disable, 18);
+						Fast_Copy_Backward, 18);
 	    }
 	  break;
 	case 19:
 	    {
 	      CHECK_GLIBC_IFUNC_PREFERRED_BOTH (n, cpu_features,
-						Fast_Unaligned_Load,
-						disable, 19);
+						Fast_Unaligned_Load, 19);
 	      CHECK_GLIBC_IFUNC_PREFERRED_BOTH (n, cpu_features,
-						Fast_Unaligned_Copy,
-						disable, 19);
+						Fast_Unaligned_Copy, 19);
 	    }
 	  break;
 	case 20:
 	    {
 	      CHECK_GLIBC_IFUNC_PREFERRED_NEED_BOTH
-		(n, cpu_features, Prefer_No_VZEROUPPER, AVX, disable,
-		 20);
+		(n, cpu_features, Prefer_No_VZEROUPPER, AVX, 20);
 	    }
 	  break;
 	case 23:
 	    {
 	      CHECK_GLIBC_IFUNC_PREFERRED_NEED_BOTH
-		(n, cpu_features, AVX_Fast_Unaligned_Load, AVX,
-		 disable, 23);
+		(n, cpu_features, AVX_Fast_Unaligned_Load, AVX, 23);
 	    }
 	  break;
 	case 24:
 	    {
 	      CHECK_GLIBC_IFUNC_PREFERRED_NEED_BOTH
-		(n, cpu_features, MathVec_Prefer_No_AVX512, AVX512F,
-		 disable, 24);
+		(n, cpu_features, MathVec_Prefer_No_AVX512, AVX512F, 24);
 	    }
 	  break;
 	case 26:
 	    {
 	      CHECK_GLIBC_IFUNC_PREFERRED_NEED_BOTH
-		(n, cpu_features, Prefer_PMINUB_for_stringop, SSE2,
-		 disable, 26);
+		(n, cpu_features, Prefer_PMINUB_for_stringop, SSE2, 26);
 	    }
 	  break;
 	}
-      p += len + 1;
     }
-  while (*c != '\0');
 }
 
 #if CET_ENABLED
@@ -277,11 +245,11 @@ attribute_hidden
 void
 TUNABLE_CALLBACK (set_x86_ibt) (tunable_val_t *valp)
 {
-  if (memcmp (valp->strval, "on", sizeof ("on")) == 0)
+  if (tunable_strcmp_cte (valp, "on"))
     GL(dl_x86_feature_control).ibt = cet_always_on;
-  else if (memcmp (valp->strval, "off", sizeof ("off")) == 0)
+  else if (tunable_strcmp_cte (valp, "off"))
     GL(dl_x86_feature_control).ibt = cet_always_off;
-  else if (memcmp (valp->strval, "permissive", sizeof ("permissive")) == 0)
+  else if (tunable_strcmp_cte (valp, "permissive"))
     GL(dl_x86_feature_control).ibt = cet_permissive;
 }
 
@@ -289,11 +257,11 @@ attribute_hidden
 void
 TUNABLE_CALLBACK (set_x86_shstk) (tunable_val_t *valp)
 {
-  if (memcmp (valp->strval, "on", sizeof ("on")) == 0)
+  if (tunable_strcmp_cte (valp, "on"))
     GL(dl_x86_feature_control).shstk = cet_always_on;
-  else if (memcmp (valp->strval, "off", sizeof ("off")) == 0)
+  else if (tunable_strcmp_cte (valp, "off"))
     GL(dl_x86_feature_control).shstk = cet_always_off;
-  else if (memcmp (valp->strval, "permissive", sizeof ("permissive")) == 0)
+  else if (tunable_strcmp_cte (valp, "permissive"))
     GL(dl_x86_feature_control).shstk = cet_permissive;
 }
 #endif
diff --git a/sysdeps/x86/tst-hwcap-tunables.c b/sysdeps/x86/tst-hwcap-tunables.c
new file mode 100644
index 0000000000..e9f50ffbe0
--- /dev/null
+++ b/sysdeps/x86/tst-hwcap-tunables.c
@@ -0,0 +1,150 @@
+/* Tests for powerpc GLIBC_TUNABLES=glibc.cpu.hwcaps filter.
+   Copyright (C) 2023 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <http://www.gnu.org/licenses/>.  */
+
+#pragma GCC optimize ("O0")
+
+#include <array_length.h>
+#include <getopt.h>
+#include <ifunc-impl-list.h>
+#include <spawn.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <intprops.h>
+#include <support/check.h>
+#include <support/support.h>
+#include <support/xunistd.h>
+#include <support/capture_subprocess.h>
+
+/* Nonzero if the program gets called via `exec'.  */
+#define CMDLINE_OPTIONS \
+  { "restart", no_argument, &restart, 1 },
+static int restart;
+
+/* Disable everything.  */
+static const char *test_1[] =
+{
+  "__memcpy_avx512_no_vzeroupper",
+  "__memcpy_avx512_unaligned",
+  "__memcpy_avx512_unaligned_erms",
+  "__memcpy_evex_unaligned",
+  "__memcpy_evex_unaligned_erms",
+  "__memcpy_avx_unaligned",
+  "__memcpy_avx_unaligned_erms",
+  "__memcpy_avx_unaligned_rtm",
+  "__memcpy_avx_unaligned_erms_rtm",
+  "__memcpy_ssse3",
+};
+
+static const struct test_t
+{
+  const char *env;
+  const char *const *funcs;
+  size_t nfuncs;
+} tests[] =
+{
+  {
+    /* Disable everything.  */
+    "-Prefer_ERMS,-Prefer_FSRM,-AVX,-AVX2,-AVX_Usable,-AVX2_Usable,"
+    "-AVX512F_Usable,-SSE4_1,-SSE4_2,-SSSE3,-Fast_Unaligned_Load,-ERMS,"
+    "-AVX_Fast_Unaligned_Load",
+    test_1,
+    array_length (test_1)
+  },
+  {
+    /* Same as before, but with some empty suboptions.  */
+    ",-,-Prefer_ERMS,-Prefer_FSRM,-AVX,-AVX2,-AVX_Usable,-AVX2_Usable,"
+    "-AVX512F_Usable,-SSE4_1,-SSE4_2,,-SSSE3,-Fast_Unaligned_Load,,-,-ERMS,"
+    "-AVX_Fast_Unaligned_Load,-,",
+    test_1,
+    array_length (test_1)
+  }
+};
+
+/* Called on process re-execution.  */
+_Noreturn static void
+handle_restart (int ntest)
+{
+  struct libc_ifunc_impl impls[32];
+  int cnt = __libc_ifunc_impl_list ("memcpy", impls, array_length (impls));
+  if (cnt == 0)
+    _exit (EXIT_SUCCESS);
+  TEST_VERIFY_EXIT (cnt >= 1);
+  for (int i = 0; i < cnt; i++)
+    {
+      for (int f = 0; f < tests[ntest].nfuncs; f++)
+	{
+	  if (strcmp (impls[i].name, tests[ntest].funcs[f]) == 0)
+	    TEST_COMPARE (impls[i].usable, false);
+	}
+    }
+
+  _exit (EXIT_SUCCESS);
+}
+
+static int
+do_test (int argc, char *argv[])
+{
+  /* We must have either:
+     - One our fource parameters left if called initially:
+       + path to ld.so         optional
+       + "--library-path"      optional
+       + the library path      optional
+       + the application name
+       + the test to check  */
+
+  TEST_VERIFY_EXIT (argc == 2 || argc == 5);
+
+  if (restart)
+    handle_restart (atoi (argv[1]));
+
+  char nteststr[INT_BUFSIZE_BOUND (int)];
+
+  char *spargv[10];
+  {
+    int i = 0;
+    for (; i < argc - 1; i++)
+      spargv[i] = argv[i + 1];
+    spargv[i++] = (char *) "--direct";
+    spargv[i++] = (char *) "--restart";
+    spargv[i++] = nteststr;
+    spargv[i] = NULL;
+  }
+
+  for (int i = 0; i < array_length (tests); i++)
+    {
+      snprintf (nteststr, sizeof nteststr, "%d", i);
+
+      printf ("[%d] Spawned test for %s\n", i, tests[i].env);
+      char *tunable = xasprintf ("glibc.cpu.hwcaps=%s", tests[i].env);
+      setenv ("GLIBC_TUNABLES", tunable, 1);
+
+      struct support_capture_subprocess result
+	= support_capture_subprogram (spargv[0], spargv);
+      support_capture_subprocess_check (&result, "tst-tunables", 0,
+					sc_allow_stderr);
+      support_capture_subprocess_free (&result);
+
+      free (tunable);
+    }
+
+  return 0;
+}
+
+#define TEST_FUNCTION_ARGV do_test
+#include <support/test-driver.c>
-- 
2.34.1


^ permalink raw reply	[flat|nested] 45+ messages in thread

* [PATCH v2 12/19] elf: Ignore LD_PROFILE for setuid binaries
  2023-10-17 13:05 [PATCH v2 00/19] Improve loader environment variable handling Adhemerval Zanella
                   ` (10 preceding siblings ...)
  2023-10-17 13:05 ` [PATCH v2 11/19] elf: Do not duplicate the GLIBC_TUNABLES string Adhemerval Zanella
@ 2023-10-17 13:05 ` Adhemerval Zanella
  2023-10-27 14:25   ` Siddhesh Poyarekar
  2023-10-17 13:05 ` [PATCH v2 13/19] elf: Remove LD_PROFILE for static binaries Adhemerval Zanella
                   ` (6 subsequent siblings)
  18 siblings, 1 reply; 45+ messages in thread
From: Adhemerval Zanella @ 2023-10-17 13:05 UTC (permalink / raw)
  To: libc-alpha, Siddhesh Poyarekar

Loader does not ignore LD_PROFILE in secure-execution mode (different
than man-page states [1]), rather it uses a different path
(/var/profile) and ignore LD_PROFILE_OUTPUT.

Allowing secure-execution profiling is already a non good security
boundary, since it enables different code paths and extra OS access by
the process.  But by ignoring LD_PROFILE_OUTPUT, the resulting profile
file might also be acceded in a racy manner since the file name does not
use any process-specific information (such as pid, timing, etc.).

Another side-effect is it forces lazy binding even on libraries that
might be with DF_BIND_NOW.

[1] https://man7.org/linux/man-pages/man8/ld.so.8.html
---
 elf/Makefile         |  3 +++
 elf/rtld.c           |  8 +++-----
 elf/tst-env-setuid.c | 12 +++++++++++-
 3 files changed, 17 insertions(+), 6 deletions(-)

diff --git a/elf/Makefile b/elf/Makefile
index f1cd6e13fa..608bef46f5 100644
--- a/elf/Makefile
+++ b/elf/Makefile
@@ -3021,3 +3021,6 @@ $(objpfx)tst-dlclose-lazy.out: \
   $(objpfx)tst-dlclose-lazy-mod1.so $(objpfx)tst-dlclose-lazy-mod2.so
 
 tst-env-setuid-ARGS = -- $(host-test-program-cmd)
+
+# Reuse a module with a SONAME, to specific as the LD_PROFILE.
+$(objpfx)tst-env-setuid: $(objpfx)tst-sonamemove-runmod2.so
diff --git a/elf/rtld.c b/elf/rtld.c
index 51b6d9f326..a09cf2a9df 100644
--- a/elf/rtld.c
+++ b/elf/rtld.c
@@ -361,6 +361,7 @@ struct rtld_global_ro _rtld_global_ro attribute_relro =
     ._dl_fpu_control = _FPU_DEFAULT,
     ._dl_pagesize = EXEC_PAGESIZE,
     ._dl_inhibit_cache = 0,
+    ._dl_profile_output = "/var/tmp",
 
     /* Function pointers.  */
     ._dl_debug_printf = _dl_debug_printf,
@@ -2534,10 +2535,6 @@ process_envvars (struct dl_main_state *state)
   char *envline;
   char *debug_output = NULL;
 
-  /* This is the default place for profiling data file.  */
-  GLRO(dl_profile_output)
-    = &"/var/tmp\0/var/profile"[__libc_enable_secure ? 9 : 0];
-
   while ((envline = _dl_next_ld_env_entry (&runp)) != NULL)
     {
       size_t len = 0;
@@ -2586,7 +2583,8 @@ process_envvars (struct dl_main_state *state)
 	    }
 
 	  /* Which shared object shall be profiled.  */
-	  if (memcmp (envline, "PROFILE", 7) == 0 && envline[8] != '\0')
+	  if (!__libc_enable_secure
+	      && memcmp (envline, "PROFILE", 7) == 0 && envline[8] != '\0')
 	    GLRO(dl_profile) = &envline[8];
 	  break;
 
diff --git a/elf/tst-env-setuid.c b/elf/tst-env-setuid.c
index ba295a6a14..76b8e1fb45 100644
--- a/elf/tst-env-setuid.c
+++ b/elf/tst-env-setuid.c
@@ -34,6 +34,9 @@ static char SETGID_CHILD[] = "setgid-child";
 
 #define FILTERED_VALUE   "some-filtered-value"
 #define UNFILTERED_VALUE "some-unfiltered-value"
+/* It assumes no other programs is being profile with a library with same
+   SONAME using the default folder.  */
+#define PROFILE_LIB      "tst-sonamemove-runmod2.so"
 
 struct envvar_t
 {
@@ -50,7 +53,7 @@ static const struct envvar_t filtered_envvars[] =
   { "LD_HWCAP_MASK",           FILTERED_VALUE },
   { "LD_LIBRARY_PATH",         FILTERED_VALUE },
   { "LD_PRELOAD",              FILTERED_VALUE },
-  { "LD_PROFILE",              FILTERED_VALUE },
+  { "LD_PROFILE",              "tst-sonamemove-runmod2.so" },
   { "MALLOC_ARENA_MAX",        FILTERED_VALUE },
   { "MALLOC_PERTURB_",         FILTERED_VALUE },
   { "MALLOC_TRACE",            FILTERED_VALUE },
@@ -87,6 +90,13 @@ test_child (void)
       ret |= !(env != NULL && strcmp (env, e->value) == 0);
     }
 
+  /* Also check if no profile file was created.  */
+  {
+    char *profilepath = xasprintf ("/var/tmp/%s.profile", PROFILE_LIB);
+    ret |= !access (profilepath, R_OK);
+    free (profilepath);
+  }
+
   return ret;
 }
 
-- 
2.34.1


^ permalink raw reply	[flat|nested] 45+ messages in thread

* [PATCH v2 13/19] elf: Remove LD_PROFILE for static binaries
  2023-10-17 13:05 [PATCH v2 00/19] Improve loader environment variable handling Adhemerval Zanella
                   ` (11 preceding siblings ...)
  2023-10-17 13:05 ` [PATCH v2 12/19] elf: Ignore LD_PROFILE for setuid binaries Adhemerval Zanella
@ 2023-10-17 13:05 ` Adhemerval Zanella
  2023-10-17 13:05 ` [PATCH v2 14/19] elf: Ignore loader debug env vars for setuid Adhemerval Zanella
                   ` (5 subsequent siblings)
  18 siblings, 0 replies; 45+ messages in thread
From: Adhemerval Zanella @ 2023-10-17 13:05 UTC (permalink / raw)
  To: libc-alpha, Siddhesh Poyarekar

The _dl_non_dynamic_init does not parse LD_PROFILE, which does not
enable profile for dlopen objects.  Since dlopen is deprecated for
static objects, it is better to remove the support.

It also allows to trim down libc.a of profile support.

Checked on x86_64-linux-gnu.
---
 elf/Makefile                              | 10 ++--
 elf/dl-load.c                             | 10 ++--
 elf/dl-runtime.c                          | 12 ++---
 elf/dl-support.c                          |  9 ----
 elf/{dl-profstub.c => libc-dl-profstub.c} |  0
 include/dlfcn.h                           |  5 ++
 sysdeps/aarch64/dl-machine.h              |  4 +-
 sysdeps/aarch64/dl-trampoline.S           |  3 +-
 sysdeps/alpha/dl-machine.h                |  6 ++-
 sysdeps/alpha/dl-trampoline.S             |  6 +++
 sysdeps/arm/dl-machine.h                  |  4 +-
 sysdeps/arm/dl-trampoline.S               |  2 +-
 sysdeps/hppa/dl-machine.h                 | 36 +++++++------
 sysdeps/hppa/dl-trampoline.S              |  2 +
 sysdeps/i386/dl-machine.h                 |  2 +
 sysdeps/i386/dl-trampoline.S              |  2 +-
 sysdeps/ia64/dl-machine.h                 | 10 ++--
 sysdeps/ia64/dl-trampoline.S              |  2 +-
 sysdeps/loongarch/dl-machine.h            |  6 ++-
 sysdeps/loongarch/dl-trampoline.h         |  2 +
 sysdeps/m68k/dl-machine.h                 |  4 +-
 sysdeps/m68k/dl-trampoline.S              |  2 +
 sysdeps/powerpc/powerpc32/dl-machine.c    |  2 +-
 sysdeps/powerpc/powerpc32/dl-machine.h    | 10 ++--
 sysdeps/powerpc/powerpc32/dl-trampoline.S |  2 +-
 sysdeps/powerpc/powerpc64/dl-machine.h    | 20 ++++---
 sysdeps/powerpc/powerpc64/dl-trampoline.S |  2 +-
 sysdeps/s390/s390-32/dl-machine.h         |  8 +--
 sysdeps/s390/s390-32/dl-trampoline.h      |  2 +-
 sysdeps/s390/s390-64/dl-machine.h         |  8 +--
 sysdeps/s390/s390-64/dl-trampoline.h      |  2 +-
 sysdeps/sh/dl-machine.h                   |  2 +
 sysdeps/sh/dl-trampoline.S                |  2 +
 sysdeps/sparc/sparc32/dl-machine.h        |  4 +-
 sysdeps/sparc/sparc32/dl-trampoline.S     |  2 +
 sysdeps/sparc/sparc64/dl-machine.h        |  4 +-
 sysdeps/sparc/sparc64/dl-trampoline.S     |  2 +
 sysdeps/x86_64/dl-machine.h               |  2 +
 sysdeps/x86_64/dl-trampoline.S            | 64 ++++++++++++-----------
 39 files changed, 168 insertions(+), 109 deletions(-)
 rename elf/{dl-profstub.c => libc-dl-profstub.c} (100%)

diff --git a/elf/Makefile b/elf/Makefile
index 608bef46f5..e16c373a1c 100644
--- a/elf/Makefile
+++ b/elf/Makefile
@@ -37,12 +37,12 @@ routines = \
   dl-iteratephdr \
   dl-libc \
   dl-origin \
-  dl-profstub \
   dl-reloc-static-pie \
   dl-support \
   dl-sym \
   dl-sysdep \
   enbl-secure \
+  libc-dl-profstub  \
   libc-dl_find_object \
   libc_early_init \
   rtld_static_init \
@@ -72,7 +72,6 @@ dl-routines = \
   dl-open \
   dl-origin \
   dl-printf \
-  dl-profile \
   dl-reloc \
   dl-runtime \
   dl-scope \
@@ -117,7 +116,11 @@ elide-routines.os = \
   # elide-routines.os
 
 # These object files are only included in the dynamically-linked libc.
-shared-only-routines = libc-dl_find_object
+shared-only-routines = \
+  libc-dl-profile \
+  libc-dl-profstub \
+  libc-dl_find_object \
+  # shared-only-routines
 
 # ld.so uses those routines, plus some special stuff for being the program
 # interpreter and operating independent of libc.
@@ -135,6 +138,7 @@ rtld-routines = \
   dl-libc_freeres \
   dl-minimal \
   dl-mutex \
+  dl-profile \
   dl-sysdep \
   dl-usage \
   rtld \
diff --git a/elf/dl-load.c b/elf/dl-load.c
index 2923b1141d..7356a4fe48 100644
--- a/elf/dl-load.c
+++ b/elf/dl-load.c
@@ -1443,11 +1443,6 @@ cannot enable executable stack as shared object requires");
      name by which the DSO is actually known.  Add that as well.  */
   if (__glibc_unlikely (origname != NULL))
     add_name_to_object (l, origname);
-#else
-  /* Audit modules only exist when linking is dynamic so ORIGNAME
-     cannot be non-NULL.  */
-  assert (origname == NULL);
-#endif
 
   /* When we profile the SONAME might be needed for something else but
      loading.  Add it right away.  */
@@ -1455,6 +1450,11 @@ cannot enable executable stack as shared object requires");
       && l->l_info[DT_SONAME] != NULL)
     add_name_to_object (l, ((const char *) D_PTR (l, l_info[DT_STRTAB])
 			    + l->l_info[DT_SONAME]->d_un.d_val));
+#else
+  /* Audit modules only exist when linking is dynamic so ORIGNAME
+     cannot be non-NULL.  */
+  assert (origname == NULL);
+#endif
 
   /* If we have newly loaded libc.so, update the namespace
      description.  */
diff --git a/elf/dl-runtime.c b/elf/dl-runtime.c
index 32a8bfcf74..fe7deda32a 100644
--- a/elf/dl-runtime.c
+++ b/elf/dl-runtime.c
@@ -162,14 +162,14 @@ _dl_fixup (
   return elf_machine_fixup_plt (l, result, refsym, sym, reloc, rel_addr, value);
 }
 
-#ifndef PROF
+#if !defined PROF && defined SHARED
 DL_FIXUP_VALUE_TYPE
 __attribute ((noinline))
 DL_ARCH_FIXUP_ATTRIBUTE
 _dl_profile_fixup (
-#ifdef ELF_MACHINE_RUNTIME_FIXUP_ARGS
+# ifdef ELF_MACHINE_RUNTIME_FIXUP_ARGS
 		   ELF_MACHINE_RUNTIME_FIXUP_ARGS,
-#endif
+# endif
 		   struct link_map *l, ElfW(Word) reloc_arg,
 		   ElfW(Addr) retaddr, void *regs, long int *framesizep)
 {
@@ -309,14 +309,12 @@ _dl_profile_fixup (
       /* And now perhaps the relocation addend.  */
       value = elf_machine_plt_value (l, reloc, value);
 
-#ifdef SHARED
       /* Auditing checkpoint: we have a new binding.  Provide the
 	 auditing libraries the possibility to change the value and
 	 tell us whether further auditing is wanted.  */
       if (defsym != NULL && GLRO(dl_naudit) > 0)
 	_dl_audit_symbind (l, reloc_result, reloc, defsym, &value, result,
 			   true);
-#endif
 
       /* Store the result for later runs.  */
       if (__glibc_likely (! GLRO(dl_bind_not)))
@@ -335,11 +333,9 @@ _dl_profile_fixup (
   long int framesize = -1;
 
 
-#ifdef SHARED
   /* Auditing checkpoint: report the PLT entering and allow the
      auditors to change the value.  */
   _dl_audit_pltenter (l, reloc_result, &value, regs, &framesize);
-#endif
 
   /* Store the frame size information.  */
   *framesizep = framesize;
@@ -349,4 +345,4 @@ _dl_profile_fixup (
   return value;
 }
 
-#endif /* PROF */
+#endif /* !defined PROF && defined SHARED */
diff --git a/elf/dl-support.c b/elf/dl-support.c
index 44a54dea07..31a608df87 100644
--- a/elf/dl-support.c
+++ b/elf/dl-support.c
@@ -60,10 +60,6 @@ int _dl_dynamic_weak;
 /* If nonzero print warnings about problematic situations.  */
 int _dl_verbose;
 
-/* We never do profiling.  */
-const char *_dl_profile;
-const char *_dl_profile_output;
-
 /* Names of shared object for which the RUNPATHs and RPATHs should be
    ignored.  */
 const char *_dl_inhibit_rpath;
@@ -301,11 +297,6 @@ _dl_non_dynamic_init (void)
 
   _dl_dynamic_weak = *(getenv ("LD_DYNAMIC_WEAK") ?: "") == '\0';
 
-  _dl_profile_output = getenv ("LD_PROFILE_OUTPUT");
-  if (_dl_profile_output == NULL || _dl_profile_output[0] == '\0')
-    _dl_profile_output
-      = &"/var/tmp\0/var/profile"[__libc_enable_secure ? 9 : 0];
-
   if (__libc_enable_secure)
     {
       static const char unsecure_envvars[] =
diff --git a/elf/dl-profstub.c b/elf/libc-dl-profstub.c
similarity index 100%
rename from elf/dl-profstub.c
rename to elf/libc-dl-profstub.c
diff --git a/include/dlfcn.h b/include/dlfcn.h
index ae25f05303..a44420fa37 100644
--- a/include/dlfcn.h
+++ b/include/dlfcn.h
@@ -135,5 +135,10 @@ extern int __dladdr1 (const void *address, Dl_info *info,
 extern int __dlinfo (void *handle, int request, void *arg);
 extern char *__dlerror (void);
 
+#ifndef SHARED
+# undef DL_CALL_FCT
+# define DL_CALL_FCT(fctp, args) ((fctp) args)
+#endif
+
 #endif
 #endif
diff --git a/sysdeps/aarch64/dl-machine.h b/sysdeps/aarch64/dl-machine.h
index 4170b9269f..a56eb96a79 100644
--- a/sysdeps/aarch64/dl-machine.h
+++ b/sysdeps/aarch64/dl-machine.h
@@ -68,7 +68,6 @@ elf_machine_runtime_setup (struct link_map *l, struct r_scope_elem *scope[],
     {
       ElfW(Addr) *got;
       extern void _dl_runtime_resolve (ElfW(Word));
-      extern void _dl_runtime_profile (ElfW(Word));
 
       got = (ElfW(Addr) *) D_PTR (l, l_info[DT_PLTGOT]);
       if (got[1])
@@ -83,6 +82,8 @@ elf_machine_runtime_setup (struct link_map *l, struct r_scope_elem *scope[],
 	 to intercept the calls to collect information.  In this case we
 	 don't store the address in the GOT so that all future calls also
 	 end in this function.  */
+#ifdef SHARED
+      extern void _dl_runtime_profile (ElfW(Word));
       if ( profile)
 	{
 	   got[2] = (ElfW(Addr)) &_dl_runtime_profile;
@@ -94,6 +95,7 @@ elf_machine_runtime_setup (struct link_map *l, struct r_scope_elem *scope[],
 	    GL(dl_profile_map) = l;
 	}
       else
+#endif
 	{
 	  /* This function will get called to fix up the GOT entry
 	     indicated by the offset on the stack, and then jump to
diff --git a/sysdeps/aarch64/dl-trampoline.S b/sysdeps/aarch64/dl-trampoline.S
index a3474ba741..d9b3dcd656 100644
--- a/sysdeps/aarch64/dl-trampoline.S
+++ b/sysdeps/aarch64/dl-trampoline.S
@@ -122,7 +122,8 @@ _dl_runtime_resolve:
 
 	cfi_endproc
 	.size _dl_runtime_resolve, .-_dl_runtime_resolve
-#ifndef PROF
+
+#if !defined PROF && defined SHARED
 	.globl _dl_runtime_profile
 	.type _dl_runtime_profile, #function
 	cfi_startproc
diff --git a/sysdeps/alpha/dl-machine.h b/sysdeps/alpha/dl-machine.h
index ed5389e3c5..7fe2afca93 100644
--- a/sysdeps/alpha/dl-machine.h
+++ b/sysdeps/alpha/dl-machine.h
@@ -75,9 +75,7 @@ elf_machine_runtime_setup (struct link_map *map, struct r_scope_elem *scope[],
 			   int lazy, int profile)
 {
   extern char _dl_runtime_resolve_new[] attribute_hidden;
-  extern char _dl_runtime_profile_new[] attribute_hidden;
   extern char _dl_runtime_resolve_old[] attribute_hidden;
-  extern char _dl_runtime_profile_old[] attribute_hidden;
 
   struct pltgot {
     char *resolve;
@@ -109,6 +107,9 @@ elf_machine_runtime_setup (struct link_map *map, struct r_scope_elem *scope[],
   else
     resolve = _dl_runtime_resolve_old;
 
+#ifdef SHARED
+  extern char _dl_runtime_profile_new[] attribute_hidden;
+  extern char _dl_runtime_profile_old[] attribute_hidden;
   if (__builtin_expect (profile, 0))
     {
       if (secureplt)
@@ -123,6 +124,7 @@ elf_machine_runtime_setup (struct link_map *map, struct r_scope_elem *scope[],
 	  GL(dl_profile_map) = map;
 	}
     }
+#endif
 
   pg->resolve = resolve;
   pg->link = map;
diff --git a/sysdeps/alpha/dl-trampoline.S b/sysdeps/alpha/dl-trampoline.S
index f8c3d33906..c37600d9d0 100644
--- a/sysdeps/alpha/dl-trampoline.S
+++ b/sysdeps/alpha/dl-trampoline.S
@@ -89,6 +89,8 @@ _dl_runtime_resolve_new:
 	.globl	_dl_runtime_profile_new
 	.type	_dl_runtime_profile_new, @function
 
+
+#ifdef SHARED
 #undef FRAMESIZE
 #define FRAMESIZE	20*8
 
@@ -207,11 +209,13 @@ _dl_runtime_profile_new:
 
 	cfi_endproc
 	.size	_dl_runtime_profile_new, .-_dl_runtime_profile_new
+#endif /* SHARED */
 
 	.align	4
 	.globl	_dl_runtime_resolve_old
 	.ent	_dl_runtime_resolve_old
 
+
 #undef FRAMESIZE
 #define FRAMESIZE	44*8
 
@@ -340,6 +344,7 @@ _dl_runtime_resolve_old:
 	.usepv	_dl_runtime_profile_old, no
 	.type	_dl_runtime_profile_old, @function
 
+#ifdef SHARED
 	/* We save the registers in a different order than desired by
 	   .mask/.fmask, so we have to use explicit cfi directives.  */
 	cfi_startproc
@@ -538,3 +543,4 @@ _dl_runtime_profile_old:
 
 	cfi_endproc
 	.size	_dl_runtime_profile_old, .-_dl_runtime_profile_old
+#endif /* SHARED */
diff --git a/sysdeps/arm/dl-machine.h b/sysdeps/arm/dl-machine.h
index d720c02c96..a68679e653 100644
--- a/sysdeps/arm/dl-machine.h
+++ b/sysdeps/arm/dl-machine.h
@@ -65,7 +65,6 @@ elf_machine_runtime_setup (struct link_map *l, struct r_scope_elem *scope[],
 {
   Elf32_Addr *got;
   extern void _dl_runtime_resolve (Elf32_Word);
-  extern void _dl_runtime_profile (Elf32_Word);
 
   if (l->l_info[DT_JMPREL] && lazy)
     {
@@ -88,6 +87,8 @@ elf_machine_runtime_setup (struct link_map *l, struct r_scope_elem *scope[],
 	 to intercept the calls to collect information.  In this case we
 	 don't store the address in the GOT so that all future calls also
 	 end in this function.  */
+#ifdef SHARED
+      extern void _dl_runtime_profile (Elf32_Word);
       if (profile)
 	{
 	  got[2] = (Elf32_Addr) &_dl_runtime_profile;
@@ -99,6 +100,7 @@ elf_machine_runtime_setup (struct link_map *l, struct r_scope_elem *scope[],
 	    GL(dl_profile_map) = l;
 	}
       else
+#endif
 	/* This function will get called to fix up the GOT entry indicated by
 	   the offset on the stack, and then jump to the resolved address.  */
 	got[2] = (Elf32_Addr) &_dl_runtime_resolve;
diff --git a/sysdeps/arm/dl-trampoline.S b/sysdeps/arm/dl-trampoline.S
index 23c2476917..2df5b7ee36 100644
--- a/sysdeps/arm/dl-trampoline.S
+++ b/sysdeps/arm/dl-trampoline.S
@@ -70,7 +70,7 @@ _dl_runtime_resolve:
 	cfi_endproc
 	.size _dl_runtime_resolve, .-_dl_runtime_resolve
 
-#ifndef PROF
+#if !defined PROF && defined SHARED
 	.globl _dl_runtime_profile
 	.type _dl_runtime_profile, #function
 	CFI_SECTIONS
diff --git a/sysdeps/hppa/dl-machine.h b/sysdeps/hppa/dl-machine.h
index 4e6e70b3c9..993593de5d 100644
--- a/sysdeps/hppa/dl-machine.h
+++ b/sysdeps/hppa/dl-machine.h
@@ -195,7 +195,6 @@ elf_machine_runtime_setup (struct link_map *l, struct r_scope_elem *scope[],
   end_jmprel = jmprel + l->l_info[DT_PLTRELSZ]->d_un.d_val;
 
   extern void _dl_runtime_resolve (void);
-  extern void _dl_runtime_profile (void);
 
   /* Linking lazily */
   if (lazy)
@@ -235,22 +234,9 @@ elf_machine_runtime_setup (struct link_map *l, struct r_scope_elem *scope[],
 	      got[1] = (Elf32_Addr) l;
 
 	      /* This function will be called to perform the relocation. */
-	      if (__builtin_expect (!profile, 1))
-		{
-		  /* If a static application called us, then _dl_runtime_resolve is not
-		     a function descriptor, but the *real* address of the function... */
-		  if((unsigned long) &_dl_runtime_resolve & 3)
-		    {
-		      got[-2] = (Elf32_Addr) ((struct fdesc *)
-				  ((unsigned long) &_dl_runtime_resolve & ~3))->ip;
-		    }
-		  else
-		    {
-		      /* Static executable! */
-		      got[-2] = (Elf32_Addr) &_dl_runtime_resolve;
-		    }
-		}
-	      else
+#ifdef SHARED
+	      extern void _dl_runtime_profile (void);
+	      if (__glibc_unlikely (profile))
 		{
 		  if (GLRO(dl_profile) != NULL
 		      && _dl_name_match_p (GLRO(dl_profile), l))
@@ -272,6 +258,22 @@ elf_machine_runtime_setup (struct link_map *l, struct r_scope_elem *scope[],
 		      got[-2] = (Elf32_Addr) &_dl_runtime_profile;
 		    }
 		}
+	      else
+#endif
+		{
+		  /* If a static application called us, then _dl_runtime_resolve is not
+		     a function descriptor, but the *real* address of the function... */
+		  if((unsigned long) &_dl_runtime_resolve & 3)
+		    {
+		      got[-2] = (Elf32_Addr) ((struct fdesc *)
+				  ((unsigned long) &_dl_runtime_resolve & ~3))->ip;
+		    }
+		  else
+		    {
+		      /* Static executable! */
+		      got[-2] = (Elf32_Addr) &_dl_runtime_resolve;
+		    }
+		}
 	      /* Plunk in the gp of this function descriptor so we
 		 can make the call to _dl_runtime_xxxxxx */
 	      got[-1] = ltp;
diff --git a/sysdeps/hppa/dl-trampoline.S b/sysdeps/hppa/dl-trampoline.S
index 689c6e1a40..9e904df3d2 100644
--- a/sysdeps/hppa/dl-trampoline.S
+++ b/sysdeps/hppa/dl-trampoline.S
@@ -156,6 +156,7 @@ _dl_runtime_resolve:
 	cfi_endproc
 	.size   _dl_runtime_resolve, . - _dl_runtime_resolve
 
+#ifdef SHARED
         .text
         .global _dl_runtime_profile
         .type _dl_runtime_profile,@function
@@ -359,3 +360,4 @@ L(cont):
         .PROCEND
 	cfi_endproc
 	.size   _dl_runtime_profile, . - _dl_runtime_profile
+#endif
diff --git a/sysdeps/i386/dl-machine.h b/sysdeps/i386/dl-machine.h
index 18749f2ec2..07469e99b0 100644
--- a/sysdeps/i386/dl-machine.h
+++ b/sysdeps/i386/dl-machine.h
@@ -92,6 +92,7 @@ elf_machine_runtime_setup (struct link_map *l, struct r_scope_elem *scope[],
 	 to intercept the calls to collect information.  In this case we
 	 don't store the address in the GOT so that all future calls also
 	 end in this function.  */
+#ifdef SHARED
       if (__glibc_unlikely (profile))
 	{
 	  got[2] = (shstk_enabled
@@ -105,6 +106,7 @@ elf_machine_runtime_setup (struct link_map *l, struct r_scope_elem *scope[],
 	    GL(dl_profile_map) = l;
 	}
       else
+#endif
 	/* This function will get called to fix up the GOT entry indicated by
 	   the offset on the stack, and then jump to the resolved address.  */
 	got[2] = (shstk_enabled
diff --git a/sysdeps/i386/dl-trampoline.S b/sysdeps/i386/dl-trampoline.S
index 2d55f373b4..3604aabe87 100644
--- a/sysdeps/i386/dl-trampoline.S
+++ b/sysdeps/i386/dl-trampoline.S
@@ -70,7 +70,7 @@ _dl_runtime_resolve_shstk:
 	cfi_endproc
 	.size _dl_runtime_resolve_shstk, .-_dl_runtime_resolve_shstk
 
-#ifndef PROF
+#if !defined PROF && defined SHARED
 # The SHSTK compatible version.
 	.globl _dl_runtime_profile_shstk
 	.type _dl_runtime_profile_shstk, @function
diff --git a/sysdeps/ia64/dl-machine.h b/sysdeps/ia64/dl-machine.h
index e1da3dadcb..3ef6b0ef4b 100644
--- a/sysdeps/ia64/dl-machine.h
+++ b/sysdeps/ia64/dl-machine.h
@@ -121,9 +121,8 @@ elf_machine_runtime_setup (struct link_map *l, struct r_scope_elem *scope[],
       reserve[0] = (Elf64_Addr) l;
 
       /* This function will be called to perform the relocation.  */
-      if (!profile)
-	doit = (Elf64_Addr) ELF_PTR_TO_FDESC (&_dl_runtime_resolve)->ip;
-      else
+#ifdef SHARED
+      if (__glibc_unlikely (profile))
 	{
 	  if (GLRO(dl_profile) != NULL
 	      && _dl_name_match_p (GLRO(dl_profile), l))
@@ -134,6 +133,11 @@ elf_machine_runtime_setup (struct link_map *l, struct r_scope_elem *scope[],
 	    }
 	  doit = (Elf64_Addr) ELF_PTR_TO_FDESC (&_dl_runtime_profile)->ip;
 	}
+      else
+#endif
+	{
+	  doit = (Elf64_Addr) ELF_PTR_TO_FDESC (&_dl_runtime_resolve)->ip;
+	}
 
       reserve[1] = doit;
       reserve[2] = gp;
diff --git a/sysdeps/ia64/dl-trampoline.S b/sysdeps/ia64/dl-trampoline.S
index 54b33c8c02..10d8432c8f 100644
--- a/sysdeps/ia64/dl-trampoline.S
+++ b/sysdeps/ia64/dl-trampoline.S
@@ -188,7 +188,7 @@ END(_dl_runtime_resolve)
 #define PLTENTER_FRAME_SIZE (4*8 + 8*8 + 8*16 + 2*8 + 16)
 #define PLTEXIT_FRAME_SIZE (PLTENTER_FRAME_SIZE + 4*8 + 8*16)
 
-#ifndef PROF
+#if !defined PROF && defined SHARED
 ENTRY(_dl_runtime_profile)
 	{ .mii
 	  .prologue
diff --git a/sysdeps/loongarch/dl-machine.h b/sysdeps/loongarch/dl-machine.h
index 57913cefaa..0d17fd21e3 100644
--- a/sysdeps/loongarch/dl-machine.h
+++ b/sysdeps/loongarch/dl-machine.h
@@ -287,15 +287,16 @@ elf_machine_runtime_setup (struct link_map *l, struct r_scope_elem *scope[],
 	 to intercept the calls to collect information.  In this case we
 	 don't store the address in the GOT so that all future calls also
 	 end in this function.  */
+#ifdef SHARED
       if (profile != 0)
 	{
-#if !defined __loongarch_soft_float
+# if !defined __loongarch_soft_float
 	  if (SUPPORT_LASX)
 	    gotplt[0] = (ElfW(Addr)) &_dl_runtime_profile_lasx;
 	  else if (SUPPORT_LSX)
 	    gotplt[0] = (ElfW(Addr)) &_dl_runtime_profile_lsx;
 	  else
-#endif
+# endif
 	    gotplt[0] = (ElfW(Addr)) &_dl_runtime_profile;
 
 	  if (GLRO(dl_profile) != NULL
@@ -305,6 +306,7 @@ elf_machine_runtime_setup (struct link_map *l, struct r_scope_elem *scope[],
 	    GL(dl_profile_map) = l;
 	}
       else
+#endif
 	{
 	  /* This function will get called to fix up the GOT entry
 	     indicated by the offset on the stack, and then jump to
diff --git a/sysdeps/loongarch/dl-trampoline.h b/sysdeps/loongarch/dl-trampoline.h
index cb4a287c65..bb6ff47782 100644
--- a/sysdeps/loongarch/dl-trampoline.h
+++ b/sysdeps/loongarch/dl-trampoline.h
@@ -126,6 +126,7 @@ ENTRY (_dl_runtime_resolve)
 	jirl	zero, t1, 0
 END (_dl_runtime_resolve)
 
+#ifdef SHARED
 #include "dl-link.h"
 
 ENTRY (_dl_runtime_profile)
@@ -367,3 +368,4 @@ ENTRY (_dl_runtime_profile)
 	jirl	zero, ra, 0
 
 END (_dl_runtime_profile)
+#endif /* SHARED */
diff --git a/sysdeps/m68k/dl-machine.h b/sysdeps/m68k/dl-machine.h
index 5ee586b27b..8d7e733e2a 100644
--- a/sysdeps/m68k/dl-machine.h
+++ b/sysdeps/m68k/dl-machine.h
@@ -75,7 +75,6 @@ elf_machine_runtime_setup (struct link_map *l, struct r_scope_elem *scope[],
 {
   Elf32_Addr *got;
   extern void _dl_runtime_resolve (Elf32_Word);
-  extern void _dl_runtime_profile (Elf32_Word);
 
   if (l->l_info[DT_JMPREL] && lazy)
     {
@@ -93,6 +92,8 @@ elf_machine_runtime_setup (struct link_map *l, struct r_scope_elem *scope[],
 	 to intercept the calls to collect information.  In this case we
 	 don't store the address in the GOT so that all future calls also
 	 end in this function.  */
+#ifdef SHARED
+      extern void _dl_runtime_profile (Elf32_Word);
       if (profile)
 	{
 	  got[2] = (Elf32_Addr) &_dl_runtime_profile;
@@ -106,6 +107,7 @@ elf_machine_runtime_setup (struct link_map *l, struct r_scope_elem *scope[],
 	    }
 	}
       else
+#endif
 	/* This function will get called to fix up the GOT entry indicated by
 	   the offset on the stack, and then jump to the resolved address.  */
 	got[2] = (Elf32_Addr) &_dl_runtime_resolve;
diff --git a/sysdeps/m68k/dl-trampoline.S b/sysdeps/m68k/dl-trampoline.S
index dba3741400..f1b4943868 100644
--- a/sysdeps/m68k/dl-trampoline.S
+++ b/sysdeps/m68k/dl-trampoline.S
@@ -60,6 +60,7 @@ _dl_runtime_resolve:
 	cfi_endproc
 	.size _dl_runtime_resolve, . - _dl_runtime_resolve
 
+#ifdef SHARED
 	.text
 	.globl _dl_runtime_profile
 	.type _dl_runtime_profile, @function
@@ -220,3 +221,4 @@ _dl_runtime_profile:
 	rts
 	cfi_endproc
 	.size _dl_runtime_profile, . - _dl_runtime_profile
+#endif /* SHARED */
diff --git a/sysdeps/powerpc/powerpc32/dl-machine.c b/sysdeps/powerpc/powerpc32/dl-machine.c
index ef84911ede..e6b603de94 100644
--- a/sysdeps/powerpc/powerpc32/dl-machine.c
+++ b/sysdeps/powerpc/powerpc32/dl-machine.c
@@ -226,7 +226,7 @@ __elf_machine_runtime_setup (struct link_map *map, int lazy, int profile)
 	  Elf32_Word dlrr;
 	  Elf32_Word offset;
 
-#ifndef PROF
+#if !defined PROF && defined SHARED
 	  dlrr = (Elf32_Word) (profile
 			       ? _dl_prof_resolve
 			       : _dl_runtime_resolve);
diff --git a/sysdeps/powerpc/powerpc32/dl-machine.h b/sysdeps/powerpc/powerpc32/dl-machine.h
index a4cad7583c..1ff46d5f8a 100644
--- a/sysdeps/powerpc/powerpc32/dl-machine.h
+++ b/sysdeps/powerpc/powerpc32/dl-machine.h
@@ -188,15 +188,19 @@ elf_machine_runtime_setup (struct link_map *map, struct r_scope_elem *scope[],
       extern void _dl_runtime_resolve (void);
       extern void _dl_prof_resolve (void);
 
-      if (__glibc_likely (!profile))
-	dlrr = _dl_runtime_resolve;
-      else
+#ifdef SHARED
+      if (__glibc_unlikely (profile))
 	{
 	  if (GLRO(dl_profile) != NULL
 	      &&_dl_name_match_p (GLRO(dl_profile), map))
 	    GL(dl_profile_map) = map;
 	  dlrr = _dl_prof_resolve;
 	}
+      else
+#endif
+	{
+	  dlrr = _dl_runtime_resolve;
+	}
       got = (Elf32_Addr *) map->l_info[DT_PPC(GOT)]->d_un.d_ptr;
       glink = got[1];
       got[1] = (Elf32_Addr) dlrr;
diff --git a/sysdeps/powerpc/powerpc32/dl-trampoline.S b/sysdeps/powerpc/powerpc32/dl-trampoline.S
index 93b1673ebb..be8de0e2dc 100644
--- a/sysdeps/powerpc/powerpc32/dl-trampoline.S
+++ b/sysdeps/powerpc/powerpc32/dl-trampoline.S
@@ -70,7 +70,7 @@ _dl_runtime_resolve:
 	cfi_endproc
 	.size	 _dl_runtime_resolve,.-_dl_runtime_resolve
 
-#ifndef PROF
+#if !defined PROF && defined SHARED
 	.align 2
 	.globl _dl_prof_resolve
 	.type _dl_prof_resolve,@function
diff --git a/sysdeps/powerpc/powerpc64/dl-machine.h b/sysdeps/powerpc/powerpc64/dl-machine.h
index 449208e86f..601c3cba9d 100644
--- a/sysdeps/powerpc/powerpc64/dl-machine.h
+++ b/sysdeps/powerpc/powerpc64/dl-machine.h
@@ -362,13 +362,19 @@ elf_machine_runtime_setup (struct link_map *map, struct r_scope_elem *scope[],
 	  Elf64_Word offset;
 	  Elf64_Addr dlrr;
 
-	  dlrr = (Elf64_Addr) (profile ? _dl_profile_resolve
-				       : _dl_runtime_resolve);
-	  if (profile && GLRO(dl_profile) != NULL
-	      && _dl_name_match_p (GLRO(dl_profile), map))
-	    /* This is the object we are looking for.  Say that we really
-	       want profiling and the timers are started.  */
-	    GL(dl_profile_map) = map;
+#ifdef SHARED
+	  if (__glibc_unlikely (profile))
+	    {
+	      dlrr = (Elf64_Addr) _dl_profile_resolve;
+	      if (profile && GLRO(dl_profile) != NULL
+		  && _dl_name_match_p (GLRO(dl_profile), map))
+		/* This is the object we are looking for.  Say that we really
+		   want profiling and the timers are started.  */
+		GL(dl_profile_map) = map;
+	    }
+	  else
+#endif
+	    dlrr = (Elf64_Addr) _dl_runtime_resolve;
 
 #if _CALL_ELF != 2
 	  /* We need to stuff the address/TOC of _dl_runtime_resolve
diff --git a/sysdeps/powerpc/powerpc64/dl-trampoline.S b/sysdeps/powerpc/powerpc64/dl-trampoline.S
index 1d04ec8109..b2fc2bb133 100644
--- a/sysdeps/powerpc/powerpc64/dl-trampoline.S
+++ b/sysdeps/powerpc/powerpc64/dl-trampoline.S
@@ -195,7 +195,7 @@ END(_dl_runtime_resolve)
    and r11 contains the link_map (from PLT0+16).  The link_map becomes
    parm1 (r3) and the index (r0) needs to be converted to an offset
    (index * 24) in parm2 (r4).  */
-#ifndef PROF
+#if !defined PROF && defined SHARED
 	.hidden _dl_profile_resolve
 ENTRY (_dl_profile_resolve, 4)
 /* Spill r30, r31 to preserve the link_map* and reloc_addr, in case we
diff --git a/sysdeps/s390/s390-32/dl-machine.h b/sysdeps/s390/s390-32/dl-machine.h
index 100a3e05f6..b8bf2796c7 100644
--- a/sysdeps/s390/s390-32/dl-machine.h
+++ b/sysdeps/s390/s390-32/dl-machine.h
@@ -124,16 +124,17 @@ elf_machine_runtime_setup (struct link_map *l, struct r_scope_elem *scope[],
 	 to intercept the calls to collect information.  In this case we
 	 don't store the address in the GOT so that all future calls also
 	 end in this function.  */
+#ifdef SHARED
       if (__glibc_unlikely (profile))
 	{
-#if defined HAVE_S390_VX_ASM_SUPPORT
+# if defined HAVE_S390_VX_ASM_SUPPORT
 	  if (GLRO(dl_hwcap) & HWCAP_S390_VX)
 	    got[2] = (Elf32_Addr) &_dl_runtime_profile_vx;
 	  else
 	    got[2] = (Elf32_Addr) &_dl_runtime_profile;
-#else
+# else
 	  got[2] = (Elf32_Addr) &_dl_runtime_profile;
-#endif
+# endif
 
 	  if (GLRO(dl_profile) != NULL
 	      && _dl_name_match_p (GLRO(dl_profile), l))
@@ -142,6 +143,7 @@ elf_machine_runtime_setup (struct link_map *l, struct r_scope_elem *scope[],
 	    GL(dl_profile_map) = l;
 	}
       else
+#endif
 	{
 	  /* This function will get called to fix up the GOT entry indicated by
 	     the offset on the stack, and then jump to the resolved address.  */
diff --git a/sysdeps/s390/s390-32/dl-trampoline.h b/sysdeps/s390/s390-32/dl-trampoline.h
index 78fdca9d53..8093ab08d3 100644
--- a/sysdeps/s390/s390-32/dl-trampoline.h
+++ b/sysdeps/s390/s390-32/dl-trampoline.h
@@ -148,7 +148,7 @@ _dl_runtime_resolve:
 #undef F0_OFF
 #undef F2_OFF
 
-#ifndef PROF
+#if !defined PROF && defined SHARED
 # define SIZEOF_STRUCT_LA_S390_32_REGS 168
 # define REGS_OFF -264
 # define R2_OFF -264
diff --git a/sysdeps/s390/s390-64/dl-machine.h b/sysdeps/s390/s390-64/dl-machine.h
index 9fabb09750..82259dad64 100644
--- a/sysdeps/s390/s390-64/dl-machine.h
+++ b/sysdeps/s390/s390-64/dl-machine.h
@@ -111,16 +111,17 @@ elf_machine_runtime_setup (struct link_map *l, struct r_scope_elem *scope[],
 	 to intercept the calls to collect information.	 In this case we
 	 don't store the address in the GOT so that all future calls also
 	 end in this function.	*/
+#ifdef SHARED
       if (__glibc_unlikely (profile))
 	{
-#if defined HAVE_S390_VX_ASM_SUPPORT
+# if defined HAVE_S390_VX_ASM_SUPPORT
 	  if (GLRO(dl_hwcap) & HWCAP_S390_VX)
 	    got[2] = (Elf64_Addr) &_dl_runtime_profile_vx;
 	  else
 	    got[2] = (Elf64_Addr) &_dl_runtime_profile;
-#else
+# else
 	  got[2] = (Elf64_Addr) &_dl_runtime_profile;
-#endif
+# endif
 
 	  if (GLRO(dl_profile) != NULL
 	      && _dl_name_match_p (GLRO(dl_profile), l))
@@ -129,6 +130,7 @@ elf_machine_runtime_setup (struct link_map *l, struct r_scope_elem *scope[],
 	    GL(dl_profile_map) = l;
 	}
       else
+#endif
 	{
 	  /* This function will get called to fix up the GOT entry indicated by
 	     the offset on the stack, and then jump to the resolved address.  */
diff --git a/sysdeps/s390/s390-64/dl-trampoline.h b/sysdeps/s390/s390-64/dl-trampoline.h
index 3a7cfc5f92..61188119e6 100644
--- a/sysdeps/s390/s390-64/dl-trampoline.h
+++ b/sysdeps/s390/s390-64/dl-trampoline.h
@@ -150,7 +150,7 @@ _dl_runtime_resolve:
 #undef F4_OFF
 #undef F6_OFF
 
-#ifndef PROF
+#if !defined PROF && defined SHARED
 # define SIZEOF_STRUCT_LA_S390_64_REGS 200
 # define REGS_OFF -360
 # define R2_OFF -360
diff --git a/sysdeps/sh/dl-machine.h b/sysdeps/sh/dl-machine.h
index 0e4eac42c3..e0480eae5a 100644
--- a/sysdeps/sh/dl-machine.h
+++ b/sysdeps/sh/dl-machine.h
@@ -101,6 +101,7 @@ elf_machine_runtime_setup (struct link_map *l, struct r_scope_elem *scope[],
 	 to intercept the calls to collect information.	 In this case we
 	 don't store the address in the GOT so that all future calls also
 	 end in this function.	*/
+#ifdef SHARED
       if (profile)
 	{
 	  got[2] = (Elf32_Addr) &_dl_runtime_profile;
@@ -110,6 +111,7 @@ elf_machine_runtime_setup (struct link_map *l, struct r_scope_elem *scope[],
 	    GL(dl_profile_map) = l;
 	}
       else
+#endif
 	/* This function will get called to fix up the GOT entry indicated by
 	   the offset on the stack, and then jump to the resolved address.  */
 	got[2] = (Elf32_Addr) &_dl_runtime_resolve;
diff --git a/sysdeps/sh/dl-trampoline.S b/sysdeps/sh/dl-trampoline.S
index 5d703341ed..ecaae34db4 100644
--- a/sysdeps/sh/dl-trampoline.S
+++ b/sysdeps/sh/dl-trampoline.S
@@ -142,6 +142,7 @@ _dl_runtime_resolve:
 	.size _dl_runtime_resolve, .-_dl_runtime_resolve
 
 
+#ifdef SHARED
 	.globl _dl_runtime_profile
 	.type _dl_runtime_profile,@function
 	cfi_startproc
@@ -428,3 +429,4 @@ _dl_runtime_profile:
 8:	.long _dl_audit_pltexit
 #endif
 	.size _dl_runtime_profile, .-_dl_runtime_profile
+#endif /* SHARED */
diff --git a/sysdeps/sparc/sparc32/dl-machine.h b/sysdeps/sparc/sparc32/dl-machine.h
index 9b57ae1a93..b10e541810 100644
--- a/sysdeps/sparc/sparc32/dl-machine.h
+++ b/sysdeps/sparc/sparc32/dl-machine.h
@@ -116,7 +116,8 @@ elf_machine_runtime_setup (struct link_map *l, struct r_scope_elem *scope[],
 	 bits of %g1 with an offset into the .rela.plt section and jump to
 	 the beginning of the PLT.  */
       plt = (Elf32_Addr *) D_PTR (l, l_info[DT_PLTGOT]);
-      if (__builtin_expect(profile, 0))
+#ifdef SHARED
+      if (__glibc_unlikely (profile))
 	{
 	  rfunc = (Elf32_Addr) &_dl_runtime_profile;
 
@@ -125,6 +126,7 @@ elf_machine_runtime_setup (struct link_map *l, struct r_scope_elem *scope[],
 	    GL(dl_profile_map) = l;
 	}
       else
+#endif
 	{
 	  rfunc = (Elf32_Addr) &_dl_runtime_resolve;
 	}
diff --git a/sysdeps/sparc/sparc32/dl-trampoline.S b/sysdeps/sparc/sparc32/dl-trampoline.S
index 08ff31b474..5e7d860ae4 100644
--- a/sysdeps/sparc/sparc32/dl-trampoline.S
+++ b/sysdeps/sparc/sparc32/dl-trampoline.S
@@ -47,6 +47,7 @@ _dl_runtime_resolve:
 
 	.size	_dl_runtime_resolve, .-_dl_runtime_resolve
 
+#ifdef SHARED
 	/* For the profiling cases we pass in our stack frame
 	 * as the base of the La_sparc32_regs, so it looks
 	 * like:
@@ -185,3 +186,4 @@ _dl_runtime_profile:
 	cfi_endproc
 
 	.size	_dl_runtime_profile, .-_dl_runtime_profile
+#endif
diff --git a/sysdeps/sparc/sparc64/dl-machine.h b/sysdeps/sparc/sparc64/dl-machine.h
index 2f04ac550e..98469e7604 100644
--- a/sysdeps/sparc/sparc64/dl-machine.h
+++ b/sysdeps/sparc/sparc64/dl-machine.h
@@ -136,7 +136,8 @@ elf_machine_runtime_setup (struct link_map *l, struct r_scope_elem *scope[],
       Elf64_Addr res0_addr, res1_addr;
       unsigned int *plt = (void *) D_PTR (l, l_info[DT_PLTGOT]);
 
-      if (__builtin_expect(profile, 0))
+#ifdef SHARED
+      if (__glibc_unlikely (profile))
 	{
 	  res0_addr = (Elf64_Addr) &_dl_runtime_profile_0;
 	  res1_addr = (Elf64_Addr) &_dl_runtime_profile_1;
@@ -146,6 +147,7 @@ elf_machine_runtime_setup (struct link_map *l, struct r_scope_elem *scope[],
 	    GL(dl_profile_map) = l;
 	}
       else
+#endif
 	{
 	  res0_addr = (Elf64_Addr) &_dl_runtime_resolve_0;
 	  res1_addr = (Elf64_Addr) &_dl_runtime_resolve_1;
diff --git a/sysdeps/sparc/sparc64/dl-trampoline.S b/sysdeps/sparc/sparc64/dl-trampoline.S
index 444690a71e..82b42681dd 100644
--- a/sysdeps/sparc/sparc64/dl-trampoline.S
+++ b/sysdeps/sparc/sparc64/dl-trampoline.S
@@ -92,6 +92,7 @@ _dl_runtime_resolve_1:
 
 	.size	_dl_runtime_resolve_1, .-_dl_runtime_resolve_1
 
+#ifdef SHARED
 	/* For the profiling cases we pass in our stack frame
 	 * as the base of the La_sparc64_regs, so it looks
 	 * like:
@@ -323,3 +324,4 @@ _dl_runtime_profile_1:
 	cfi_endproc
 
 	.size	_dl_runtime_resolve_1, .-_dl_runtime_resolve_1
+#endif
diff --git a/sysdeps/x86_64/dl-machine.h b/sysdeps/x86_64/dl-machine.h
index 9ea2a70837..581a2f1a9e 100644
--- a/sysdeps/x86_64/dl-machine.h
+++ b/sysdeps/x86_64/dl-machine.h
@@ -89,6 +89,7 @@ elf_machine_runtime_setup (struct link_map *l, struct r_scope_elem *scope[],
 
       const struct cpu_features* cpu_features = __get_cpu_features ();
 
+#ifdef SHARED
       /* The got[2] entry contains the address of a function which gets
 	 called to get the address of a so far unresolved function and
 	 jump to it.  The profiling extension of the dynamic linker allows
@@ -111,6 +112,7 @@ elf_machine_runtime_setup (struct link_map *l, struct r_scope_elem *scope[],
 	    GL(dl_profile_map) = l;
 	}
       else
+#endif
 	{
 	  /* This function will get called to fix up the GOT entry
 	     indicated by the offset on the stack, and then jump to
diff --git a/sysdeps/x86_64/dl-trampoline.S b/sysdeps/x86_64/dl-trampoline.S
index a6b9a1826b..3fd30d58fc 100644
--- a/sysdeps/x86_64/dl-trampoline.S
+++ b/sysdeps/x86_64/dl-trampoline.S
@@ -53,45 +53,49 @@
 
 #define RESTORE_AVX
 
-#define VEC_SIZE		64
-#define VMOVA			vmovdqa64
-#define VEC(i)			zmm##i
-#define _dl_runtime_profile	_dl_runtime_profile_avx512
-# define SECTION(p)		p##.evex512
-#include "dl-trampoline.h"
-#undef _dl_runtime_profile
-#undef VEC
-#undef VMOVA
-#undef VEC_SIZE
-#undef SECTION
-
-#if MINIMUM_X86_ISA_LEVEL <= AVX_X86_ISA_LEVEL
-# define VEC_SIZE		32
-# define VMOVA			vmovdqa
-# define VEC(i)			ymm##i
-# define SECTION(p)		p##.avx
-# define _dl_runtime_profile	_dl_runtime_profile_avx
+#ifdef SHARED
+# define VEC_SIZE		64
+# define VMOVA			vmovdqa64
+# define VEC(i)			zmm##i
+# define _dl_runtime_profile	_dl_runtime_profile_avx512
+#  define SECTION(p)		p##.evex512
 # include "dl-trampoline.h"
 # undef _dl_runtime_profile
 # undef VEC
 # undef VMOVA
 # undef VEC_SIZE
 # undef SECTION
-#endif
 
-#if MINIMUM_X86_ISA_LEVEL < AVX_X86_ISA_LEVEL
+# if MINIMUM_X86_ISA_LEVEL <= AVX_X86_ISA_LEVEL
+#  define VEC_SIZE		32
+#  define VMOVA			vmovdqa
+#  define VEC(i)			ymm##i
+#  define SECTION(p)		p##.avx
+#  define _dl_runtime_profile	_dl_runtime_profile_avx
+#  include "dl-trampoline.h"
+#  undef _dl_runtime_profile
+#  undef VEC
+#  undef VMOVA
+#  undef VEC_SIZE
+#  undef SECTION
+# endif
+
+# if MINIMUM_X86_ISA_LEVEL < AVX_X86_ISA_LEVEL
 /* movaps/movups is 1-byte shorter.  */
-# define VEC_SIZE		16
-# define VMOVA			movaps
-# define VEC(i)			xmm##i
-# define _dl_runtime_profile	_dl_runtime_profile_sse
-# undef RESTORE_AVX
-# include "dl-trampoline.h"
-# undef _dl_runtime_profile
-# undef VEC
-# undef VMOVA
-# undef VEC_SIZE
+#  define VEC_SIZE		16
+#  define VMOVA			movaps
+#  define VEC(i)			xmm##i
+#  define _dl_runtime_profile	_dl_runtime_profile_sse
+#  undef RESTORE_AVX
+#  include "dl-trampoline.h"
+#  undef _dl_runtime_profile
+#  undef VEC
+#  undef VMOVA
+#  undef VEC_SIZE
+# endif
+#endif /* SHARED */
 
+#if MINIMUM_X86_ISA_LEVEL < AVX_X86_ISA_LEVEL
 # define USE_FXSAVE
 # define STATE_SAVE_ALIGNMENT	16
 # define _dl_runtime_resolve	_dl_runtime_resolve_fxsave
-- 
2.34.1


^ permalink raw reply	[flat|nested] 45+ messages in thread

* [PATCH v2 14/19] elf: Ignore loader debug env vars for setuid
  2023-10-17 13:05 [PATCH v2 00/19] Improve loader environment variable handling Adhemerval Zanella
                   ` (12 preceding siblings ...)
  2023-10-17 13:05 ` [PATCH v2 13/19] elf: Remove LD_PROFILE for static binaries Adhemerval Zanella
@ 2023-10-17 13:05 ` Adhemerval Zanella
  2023-10-17 13:05 ` [PATCH v2 15/19] elf: Remove any_debug from dl_main_state Adhemerval Zanella
                   ` (4 subsequent siblings)
  18 siblings, 0 replies; 45+ messages in thread
From: Adhemerval Zanella @ 2023-10-17 13:05 UTC (permalink / raw)
  To: libc-alpha, Siddhesh Poyarekar

Loader already ignores LD_DEBUG, LD_DEBUG_OUTPUT, and
LD_TRACE_LOADED_OBJECTS. Both LD_WARN and LD_VERBOSE are similar to
LD_DEBUG, in the sense they enable additional checks and debug
information, so it makes sense to disable them.

Checked on x86_64-linux-gnu.
---
 elf/rtld.c           | 22 ++++++++++++++--------
 elf/tst-env-setuid.c |  2 ++
 2 files changed, 16 insertions(+), 8 deletions(-)

diff --git a/elf/rtld.c b/elf/rtld.c
index a09cf2a9df..8749ad6288 100644
--- a/elf/rtld.c
+++ b/elf/rtld.c
@@ -2552,13 +2552,15 @@ process_envvars (struct dl_main_state *state)
 	{
 	case 4:
 	  /* Warning level, verbose or not.  */
-	  if (memcmp (envline, "WARN", 4) == 0)
+	  if (!!__libc_enable_secure
+	      && memcmp (envline, "WARN", 4) == 0)
 	    GLRO(dl_verbose) = envline[5] != '\0';
 	  break;
 
 	case 5:
 	  /* Debugging of the dynamic linker?  */
-	  if (memcmp (envline, "DEBUG", 5) == 0)
+	  if (!__libc_enable_secure
+	      && memcmp (envline, "DEBUG", 5) == 0)
 	    {
 	      process_dl_debug (state, &envline[6]);
 	      break;
@@ -2569,7 +2571,8 @@ process_envvars (struct dl_main_state *state)
 
 	case 7:
 	  /* Print information about versions.  */
-	  if (memcmp (envline, "VERBOSE", 7) == 0)
+	  if (!__libc_enable_secure
+	      && memcmp (envline, "VERBOSE", 7) == 0)
 	    {
 	      state->version_info = envline[8] != '\0';
 	      break;
@@ -2625,7 +2628,8 @@ process_envvars (struct dl_main_state *state)
 	    }
 
 	  /* Where to place the profiling data file.  */
-	  if (memcmp (envline, "DEBUG_OUTPUT", 12) == 0)
+	  if (!__libc_enable_secure
+	      && memcmp (envline, "DEBUG_OUTPUT", 12) == 0)
 	    {
 	      debug_output = &envline[13];
 	      break;
@@ -2646,7 +2650,8 @@ process_envvars (struct dl_main_state *state)
 
 	case 20:
 	  /* The mode of the dynamic linker can be set.  */
-	  if (memcmp (envline, "TRACE_LOADED_OBJECTS", 20) == 0)
+	  if (!__libc_enable_secure
+	      && memcmp (envline, "TRACE_LOADED_OBJECTS", 20) == 0)
 	    {
 	      state->mode = rtld_mode_trace;
 	      state->mode_trace_program
@@ -2668,9 +2673,10 @@ process_envvars (struct dl_main_state *state)
 	}
       while (*nextp != '\0');
 
-      GLRO(dl_debug_mask) = 0;
-
-      if (state->mode != rtld_mode_normal)
+      if (GLRO(dl_debug_mask) != 0
+	  || GLRO(dl_verbose) != 0
+	  || state->mode != rtld_mode_normal
+	  || state->version_info)
 	_exit (5);
     }
   /* If we have to run the dynamic linker in debugging mode and the
diff --git a/elf/tst-env-setuid.c b/elf/tst-env-setuid.c
index 76b8e1fb45..dcf213a4cd 100644
--- a/elf/tst-env-setuid.c
+++ b/elf/tst-env-setuid.c
@@ -59,6 +59,8 @@ static const struct envvar_t filtered_envvars[] =
   { "MALLOC_TRACE",            FILTERED_VALUE },
   { "MALLOC_TRIM_THRESHOLD_",  FILTERED_VALUE },
   { "RES_OPTIONS",             FILTERED_VALUE },
+  { "LD_DEBUG",                "all" },
+  { "LD_DEBUG_OUTPUT",         "/tmp/some-file" },
 };
 
 static const struct envvar_t unfiltered_envvars[] =
-- 
2.34.1


^ permalink raw reply	[flat|nested] 45+ messages in thread

* [PATCH v2 15/19] elf: Remove any_debug from dl_main_state
  2023-10-17 13:05 [PATCH v2 00/19] Improve loader environment variable handling Adhemerval Zanella
                   ` (13 preceding siblings ...)
  2023-10-17 13:05 ` [PATCH v2 14/19] elf: Ignore loader debug env vars for setuid Adhemerval Zanella
@ 2023-10-17 13:05 ` Adhemerval Zanella
  2023-10-27 14:26   ` Siddhesh Poyarekar
  2023-10-17 13:05 ` [PATCH v2 16/19] elf: Ignore LD_LIBRARY_PATH and debug env var for setuid for static Adhemerval Zanella
                   ` (3 subsequent siblings)
  18 siblings, 1 reply; 45+ messages in thread
From: Adhemerval Zanella @ 2023-10-17 13:05 UTC (permalink / raw)
  To: libc-alpha, Siddhesh Poyarekar

Its usage can be implied by the GLRO(dl_debug_mask).
---
 elf/dl-main.h | 3 ---
 elf/rtld.c    | 6 ++----
 2 files changed, 2 insertions(+), 7 deletions(-)

diff --git a/elf/dl-main.h b/elf/dl-main.h
index 92766d06b4..f876904cb6 100644
--- a/elf/dl-main.h
+++ b/elf/dl-main.h
@@ -97,9 +97,6 @@ struct dl_main_state
   /* True if program should be also printed for rtld_mode_trace.  */
   bool mode_trace_program;
 
-  /* True if any of the debugging options is enabled.  */
-  bool any_debug;
-
   /* True if information about versions has to be printed.  */
   bool version_info;
 };
diff --git a/elf/rtld.c b/elf/rtld.c
index 8749ad6288..638b019670 100644
--- a/elf/rtld.c
+++ b/elf/rtld.c
@@ -300,7 +300,6 @@ dl_main_state_init (struct dl_main_state *state)
   state->glibc_hwcaps_prepend = NULL;
   state->glibc_hwcaps_mask = NULL;
   state->mode = rtld_mode_normal;
-  state->any_debug = false;
   state->version_info = false;
 }
 
@@ -2481,7 +2480,6 @@ process_dl_debug (struct dl_main_state *state, const char *dl_debug)
 		&& memcmp (dl_debug, debopts[cnt].name, len) == 0)
 	      {
 		GLRO(dl_debug_mask) |= debopts[cnt].mask;
-		state->any_debug = true;
 		break;
 	      }
 
@@ -2552,7 +2550,7 @@ process_envvars (struct dl_main_state *state)
 	{
 	case 4:
 	  /* Warning level, verbose or not.  */
-	  if (!!__libc_enable_secure
+	  if (!__libc_enable_secure
 	      && memcmp (envline, "WARN", 4) == 0)
 	    GLRO(dl_verbose) = envline[5] != '\0';
 	  break;
@@ -2682,7 +2680,7 @@ process_envvars (struct dl_main_state *state)
   /* If we have to run the dynamic linker in debugging mode and the
      LD_DEBUG_OUTPUT environment variable is given, we write the debug
      messages to this file.  */
-  else if (state->any_debug && debug_output != NULL)
+  else if (GLRO(dl_debug_mask) != 0 && debug_output != NULL)
     {
       const int flags = O_WRONLY | O_APPEND | O_CREAT | O_NOFOLLOW;
       size_t name_len = strlen (debug_output);
-- 
2.34.1


^ permalink raw reply	[flat|nested] 45+ messages in thread

* [PATCH v2 16/19] elf: Ignore LD_LIBRARY_PATH and debug env var for setuid for static
  2023-10-17 13:05 [PATCH v2 00/19] Improve loader environment variable handling Adhemerval Zanella
                   ` (14 preceding siblings ...)
  2023-10-17 13:05 ` [PATCH v2 15/19] elf: Remove any_debug from dl_main_state Adhemerval Zanella
@ 2023-10-17 13:05 ` Adhemerval Zanella
  2023-10-17 13:05 ` [PATCH v2 17/19] elf: Add comments on how LD_AUDIT and LD_PRELOAD handle __libc_enable_secure Adhemerval Zanella
                   ` (2 subsequent siblings)
  18 siblings, 0 replies; 45+ messages in thread
From: Adhemerval Zanella @ 2023-10-17 13:05 UTC (permalink / raw)
  To: libc-alpha, Siddhesh Poyarekar

It mimics the ld.so behavior.

Checked on x86_64-linux-gnu.
---
 elf/dl-support.c | 32 ++++++++++++++++----------------
 1 file changed, 16 insertions(+), 16 deletions(-)

diff --git a/elf/dl-support.c b/elf/dl-support.c
index 31a608df87..837fa1c836 100644
--- a/elf/dl-support.c
+++ b/elf/dl-support.c
@@ -272,8 +272,6 @@ _dl_non_dynamic_init (void)
   _dl_main_map.l_phdr = GL(dl_phdr);
   _dl_main_map.l_phnum = GL(dl_phnum);
 
-  _dl_verbose = *(getenv ("LD_WARN") ?: "") == '\0' ? 0 : 1;
-
   /* Set up the data structures for the system-supplied DSO early,
      so they can influence _dl_init_paths.  */
   setup_vdso (NULL, NULL);
@@ -281,6 +279,22 @@ _dl_non_dynamic_init (void)
   /* With vDSO setup we can initialize the function pointers.  */
   setup_vdso_pointers ();
 
+  if (__libc_enable_secure)
+    {
+      static const char unsecure_envvars[] =
+	UNSECURE_ENVVARS
+	;
+      const char *cp = unsecure_envvars;
+
+      while (cp < unsecure_envvars + sizeof (unsecure_envvars))
+	{
+	  __unsetenv (cp);
+	  cp = strchr (cp, '\0') + 1;
+	}
+    }
+
+  _dl_verbose = *(getenv ("LD_WARN") ?: "") == '\0' ? 0 : 1;
+
   /* Initialize the data structures for the search paths for shared
      objects.  */
   _dl_init_paths (getenv ("LD_LIBRARY_PATH"), "LD_LIBRARY_PATH",
@@ -297,20 +311,6 @@ _dl_non_dynamic_init (void)
 
   _dl_dynamic_weak = *(getenv ("LD_DYNAMIC_WEAK") ?: "") == '\0';
 
-  if (__libc_enable_secure)
-    {
-      static const char unsecure_envvars[] =
-	UNSECURE_ENVVARS
-	;
-      const char *cp = unsecure_envvars;
-
-      while (cp < unsecure_envvars + sizeof (unsecure_envvars))
-	{
-	  __unsetenv (cp);
-	  cp = strchr (cp, '\0') + 1;
-	}
-    }
-
 #ifdef DL_PLATFORM_INIT
   DL_PLATFORM_INIT;
 #endif
-- 
2.34.1


^ permalink raw reply	[flat|nested] 45+ messages in thread

* [PATCH v2 17/19] elf: Add comments on how LD_AUDIT and LD_PRELOAD handle __libc_enable_secure
  2023-10-17 13:05 [PATCH v2 00/19] Improve loader environment variable handling Adhemerval Zanella
                   ` (15 preceding siblings ...)
  2023-10-17 13:05 ` [PATCH v2 16/19] elf: Ignore LD_LIBRARY_PATH and debug env var for setuid for static Adhemerval Zanella
@ 2023-10-17 13:05 ` Adhemerval Zanella
  2023-10-27 14:31   ` Siddhesh Poyarekar
  2023-10-17 13:05 ` [PATCH v2 18/19] elf: Ignore LD_BIND_NOW and LD_BIND_NOT for setuid binaries Adhemerval Zanella
  2023-10-17 13:05 ` [PATCH v2 19/19] elf: Refactor process_envvars Adhemerval Zanella
  18 siblings, 1 reply; 45+ messages in thread
From: Adhemerval Zanella @ 2023-10-17 13:05 UTC (permalink / raw)
  To: libc-alpha, Siddhesh Poyarekar

To make explicit why __libc_enable_secure is not checked.
---
 elf/rtld.c | 9 ++++++++-
 1 file changed, 8 insertions(+), 1 deletion(-)

diff --git a/elf/rtld.c b/elf/rtld.c
index 638b019670..d1017ba9e9 100644
--- a/elf/rtld.c
+++ b/elf/rtld.c
@@ -2563,6 +2563,10 @@ process_envvars (struct dl_main_state *state)
 	      process_dl_debug (state, &envline[6]);
 	      break;
 	    }
+	  /* For __libc_enable_secure mode, audit pathnames containing slashes
+	     are ignored.  Also, shared audit objects are only loaded only from
+	     the standard search directories and only if they have set-user-ID
+	     mode bit enabled.  */
 	  if (memcmp (envline, "AUDIT", 5) == 0)
 	    audit_list_add_string (&state->audit_list, &envline[6]);
 	  break;
@@ -2576,7 +2580,10 @@ process_envvars (struct dl_main_state *state)
 	      break;
 	    }
 
-	  /* List of objects to be preloaded.  */
+	  /* For __libc_enable_secure mode, preload pathnames containing slashes
+	     are ignored.  Also, shared objects are only preloaded from the
+	     standard search directories and only if they have set-user-ID mode
+	     bit enabled.  */
 	  if (memcmp (envline, "PRELOAD", 7) == 0)
 	    {
 	      state->preloadlist = &envline[8];
-- 
2.34.1


^ permalink raw reply	[flat|nested] 45+ messages in thread

* [PATCH v2 18/19] elf: Ignore LD_BIND_NOW and LD_BIND_NOT for setuid binaries
  2023-10-17 13:05 [PATCH v2 00/19] Improve loader environment variable handling Adhemerval Zanella
                   ` (16 preceding siblings ...)
  2023-10-17 13:05 ` [PATCH v2 17/19] elf: Add comments on how LD_AUDIT and LD_PRELOAD handle __libc_enable_secure Adhemerval Zanella
@ 2023-10-17 13:05 ` Adhemerval Zanella
  2023-10-17 13:05 ` [PATCH v2 19/19] elf: Refactor process_envvars Adhemerval Zanella
  18 siblings, 0 replies; 45+ messages in thread
From: Adhemerval Zanella @ 2023-10-17 13:05 UTC (permalink / raw)
  To: libc-alpha, Siddhesh Poyarekar

To avoid any environment variable to change setuid binaries
semantics.

Checked on x86_64-linux-gnu.
---
 elf/rtld.c                  | 8 ++++++--
 elf/tst-env-setuid.c        | 4 ++--
 sysdeps/generic/unsecvars.h | 2 ++
 3 files changed, 10 insertions(+), 4 deletions(-)

diff --git a/elf/rtld.c b/elf/rtld.c
index d1017ba9e9..cfba30eba0 100644
--- a/elf/rtld.c
+++ b/elf/rtld.c
@@ -2598,12 +2598,14 @@ process_envvars (struct dl_main_state *state)
 
 	case 8:
 	  /* Do we bind early?  */
-	  if (memcmp (envline, "BIND_NOW", 8) == 0)
+	  if (!__libc_enable_secure
+	      && memcmp (envline, "BIND_NOW", 8) == 0)
 	    {
 	      GLRO(dl_lazy) = envline[9] == '\0';
 	      break;
 	    }
-	  if (memcmp (envline, "BIND_NOT", 8) == 0)
+	  if (! __libc_enable_secure
+	      && memcmp (envline, "BIND_NOT", 8) == 0)
 	    GLRO(dl_bind_not) = envline[9] != '\0';
 	  break;
 
@@ -2680,6 +2682,8 @@ process_envvars (struct dl_main_state *state)
 
       if (GLRO(dl_debug_mask) != 0
 	  || GLRO(dl_verbose) != 0
+	  || GLRO(dl_lazy) != 1
+	  || GLRO(dl_bind_not) != 0
 	  || state->mode != rtld_mode_normal
 	  || state->version_info)
 	_exit (5);
diff --git a/elf/tst-env-setuid.c b/elf/tst-env-setuid.c
index dcf213a4cd..eb9613d717 100644
--- a/elf/tst-env-setuid.c
+++ b/elf/tst-env-setuid.c
@@ -61,12 +61,12 @@ static const struct envvar_t filtered_envvars[] =
   { "RES_OPTIONS",             FILTERED_VALUE },
   { "LD_DEBUG",                "all" },
   { "LD_DEBUG_OUTPUT",         "/tmp/some-file" },
+  { "LD_BIND_NOW",             "0" },
+  { "LD_BIND_NOT",             "1" },
 };
 
 static const struct envvar_t unfiltered_envvars[] =
 {
-  { "LD_BIND_NOW",             "0" },
-  { "LD_BIND_NOT",             "1" },
   /* Non longer supported option.  */
   { "LD_ASSUME_KERNEL",        UNFILTERED_VALUE },
 };
diff --git a/sysdeps/generic/unsecvars.h b/sysdeps/generic/unsecvars.h
index f7ebed60e5..c71b70ed95 100644
--- a/sysdeps/generic/unsecvars.h
+++ b/sysdeps/generic/unsecvars.h
@@ -7,6 +7,8 @@
   "GLIBC_TUNABLES\0"							      \
   "HOSTALIASES\0"							      \
   "LD_AUDIT\0"								      \
+  "LD_BIND_NOT\0"							      \
+  "LD_BIND_NOW\0"							      \
   "LD_DEBUG\0"								      \
   "LD_DEBUG_OUTPUT\0"							      \
   "LD_DYNAMIC_WEAK\0"							      \
-- 
2.34.1


^ permalink raw reply	[flat|nested] 45+ messages in thread

* [PATCH v2 19/19] elf: Refactor process_envvars
  2023-10-17 13:05 [PATCH v2 00/19] Improve loader environment variable handling Adhemerval Zanella
                   ` (17 preceding siblings ...)
  2023-10-17 13:05 ` [PATCH v2 18/19] elf: Ignore LD_BIND_NOW and LD_BIND_NOT for setuid binaries Adhemerval Zanella
@ 2023-10-17 13:05 ` Adhemerval Zanella
  18 siblings, 0 replies; 45+ messages in thread
From: Adhemerval Zanella @ 2023-10-17 13:05 UTC (permalink / raw)
  To: libc-alpha, Siddhesh Poyarekar

It splits between process_envvars_secure and process_envvars_default,
with the former used to process arguments for __libc_enable_secure.
It does not have any semantic change, just simplify the code so there
is no need to handle __libc_enable_secure on each len switch.

Checked on x86_64-linux-gnu and aarch64-linux-gnu.
---
 elf/rtld.c | 132 ++++++++++++++++++++++++++++++++++-------------------
 1 file changed, 84 insertions(+), 48 deletions(-)

diff --git a/elf/rtld.c b/elf/rtld.c
index cfba30eba0..95dcd32185 100644
--- a/elf/rtld.c
+++ b/elf/rtld.c
@@ -2527,7 +2527,67 @@ a filename can be specified using the LD_DEBUG_OUTPUT environment variable.\n");
 }
 \f
 static void
-process_envvars (struct dl_main_state *state)
+process_envvars_secure (struct dl_main_state *state)
+{
+  char **runp = _environ;
+  char *envline;
+
+  while ((envline = _dl_next_ld_env_entry (&runp)) != NULL)
+    {
+      size_t len = 0;
+
+      while (envline[len] != '\0' && envline[len] != '=')
+	++len;
+
+      if (envline[len] != '=')
+	/* This is a "LD_" variable at the end of the string without
+	   a '=' character.  Ignore it since otherwise we will access
+	   invalid memory below.  */
+	continue;
+
+      switch (len)
+	{
+	case 5:
+	  /* For __libc_enable_secure mode, audit pathnames containing slashes
+	     are ignored.  Also, shared audit objects are only loaded only from
+	     the standard search directories and only if they have set-user-ID
+	     mode bit enabled.  */
+	  if (memcmp (envline, "AUDIT", 5) == 0)
+	    audit_list_add_string (&state->audit_list, &envline[6]);
+	  break;
+
+	case 7:
+	  /* For __libc_enable_secure mode, preload pathnames containing slashes
+	     are ignored.  Also, shared objects are only preloaded from the
+	     standard search directories and only if they have set-user-ID mode
+	     bit enabled.  */
+	  if (memcmp (envline, "PRELOAD", 7) == 0)
+	    state->preloadlist = &envline[8];
+	  break;
+	}
+    }
+
+  /* Extra security for SUID binaries.  Remove all dangerous environment
+     variables.  */
+  const char *nextp = UNSECURE_ENVVARS;
+  do
+    {
+      unsetenv (nextp);
+      nextp = strchr (nextp, '\0') + 1;
+    }
+  while (*nextp != '\0');
+
+  if (GLRO(dl_debug_mask) != 0
+      || GLRO(dl_verbose) != 0
+      || GLRO(dl_lazy) != 1
+      || GLRO(dl_bind_not) != 0
+      || state->mode != rtld_mode_normal
+      || state->version_info)
+    _exit (5);
+}
+
+static void
+process_envvars_default (struct dl_main_state *state)
 {
   char **runp = _environ;
   char *envline;
@@ -2550,15 +2610,13 @@ process_envvars (struct dl_main_state *state)
 	{
 	case 4:
 	  /* Warning level, verbose or not.  */
-	  if (!__libc_enable_secure
-	      && memcmp (envline, "WARN", 4) == 0)
+	  if (memcmp (envline, "WARN", 4) == 0)
 	    GLRO(dl_verbose) = envline[5] != '\0';
 	  break;
 
 	case 5:
 	  /* Debugging of the dynamic linker?  */
-	  if (!__libc_enable_secure
-	      && memcmp (envline, "DEBUG", 5) == 0)
+	  if (memcmp (envline, "DEBUG", 5) == 0)
 	    {
 	      process_dl_debug (state, &envline[6]);
 	      break;
@@ -2573,8 +2631,7 @@ process_envvars (struct dl_main_state *state)
 
 	case 7:
 	  /* Print information about versions.  */
-	  if (!__libc_enable_secure
-	      && memcmp (envline, "VERBOSE", 7) == 0)
+	  if (memcmp (envline, "VERBOSE", 7) == 0)
 	    {
 	      state->version_info = envline[8] != '\0';
 	      break;
@@ -2591,43 +2648,37 @@ process_envvars (struct dl_main_state *state)
 	    }
 
 	  /* Which shared object shall be profiled.  */
-	  if (!__libc_enable_secure
-	      && memcmp (envline, "PROFILE", 7) == 0 && envline[8] != '\0')
+	  if (memcmp (envline, "PROFILE", 7) == 0 && envline[8] != '\0')
 	    GLRO(dl_profile) = &envline[8];
 	  break;
 
 	case 8:
 	  /* Do we bind early?  */
-	  if (!__libc_enable_secure
-	      && memcmp (envline, "BIND_NOW", 8) == 0)
+	  if (memcmp (envline, "BIND_NOW", 8) == 0)
 	    {
 	      GLRO(dl_lazy) = envline[9] == '\0';
 	      break;
 	    }
-	  if (! __libc_enable_secure
-	      && memcmp (envline, "BIND_NOT", 8) == 0)
+	  if (memcmp (envline, "BIND_NOT", 8) == 0)
 	    GLRO(dl_bind_not) = envline[9] != '\0';
 	  break;
 
 	case 9:
 	  /* Test whether we want to see the content of the auxiliary
 	     array passed up from the kernel.  */
-	  if (!__libc_enable_secure
-	      && memcmp (envline, "SHOW_AUXV", 9) == 0)
+	  if (memcmp (envline, "SHOW_AUXV", 9) == 0)
 	    _dl_show_auxv ();
 	  break;
 
 	case 11:
 	  /* Path where the binary is found.  */
-	  if (!__libc_enable_secure
-	      && memcmp (envline, "ORIGIN_PATH", 11) == 0)
+	  if (memcmp (envline, "ORIGIN_PATH", 11) == 0)
 	    GLRO(dl_origin_path) = &envline[12];
 	  break;
 
 	case 12:
 	  /* The library search path.  */
-	  if (!__libc_enable_secure
-	      && memcmp (envline, "LIBRARY_PATH", 12) == 0)
+	  if (memcmp (envline, "LIBRARY_PATH", 12) == 0)
 	    {
 	      state->library_path = &envline[13];
 	      state->library_path_source = "LD_LIBRARY_PATH";
@@ -2635,30 +2686,26 @@ process_envvars (struct dl_main_state *state)
 	    }
 
 	  /* Where to place the profiling data file.  */
-	  if (!__libc_enable_secure
-	      && memcmp (envline, "DEBUG_OUTPUT", 12) == 0)
+	  if (memcmp (envline, "DEBUG_OUTPUT", 12) == 0)
 	    {
 	      debug_output = &envline[13];
 	      break;
 	    }
 
-	  if (!__libc_enable_secure
-	      && memcmp (envline, "DYNAMIC_WEAK", 12) == 0)
+	  if (memcmp (envline, "DYNAMIC_WEAK", 12) == 0)
 	    GLRO(dl_dynamic_weak) = 1;
 	  break;
 
 	case 14:
 	  /* Where to place the profiling data file.  */
-	  if (!__libc_enable_secure
-	      && memcmp (envline, "PROFILE_OUTPUT", 14) == 0
+	  if (memcmp (envline, "PROFILE_OUTPUT", 14) == 0
 	      && envline[15] != '\0')
 	    GLRO(dl_profile_output) = &envline[15];
 	  break;
 
 	case 20:
 	  /* The mode of the dynamic linker can be set.  */
-	  if (!__libc_enable_secure
-	      && memcmp (envline, "TRACE_LOADED_OBJECTS", 20) == 0)
+	  if (memcmp (envline, "TRACE_LOADED_OBJECTS", 20) == 0)
 	    {
 	      state->mode = rtld_mode_trace;
 	      state->mode_trace_program
@@ -2668,30 +2715,10 @@ process_envvars (struct dl_main_state *state)
 	}
     }
 
-  /* Extra security for SUID binaries.  Remove all dangerous environment
-     variables.  */
-  if (__glibc_unlikely (__libc_enable_secure))
-    {
-      const char *nextp = UNSECURE_ENVVARS;
-      do
-	{
-	  unsetenv (nextp);
-	  nextp = strchr (nextp, '\0') + 1;
-	}
-      while (*nextp != '\0');
-
-      if (GLRO(dl_debug_mask) != 0
-	  || GLRO(dl_verbose) != 0
-	  || GLRO(dl_lazy) != 1
-	  || GLRO(dl_bind_not) != 0
-	  || state->mode != rtld_mode_normal
-	  || state->version_info)
-	_exit (5);
-    }
   /* If we have to run the dynamic linker in debugging mode and the
      LD_DEBUG_OUTPUT environment variable is given, we write the debug
      messages to this file.  */
-  else if (GLRO(dl_debug_mask) != 0 && debug_output != NULL)
+  if (GLRO(dl_debug_mask) != 0 && debug_output != NULL)
     {
       const int flags = O_WRONLY | O_APPEND | O_CREAT | O_NOFOLLOW;
       size_t name_len = strlen (debug_output);
@@ -2710,6 +2737,15 @@ process_envvars (struct dl_main_state *state)
     }
 }
 
+static void
+process_envvars (struct dl_main_state *state)
+{
+  if (__glibc_unlikely (__libc_enable_secure))
+    process_envvars_secure (state);
+  else
+    process_envvars_default (state);
+}
+
 #if HP_TIMING_INLINE
 static void
 print_statistics_item (const char *title, hp_timing_t time,
-- 
2.34.1


^ permalink raw reply	[flat|nested] 45+ messages in thread

* Re: [PATCH v2 01/19] elf: Remove /etc/suid-debug support
  2023-10-17 13:05 ` [PATCH v2 01/19] elf: Remove /etc/suid-debug support Adhemerval Zanella
@ 2023-10-18 12:31   ` Siddhesh Poyarekar
  2023-10-18 18:27     ` Adhemerval Zanella Netto
  0 siblings, 1 reply; 45+ messages in thread
From: Siddhesh Poyarekar @ 2023-10-18 12:31 UTC (permalink / raw)
  To: Adhemerval Zanella, libc-alpha

On 2023-10-17 09:05, Adhemerval Zanella wrote:
> Since malloc debug support moved to a different library
> (libc_malloc_debug.so), the glibc.malloc.check requires preloading the
> debug library to enable it.  It means that suid-debug support has not
> been working since 2.34.
> 
> To restore its support, it would require to add additional information
> and parsing to where to find libc_malloc_debug.so.

Could you elaborate slightly in the commit message, like so:

   To restore its support, we would require to add additional information
   and parsing to locate libc_malloc_debug.so, which is not worth the
   additional risk it might pose.

LGTM.

Reviewed-by: Siddhesh Poyarekar <siddhesh@sourceware.org>

> 
> It is one thing less that might change AT_SECURE binaries' behavior
> due to environment configurations.
> 
> Checked on x86_64-linux-gnu.
> ---
>   elf/dl-tunables.c    | 16 ----------------
>   elf/rtld.c           |  3 +--
>   manual/memory.texi   |  4 +---
>   manual/tunables.texi |  4 +---
>   4 files changed, 3 insertions(+), 24 deletions(-)
> 
> diff --git a/elf/dl-tunables.c b/elf/dl-tunables.c
> index cae67efa0a..24252af22c 100644
> --- a/elf/dl-tunables.c
> +++ b/elf/dl-tunables.c
> @@ -252,20 +252,6 @@ parse_tunables (char *tunestr, char *valstring)
>       tunestr[off] = '\0';
>   }
>   
> -/* Enable the glibc.malloc.check tunable in SETUID/SETGID programs only when
> -   the system administrator has created the /etc/suid-debug file.  This is a
> -   special case where we want to conditionally enable/disable a tunable even
> -   for setuid binaries.  We use the special version of access() to avoid
> -   setting ERRNO, which is a TLS variable since TLS has not yet been set
> -   up.  */
> -static __always_inline void
> -maybe_enable_malloc_check (void)
> -{
> -  tunable_id_t id = TUNABLE_ENUM_NAME (glibc, malloc, check);
> -  if (__libc_enable_secure && __access_noerrno ("/etc/suid-debug", F_OK) == 0)
> -    tunable_list[id].security_level = TUNABLE_SECLEVEL_NONE;
> -}
> -

Dropped function.  OK.

>   /* Initialize the tunables list from the environment.  For now we only use the
>      ENV_ALIAS to find values.  Later we will also use the tunable names to find
>      values.  */
> @@ -277,8 +263,6 @@ __tunables_init (char **envp)
>     size_t len = 0;
>     char **prev_envp = envp;
>   
> -  maybe_enable_malloc_check ();
> -

Drop malloc checking.  OK.  Current testsuite run should cover any 
possible regressions from this and it's clean so we're good.

>     while ((envp = get_next_env (envp, &envname, &len, &envval,
>   			       &prev_envp)) != NULL)
>       {
> diff --git a/elf/rtld.c b/elf/rtld.c
> index 5107d16fe3..51b6d9f326 100644
> --- a/elf/rtld.c
> +++ b/elf/rtld.c
> @@ -2670,8 +2670,7 @@ process_envvars (struct dl_main_state *state)
>   	}
>         while (*nextp != '\0');
>   
> -      if (__access ("/etc/suid-debug", F_OK) != 0)
> -	GLRO(dl_debug_mask) = 0;
> +      GLRO(dl_debug_mask) = 0;

Unconditionally set dl_debug_mask.  OK.

>   
>         if (state->mode != rtld_mode_normal)
>   	_exit (5);
> diff --git a/manual/memory.texi b/manual/memory.texi
> index 5781a64f35..258fdbd3a0 100644
> --- a/manual/memory.texi
> +++ b/manual/memory.texi
> @@ -1379,9 +1379,7 @@ There is one problem with @code{MALLOC_CHECK_}: in SUID or SGID binaries
>   it could possibly be exploited since diverging from the normal programs
>   behavior it now writes something to the standard error descriptor.
>   Therefore the use of @code{MALLOC_CHECK_} is disabled by default for
> -SUID and SGID binaries.  It can be enabled again by the system
> -administrator by adding a file @file{/etc/suid-debug} (the content is
> -not important it could be empty).
> +SUID and SGID binaries.
>   
>   So, what's the difference between using @code{MALLOC_CHECK_} and linking
>   with @samp{-lmcheck}?  @code{MALLOC_CHECK_} is orthogonal with respect to
> diff --git a/manual/tunables.texi b/manual/tunables.texi
> index 776fd93fd9..347b5698b5 100644
> --- a/manual/tunables.texi
> +++ b/manual/tunables.texi
> @@ -136,9 +136,7 @@ termination of the process.
>   Like @env{MALLOC_CHECK_}, @code{glibc.malloc.check} has a problem in that it
>   diverges from normal program behavior by writing to @code{stderr}, which could
>   by exploited in SUID and SGID binaries.  Therefore, @code{glibc.malloc.check}
> -is disabled by default for SUID and SGID binaries.  This can be enabled again
> -by the system administrator by adding a file @file{/etc/suid-debug}; the
> -content of the file could be anything or even empty.
> +is disabled by default for SUID and SGID binaries.
>   @end deftp
>   
>   @deftp Tunable glibc.malloc.top_pad

Manual updates.  OK.

Thanks,
Sid

^ permalink raw reply	[flat|nested] 45+ messages in thread

* Re: [PATCH v2 02/19] elf: Add GLIBC_TUNABLES to unsecvars
  2023-10-17 13:05 ` [PATCH v2 02/19] elf: Add GLIBC_TUNABLES to unsecvars Adhemerval Zanella
@ 2023-10-18 12:52   ` Siddhesh Poyarekar
  0 siblings, 0 replies; 45+ messages in thread
From: Siddhesh Poyarekar @ 2023-10-18 12:52 UTC (permalink / raw)
  To: Adhemerval Zanella, libc-alpha; +Cc: Florian Weimer



On 2023-10-17 09:05, Adhemerval Zanella wrote:
> setuid/setgid process now ignores any glibc tunables, and filters out
> all environment variables that might changes its behavior. This patch
> also adds GLIBC_TUNABLES, so any spawned process by setuid/setgid
> processes should set tunable explicitly.
> 
> Checked on x86_64-linux-gnu.
> 
> Reviewed-by: Florian Weimer <fweimer@redhat.com>

Also.

Reviewed-by: Siddhesh Poyarekar <siddhesh@sourceware.org>

> ---
>   elf/tst-env-setuid-tunables.c | 32 ++++----------------------------
>   sysdeps/generic/unsecvars.h   |  1 +
>   2 files changed, 5 insertions(+), 28 deletions(-)
> 
> diff --git a/elf/tst-env-setuid-tunables.c b/elf/tst-env-setuid-tunables.c
> index f0b92c97e7..2603007b7b 100644
> --- a/elf/tst-env-setuid-tunables.c
> +++ b/elf/tst-env-setuid-tunables.c
> @@ -60,45 +60,21 @@ const char *teststrings[] =
>     "glibc.not_valid.check=2",
>   };
>   
> -const char *resultstrings[] =
> -{
> -  "glibc.malloc.mmap_threshold=4096",
> -  "glibc.malloc.mmap_threshold=4096",
> -  "glibc.malloc.mmap_threshold=4096",
> -  "glibc.malloc.perturb=0x800",
> -  "glibc.malloc.perturb=0x800:glibc.malloc.mmap_threshold=4096",
> -  "glibc.malloc.perturb=0x800:glibc.malloc.mmap_threshold=4096",
> -  "glibc.malloc.mmap_threshold=4096",
> -  "glibc.malloc.mmap_threshold=4096",
> -  "glibc.malloc.mmap_threshold=glibc.malloc.mmap_threshold=4096",
> -  "",
> -  "",
> -  "",
> -  "",
> -  "",
> -  "",
> -  "",
> -};
> -
>   static int
>   test_child (int off)
>   {
>     const char *val = getenv ("GLIBC_TUNABLES");
> +  int ret = 1;
>   
>     printf ("    [%d] GLIBC_TUNABLES is %s\n", off, val);
>     fflush (stdout);
> -  if (val != NULL && strcmp (val, resultstrings[off]) == 0)
> -    return 0;
> -
>     if (val != NULL)
> -    printf ("    [%d] Unexpected GLIBC_TUNABLES VALUE %s, expected %s\n",
> -	    off, val, resultstrings[off]);
> +    printf ("    [%d] Unexpected GLIBC_TUNABLES VALUE %s\n", off, val);
>     else
> -    printf ("    [%d] GLIBC_TUNABLES environment variable absent\n", off);
> -
> +    ret = 0;
>     fflush (stdout);
>   
> -  return 1;
> +  return ret;
>   }
>   
>   static int
> diff --git a/sysdeps/generic/unsecvars.h b/sysdeps/generic/unsecvars.h
> index 8278c50a84..81397fb90b 100644
> --- a/sysdeps/generic/unsecvars.h
> +++ b/sysdeps/generic/unsecvars.h
> @@ -4,6 +4,7 @@
>   #define UNSECURE_ENVVARS \
>     "GCONV_PATH\0"							      \
>     "GETCONF_DIR\0"							      \
> +  "GLIBC_TUNABLES\0"							      \
>     "HOSTALIASES\0"							      \
>     "LD_AUDIT\0"								      \
>     "LD_DEBUG\0"								      \

^ permalink raw reply	[flat|nested] 45+ messages in thread

* Re: [PATCH v2 03/19] elf: Ignore GLIBC_TUNABLES for setuid/setgid binaries
  2023-10-17 13:05 ` [PATCH v2 03/19] elf: Ignore GLIBC_TUNABLES for setuid/setgid binaries Adhemerval Zanella
@ 2023-10-18 13:04   ` Siddhesh Poyarekar
  2023-10-27 10:38     ` Siddhesh Poyarekar
  2023-10-28  2:14   ` DJ Delorie
  1 sibling, 1 reply; 45+ messages in thread
From: Siddhesh Poyarekar @ 2023-10-18 13:04 UTC (permalink / raw)
  To: Adhemerval Zanella, libc-alpha

On 2023-10-17 09:05, Adhemerval Zanella wrote:
> The tunable privilege levels were a retrofit to try and keep the malloc
> tunable environment variables' behavior unchanged across security
> boundaries.  However, CVE-2023-4911 shows how tricky can be
> tunable parsing in a security-sensitive environment.
> 
> Not only parsing, but the malloc tunable essentially changes some
> semantics on setuid/setgid processes.  Although it is not a direct
> security issue, allowing users to change setuid/setgid semantics is not
> a good security practice, and requires extra code and analysis to check
> if each tunable is safe to use on all security boundaries.
> 
> It also means that security opt-in features, like aarch64 MTE, would
> need to be explicit enabled by an administrator with a wrapper script
> or with a possible future system-wide tunable setting.
> 
> Co-authored-by: Siddhesh Poyarekar  <siddhesh@sourceware.org>

I'll leave this for someone else to review since I wrote some parts of this.

Thanks,
Sid

> ---
>   elf/Makefile                       |   5 +-
>   elf/dl-tunable-types.h             |  10 --
>   elf/dl-tunables.c                  | 127 ++++-----------
>   elf/dl-tunables.list               |   9 --
>   elf/tst-env-setuid-tunables.c      |  29 +++-
>   elf/tst-tunables.c                 | 244 +++++++++++++++++++++++++++++
>   manual/README.tunables             |   9 --
>   scripts/gen-tunables.awk           |  18 +--
>   sysdeps/x86_64/64/dl-tunables.list |   1 -
>   9 files changed, 299 insertions(+), 153 deletions(-)
>   create mode 100644 elf/tst-tunables.c
> 
> diff --git a/elf/Makefile b/elf/Makefile
> index 9176cbf1e3..c824f7dab7 100644
> --- a/elf/Makefile
> +++ b/elf/Makefile
> @@ -263,7 +263,6 @@ tests-static-normal := \
>     tst-dl-iter-static \
>     tst-dst-static \
>     tst-env-setuid \
> -  tst-env-setuid-tunables \
>     tst-getauxval-static \
>     tst-linkall-static \
>     tst-single_threaded-pthread-static \
> @@ -276,10 +275,12 @@ tests-static-normal := \
>   tests-static-internal := \
>     tst-dl-printf-static \
>     tst-dl_find_object-static \
> +  tst-env-setuid-tunables \
>     tst-ptrguard1-static \
>     tst-stackguard1-static \
>     tst-tls1-static \
>     tst-tls1-static-non-pie \
> +  tst-tunables \
>     # tests-static-internal
>   
>   CRT-tst-tls1-static-non-pie := $(csu-objpfx)crt1.o
> @@ -2696,6 +2697,8 @@ $(objpfx)tst-glibc-hwcaps-mask.out: \
>   # tst-glibc-hwcaps-cache.
>   $(objpfx)tst-glibc-hwcaps-cache.out: $(objpfx)tst-glibc-hwcaps
>   
> +tst-tunables-ARGS = -- $(host-test-program-cmd)
> +
>   $(objpfx)list-tunables.out: tst-rtld-list-tunables.sh $(objpfx)ld.so
>   	$(SHELL) $< $(objpfx)ld.so '$(test-wrapper-env)' \
>   	    '$(run_program_env)' > $(objpfx)/tst-rtld-list-tunables.out
> diff --git a/elf/dl-tunable-types.h b/elf/dl-tunable-types.h
> index c88332657e..62d6d9e629 100644
> --- a/elf/dl-tunable-types.h
> +++ b/elf/dl-tunable-types.h
> @@ -64,16 +64,6 @@ struct _tunable
>     tunable_val_t val;			/* The value.  */
>     bool initialized;			/* Flag to indicate that the tunable is
>   					   initialized.  */
> -  tunable_seclevel_t security_level;	/* Specify the security level for the
> -					   tunable with respect to AT_SECURE
> -					   programs.  See description of
> -					   tunable_seclevel_t to see a
> -					   description of the values.
> -
> -					   Note that even if the tunable is
> -					   read, it may not get used by the
> -					   target module if the value is
> -					   considered unsafe.  */
>     /* Compatibility elements.  */
>     const char env_alias[TUNABLE_ALIAS_MAX]; /* The compatibility environment
>   					   variable name.  */
> diff --git a/elf/dl-tunables.c b/elf/dl-tunables.c
> index 24252af22c..a83bd2b8bc 100644
> --- a/elf/dl-tunables.c
> +++ b/elf/dl-tunables.c
> @@ -154,50 +154,51 @@ __tunable_set_val (tunable_id_t id, tunable_val_t *valp, tunable_num_t *minp,
>     do_tunable_update_val (cur, valp, minp, maxp);
>   }
>   
> -/* Parse the tunable string TUNESTR and adjust it to drop any tunables that may
> -   be unsafe for AT_SECURE processes so that it can be used as the new
> -   environment variable value for GLIBC_TUNABLES.  VALSTRING is the original
> -   environment variable string which we use to make NULL terminated values so
> -   that we don't have to allocate memory again for it.  */
> +/* Parse the tunable string VALSTRING.  VALSTRING is a duplicated values,
> +   where delimiters ':' are replaced with '\0', so string tunables are null
> +   terminated.  */
>   static void
> -parse_tunables (char *tunestr, char *valstring)
> +parse_tunables (char *valstring)
>   {
> -  if (tunestr == NULL || *tunestr == '\0')
> +  if (valstring == NULL || *valstring == '\0')
>       return;
>   
> -  char *p = tunestr;
> -  size_t off = 0;
> +  char *p = valstring;
> +  bool done = false;
>   
> -  while (true)
> +  while (!done)
>       {
>         char *name = p;
> -      size_t len = 0;
>   
>         /* First, find where the name ends.  */
> -      while (p[len] != '=' && p[len] != ':' && p[len] != '\0')
> -	len++;
> +      while (*p != '=' && *p != ':' && *p != '\0')
> +	p++;
>   
>         /* If we reach the end of the string before getting a valid name-value
>   	 pair, bail out.  */
> -      if (p[len] == '\0')
> +      if (*p == '\0')
>   	break;
>   
>         /* We did not find a valid name-value pair before encountering the
>   	 colon.  */
> -      if (p[len]== ':')
> +      if (*p == ':')
>   	{
> -	  p += len + 1;
> +	  p++;
>   	  continue;
>   	}
>   
> -      p += len + 1;
> +      /* Skip the ':' or '='.  */
> +      p++;
>   
> -      /* Take the value from the valstring since we need to NULL terminate it.  */
> -      char *value = &valstring[p - tunestr];
> -      len = 0;
> +      const char *value = p;
>   
> -      while (p[len] != ':' && p[len] != '\0')
> -	len++;
> +      while (*p != ':' && *p != '\0')
> +	p++;
> +
> +      if (*p == '\0')
> +	done = true;
> +      else
> +	*p++ = '\0';
>   
>         /* Add the tunable if it exists.  */
>         for (size_t i = 0; i < sizeof (tunable_list) / sizeof (tunable_t); i++)
> @@ -206,50 +207,11 @@ parse_tunables (char *tunestr, char *valstring)
>   
>   	  if (tunable_is_name (cur->name, name))
>   	    {
> -	      /* If we are in a secure context (AT_SECURE) then ignore the
> -		 tunable unless it is explicitly marked as secure.  Tunable
> -		 values take precedence over their envvar aliases.  We write
> -		 the tunables that are not SXID_ERASE back to TUNESTR, thus
> -		 dropping all SXID_ERASE tunables and any invalid or
> -		 unrecognized tunables.  */
> -	      if (__libc_enable_secure)
> -		{
> -		  if (cur->security_level != TUNABLE_SECLEVEL_SXID_ERASE)
> -		    {
> -		      if (off > 0)
> -			tunestr[off++] = ':';
> -
> -		      const char *n = cur->name;
> -
> -		      while (*n != '\0')
> -			tunestr[off++] = *n++;
> -
> -		      tunestr[off++] = '=';
> -
> -		      for (size_t j = 0; j < len; j++)
> -			tunestr[off++] = value[j];
> -		    }
> -
> -		  if (cur->security_level != TUNABLE_SECLEVEL_NONE)
> -		    break;
> -		}
> -
> -	      value[len] = '\0';
>   	      tunable_initialize (cur, value);
>   	      break;
>   	    }
>   	}
> -
> -      /* We reached the end while processing the tunable string.  */
> -      if (p[len] == '\0')
> -	break;
> -
> -      p += len + 1;
>       }
> -
> -  /* Terminate tunestr before we leave.  */
> -  if (__libc_enable_secure)
> -    tunestr[off] = '\0';
>   }
>   
>   /* Initialize the tunables list from the environment.  For now we only use the
> @@ -263,16 +225,16 @@ __tunables_init (char **envp)
>     size_t len = 0;
>     char **prev_envp = envp;
>   
> +  /* Ignore tunables for AT_SECURE programs.  */
> +  if (__libc_enable_secure)
> +    return;
> +
>     while ((envp = get_next_env (envp, &envname, &len, &envval,
>   			       &prev_envp)) != NULL)
>       {
>         if (tunable_is_name ("GLIBC_TUNABLES", envname))
>   	{
> -	  char *new_env = tunables_strdup (envname);
> -	  if (new_env != NULL)
> -	    parse_tunables (new_env + len + 1, envval);
> -	  /* Put in the updated envval.  */
> -	  *prev_envp = new_env;
> +	  parse_tunables (tunables_strdup (envval));
>   	  continue;
>   	}
>   
> @@ -290,39 +252,6 @@ __tunables_init (char **envp)
>   	  /* We have a match.  Initialize and move on to the next line.  */
>   	  if (tunable_is_name (name, envname))
>   	    {
> -	      /* For AT_SECURE binaries, we need to check the security settings of
> -		 the tunable and decide whether we read the value and also whether
> -		 we erase the value so that child processes don't inherit them in
> -		 the environment.  */
> -	      if (__libc_enable_secure)
> -		{
> -		  if (cur->security_level == TUNABLE_SECLEVEL_SXID_ERASE)
> -		    {
> -		      /* Erase the environment variable.  */
> -		      char **ep = prev_envp;
> -
> -		      while (*ep != NULL)
> -			{
> -			  if (tunable_is_name (name, *ep))
> -			    {
> -			      char **dp = ep;
> -
> -			      do
> -				dp[0] = dp[1];
> -			      while (*dp++);
> -			    }
> -			  else
> -			    ++ep;
> -			}
> -		      /* Reset the iterator so that we read the environment again
> -			 from the point we erased.  */
> -		      envp = prev_envp;
> -		    }
> -
> -		  if (cur->security_level != TUNABLE_SECLEVEL_NONE)
> -		    continue;
> -		}
> -
>   	      tunable_initialize (cur, envval);
>   	      break;
>   	    }
> diff --git a/elf/dl-tunables.list b/elf/dl-tunables.list
> index 695ba7192e..471895af7b 100644
> --- a/elf/dl-tunables.list
> +++ b/elf/dl-tunables.list
> @@ -21,14 +21,6 @@
>   # minval: Optional minimum acceptable value
>   # maxval: Optional maximum acceptable value
>   # env_alias: An alias environment variable
> -# security_level: Specify security level of the tunable for AT_SECURE binaries.
> -# 		  Valid values are:
> -#
> -# 	     SXID_ERASE: (default) Do not read and do not pass on to
> -# 	     child processes.
> -# 	     SXID_IGNORE: Do not read, but retain for non-AT_SECURE
> -# 	     subprocesses.
> -# 	     NONE: Read all the time.
>   
>   glibc {
>     malloc {
> @@ -158,7 +150,6 @@ glibc {
>         type: INT_32
>         minval: 0
>         maxval: 255
> -      security_level: SXID_IGNORE
>       }
>     }
>   
> diff --git a/elf/tst-env-setuid-tunables.c b/elf/tst-env-setuid-tunables.c
> index 2603007b7b..ca02dbba58 100644
> --- a/elf/tst-env-setuid-tunables.c
> +++ b/elf/tst-env-setuid-tunables.c
> @@ -15,14 +15,10 @@
>      License along with the GNU C Library; if not, see
>      <https://www.gnu.org/licenses/>.  */
>   
> -/* Verify that tunables correctly filter out unsafe tunables like
> -   glibc.malloc.check and glibc.malloc.mmap_threshold but also retain
> -   glibc.malloc.mmap_threshold in an unprivileged child.  */
> -
> -#define _LIBC 1
> -#include "config.h"
> -#undef _LIBC
> +/* Verify that GLIBC_TUNABLES is kept unchanged but no tunable is actually
> +   enabled for AT_SECURE processes.  */
>   
> +#include <dl-tunables.h>
>   #include <errno.h>
>   #include <fcntl.h>
>   #include <stdlib.h>
> @@ -40,7 +36,7 @@
>   #include <support/test-driver.h>
>   #include <support/capture_subprocess.h>
>   
> -const char *teststrings[] =
> +static const char *teststrings[] =
>   {
>     "glibc.malloc.check=2:glibc.malloc.mmap_threshold=4096",
>     "glibc.malloc.check=2:glibc.malloc.check=2:glibc.malloc.mmap_threshold=4096",
> @@ -74,6 +70,23 @@ test_child (int off)
>       ret = 0;
>     fflush (stdout);
>   
> +  /* Also check if the set tunables are effectively unchanged.  */
> +  int32_t check = TUNABLE_GET_FULL (glibc, malloc, check, int32_t, NULL);
> +  size_t mmap_threshold = TUNABLE_GET_FULL (glibc, malloc, mmap_threshold,
> +					    size_t, NULL);
> +  int32_t perturb = TUNABLE_GET_FULL (glibc, malloc, perturb, int32_t, NULL);
> +
> +  printf ("    [%d] glibc.malloc.check=%d\n", off, check);
> +  fflush (stdout);
> +  printf ("    [%d] glibc.malloc.mmap_threshold=%zu\n", off, mmap_threshold);
> +  fflush (stdout);
> +  printf ("    [%d] glibc.malloc.perturb=%d\n", off, perturb);
> +  fflush (stdout);
> +
> +  ret |= check != 0;
> +  ret |= mmap_threshold != 0;
> +  ret |= perturb != 0;
> +
>     return ret;
>   }
>   
> diff --git a/elf/tst-tunables.c b/elf/tst-tunables.c
> new file mode 100644
> index 0000000000..57cf532fc4
> --- /dev/null
> +++ b/elf/tst-tunables.c
> @@ -0,0 +1,244 @@
> +/* Check GLIBC_TUNABLES parsing.
> +   Copyright (C) 2023 Free Software Foundation, Inc.
> +   This file is part of the GNU C Library.
> +
> +   The GNU C Library is free software; you can redistribute it and/or
> +   modify it under the terms of the GNU Lesser General Public
> +   License as published by the Free Software Foundation; either
> +   version 2.1 of the License, or (at your option) any later version.
> +
> +   The GNU C Library is distributed in the hope that it will be useful,
> +   but WITHOUT ANY WARRANTY; without even the implied warranty of
> +   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
> +   Lesser General Public License for more details.
> +
> +   You should have received a copy of the GNU Lesser General Public
> +   License along with the GNU C Library; if not, see
> +   <https://www.gnu.org/licenses/>.  */
> +
> +#include <array_length.h>
> +#include <dl-tunables.h>
> +#include <getopt.h>
> +#include <intprops.h>
> +#include <stdint.h>
> +#include <stdlib.h>
> +#include <support/capture_subprocess.h>
> +#include <support/check.h>
> +
> +static int restart;
> +#define CMDLINE_OPTIONS \
> +  { "restart", no_argument, &restart, 1 },
> +
> +static const struct test_t
> +{
> +  const char *env;
> +  int32_t expected_malloc_check;
> +  size_t expected_mmap_threshold;
> +  int32_t expected_perturb;
> +} tests[] =
> +{
> +  /* Expected tunable format.  */
> +  {
> +    "glibc.malloc.check=2",
> +    2,
> +    0,
> +    0,
> +  },
> +  {
> +    "glibc.malloc.check=2:glibc.malloc.mmap_threshold=4096",
> +    2,
> +    4096,
> +    0,
> +  },
> +  /* Empty tunable are ignored.  */
> +  {
> +    "glibc.malloc.check=2::glibc.malloc.mmap_threshold=4096",
> +    2,
> +    4096,
> +    0,
> +  },
> +  /* As well empty values.  */
> +  {
> +    "glibc.malloc.check=:glibc.malloc.mmap_threshold=4096",
> +    0,
> +    4096,
> +    0,
> +  },
> +  /* Tunable are processed from left to right, so last one is the one set.  */
> +  {
> +    "glibc.malloc.check=1:glibc.malloc.check=2",
> +    2,
> +    0,
> +    0,
> +  },
> +  {
> +    "glibc.malloc.check=2:glibc.malloc.check=2:glibc.malloc.mmap_threshold=4096",
> +    2,
> +    4096,
> +    0,
> +  },
> +  {
> +    "glibc.malloc.check=2:glibc.malloc.mmap_threshold=4096:glibc.malloc.check=2",
> +    2,
> +    4096,
> +    0,
> +  },
> +  /* 0x800 is larger than tunable maxval (0xff), so the tunable is unchanged.  */
> +  {
> +    "glibc.malloc.perturb=0x800",
> +    0,
> +    0,
> +    0,
> +  },
> +  {
> +    "glibc.malloc.perturb=0x55",
> +    0,
> +    0,
> +    0x55,
> +  },
> +  /* Out of range values are just ignored.  */
> +  {
> +    "glibc.malloc.perturb=0x800:glibc.malloc.mmap_threshold=4096",
> +    0,
> +    4096,
> +    0,
> +  },
> +  /* Invalid keys are ignored.  */
> +  {
> +    ":glibc.malloc.garbage=2:glibc.malloc.check=1",
> +    1,
> +    0,
> +    0,
> +  },
> +  {
> +    "glibc.malloc.perturb=0x800:not_valid.malloc.check=2:glibc.malloc.mmap_threshold=4096",
> +    0,
> +    4096,
> +    0,
> +  },
> +  {
> +    "glibc.not_valid.check=2:glibc.malloc.mmap_threshold=4096",
> +    0,
> +    4096,
> +    0,
> +  },
> +  {
> +    "not_valid.malloc.check=2:glibc.malloc.mmap_threshold=4096",
> +    0,
> +    4096,
> +    0,
> +  },
> +  /* Invalid subkeys are ignored.  */
> +  {
> +    "glibc.malloc.garbage=2:glibc.maoc.mmap_threshold=4096:glibc.malloc.check=2",
> +    2,
> +    0,
> +    0,
> +  },
> +  {
> +    "glibc.malloc.check=4:glibc.malloc.garbage=2:glibc.maoc.mmap_threshold=4096",
> +    0,
> +    0,
> +    0,
> +  },
> +  {
> +    "not_valid.malloc.check=2",
> +    0,
> +    0,
> +    0,
> +  },
> +  {
> +    "glibc.not_valid.check=2",
> +    0,
> +    0,
> +    0,
> +  },
> +  /* An ill-formatted tunable in the for key=key=value will considere the
> +     value as 'key=value' (which can not be parsed as an integer).  */
> +  {
> +    "glibc.malloc.mmap_threshold=glibc.malloc.mmap_threshold=4096",
> +    0,
> +    0,
> +    0,
> +  },
> +  /* The ill-formatted tunable is also skipped.  */
> +  {
> +    "glibc.malloc.mmap_threshold=glibc.malloc.mmap_threshold=4096:glibc.malloc.check=2",
> +    2,
> +    0,
> +    0,
> +  },
> +  /* For an integer tunable, parse will stop on non number character.  */
> +  {
> +    "glibc.malloc.check=2=2",
> +    2,
> +    0,
> +    0,
> +  },
> +  {
> +    "glibc.malloc.check=2=2:glibc.malloc.mmap_threshold=4096",
> +    2,
> +    4096,
> +    0,
> +  }
> +};
> +
> +static int
> +handle_restart (int i)
> +{
> +  TEST_COMPARE (tests[i].expected_malloc_check,
> +		TUNABLE_GET_FULL (glibc, malloc, check, int32_t, NULL));
> +  TEST_COMPARE (tests[i].expected_mmap_threshold,
> +		TUNABLE_GET_FULL (glibc, malloc, mmap_threshold, size_t, NULL));
> +  TEST_COMPARE (tests[i].expected_perturb,
> +		TUNABLE_GET_FULL (glibc, malloc, perturb, int32_t, NULL));
> +  return 0;
> +}
> +
> +static int
> +do_test (int argc, char *argv[])
> +{
> +  /* We must have either:
> +     - One our fource parameters left if called initially:
> +       + path to ld.so         optional
> +       + "--library-path"      optional
> +       + the library path      optional
> +       + the application name
> +       + the test to check  */
> +
> +  TEST_VERIFY_EXIT (argc == 2 || argc == 5);
> +
> +  if (restart)
> +    return handle_restart (atoi (argv[1]));
> +
> +  char nteststr[INT_BUFSIZE_BOUND (int)];
> +
> +  char *spargv[10];
> +  {
> +    int i = 0;
> +    for (; i < argc - 1; i++)
> +      spargv[i] = argv[i + 1];
> +    spargv[i++] = (char *) "--direct";
> +    spargv[i++] = (char *) "--restart";
> +    spargv[i++] = nteststr;
> +    spargv[i] = NULL;
> +  }
> +
> +  for (int i = 0; i < array_length (tests); i++)
> +    {
> +      snprintf (nteststr, sizeof nteststr, "%d", i);
> +
> +      printf ("[%d] Spawned test for %s\n", i, tests[i].env);
> +      setenv ("GLIBC_TUNABLES", tests[i].env, 1);
> +      struct support_capture_subprocess result
> +	= support_capture_subprogram (spargv[0], spargv);
> +      support_capture_subprocess_check (&result, "tst-tunables", 0,
> +					sc_allow_stderr);
> +      support_capture_subprocess_free (&result);
> +    }
> +
> +  return 0;
> +}
> +
> +#define TEST_FUNCTION_ARGV do_test
> +#include <support/test-driver.c>
> diff --git a/manual/README.tunables b/manual/README.tunables
> index 605ddd78cd..72ae00dc02 100644
> --- a/manual/README.tunables
> +++ b/manual/README.tunables
> @@ -59,15 +59,6 @@ The list of allowed attributes are:
>   
>   - env_alias:		An alias environment variable
>   
> -- security_level:	Specify security level of the tunable for AT_SECURE
> -			binaries.  Valid values are:
> -
> -			SXID_ERASE: (default) Do not read and do not pass on to
> -			child processes.
> -			SXID_IGNORE: Do not read, but retain for non-AT_SECURE
> -			child processes.
> -			NONE: Read all the time.
> -
>   2. Use TUNABLE_GET/TUNABLE_SET/TUNABLE_SET_WITH_BOUNDS to get and set tunables.
>   
>   3. OPTIONAL: If tunables in a namespace are being used multiple times within a
> diff --git a/scripts/gen-tunables.awk b/scripts/gen-tunables.awk
> index d6de100df0..1e9d6b534e 100644
> --- a/scripts/gen-tunables.awk
> +++ b/scripts/gen-tunables.awk
> @@ -61,9 +61,6 @@ $1 == "}" {
>       if (!env_alias[top_ns,ns,tunable]) {
>         env_alias[top_ns,ns,tunable] = "{0}"
>       }
> -    if (!security_level[top_ns,ns,tunable]) {
> -      security_level[top_ns,ns,tunable] = "SXID_ERASE"
> -    }
>       len = length(top_ns"."ns"."tunable)
>       if (len > max_name_len)
>         max_name_len = len
> @@ -118,17 +115,6 @@ $1 == "}" {
>       if (len > max_alias_len)
>         max_alias_len = len
>     }
> -  else if (attr == "security_level") {
> -    if (val == "SXID_ERASE" || val == "SXID_IGNORE" || val == "NONE") {
> -      security_level[top_ns,ns,tunable] = val
> -    }
> -    else {
> -      printf("Line %d: Invalid value (%s) for security_level: %s, ", NR, val,
> -	     $0)
> -      print("Allowed values are 'SXID_ERASE', 'SXID_IGNORE', or 'NONE'")
> -      exit 1
> -    }
> -  }
>     else if (attr == "default") {
>       if (types[top_ns,ns,tunable] == "STRING") {
>         default_val[top_ns,ns,tunable] = sprintf(".strval = \"%s\"", val);
> @@ -177,9 +163,9 @@ END {
>       n = indices[2];
>       m = indices[3];
>       printf ("  {TUNABLE_NAME_S(%s, %s, %s)", t, n, m)
> -    printf (", {TUNABLE_TYPE_%s, %s, %s}, {%s}, false, TUNABLE_SECLEVEL_%s, %s},\n",
> +    printf (", {TUNABLE_TYPE_%s, %s, %s}, {%s}, false, %s},\n",
>   	    types[t,n,m], minvals[t,n,m], maxvals[t,n,m],
> -	    default_val[t,n,m], security_level[t,n,m], env_alias[t,n,m]);
> +	    default_val[t,n,m], env_alias[t,n,m]);
>     }
>     print "};"
>     print "#endif"
> diff --git a/sysdeps/x86_64/64/dl-tunables.list b/sysdeps/x86_64/64/dl-tunables.list
> index 0aab52e662..54a216a461 100644
> --- a/sysdeps/x86_64/64/dl-tunables.list
> +++ b/sysdeps/x86_64/64/dl-tunables.list
> @@ -23,7 +23,6 @@ glibc {
>         minval: 0
>         maxval: 1
>         env_alias: LD_PREFER_MAP_32BIT_EXEC
> -      security_level: SXID_IGNORE
>       }
>     }
>   }

^ permalink raw reply	[flat|nested] 45+ messages in thread

* Re: [PATCH v2 04/19] elf: Add all malloc tunable to unsecvars
  2023-10-17 13:05 ` [PATCH v2 04/19] elf: Add all malloc tunable to unsecvars Adhemerval Zanella
@ 2023-10-18 13:04   ` Siddhesh Poyarekar
  2023-10-27 10:38     ` Siddhesh Poyarekar
  2023-10-28  3:45   ` DJ Delorie
  1 sibling, 1 reply; 45+ messages in thread
From: Siddhesh Poyarekar @ 2023-10-18 13:04 UTC (permalink / raw)
  To: Adhemerval Zanella, libc-alpha



On 2023-10-17 09:05, Adhemerval Zanella wrote:
> Some environment variables allow alteration of allocator behavior
> across setuid boundaries, where a setuid program may ignore the
> tunable, but its non-setuid child can read it and adjust the memory
> allocator behavior accordingly.
> 
> Most library behavior tunings is limited to the current process and does
> not bleed in scope; so it is unclear how pratical this misfeature is.
> If behavior change across privilege boundaries is desirable, it would be
> better done with a wrapper program around the non-setuid child that sets
> these envvars, instead of using the setuid process as the messenger.
> 
> The patch as fixes tst-env-setuid, where it fail if any unsecvars is
> set.  It also adds a dynamic test, although it requires
> --enable-hardcoded-path-in-tests so kernel correctly sets the setuid
> bit (using the loader command directly would require to set the
> setuid bit on the loader itself, which is not a usual deployment).
> 
> Co-authored-by: Siddhesh Poyarekar <siddhesh@sourceware.org>

Likewise, I'll leave this for someone else since I am a co-author.

Thanks,
Sid

> 
> Checked on x86_64-linux-gnu.
> ---
>   elf/Makefile                |   8 +--
>   elf/tst-env-setuid-static.c |   1 +
>   elf/tst-env-setuid.c        | 128 +++++++++++++++++++++---------------
>   sysdeps/generic/unsecvars.h |   7 ++
>   4 files changed, 86 insertions(+), 58 deletions(-)
>   create mode 100644 elf/tst-env-setuid-static.c
> 
> diff --git a/elf/Makefile b/elf/Makefile
> index c824f7dab7..f1cd6e13fa 100644
> --- a/elf/Makefile
> +++ b/elf/Makefile
> @@ -262,7 +262,7 @@ tests-static-normal := \
>     tst-array5-static \
>     tst-dl-iter-static \
>     tst-dst-static \
> -  tst-env-setuid \
> +  tst-env-setuid-static \
>     tst-getauxval-static \
>     tst-linkall-static \
>     tst-single_threaded-pthread-static \
> @@ -305,6 +305,7 @@ tests := \
>     tst-array5 \
>     tst-auxv \
>     tst-dl-hash \
> +  tst-env-setuid \
>     tst-leaks1 \
>     tst-stringtable \
>     tst-tls9 \
> @@ -2467,9 +2468,6 @@ $(objpfx)tst-nodelete-dlclose: $(objpfx)tst-nodelete-dlclose-dso.so
>   $(objpfx)tst-nodelete-dlclose.out: $(objpfx)tst-nodelete-dlclose-dso.so \
>   				   $(objpfx)tst-nodelete-dlclose-plugin.so
>   
> -tst-env-setuid-ENV = MALLOC_CHECK_=2 MALLOC_MMAP_THRESHOLD_=4096 \
> -		     LD_HWCAP_MASK=0x1
> -
>   $(objpfx)tst-debug1.out: $(objpfx)tst-debug1mod1.so
>   
>   $(objpfx)tst-debug1mod1.so: $(objpfx)testobj1.so
> @@ -3021,3 +3019,5 @@ LDFLAGS-tst-dlclose-lazy-mod1.so = -Wl,-z,lazy,--no-as-needed
>   $(objpfx)tst-dlclose-lazy-mod1.so: $(objpfx)tst-dlclose-lazy-mod2.so
>   $(objpfx)tst-dlclose-lazy.out: \
>     $(objpfx)tst-dlclose-lazy-mod1.so $(objpfx)tst-dlclose-lazy-mod2.so
> +
> +tst-env-setuid-ARGS = -- $(host-test-program-cmd)
> diff --git a/elf/tst-env-setuid-static.c b/elf/tst-env-setuid-static.c
> new file mode 100644
> index 0000000000..0d88ae88b9
> --- /dev/null
> +++ b/elf/tst-env-setuid-static.c
> @@ -0,0 +1 @@
> +#include "tst-env-setuid.c"
> diff --git a/elf/tst-env-setuid.c b/elf/tst-env-setuid.c
> index 032ab44be2..ba295a6a14 100644
> --- a/elf/tst-env-setuid.c
> +++ b/elf/tst-env-setuid.c
> @@ -15,18 +15,14 @@
>      License along with the GNU C Library; if not, see
>      <https://www.gnu.org/licenses/>.  */
>   
> -/* Verify that tunables correctly filter out unsafe environment variables like
> -   MALLOC_CHECK_ and MALLOC_MMAP_THRESHOLD_ but also retain
> -   MALLOC_MMAP_THRESHOLD_ in an unprivileged child.  */
> +/* Verify that correctly filter out unsafe environment variables defined
> +   in unsecvars.h.  */
>   
> -#include <errno.h>
> -#include <fcntl.h>
> -#include <stdlib.h>
> -#include <stdint.h>
> +#include <array_length.h>
> +#include <gnu/lib-names.h>
>   #include <stdio.h>
> +#include <stdlib.h>
>   #include <string.h>
> -#include <sys/stat.h>
> -#include <sys/wait.h>
>   #include <unistd.h>
>   
>   #include <support/check.h>
> @@ -36,61 +32,72 @@
>   
>   static char SETGID_CHILD[] = "setgid-child";
>   
> -#ifndef test_child
> -static int
> -test_child (void)
> -{
> -  if (getenv ("MALLOC_CHECK_") != NULL)
> -    {
> -      printf ("MALLOC_CHECK_ is still set\n");
> -      return 1;
> -    }
> -
> -  if (getenv ("MALLOC_MMAP_THRESHOLD_") == NULL)
> -    {
> -      printf ("MALLOC_MMAP_THRESHOLD_ lost\n");
> -      return 1;
> -    }
> +#define FILTERED_VALUE   "some-filtered-value"
> +#define UNFILTERED_VALUE "some-unfiltered-value"
>   
> -  if (getenv ("LD_HWCAP_MASK") != NULL)
> -    {
> -      printf ("LD_HWCAP_MASK still set\n");
> -      return 1;
> -    }
> +struct envvar_t
> +{
> +  const char *env;
> +  const char *value;
> +};
>   
> -  return 0;
> -}
> -#endif
> +/* That is not an extensible list of all filtered out environment
> +   variables.  */
> +static const struct envvar_t filtered_envvars[] =
> +{
> +  { "GLIBC_TUNABLES",          FILTERED_VALUE },
> +  { "LD_AUDIT",                FILTERED_VALUE },
> +  { "LD_HWCAP_MASK",           FILTERED_VALUE },
> +  { "LD_LIBRARY_PATH",         FILTERED_VALUE },
> +  { "LD_PRELOAD",              FILTERED_VALUE },
> +  { "LD_PROFILE",              FILTERED_VALUE },
> +  { "MALLOC_ARENA_MAX",        FILTERED_VALUE },
> +  { "MALLOC_PERTURB_",         FILTERED_VALUE },
> +  { "MALLOC_TRACE",            FILTERED_VALUE },
> +  { "MALLOC_TRIM_THRESHOLD_",  FILTERED_VALUE },
> +  { "RES_OPTIONS",             FILTERED_VALUE },
> +};
> +
> +static const struct envvar_t unfiltered_envvars[] =
> +{
> +  { "LD_BIND_NOW",             "0" },
> +  { "LD_BIND_NOT",             "1" },
> +  /* Non longer supported option.  */
> +  { "LD_ASSUME_KERNEL",        UNFILTERED_VALUE },
> +};
>   
> -#ifndef test_parent
>   static int
> -test_parent (void)
> +test_child (void)
>   {
> -  if (getenv ("MALLOC_CHECK_") == NULL)
> -    {
> -      printf ("MALLOC_CHECK_ lost\n");
> -      return 1;
> -    }
> +  int ret = 0;
>   
> -  if (getenv ("MALLOC_MMAP_THRESHOLD_") == NULL)
> +  for (const struct envvar_t *e = filtered_envvars;
> +       e != array_end (filtered_envvars);
> +       e++)
>       {
> -      printf ("MALLOC_MMAP_THRESHOLD_ lost\n");
> -      return 1;
> +      const char *env = getenv (e->env);
> +      ret |= env != NULL;
>       }
>   
> -  if (getenv ("LD_HWCAP_MASK") == NULL)
> +  for (const struct envvar_t *e = unfiltered_envvars;
> +       e != array_end (unfiltered_envvars);
> +       e++)
>       {
> -      printf ("LD_HWCAP_MASK lost\n");
> -      return 1;
> +      const char *env = getenv (e->env);
> +      ret |= !(env != NULL && strcmp (env, e->value) == 0);
>       }
>   
> -  return 0;
> +  return ret;
>   }
> -#endif
>   
>   static int
>   do_test (int argc, char **argv)
>   {
> +  /* For dynamic loader, the test requires --enable-hardcoded-path-in-tests so
> +     the kernel sets the AT_SECURE on process initialization.  */
> +  if (argc >= 2 && strstr (argv[1], LD_SO) != 0)
> +    FAIL_UNSUPPORTED ("dynamic test requires --enable-hardcoded-path-in-tests");
> +
>     /* Setgid child process.  */
>     if (argc == 2 && strcmp (argv[1], SETGID_CHILD) == 0)
>       {
> @@ -104,20 +111,33 @@ do_test (int argc, char **argv)
>         if (ret != 0)
>   	exit (1);
>   
> -      exit (EXIT_SUCCESS);
> +      /* Special return code to make sure that the child executed all the way
> +	 through.  */
> +      exit (42);
>       }
>     else
>       {
> -      if (test_parent () != 0)
> -	exit (1);
> +      for (const struct envvar_t *e = filtered_envvars;
> +	   e != array_end (filtered_envvars);
> +	   e++)
> +	setenv (e->env, e->value, 1);
> +
> +      for (const struct envvar_t *e = unfiltered_envvars;
> +	   e != array_end (unfiltered_envvars);
> +	   e++)
> +	setenv (e->env, e->value, 1);
>   
>         int status = support_capture_subprogram_self_sgid (SETGID_CHILD);
>   
>         if (WEXITSTATUS (status) == EXIT_UNSUPPORTED)
> -	return EXIT_UNSUPPORTED;
> -
> -      if (!WIFEXITED (status))
> -	FAIL_EXIT1 ("Unexpected exit status %d from child process\n", status);
> +	exit (EXIT_UNSUPPORTED);
> +
> +      if (WEXITSTATUS (status) != 42)
> +	{
> +	  printf ("    child failed with status %d\n",
> +		  WEXITSTATUS (status));
> +	  support_record_failure ();
> +	}
>   
>         return 0;
>       }
> diff --git a/sysdeps/generic/unsecvars.h b/sysdeps/generic/unsecvars.h
> index 81397fb90b..f7ebed60e5 100644
> --- a/sysdeps/generic/unsecvars.h
> +++ b/sysdeps/generic/unsecvars.h
> @@ -18,7 +18,14 @@
>     "LD_SHOW_AUXV\0"							      \
>     "LOCALDOMAIN\0"							      \
>     "LOCPATH\0"								      \
> +  "MALLOC_ARENA_MAX\0"							      \
> +  "MALLOC_ARENA_TEST\0"							      \
> +  "MALLOC_MMAP_MAX_\0"							      \
> +  "MALLOC_MMAP_THRESHOLD_\0"						      \
> +  "MALLOC_PERTURB_\0"							      \
> +  "MALLOC_TOP_PAD_\0"							      \
>     "MALLOC_TRACE\0"							      \
> +  "MALLOC_TRIM_THRESHOLD_\0"						      \
>     "NIS_PATH\0"								      \
>     "NLSPATH\0"								      \
>     "RESOLV_HOST_CONF\0"							      \

^ permalink raw reply	[flat|nested] 45+ messages in thread

* Re: [PATCH v2 05/19] elf: Do not process invalid tunable format
  2023-10-17 13:05 ` [PATCH v2 05/19] elf: Do not process invalid tunable format Adhemerval Zanella
@ 2023-10-18 13:42   ` Siddhesh Poyarekar
  0 siblings, 0 replies; 45+ messages in thread
From: Siddhesh Poyarekar @ 2023-10-18 13:42 UTC (permalink / raw)
  To: Adhemerval Zanella, libc-alpha



On 2023-10-17 09:05, Adhemerval Zanella wrote:
> Tunable definitions with more than one '=' on are parsed and enabled,
> and any subsequent '=' are ignored.  It means that tunables in the form
> 'tunable=tunable=value' or 'tunable=value=value' are handled as
> 'tunable=value'.  These inputs are likely user input errors, which
> should not be accepted.
> 
> Checked on x86_64-linux-gnu.
> ---
>   elf/dl-tunables.c  |  6 ++++--
>   elf/tst-tunables.c | 22 +++++++++++++++++-----
>   2 files changed, 21 insertions(+), 7 deletions(-)
> 
> diff --git a/elf/dl-tunables.c b/elf/dl-tunables.c
> index a83bd2b8bc..59bee61124 100644
> --- a/elf/dl-tunables.c
> +++ b/elf/dl-tunables.c
> @@ -192,10 +192,12 @@ parse_tunables (char *valstring)
>   
>         const char *value = p;
>   
> -      while (*p != ':' && *p != '\0')
> +      while (*p != '=' && *p != ':' && *p != '\0')
>   	p++;
>   
> -      if (*p == '\0')
> +      if (*p == '=')
> +	break;
> +      else if (*p == '\0')

So we're not going to attempt to parse any tunables after the malformed 
one.  A bit harsh, but probably safer.  OK.

Reviewed-by: Siddhesh Poyarekar <siddhesh@sourceware.org>

>   	done = true;
>         else
>   	*p++ = '\0';
> diff --git a/elf/tst-tunables.c b/elf/tst-tunables.c
> index 57cf532fc4..03039b5260 100644
> --- a/elf/tst-tunables.c
> +++ b/elf/tst-tunables.c
> @@ -161,24 +161,36 @@ static const struct test_t
>       0,
>       0,
>     },
> -  /* The ill-formatted tunable is also skipped.  */
> +  /* If there is a ill-formatted key=value, everything after is also ignored.  */
>     {
>       "glibc.malloc.mmap_threshold=glibc.malloc.mmap_threshold=4096:glibc.malloc.check=2",
> -    2,
> +    0,
>       0,
>       0,
>     },
> -  /* For an integer tunable, parse will stop on non number character.  */
>     {
>       "glibc.malloc.check=2=2",
> -    2,
> +    0,
>       0,
>       0,
>     },
>     {
>       "glibc.malloc.check=2=2:glibc.malloc.mmap_threshold=4096",
> +    0,
> +    0,
> +    0,
> +  },
> +  {
> +    "glibc.malloc.check=2=2:glibc.malloc.check=2",
> +    0,
> +    0,
> +    0,
> +  },
> +  /* Valid tunables set before ill-formatted ones are set.  */
> +  {
> +    "glibc.malloc.check=2:glibc.malloc.mmap_threshold=4096=4096",
>       2,
> -    4096,
> +    0,
>       0,
>     }
>   };

^ permalink raw reply	[flat|nested] 45+ messages in thread

* Re: [PATCH v2 01/19] elf: Remove /etc/suid-debug support
  2023-10-18 12:31   ` Siddhesh Poyarekar
@ 2023-10-18 18:27     ` Adhemerval Zanella Netto
  0 siblings, 0 replies; 45+ messages in thread
From: Adhemerval Zanella Netto @ 2023-10-18 18:27 UTC (permalink / raw)
  To: Siddhesh Poyarekar, libc-alpha



On 18/10/23 09:31, Siddhesh Poyarekar wrote:
> On 2023-10-17 09:05, Adhemerval Zanella wrote:
>> Since malloc debug support moved to a different library
>> (libc_malloc_debug.so), the glibc.malloc.check requires preloading the
>> debug library to enable it.  It means that suid-debug support has not
>> been working since 2.34.
>>
>> To restore its support, it would require to add additional information
>> and parsing to where to find libc_malloc_debug.so.
> 
> Could you elaborate slightly in the commit message, like so:
> 
>   To restore its support, we would require to add additional information
>   and parsing to locate libc_malloc_debug.so, which is not worth the
>   additional risk it might pose.

Ack.

> 
> LGTM.
> 
> Reviewed-by: Siddhesh Poyarekar <siddhesh@sourceware.org>

^ permalink raw reply	[flat|nested] 45+ messages in thread

* Re: [PATCH v2 06/19] elf: Do not parse ill-formatted strings
  2023-10-17 13:05 ` [PATCH v2 06/19] elf: Do not parse ill-formatted strings Adhemerval Zanella
@ 2023-10-27 10:25   ` Siddhesh Poyarekar
  2023-10-27 12:51     ` Adhemerval Zanella Netto
  0 siblings, 1 reply; 45+ messages in thread
From: Siddhesh Poyarekar @ 2023-10-27 10:25 UTC (permalink / raw)
  To: Adhemerval Zanella, libc-alpha



On 2023-10-17 09:05, Adhemerval Zanella wrote:
> Instead of ignoring ill-formatted tunable strings, first, check all the
> tunable definitions are correct and then set each tunable value. It
> means that partially invalid strings, like "key1=value1:key2=key2=value'
> or 'key1=value':key2=value2=value2' do not enable 'key1=value1'. It
> avoids possible user-defined errors in tunable definitions.
> 
> Checked on x86_64-linux-gnu.
> ---

Harsher than 5/19, but fair I guess.  Please send v3 with a tiny nit 
fixup I've mentioned below.

Thanks,
Sid

>   elf/dl-tunables.c  | 50 +++++++++++++++++++++++++++++++++++-----------
>   elf/tst-tunables.c | 13 ++++++++----
>   2 files changed, 47 insertions(+), 16 deletions(-)
> 
> diff --git a/elf/dl-tunables.c b/elf/dl-tunables.c
> index 59bee61124..5d4b8c5bc0 100644
> --- a/elf/dl-tunables.c
> +++ b/elf/dl-tunables.c
> @@ -154,17 +154,29 @@ __tunable_set_val (tunable_id_t id, tunable_val_t *valp, tunable_num_t *minp,
>     do_tunable_update_val (cur, valp, minp, maxp);
>   }
>   
> -/* Parse the tunable string VALSTRING.  VALSTRING is a duplicated values,
> -   where delimiters ':' are replaced with '\0', so string tunables are null
> -   terminated.  */
> -static void
> -parse_tunables (char *valstring)
> +struct tunable_toset_t
> +{
> +  tunable_t *t;
> +  const char *value;
> +};
> +
> +enum { tunables_list_size = array_length (tunable_list) };
> +
> +/* Parse the tunable string VALSTRING and set TUNABLES with the found tunables
> +   and their respectibles values.  VALSTRING is a duplicated values,  where
> +   delimiters ':' are replaced with '\0', so string tunables are null
> +   terminated.
> +   Return the number of tunables found (including 0 if the string is empty)
> +   or -1 if for a ill-formatted definition.  */
> +static int
> +parse_tunables_string (char *valstring, struct tunable_toset_t *tunables)
>   {
>     if (valstring == NULL || *valstring == '\0')
> -    return;
> +    return 0;
>   
>     char *p = valstring;
>     bool done = false;
> +  int ntunables = 0;
>   
>     while (!done)
>       {
> @@ -177,7 +189,7 @@ parse_tunables (char *valstring)
>         /* If we reach the end of the string before getting a valid name-value
>   	 pair, bail out.  */
>         if (*p == '\0')
> -	break;
> +	return -1;
>   
>         /* We did not find a valid name-value pair before encountering the
>   	 colon.  */
> @@ -190,30 +202,44 @@ parse_tunables (char *valstring)
>         /* Skip the ':' or '='.  */
>         p++;
>   
> -      const char *value = p;
> +      char *value = p;
>   
>         while (*p != '=' && *p != ':' && *p != '\0')
>   	p++;
>   
>         if (*p == '=')
> -	break;
> +	return -1;
>         else if (*p == '\0')
>   	done = true;
>         else
>   	*p++ = '\0';
>   
>         /* Add the tunable if it exists.  */
> -      for (size_t i = 0; i < sizeof (tunable_list) / sizeof (tunable_t); i++)
> +      for (size_t i = 0; i < tunables_list_size; i++)
>   	{
>   	  tunable_t *cur = &tunable_list[i];
>   
>   	  if (tunable_is_name (cur->name, name))
>   	    {
> -	      tunable_initialize (cur, value);
> +	      tunables[ntunables++] = (struct tunable_toset_t) { cur, value };
>   	      break;
>   	    }
>   	}
>       }
> +
> +  return ntunables;
> +}
> +
> +static void
> +parse_tunables (char *valstring)
> +{
> +  struct tunable_toset_t tunables[tunables_list_size];
> +  int ntunables = parse_tunables_string (valstring, tunables);
> +  if (ntunables == -1)
> +    return;

You don't actually need this; the for loop below will return without 
doing anything if ntunables == -1.

> +
> +  for (int i = 0; i < ntunables; i++)
> +    tunable_initialize (tunables[i].t, tunables[i].value);
>   }
>   
>   /* Initialize the tunables list from the environment.  For now we only use the
> @@ -240,7 +266,7 @@ __tunables_init (char **envp)
>   	  continue;
>   	}
>   
> -      for (int i = 0; i < sizeof (tunable_list) / sizeof (tunable_t); i++)
> +      for (int i = 0; i < tunables_list_size; i++)
>   	{
>   	  tunable_t *cur = &tunable_list[i];
>   
> diff --git a/elf/tst-tunables.c b/elf/tst-tunables.c
> index 03039b5260..e124fa4c6d 100644
> --- a/elf/tst-tunables.c
> +++ b/elf/tst-tunables.c
> @@ -161,7 +161,7 @@ static const struct test_t
>       0,
>       0,
>     },
> -  /* If there is a ill-formatted key=value, everything after is also ignored.  */
> +  /* Ill-formatted tunables string is not parsed.  */
>     {
>       "glibc.malloc.mmap_threshold=glibc.malloc.mmap_threshold=4096:glibc.malloc.check=2",
>       0,
> @@ -186,13 +186,18 @@ static const struct test_t
>       0,
>       0,
>     },
> -  /* Valid tunables set before ill-formatted ones are set.  */
>     {
>       "glibc.malloc.check=2:glibc.malloc.mmap_threshold=4096=4096",
> -    2,
>       0,
>       0,
> -  }
> +    0,
> +  },
> +  {
> +    "glibc.malloc.check=2:glibc.malloc.mmap_threshold=4096=4096",
> +    0,
> +    0,
> +    0,
> +  },
>   };
>   
>   static int

^ permalink raw reply	[flat|nested] 45+ messages in thread

* Re: [PATCH v2 07/19] elf: Fix _dl_debug_vdprintf to work before self-relocation
  2023-10-17 13:05 ` [PATCH v2 07/19] elf: Fix _dl_debug_vdprintf to work before self-relocation Adhemerval Zanella
@ 2023-10-27 10:27   ` Siddhesh Poyarekar
  0 siblings, 0 replies; 45+ messages in thread
From: Siddhesh Poyarekar @ 2023-10-27 10:27 UTC (permalink / raw)
  To: Adhemerval Zanella, libc-alpha

On 2023-10-17 09:05, Adhemerval Zanella wrote:
> The strlen might trigger and invalid GOT entry if it used before
> the process is self-relocated (for instance on dl-tunables if any
> error occurs).
> 
> Checked on x86_64-linux-gnu.
> ---

LGTM.

Reviewed-by: Siddhesh Poyarekar <siddhesh@sourceware.org>

>   elf/dl-printf.c       | 16 ++++++++++++++--
>   stdio-common/Makefile |  5 +++++
>   stdio-common/_itoa.c  |  5 +++++
>   3 files changed, 24 insertions(+), 2 deletions(-)
> 
> diff --git a/elf/dl-printf.c b/elf/dl-printf.c
> index 6efb4c019a..5e93208535 100644
> --- a/elf/dl-printf.c
> +++ b/elf/dl-printf.c
> @@ -17,6 +17,10 @@
>      License along with the GNU C Library; if not, see
>      <https://www.gnu.org/licenses/>.  */
>   
> +#include <string.h>
> +#if BUILD_PIE_DEFAULT
> +# pragma GCC visibility push(hidden)
> +#endif
>   #include <_itoa.h>
>   #include <assert.h>
>   #include <dl-writev.h>
> @@ -25,11 +29,19 @@
>   #include <stdarg.h>
>   #include <stdint.h>
>   #include <stdlib.h>
> -#include <string.h>
>   #include <sys/uio.h>
>   #include <unistd.h>
>   #include <intprops.h>
>   
> +/* The function might be called before the process is self-relocated.  */
> +static size_t
> +_dl_debug_strlen (const char *s)
> +{
> +  const char *p = s;
> +  for (; *s != '\0'; s++);
> +  return s - p;
> +}
> +
>   /* Bare-bones printf implementation.  This function only knows about
>      the formats and flags needed and can handle only up to 64 stripes in
>      the output.  */
> @@ -193,7 +205,7 @@ _dl_debug_vdprintf (int fd, int tag_p, const char *fmt, va_list arg)
>   	    case 's':
>   	      /* Get the string argument.  */
>   	      iov[niov].iov_base = va_arg (arg, char *);
> -	      iov[niov].iov_len = strlen (iov[niov].iov_base);
> +	      iov[niov].iov_len = _dl_debug_strlen (iov[niov].iov_base);
>   	      if (prec != -1)
>   		iov[niov].iov_len = MIN ((size_t) prec, iov[niov].iov_len);
>   	      ++niov;
> diff --git a/stdio-common/Makefile b/stdio-common/Makefile
> index bacb795fed..e88a9cea29 100644
> --- a/stdio-common/Makefile
> +++ b/stdio-common/Makefile
> @@ -460,6 +460,11 @@ CFLAGS-isoc23_scanf.c += -fexceptions
>   
>   CFLAGS-dprintf.c += $(config-cflags-wno-ignored-attributes)
>   
> +# Called during static library initialization, so turn stack-protection
> +# off for non-shared builds.
> +CFLAGS-_itoa.o = $(no-stack-protector)
> +CFLAGS-_itoa.op = $(no-stack-protector)
> +
>   # scanf18.c and scanf19.c test a deprecated extension which is no
>   # longer visible under most conformance levels; see the source files
>   # for more detail.
> diff --git a/stdio-common/_itoa.c b/stdio-common/_itoa.c
> index 3037b0f529..48f2903ecb 100644
> --- a/stdio-common/_itoa.c
> +++ b/stdio-common/_itoa.c
> @@ -16,6 +16,11 @@
>      License along with the GNU C Library; if not, see
>      <https://www.gnu.org/licenses/>.  */
>   
> +/* Mark symbols hidden in static PIE for early self relocation to work.
> +   Note: string.h may have ifuncs which cannot be hidden on i686.  */
> +#if BUILD_PIE_DEFAULT
> +# pragma GCC visibility push(hidden)
> +#endif
>   #include <gmp-mparam.h>
>   #include <gmp.h>
>   #include <limits.h>

^ permalink raw reply	[flat|nested] 45+ messages in thread

* Re: [PATCH v2 08/19] elf: Emit warning if tunable is ill-formatted
  2023-10-17 13:05 ` [PATCH v2 08/19] elf: Emit warning if tunable is ill-formatted Adhemerval Zanella
@ 2023-10-27 10:28   ` Siddhesh Poyarekar
  2023-10-27 12:51     ` Adhemerval Zanella Netto
  0 siblings, 1 reply; 45+ messages in thread
From: Siddhesh Poyarekar @ 2023-10-27 10:28 UTC (permalink / raw)
  To: Adhemerval Zanella, libc-alpha



On 2023-10-17 09:05, Adhemerval Zanella wrote:
> So caller knows that the tunable will be ignored.
> 
> Checked on x86_64-linux-gnu.
> ---
>   elf/dl-tunables.c | 6 +++++-
>   1 file changed, 5 insertions(+), 1 deletion(-)
> 
> diff --git a/elf/dl-tunables.c b/elf/dl-tunables.c
> index 5d4b8c5bc0..b39c14694c 100644
> --- a/elf/dl-tunables.c
> +++ b/elf/dl-tunables.c
> @@ -236,7 +236,11 @@ parse_tunables (char *valstring)
>     struct tunable_toset_t tunables[tunables_list_size];
>     int ntunables = parse_tunables_string (valstring, tunables);
>     if (ntunables == -1)
> -    return;
> +    {
> +      _dl_error_printf (
> +        "ERROR: ld.so: invalid GLIBC_TUNABLES `%s': ignored.\n", valstring);

If it's a warning, then we should say WARNING here.

> +      return;
> +    }
>   
>     for (int i = 0; i < ntunables; i++)
>       tunable_initialize (tunables[i].t, tunables[i].value);

^ permalink raw reply	[flat|nested] 45+ messages in thread

* Re: [PATCH v2 09/19] x86: Use dl-symbol-redir-ifunc.h on cpu-tunables
  2023-10-17 13:05 ` [PATCH v2 09/19] x86: Use dl-symbol-redir-ifunc.h on cpu-tunables Adhemerval Zanella
@ 2023-10-27 10:32   ` Siddhesh Poyarekar
  0 siblings, 0 replies; 45+ messages in thread
From: Siddhesh Poyarekar @ 2023-10-27 10:32 UTC (permalink / raw)
  To: Adhemerval Zanella, libc-alpha; +Cc: Noah Goldstein



On 2023-10-17 09:05, Adhemerval Zanella wrote:
> The dl-symbol-redir-ifunc.h redirects compiler-generated libcalls to
> arch-specific memory implementations to avoid ifun calls where it is not

ifunc

> yet possible. The memcmp-isa-default-impl.h aims to fix the same issue
> by calling the specific memset implementation directly.
> 
> Using the memcmp symbol directly allows the compile to inline the memset

compiler

> calls (especially because _dl_tunable_set_hwcaps uses constants values),
> generating better code.
> 
> For i386, _dl_writev with PIE requires to use the old 'int $0x80'
> syscall mode because the calling the TLS register (gs) is not yet
> initialized.
> 
> Checked on x86_64-linux-gnu.
> 
> Reviewed-by: Noah Goldstein <goldstein.w.n@gmail.com>

Reviewed-by: Siddhesh Poyarekar <siddhesh@sourceware.org>

> ---
>   .../i686/multiarch/dl-symbol-redir-ifunc.h    |  5 +++
>   .../sysv/linux/i386/dl-writev.h}              | 18 ++++-----
>   sysdeps/x86/cpu-tunables.c                    | 39 ++++++-------------
>   .../x86_64/multiarch/dl-symbol-redir-ifunc.h  | 15 +++++++
>   4 files changed, 39 insertions(+), 38 deletions(-)
>   rename sysdeps/{x86_64/memcmp-isa-default-impl.h => unix/sysv/linux/i386/dl-writev.h} (62%)
> 
> diff --git a/sysdeps/i386/i686/multiarch/dl-symbol-redir-ifunc.h b/sysdeps/i386/i686/multiarch/dl-symbol-redir-ifunc.h
> index dee69d19db..220c586bd2 100644
> --- a/sysdeps/i386/i686/multiarch/dl-symbol-redir-ifunc.h
> +++ b/sysdeps/i386/i686/multiarch/dl-symbol-redir-ifunc.h
> @@ -19,6 +19,11 @@
>   #ifndef _DL_IFUNC_GENERIC_H
>   #define _DL_IFUNC_GENERIC_H
>   
> +#ifndef SHARED
> +
>   asm ("memset = __memset_ia32");
> +asm ("memcmp = __memcmp_ia32");
> +
> +#endif /* SHARED */
>   
>   #endif
> diff --git a/sysdeps/x86_64/memcmp-isa-default-impl.h b/sysdeps/unix/sysv/linux/i386/dl-writev.h
> similarity index 62%
> rename from sysdeps/x86_64/memcmp-isa-default-impl.h
> rename to sysdeps/unix/sysv/linux/i386/dl-writev.h
> index 0962e83c3d..624d0e46b0 100644
> --- a/sysdeps/x86_64/memcmp-isa-default-impl.h
> +++ b/sysdeps/unix/sysv/linux/i386/dl-writev.h
> @@ -1,5 +1,5 @@
> -/* Set default memcmp impl based on ISA level.
> -   Copyright (C) 2022-2023 Free Software Foundation, Inc.
> +/* Message-writing for the dynamic linker.  Linux/i386 version.
> +   Copyright (C) 2013-2023 Free Software Foundation, Inc.
>      This file is part of the GNU C Library.
>   
>      The GNU C Library is free software; you can redistribute it and/or
> @@ -16,13 +16,9 @@
>      License along with the GNU C Library; if not, see
>      <https://www.gnu.org/licenses/>.  */
>   
> -#include <isa-level.h>
> -#if MINIMUM_X86_ISA_LEVEL == 1 || MINIMUM_X86_ISA_LEVEL == 2
> -# define DEFAULT_MEMCMP	__memcmp_sse2
> -#elif MINIMUM_X86_ISA_LEVEL == 3
> -# define DEFAULT_MEMCMP	__memcmp_avx2_movbe
> -#elif MINIMUM_X86_ISA_LEVEL == 4
> -# define DEFAULT_MEMCMP	__memcmp_evex_movbe
> -#else
> -# error "Unknown default memcmp implementation"
> +#if BUILD_PIE_DEFAULT
> +/* Can't use "call *%gs:SYSINFO_OFFSET" during startup in static PIE.  */
> +# define I386_USE_SYSENTER 0
>   #endif
> +
> +#include <sysdeps/unix/sysv/linux/dl-writev.h>
> diff --git a/sysdeps/x86/cpu-tunables.c b/sysdeps/x86/cpu-tunables.c
> index 0d4f328585..5697885226 100644
> --- a/sysdeps/x86/cpu-tunables.c
> +++ b/sysdeps/x86/cpu-tunables.c
> @@ -24,24 +24,11 @@
>   #include <string.h>
>   #include <cpu-features.h>
>   #include <ldsodefs.h>
> -
> -/* We can't use IFUNC memcmp nor strlen in init_cpu_features from libc.a
> -   since IFUNC must be set up by init_cpu_features.  */
> -#if defined USE_MULTIARCH && !defined SHARED
> -# ifdef __x86_64__
> -/* DEFAULT_MEMCMP by sysdeps/x86_64/memcmp-isa-default-impl.h.  */
> -#  include <sysdeps/x86_64/memcmp-isa-default-impl.h>
> -# else
> -#  define DEFAULT_MEMCMP	__memcmp_ia32
> -# endif
> -extern __typeof (memcmp) DEFAULT_MEMCMP;
> -#else
> -# define DEFAULT_MEMCMP	memcmp
> -#endif
> +#include <dl-symbol-redir-ifunc.h>
>   
>   #define CHECK_GLIBC_IFUNC_CPU_OFF(f, cpu_features, name, len)		\
>     _Static_assert (sizeof (#name) - 1 == len, #name " != " #len);	\
> -  if (!DEFAULT_MEMCMP (f, #name, len))					\
> +  if (memcmp (f, #name, len) == 0)					\
>       {									\
>         CPU_FEATURE_UNSET (cpu_features, name)				\
>         break;								\
> @@ -51,7 +38,7 @@ extern __typeof (memcmp) DEFAULT_MEMCMP;
>      which isn't available.  */
>   #define CHECK_GLIBC_IFUNC_PREFERRED_OFF(f, cpu_features, name, len)	\
>     _Static_assert (sizeof (#name) - 1 == len, #name " != " #len);	\
> -  if (!DEFAULT_MEMCMP (f, #name, len))					\
> +  if (memcmp (f, #name, len) == 0)					\
>       {									\
>         cpu_features->preferred[index_arch_##name]			\
>   	&= ~bit_arch_##name;						\
> @@ -62,7 +49,7 @@ extern __typeof (memcmp) DEFAULT_MEMCMP;
>   #define CHECK_GLIBC_IFUNC_PREFERRED_BOTH(f, cpu_features, name,	\
>   					  disable, len)			\
>     _Static_assert (sizeof (#name) - 1 == len, #name " != " #len);	\
> -  if (!DEFAULT_MEMCMP (f, #name, len))					\
> +  if (memcmp (f, #name, len) == 0)					\
>       {									\
>         if (disable)							\
>   	cpu_features->preferred[index_arch_##name] &= ~bit_arch_##name;	\
> @@ -76,7 +63,7 @@ extern __typeof (memcmp) DEFAULT_MEMCMP;
>   #define CHECK_GLIBC_IFUNC_PREFERRED_NEED_BOTH(f, cpu_features, name,	\
>   					       need, disable, len)	\
>     _Static_assert (sizeof (#name) - 1 == len, #name " != " #len);	\
> -  if (!DEFAULT_MEMCMP (f, #name, len))					\
> +  if (memcmp (f, #name, len) == 0)					\
>       {									\
>         if (disable)							\
>   	cpu_features->preferred[index_arch_##name] &= ~bit_arch_##name;	\
> @@ -177,7 +164,7 @@ TUNABLE_CALLBACK (set_hwcaps) (tunable_val_t *valp)
>   	      CHECK_GLIBC_IFUNC_CPU_OFF (n, cpu_features, POPCNT, 6);
>   	      CHECK_GLIBC_IFUNC_CPU_OFF (n, cpu_features, SSE4_1, 6);
>   	      CHECK_GLIBC_IFUNC_CPU_OFF (n, cpu_features, SSE4_2, 6);
> -	      if (!DEFAULT_MEMCMP (n, "XSAVEC", 6))
> +	      if (memcmp (n, "XSAVEC", 6) == 0)
>   		{
>   		  /* Update xsave_state_size to XSAVE state size.  */
>   		  cpu_features->xsave_state_size
> @@ -290,12 +277,11 @@ attribute_hidden
>   void
>   TUNABLE_CALLBACK (set_x86_ibt) (tunable_val_t *valp)
>   {
> -  if (DEFAULT_MEMCMP (valp->strval, "on", sizeof ("on")) == 0)
> +  if (memcmp (valp->strval, "on", sizeof ("on")) == 0)
>       GL(dl_x86_feature_control).ibt = cet_always_on;
> -  else if (DEFAULT_MEMCMP (valp->strval, "off", sizeof ("off")) == 0)
> +  else if (memcmp (valp->strval, "off", sizeof ("off")) == 0)
>       GL(dl_x86_feature_control).ibt = cet_always_off;
> -  else if (DEFAULT_MEMCMP (valp->strval, "permissive",
> -			   sizeof ("permissive")) == 0)
> +  else if (memcmp (valp->strval, "permissive", sizeof ("permissive")) == 0)
>       GL(dl_x86_feature_control).ibt = cet_permissive;
>   }
>   
> @@ -303,12 +289,11 @@ attribute_hidden
>   void
>   TUNABLE_CALLBACK (set_x86_shstk) (tunable_val_t *valp)
>   {
> -  if (DEFAULT_MEMCMP (valp->strval, "on", sizeof ("on")) == 0)
> +  if (memcmp (valp->strval, "on", sizeof ("on")) == 0)
>       GL(dl_x86_feature_control).shstk = cet_always_on;
> -  else if (DEFAULT_MEMCMP (valp->strval, "off", sizeof ("off")) == 0)
> +  else if (memcmp (valp->strval, "off", sizeof ("off")) == 0)
>       GL(dl_x86_feature_control).shstk = cet_always_off;
> -  else if (DEFAULT_MEMCMP (valp->strval, "permissive",
> -			   sizeof ("permissive")) == 0)
> +  else if (memcmp (valp->strval, "permissive", sizeof ("permissive")) == 0)
>       GL(dl_x86_feature_control).shstk = cet_permissive;
>   }
>   #endif
> diff --git a/sysdeps/x86_64/multiarch/dl-symbol-redir-ifunc.h b/sysdeps/x86_64/multiarch/dl-symbol-redir-ifunc.h
> index 3fe73ca1c3..c7d8961bb6 100644
> --- a/sysdeps/x86_64/multiarch/dl-symbol-redir-ifunc.h
> +++ b/sysdeps/x86_64/multiarch/dl-symbol-redir-ifunc.h
> @@ -19,6 +19,8 @@
>   #ifndef _DL_IFUNC_GENERIC_H
>   #define _DL_IFUNC_GENERIC_H
>   
> +#ifndef SHARED
> +
>   #include <isa-level.h>
>   
>   #if MINIMUM_X86_ISA_LEVEL >= 4
> @@ -31,4 +33,17 @@
>   
>   asm ("memset = " HAVE_MEMSET_IFUNC_GENERIC);
>   
> +
> +#if MINIMUM_X86_ISA_LEVEL >= 4
> +# define HAVE_MEMCMP_IFUNC_GENERIC "__memcmp_evex_movbe"
> +#elif MINIMUM_X86_ISA_LEVEL == 3
> +# define HAVE_MEMCMP_IFUNC_GENERIC "__memcmp_avx2_movbe"
> +#else
> +# define HAVE_MEMCMP_IFUNC_GENERIC "__memcmp_sse2"
> +#endif
> +
> +asm ("memcmp = " HAVE_MEMCMP_IFUNC_GENERIC);
> +
> +#endif /* SHARED */
> +
>   #endif

^ permalink raw reply	[flat|nested] 45+ messages in thread

* Re: [PATCH v2 10/19] s390: Use dl-symbol-redir-ifunc.h on cpu-tunables
  2023-10-17 13:05 ` [PATCH v2 10/19] s390: " Adhemerval Zanella
@ 2023-10-27 10:34   ` Siddhesh Poyarekar
  0 siblings, 0 replies; 45+ messages in thread
From: Siddhesh Poyarekar @ 2023-10-27 10:34 UTC (permalink / raw)
  To: Adhemerval Zanella, libc-alpha

On 2023-10-17 09:05, Adhemerval Zanella wrote:
> Using the memcmp symbol directly allows the compile to inline the
> memcmp calls (especially because _dl_tunable_set_hwcaps uses constants
> values), generating better code.
> 
> Checked with tst-tunables on s390x-linux-gnu (qemu system).
> ---

LGTM.

Reviewed-by: Siddhesh Poyarekar <siddhesh@sourceware.org>

>   sysdeps/s390/cpu-features.c                   | 30 +++++++++----------
>   .../s390/multiarch/dl-symbol-redir-ifunc.h    |  2 ++
>   2 files changed, 17 insertions(+), 15 deletions(-)
> 
> diff --git a/sysdeps/s390/cpu-features.c b/sysdeps/s390/cpu-features.c
> index 39f8c23a60..55449ba07f 100644
> --- a/sysdeps/s390/cpu-features.c
> +++ b/sysdeps/s390/cpu-features.c
> @@ -21,7 +21,7 @@
>   #include <elf/dl-tunables.h>
>   #include <ifunc-memcmp.h>
>   #include <string.h>
> -extern __typeof (memcmp) MEMCMP_DEFAULT;
> +#include <dl-symbol-redir-ifunc.h>
>   
>   #define S390_COPY_CPU_FEATURES(SRC_PTR, DEST_PTR)	\
>     (DEST_PTR)->hwcap = (SRC_PTR)->hwcap;			\
> @@ -89,9 +89,9 @@ TUNABLE_CALLBACK (set_hwcaps) (tunable_val_t *valp)
>         if ((*feature == 'z' || *feature == 'a'))
>   	{
>   	  if ((feature_len == 5 && *feature == 'z'
> -	       && MEMCMP_DEFAULT (feature, "zEC12", 5) == 0)
> +	       && memcmp (feature, "zEC12", 5) == 0)
>   	      || (feature_len == 6 && *feature == 'a'
> -		  && MEMCMP_DEFAULT (feature, "arch10", 6) == 0))
> +		  && memcmp (feature, "arch10", 6) == 0))
>   	    {
>   	      reset_features = true;
>   	      disable = true;
> @@ -100,9 +100,9 @@ TUNABLE_CALLBACK (set_hwcaps) (tunable_val_t *valp)
>   	      stfle_bits0_mask = S390_STFLE_MASK_ARCH13_MIE3;
>   	    }
>   	  else if ((feature_len == 3 && *feature == 'z'
> -		    && MEMCMP_DEFAULT (feature, "z13", 3) == 0)
> +		    && memcmp (feature, "z13", 3) == 0)
>   		   || (feature_len == 6 && *feature == 'a'
> -		       && MEMCMP_DEFAULT (feature, "arch11", 6) == 0))
> +		       && memcmp (feature, "arch11", 6) == 0))
>   	    {
>   	      reset_features = true;
>   	      disable = true;
> @@ -110,9 +110,9 @@ TUNABLE_CALLBACK (set_hwcaps) (tunable_val_t *valp)
>   	      stfle_bits0_mask = S390_STFLE_MASK_ARCH13_MIE3;
>   	    }
>   	  else if ((feature_len == 3 && *feature == 'z'
> -		    && MEMCMP_DEFAULT (feature, "z14", 3) == 0)
> +		    && memcmp (feature, "z14", 3) == 0)
>   		   || (feature_len == 6 && *feature == 'a'
> -		       && MEMCMP_DEFAULT (feature, "arch12", 6) == 0))
> +		       && memcmp (feature, "arch12", 6) == 0))
>   	    {
>   	      reset_features = true;
>   	      disable = true;
> @@ -120,11 +120,11 @@ TUNABLE_CALLBACK (set_hwcaps) (tunable_val_t *valp)
>   	      stfle_bits0_mask = S390_STFLE_MASK_ARCH13_MIE3;
>   	    }
>   	  else if ((feature_len == 3 && *feature == 'z'
> -		    && (MEMCMP_DEFAULT (feature, "z15", 3) == 0
> -			|| MEMCMP_DEFAULT (feature, "z16", 3) == 0))
> +		    && (memcmp (feature, "z15", 3) == 0
> +			|| memcmp (feature, "z16", 3) == 0))
>   		   || (feature_len == 6
> -		       && (MEMCMP_DEFAULT (feature, "arch13", 6) == 0
> -			   || MEMCMP_DEFAULT (feature, "arch14", 6) == 0)))
> +		       && (memcmp (feature, "arch13", 6) == 0
> +			   || memcmp (feature, "arch14", 6) == 0)))
>   	    {
>   	      /* For z15 or newer we don't have to disable something,
>   		 but we have to reset to the original values.  */
> @@ -134,14 +134,14 @@ TUNABLE_CALLBACK (set_hwcaps) (tunable_val_t *valp)
>         else if (*feature == 'H')
>   	{
>   	  if (feature_len == 15
> -	      && MEMCMP_DEFAULT (feature, "HWCAP_S390_VXRS", 15) == 0)
> +	      && memcmp (feature, "HWCAP_S390_VXRS", 15) == 0)
>   	    {
>   	      hwcap_mask = HWCAP_S390_VXRS;
>   	      if (disable)
>   		hwcap_mask |= HWCAP_S390_VXRS_EXT | HWCAP_S390_VXRS_EXT2;
>   	    }
>   	  else if (feature_len == 19
> -		   && MEMCMP_DEFAULT (feature, "HWCAP_S390_VXRS_EXT", 19) == 0)
> +		   && memcmp (feature, "HWCAP_S390_VXRS_EXT", 19) == 0)
>   	    {
>   	      hwcap_mask = HWCAP_S390_VXRS_EXT;
>   	      if (disable)
> @@ -150,7 +150,7 @@ TUNABLE_CALLBACK (set_hwcaps) (tunable_val_t *valp)
>   		hwcap_mask |= HWCAP_S390_VXRS;
>   	    }
>   	  else if (feature_len == 20
> -		   && MEMCMP_DEFAULT (feature, "HWCAP_S390_VXRS_EXT2", 20) == 0)
> +		   && memcmp (feature, "HWCAP_S390_VXRS_EXT2", 20) == 0)
>   	    {
>   	      hwcap_mask = HWCAP_S390_VXRS_EXT2;
>   	      if (!disable)
> @@ -160,7 +160,7 @@ TUNABLE_CALLBACK (set_hwcaps) (tunable_val_t *valp)
>         else if (*feature == 'S')
>   	{
>   	  if (feature_len == 10
> -	      && MEMCMP_DEFAULT (feature, "STFLE_MIE3", 10) == 0)
> +	      && memcmp (feature, "STFLE_MIE3", 10) == 0)
>   	    {
>   	      stfle_bits0_mask = S390_STFLE_MASK_ARCH13_MIE3;
>   	    }
> diff --git a/sysdeps/s390/multiarch/dl-symbol-redir-ifunc.h b/sysdeps/s390/multiarch/dl-symbol-redir-ifunc.h
> index aa084fdcde..0f58897c48 100644
> --- a/sysdeps/s390/multiarch/dl-symbol-redir-ifunc.h
> +++ b/sysdeps/s390/multiarch/dl-symbol-redir-ifunc.h
> @@ -20,10 +20,12 @@
>   #define _DL_IFUNC_GENERIC_H
>   
>   #include <ifunc-memset.h>
> +#include <ifunc-memcmp.h>
>   
>   #define IFUNC_SYMBOL_STR1(s)	#s
>   #define IFUNC_SYMBOL_STR(s)	IFUNC_SYMBOL_STR1(s)
>   
>   asm ("memset = " IFUNC_SYMBOL_STR(MEMSET_DEFAULT));
> +asm ("memcmp = " IFUNC_SYMBOL_STR(MEMCMP_DEFAULT));
>   
>   #endif

^ permalink raw reply	[flat|nested] 45+ messages in thread

* Re: [PATCH v2 03/19] elf: Ignore GLIBC_TUNABLES for setuid/setgid binaries
  2023-10-18 13:04   ` Siddhesh Poyarekar
@ 2023-10-27 10:38     ` Siddhesh Poyarekar
  0 siblings, 0 replies; 45+ messages in thread
From: Siddhesh Poyarekar @ 2023-10-27 10:38 UTC (permalink / raw)
  To: Adhemerval Zanella, libc-alpha



On 2023-10-18 09:04, Siddhesh Poyarekar wrote:
> On 2023-10-17 09:05, Adhemerval Zanella wrote:
>> The tunable privilege levels were a retrofit to try and keep the malloc
>> tunable environment variables' behavior unchanged across security
>> boundaries.  However, CVE-2023-4911 shows how tricky can be
>> tunable parsing in a security-sensitive environment.
>>
>> Not only parsing, but the malloc tunable essentially changes some
>> semantics on setuid/setgid processes.  Although it is not a direct
>> security issue, allowing users to change setuid/setgid semantics is not
>> a good security practice, and requires extra code and analysis to check
>> if each tunable is safe to use on all security boundaries.
>>
>> It also means that security opt-in features, like aarch64 MTE, would
>> need to be explicit enabled by an administrator with a wrapper script
>> or with a possible future system-wide tunable setting.
>>
>> Co-authored-by: Siddhesh Poyarekar  <siddhesh@sourceware.org>
> 
> I'll leave this for someone else to review since I wrote some parts of 
> this.

Carlos, can you please review this?

Thanks,
Sid

> 
> Thanks,
> Sid
> 
>> ---
>>   elf/Makefile                       |   5 +-
>>   elf/dl-tunable-types.h             |  10 --
>>   elf/dl-tunables.c                  | 127 ++++-----------
>>   elf/dl-tunables.list               |   9 --
>>   elf/tst-env-setuid-tunables.c      |  29 +++-
>>   elf/tst-tunables.c                 | 244 +++++++++++++++++++++++++++++
>>   manual/README.tunables             |   9 --
>>   scripts/gen-tunables.awk           |  18 +--
>>   sysdeps/x86_64/64/dl-tunables.list |   1 -
>>   9 files changed, 299 insertions(+), 153 deletions(-)
>>   create mode 100644 elf/tst-tunables.c
>>
>> diff --git a/elf/Makefile b/elf/Makefile
>> index 9176cbf1e3..c824f7dab7 100644
>> --- a/elf/Makefile
>> +++ b/elf/Makefile
>> @@ -263,7 +263,6 @@ tests-static-normal := \
>>     tst-dl-iter-static \
>>     tst-dst-static \
>>     tst-env-setuid \
>> -  tst-env-setuid-tunables \
>>     tst-getauxval-static \
>>     tst-linkall-static \
>>     tst-single_threaded-pthread-static \
>> @@ -276,10 +275,12 @@ tests-static-normal := \
>>   tests-static-internal := \
>>     tst-dl-printf-static \
>>     tst-dl_find_object-static \
>> +  tst-env-setuid-tunables \
>>     tst-ptrguard1-static \
>>     tst-stackguard1-static \
>>     tst-tls1-static \
>>     tst-tls1-static-non-pie \
>> +  tst-tunables \
>>     # tests-static-internal
>>   CRT-tst-tls1-static-non-pie := $(csu-objpfx)crt1.o
>> @@ -2696,6 +2697,8 @@ $(objpfx)tst-glibc-hwcaps-mask.out: \
>>   # tst-glibc-hwcaps-cache.
>>   $(objpfx)tst-glibc-hwcaps-cache.out: $(objpfx)tst-glibc-hwcaps
>> +tst-tunables-ARGS = -- $(host-test-program-cmd)
>> +
>>   $(objpfx)list-tunables.out: tst-rtld-list-tunables.sh $(objpfx)ld.so
>>       $(SHELL) $< $(objpfx)ld.so '$(test-wrapper-env)' \
>>           '$(run_program_env)' > $(objpfx)/tst-rtld-list-tunables.out
>> diff --git a/elf/dl-tunable-types.h b/elf/dl-tunable-types.h
>> index c88332657e..62d6d9e629 100644
>> --- a/elf/dl-tunable-types.h
>> +++ b/elf/dl-tunable-types.h
>> @@ -64,16 +64,6 @@ struct _tunable
>>     tunable_val_t val;            /* The value.  */
>>     bool initialized;            /* Flag to indicate that the tunable is
>>                          initialized.  */
>> -  tunable_seclevel_t security_level;    /* Specify the security level 
>> for the
>> -                       tunable with respect to AT_SECURE
>> -                       programs.  See description of
>> -                       tunable_seclevel_t to see a
>> -                       description of the values.
>> -
>> -                       Note that even if the tunable is
>> -                       read, it may not get used by the
>> -                       target module if the value is
>> -                       considered unsafe.  */
>>     /* Compatibility elements.  */
>>     const char env_alias[TUNABLE_ALIAS_MAX]; /* The compatibility 
>> environment
>>                          variable name.  */
>> diff --git a/elf/dl-tunables.c b/elf/dl-tunables.c
>> index 24252af22c..a83bd2b8bc 100644
>> --- a/elf/dl-tunables.c
>> +++ b/elf/dl-tunables.c
>> @@ -154,50 +154,51 @@ __tunable_set_val (tunable_id_t id, 
>> tunable_val_t *valp, tunable_num_t *minp,
>>     do_tunable_update_val (cur, valp, minp, maxp);
>>   }
>> -/* Parse the tunable string TUNESTR and adjust it to drop any 
>> tunables that may
>> -   be unsafe for AT_SECURE processes so that it can be used as the new
>> -   environment variable value for GLIBC_TUNABLES.  VALSTRING is the 
>> original
>> -   environment variable string which we use to make NULL terminated 
>> values so
>> -   that we don't have to allocate memory again for it.  */
>> +/* Parse the tunable string VALSTRING.  VALSTRING is a duplicated 
>> values,
>> +   where delimiters ':' are replaced with '\0', so string tunables 
>> are null
>> +   terminated.  */
>>   static void
>> -parse_tunables (char *tunestr, char *valstring)
>> +parse_tunables (char *valstring)
>>   {
>> -  if (tunestr == NULL || *tunestr == '\0')
>> +  if (valstring == NULL || *valstring == '\0')
>>       return;
>> -  char *p = tunestr;
>> -  size_t off = 0;
>> +  char *p = valstring;
>> +  bool done = false;
>> -  while (true)
>> +  while (!done)
>>       {
>>         char *name = p;
>> -      size_t len = 0;
>>         /* First, find where the name ends.  */
>> -      while (p[len] != '=' && p[len] != ':' && p[len] != '\0')
>> -    len++;
>> +      while (*p != '=' && *p != ':' && *p != '\0')
>> +    p++;
>>         /* If we reach the end of the string before getting a valid 
>> name-value
>>        pair, bail out.  */
>> -      if (p[len] == '\0')
>> +      if (*p == '\0')
>>       break;
>>         /* We did not find a valid name-value pair before encountering 
>> the
>>        colon.  */
>> -      if (p[len]== ':')
>> +      if (*p == ':')
>>       {
>> -      p += len + 1;
>> +      p++;
>>         continue;
>>       }
>> -      p += len + 1;
>> +      /* Skip the ':' or '='.  */
>> +      p++;
>> -      /* Take the value from the valstring since we need to NULL 
>> terminate it.  */
>> -      char *value = &valstring[p - tunestr];
>> -      len = 0;
>> +      const char *value = p;
>> -      while (p[len] != ':' && p[len] != '\0')
>> -    len++;
>> +      while (*p != ':' && *p != '\0')
>> +    p++;
>> +
>> +      if (*p == '\0')
>> +    done = true;
>> +      else
>> +    *p++ = '\0';
>>         /* Add the tunable if it exists.  */
>>         for (size_t i = 0; i < sizeof (tunable_list) / sizeof 
>> (tunable_t); i++)
>> @@ -206,50 +207,11 @@ parse_tunables (char *tunestr, char *valstring)
>>         if (tunable_is_name (cur->name, name))
>>           {
>> -          /* If we are in a secure context (AT_SECURE) then ignore the
>> -         tunable unless it is explicitly marked as secure.  Tunable
>> -         values take precedence over their envvar aliases.  We write
>> -         the tunables that are not SXID_ERASE back to TUNESTR, thus
>> -         dropping all SXID_ERASE tunables and any invalid or
>> -         unrecognized tunables.  */
>> -          if (__libc_enable_secure)
>> -        {
>> -          if (cur->security_level != TUNABLE_SECLEVEL_SXID_ERASE)
>> -            {
>> -              if (off > 0)
>> -            tunestr[off++] = ':';
>> -
>> -              const char *n = cur->name;
>> -
>> -              while (*n != '\0')
>> -            tunestr[off++] = *n++;
>> -
>> -              tunestr[off++] = '=';
>> -
>> -              for (size_t j = 0; j < len; j++)
>> -            tunestr[off++] = value[j];
>> -            }
>> -
>> -          if (cur->security_level != TUNABLE_SECLEVEL_NONE)
>> -            break;
>> -        }
>> -
>> -          value[len] = '\0';
>>             tunable_initialize (cur, value);
>>             break;
>>           }
>>       }
>> -
>> -      /* We reached the end while processing the tunable string.  */
>> -      if (p[len] == '\0')
>> -    break;
>> -
>> -      p += len + 1;
>>       }
>> -
>> -  /* Terminate tunestr before we leave.  */
>> -  if (__libc_enable_secure)
>> -    tunestr[off] = '\0';
>>   }
>>   /* Initialize the tunables list from the environment.  For now we 
>> only use the
>> @@ -263,16 +225,16 @@ __tunables_init (char **envp)
>>     size_t len = 0;
>>     char **prev_envp = envp;
>> +  /* Ignore tunables for AT_SECURE programs.  */
>> +  if (__libc_enable_secure)
>> +    return;
>> +
>>     while ((envp = get_next_env (envp, &envname, &len, &envval,
>>                      &prev_envp)) != NULL)
>>       {
>>         if (tunable_is_name ("GLIBC_TUNABLES", envname))
>>       {
>> -      char *new_env = tunables_strdup (envname);
>> -      if (new_env != NULL)
>> -        parse_tunables (new_env + len + 1, envval);
>> -      /* Put in the updated envval.  */
>> -      *prev_envp = new_env;
>> +      parse_tunables (tunables_strdup (envval));
>>         continue;
>>       }
>> @@ -290,39 +252,6 @@ __tunables_init (char **envp)
>>         /* We have a match.  Initialize and move on to the next line.  */
>>         if (tunable_is_name (name, envname))
>>           {
>> -          /* For AT_SECURE binaries, we need to check the security 
>> settings of
>> -         the tunable and decide whether we read the value and also 
>> whether
>> -         we erase the value so that child processes don't inherit 
>> them in
>> -         the environment.  */
>> -          if (__libc_enable_secure)
>> -        {
>> -          if (cur->security_level == TUNABLE_SECLEVEL_SXID_ERASE)
>> -            {
>> -              /* Erase the environment variable.  */
>> -              char **ep = prev_envp;
>> -
>> -              while (*ep != NULL)
>> -            {
>> -              if (tunable_is_name (name, *ep))
>> -                {
>> -                  char **dp = ep;
>> -
>> -                  do
>> -                dp[0] = dp[1];
>> -                  while (*dp++);
>> -                }
>> -              else
>> -                ++ep;
>> -            }
>> -              /* Reset the iterator so that we read the environment 
>> again
>> -             from the point we erased.  */
>> -              envp = prev_envp;
>> -            }
>> -
>> -          if (cur->security_level != TUNABLE_SECLEVEL_NONE)
>> -            continue;
>> -        }
>> -
>>             tunable_initialize (cur, envval);
>>             break;
>>           }
>> diff --git a/elf/dl-tunables.list b/elf/dl-tunables.list
>> index 695ba7192e..471895af7b 100644
>> --- a/elf/dl-tunables.list
>> +++ b/elf/dl-tunables.list
>> @@ -21,14 +21,6 @@
>>   # minval: Optional minimum acceptable value
>>   # maxval: Optional maximum acceptable value
>>   # env_alias: An alias environment variable
>> -# security_level: Specify security level of the tunable for AT_SECURE 
>> binaries.
>> -#           Valid values are:
>> -#
>> -#          SXID_ERASE: (default) Do not read and do not pass on to
>> -#          child processes.
>> -#          SXID_IGNORE: Do not read, but retain for non-AT_SECURE
>> -#          subprocesses.
>> -#          NONE: Read all the time.
>>   glibc {
>>     malloc {
>> @@ -158,7 +150,6 @@ glibc {
>>         type: INT_32
>>         minval: 0
>>         maxval: 255
>> -      security_level: SXID_IGNORE
>>       }
>>     }
>> diff --git a/elf/tst-env-setuid-tunables.c 
>> b/elf/tst-env-setuid-tunables.c
>> index 2603007b7b..ca02dbba58 100644
>> --- a/elf/tst-env-setuid-tunables.c
>> +++ b/elf/tst-env-setuid-tunables.c
>> @@ -15,14 +15,10 @@
>>      License along with the GNU C Library; if not, see
>>      <https://www.gnu.org/licenses/>.  */
>> -/* Verify that tunables correctly filter out unsafe tunables like
>> -   glibc.malloc.check and glibc.malloc.mmap_threshold but also retain
>> -   glibc.malloc.mmap_threshold in an unprivileged child.  */
>> -
>> -#define _LIBC 1
>> -#include "config.h"
>> -#undef _LIBC
>> +/* Verify that GLIBC_TUNABLES is kept unchanged but no tunable is 
>> actually
>> +   enabled for AT_SECURE processes.  */
>> +#include <dl-tunables.h>
>>   #include <errno.h>
>>   #include <fcntl.h>
>>   #include <stdlib.h>
>> @@ -40,7 +36,7 @@
>>   #include <support/test-driver.h>
>>   #include <support/capture_subprocess.h>
>> -const char *teststrings[] =
>> +static const char *teststrings[] =
>>   {
>>     "glibc.malloc.check=2:glibc.malloc.mmap_threshold=4096",
>>     
>> "glibc.malloc.check=2:glibc.malloc.check=2:glibc.malloc.mmap_threshold=4096",
>> @@ -74,6 +70,23 @@ test_child (int off)
>>       ret = 0;
>>     fflush (stdout);
>> +  /* Also check if the set tunables are effectively unchanged.  */
>> +  int32_t check = TUNABLE_GET_FULL (glibc, malloc, check, int32_t, 
>> NULL);
>> +  size_t mmap_threshold = TUNABLE_GET_FULL (glibc, malloc, 
>> mmap_threshold,
>> +                        size_t, NULL);
>> +  int32_t perturb = TUNABLE_GET_FULL (glibc, malloc, perturb, 
>> int32_t, NULL);
>> +
>> +  printf ("    [%d] glibc.malloc.check=%d\n", off, check);
>> +  fflush (stdout);
>> +  printf ("    [%d] glibc.malloc.mmap_threshold=%zu\n", off, 
>> mmap_threshold);
>> +  fflush (stdout);
>> +  printf ("    [%d] glibc.malloc.perturb=%d\n", off, perturb);
>> +  fflush (stdout);
>> +
>> +  ret |= check != 0;
>> +  ret |= mmap_threshold != 0;
>> +  ret |= perturb != 0;
>> +
>>     return ret;
>>   }
>> diff --git a/elf/tst-tunables.c b/elf/tst-tunables.c
>> new file mode 100644
>> index 0000000000..57cf532fc4
>> --- /dev/null
>> +++ b/elf/tst-tunables.c
>> @@ -0,0 +1,244 @@
>> +/* Check GLIBC_TUNABLES parsing.
>> +   Copyright (C) 2023 Free Software Foundation, Inc.
>> +   This file is part of the GNU C Library.
>> +
>> +   The GNU C Library is free software; you can redistribute it and/or
>> +   modify it under the terms of the GNU Lesser General Public
>> +   License as published by the Free Software Foundation; either
>> +   version 2.1 of the License, or (at your option) any later version.
>> +
>> +   The GNU C Library is distributed in the hope that it will be useful,
>> +   but WITHOUT ANY WARRANTY; without even the implied warranty of
>> +   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
>> +   Lesser General Public License for more details.
>> +
>> +   You should have received a copy of the GNU Lesser General Public
>> +   License along with the GNU C Library; if not, see
>> +   <https://www.gnu.org/licenses/>.  */
>> +
>> +#include <array_length.h>
>> +#include <dl-tunables.h>
>> +#include <getopt.h>
>> +#include <intprops.h>
>> +#include <stdint.h>
>> +#include <stdlib.h>
>> +#include <support/capture_subprocess.h>
>> +#include <support/check.h>
>> +
>> +static int restart;
>> +#define CMDLINE_OPTIONS \
>> +  { "restart", no_argument, &restart, 1 },
>> +
>> +static const struct test_t
>> +{
>> +  const char *env;
>> +  int32_t expected_malloc_check;
>> +  size_t expected_mmap_threshold;
>> +  int32_t expected_perturb;
>> +} tests[] =
>> +{
>> +  /* Expected tunable format.  */
>> +  {
>> +    "glibc.malloc.check=2",
>> +    2,
>> +    0,
>> +    0,
>> +  },
>> +  {
>> +    "glibc.malloc.check=2:glibc.malloc.mmap_threshold=4096",
>> +    2,
>> +    4096,
>> +    0,
>> +  },
>> +  /* Empty tunable are ignored.  */
>> +  {
>> +    "glibc.malloc.check=2::glibc.malloc.mmap_threshold=4096",
>> +    2,
>> +    4096,
>> +    0,
>> +  },
>> +  /* As well empty values.  */
>> +  {
>> +    "glibc.malloc.check=:glibc.malloc.mmap_threshold=4096",
>> +    0,
>> +    4096,
>> +    0,
>> +  },
>> +  /* Tunable are processed from left to right, so last one is the one 
>> set.  */
>> +  {
>> +    "glibc.malloc.check=1:glibc.malloc.check=2",
>> +    2,
>> +    0,
>> +    0,
>> +  },
>> +  {
>> +    
>> "glibc.malloc.check=2:glibc.malloc.check=2:glibc.malloc.mmap_threshold=4096",
>> +    2,
>> +    4096,
>> +    0,
>> +  },
>> +  {
>> +    
>> "glibc.malloc.check=2:glibc.malloc.mmap_threshold=4096:glibc.malloc.check=2",
>> +    2,
>> +    4096,
>> +    0,
>> +  },
>> +  /* 0x800 is larger than tunable maxval (0xff), so the tunable is 
>> unchanged.  */
>> +  {
>> +    "glibc.malloc.perturb=0x800",
>> +    0,
>> +    0,
>> +    0,
>> +  },
>> +  {
>> +    "glibc.malloc.perturb=0x55",
>> +    0,
>> +    0,
>> +    0x55,
>> +  },
>> +  /* Out of range values are just ignored.  */
>> +  {
>> +    "glibc.malloc.perturb=0x800:glibc.malloc.mmap_threshold=4096",
>> +    0,
>> +    4096,
>> +    0,
>> +  },
>> +  /* Invalid keys are ignored.  */
>> +  {
>> +    ":glibc.malloc.garbage=2:glibc.malloc.check=1",
>> +    1,
>> +    0,
>> +    0,
>> +  },
>> +  {
>> +    
>> "glibc.malloc.perturb=0x800:not_valid.malloc.check=2:glibc.malloc.mmap_threshold=4096",
>> +    0,
>> +    4096,
>> +    0,
>> +  },
>> +  {
>> +    "glibc.not_valid.check=2:glibc.malloc.mmap_threshold=4096",
>> +    0,
>> +    4096,
>> +    0,
>> +  },
>> +  {
>> +    "not_valid.malloc.check=2:glibc.malloc.mmap_threshold=4096",
>> +    0,
>> +    4096,
>> +    0,
>> +  },
>> +  /* Invalid subkeys are ignored.  */
>> +  {
>> +    
>> "glibc.malloc.garbage=2:glibc.maoc.mmap_threshold=4096:glibc.malloc.check=2",
>> +    2,
>> +    0,
>> +    0,
>> +  },
>> +  {
>> +    
>> "glibc.malloc.check=4:glibc.malloc.garbage=2:glibc.maoc.mmap_threshold=4096",
>> +    0,
>> +    0,
>> +    0,
>> +  },
>> +  {
>> +    "not_valid.malloc.check=2",
>> +    0,
>> +    0,
>> +    0,
>> +  },
>> +  {
>> +    "glibc.not_valid.check=2",
>> +    0,
>> +    0,
>> +    0,
>> +  },
>> +  /* An ill-formatted tunable in the for key=key=value will considere 
>> the
>> +     value as 'key=value' (which can not be parsed as an integer).  */
>> +  {
>> +    "glibc.malloc.mmap_threshold=glibc.malloc.mmap_threshold=4096",
>> +    0,
>> +    0,
>> +    0,
>> +  },
>> +  /* The ill-formatted tunable is also skipped.  */
>> +  {
>> +    
>> "glibc.malloc.mmap_threshold=glibc.malloc.mmap_threshold=4096:glibc.malloc.check=2",
>> +    2,
>> +    0,
>> +    0,
>> +  },
>> +  /* For an integer tunable, parse will stop on non number 
>> character.  */
>> +  {
>> +    "glibc.malloc.check=2=2",
>> +    2,
>> +    0,
>> +    0,
>> +  },
>> +  {
>> +    "glibc.malloc.check=2=2:glibc.malloc.mmap_threshold=4096",
>> +    2,
>> +    4096,
>> +    0,
>> +  }
>> +};
>> +
>> +static int
>> +handle_restart (int i)
>> +{
>> +  TEST_COMPARE (tests[i].expected_malloc_check,
>> +        TUNABLE_GET_FULL (glibc, malloc, check, int32_t, NULL));
>> +  TEST_COMPARE (tests[i].expected_mmap_threshold,
>> +        TUNABLE_GET_FULL (glibc, malloc, mmap_threshold, size_t, NULL));
>> +  TEST_COMPARE (tests[i].expected_perturb,
>> +        TUNABLE_GET_FULL (glibc, malloc, perturb, int32_t, NULL));
>> +  return 0;
>> +}
>> +
>> +static int
>> +do_test (int argc, char *argv[])
>> +{
>> +  /* We must have either:
>> +     - One our fource parameters left if called initially:
>> +       + path to ld.so         optional
>> +       + "--library-path"      optional
>> +       + the library path      optional
>> +       + the application name
>> +       + the test to check  */
>> +
>> +  TEST_VERIFY_EXIT (argc == 2 || argc == 5);
>> +
>> +  if (restart)
>> +    return handle_restart (atoi (argv[1]));
>> +
>> +  char nteststr[INT_BUFSIZE_BOUND (int)];
>> +
>> +  char *spargv[10];
>> +  {
>> +    int i = 0;
>> +    for (; i < argc - 1; i++)
>> +      spargv[i] = argv[i + 1];
>> +    spargv[i++] = (char *) "--direct";
>> +    spargv[i++] = (char *) "--restart";
>> +    spargv[i++] = nteststr;
>> +    spargv[i] = NULL;
>> +  }
>> +
>> +  for (int i = 0; i < array_length (tests); i++)
>> +    {
>> +      snprintf (nteststr, sizeof nteststr, "%d", i);
>> +
>> +      printf ("[%d] Spawned test for %s\n", i, tests[i].env);
>> +      setenv ("GLIBC_TUNABLES", tests[i].env, 1);
>> +      struct support_capture_subprocess result
>> +    = support_capture_subprogram (spargv[0], spargv);
>> +      support_capture_subprocess_check (&result, "tst-tunables", 0,
>> +                    sc_allow_stderr);
>> +      support_capture_subprocess_free (&result);
>> +    }
>> +
>> +  return 0;
>> +}
>> +
>> +#define TEST_FUNCTION_ARGV do_test
>> +#include <support/test-driver.c>
>> diff --git a/manual/README.tunables b/manual/README.tunables
>> index 605ddd78cd..72ae00dc02 100644
>> --- a/manual/README.tunables
>> +++ b/manual/README.tunables
>> @@ -59,15 +59,6 @@ The list of allowed attributes are:
>>   - env_alias:        An alias environment variable
>> -- security_level:    Specify security level of the tunable for AT_SECURE
>> -            binaries.  Valid values are:
>> -
>> -            SXID_ERASE: (default) Do not read and do not pass on to
>> -            child processes.
>> -            SXID_IGNORE: Do not read, but retain for non-AT_SECURE
>> -            child processes.
>> -            NONE: Read all the time.
>> -
>>   2. Use TUNABLE_GET/TUNABLE_SET/TUNABLE_SET_WITH_BOUNDS to get and 
>> set tunables.
>>   3. OPTIONAL: If tunables in a namespace are being used multiple 
>> times within a
>> diff --git a/scripts/gen-tunables.awk b/scripts/gen-tunables.awk
>> index d6de100df0..1e9d6b534e 100644
>> --- a/scripts/gen-tunables.awk
>> +++ b/scripts/gen-tunables.awk
>> @@ -61,9 +61,6 @@ $1 == "}" {
>>       if (!env_alias[top_ns,ns,tunable]) {
>>         env_alias[top_ns,ns,tunable] = "{0}"
>>       }
>> -    if (!security_level[top_ns,ns,tunable]) {
>> -      security_level[top_ns,ns,tunable] = "SXID_ERASE"
>> -    }
>>       len = length(top_ns"."ns"."tunable)
>>       if (len > max_name_len)
>>         max_name_len = len
>> @@ -118,17 +115,6 @@ $1 == "}" {
>>       if (len > max_alias_len)
>>         max_alias_len = len
>>     }
>> -  else if (attr == "security_level") {
>> -    if (val == "SXID_ERASE" || val == "SXID_IGNORE" || val == "NONE") {
>> -      security_level[top_ns,ns,tunable] = val
>> -    }
>> -    else {
>> -      printf("Line %d: Invalid value (%s) for security_level: %s, ", 
>> NR, val,
>> -         $0)
>> -      print("Allowed values are 'SXID_ERASE', 'SXID_IGNORE', or 'NONE'")
>> -      exit 1
>> -    }
>> -  }
>>     else if (attr == "default") {
>>       if (types[top_ns,ns,tunable] == "STRING") {
>>         default_val[top_ns,ns,tunable] = sprintf(".strval = \"%s\"", 
>> val);
>> @@ -177,9 +163,9 @@ END {
>>       n = indices[2];
>>       m = indices[3];
>>       printf ("  {TUNABLE_NAME_S(%s, %s, %s)", t, n, m)
>> -    printf (", {TUNABLE_TYPE_%s, %s, %s}, {%s}, false, 
>> TUNABLE_SECLEVEL_%s, %s},\n",
>> +    printf (", {TUNABLE_TYPE_%s, %s, %s}, {%s}, false, %s},\n",
>>           types[t,n,m], minvals[t,n,m], maxvals[t,n,m],
>> -        default_val[t,n,m], security_level[t,n,m], env_alias[t,n,m]);
>> +        default_val[t,n,m], env_alias[t,n,m]);
>>     }
>>     print "};"
>>     print "#endif"
>> diff --git a/sysdeps/x86_64/64/dl-tunables.list 
>> b/sysdeps/x86_64/64/dl-tunables.list
>> index 0aab52e662..54a216a461 100644
>> --- a/sysdeps/x86_64/64/dl-tunables.list
>> +++ b/sysdeps/x86_64/64/dl-tunables.list
>> @@ -23,7 +23,6 @@ glibc {
>>         minval: 0
>>         maxval: 1
>>         env_alias: LD_PREFER_MAP_32BIT_EXEC
>> -      security_level: SXID_IGNORE
>>       }
>>     }
>>   }
> 

^ permalink raw reply	[flat|nested] 45+ messages in thread

* Re: [PATCH v2 04/19] elf: Add all malloc tunable to unsecvars
  2023-10-18 13:04   ` Siddhesh Poyarekar
@ 2023-10-27 10:38     ` Siddhesh Poyarekar
  0 siblings, 0 replies; 45+ messages in thread
From: Siddhesh Poyarekar @ 2023-10-27 10:38 UTC (permalink / raw)
  To: Adhemerval Zanella, libc-alpha



On 2023-10-18 09:04, Siddhesh Poyarekar wrote:
> 
> 
> On 2023-10-17 09:05, Adhemerval Zanella wrote:
>> Some environment variables allow alteration of allocator behavior
>> across setuid boundaries, where a setuid program may ignore the
>> tunable, but its non-setuid child can read it and adjust the memory
>> allocator behavior accordingly.
>>
>> Most library behavior tunings is limited to the current process and does
>> not bleed in scope; so it is unclear how pratical this misfeature is.
>> If behavior change across privilege boundaries is desirable, it would be
>> better done with a wrapper program around the non-setuid child that sets
>> these envvars, instead of using the setuid process as the messenger.
>>
>> The patch as fixes tst-env-setuid, where it fail if any unsecvars is
>> set.  It also adds a dynamic test, although it requires
>> --enable-hardcoded-path-in-tests so kernel correctly sets the setuid
>> bit (using the loader command directly would require to set the
>> setuid bit on the loader itself, which is not a usual deployment).
>>
>> Co-authored-by: Siddhesh Poyarekar <siddhesh@sourceware.org>
> 
> Likewise, I'll leave this for someone else since I am a co-author.

Carlos, can you please review this?

Thanks,
Sid


> 
> Thanks,
> Sid
> 
>>
>> Checked on x86_64-linux-gnu.
>> ---
>>   elf/Makefile                |   8 +--
>>   elf/tst-env-setuid-static.c |   1 +
>>   elf/tst-env-setuid.c        | 128 +++++++++++++++++++++---------------
>>   sysdeps/generic/unsecvars.h |   7 ++
>>   4 files changed, 86 insertions(+), 58 deletions(-)
>>   create mode 100644 elf/tst-env-setuid-static.c
>>
>> diff --git a/elf/Makefile b/elf/Makefile
>> index c824f7dab7..f1cd6e13fa 100644
>> --- a/elf/Makefile
>> +++ b/elf/Makefile
>> @@ -262,7 +262,7 @@ tests-static-normal := \
>>     tst-array5-static \
>>     tst-dl-iter-static \
>>     tst-dst-static \
>> -  tst-env-setuid \
>> +  tst-env-setuid-static \
>>     tst-getauxval-static \
>>     tst-linkall-static \
>>     tst-single_threaded-pthread-static \
>> @@ -305,6 +305,7 @@ tests := \
>>     tst-array5 \
>>     tst-auxv \
>>     tst-dl-hash \
>> +  tst-env-setuid \
>>     tst-leaks1 \
>>     tst-stringtable \
>>     tst-tls9 \
>> @@ -2467,9 +2468,6 @@ $(objpfx)tst-nodelete-dlclose: 
>> $(objpfx)tst-nodelete-dlclose-dso.so
>>   $(objpfx)tst-nodelete-dlclose.out: 
>> $(objpfx)tst-nodelete-dlclose-dso.so \
>>                      $(objpfx)tst-nodelete-dlclose-plugin.so
>> -tst-env-setuid-ENV = MALLOC_CHECK_=2 MALLOC_MMAP_THRESHOLD_=4096 \
>> -             LD_HWCAP_MASK=0x1
>> -
>>   $(objpfx)tst-debug1.out: $(objpfx)tst-debug1mod1.so
>>   $(objpfx)tst-debug1mod1.so: $(objpfx)testobj1.so
>> @@ -3021,3 +3019,5 @@ LDFLAGS-tst-dlclose-lazy-mod1.so = 
>> -Wl,-z,lazy,--no-as-needed
>>   $(objpfx)tst-dlclose-lazy-mod1.so: $(objpfx)tst-dlclose-lazy-mod2.so
>>   $(objpfx)tst-dlclose-lazy.out: \
>>     $(objpfx)tst-dlclose-lazy-mod1.so $(objpfx)tst-dlclose-lazy-mod2.so
>> +
>> +tst-env-setuid-ARGS = -- $(host-test-program-cmd)
>> diff --git a/elf/tst-env-setuid-static.c b/elf/tst-env-setuid-static.c
>> new file mode 100644
>> index 0000000000..0d88ae88b9
>> --- /dev/null
>> +++ b/elf/tst-env-setuid-static.c
>> @@ -0,0 +1 @@
>> +#include "tst-env-setuid.c"
>> diff --git a/elf/tst-env-setuid.c b/elf/tst-env-setuid.c
>> index 032ab44be2..ba295a6a14 100644
>> --- a/elf/tst-env-setuid.c
>> +++ b/elf/tst-env-setuid.c
>> @@ -15,18 +15,14 @@
>>      License along with the GNU C Library; if not, see
>>      <https://www.gnu.org/licenses/>.  */
>> -/* Verify that tunables correctly filter out unsafe environment 
>> variables like
>> -   MALLOC_CHECK_ and MALLOC_MMAP_THRESHOLD_ but also retain
>> -   MALLOC_MMAP_THRESHOLD_ in an unprivileged child.  */
>> +/* Verify that correctly filter out unsafe environment variables defined
>> +   in unsecvars.h.  */
>> -#include <errno.h>
>> -#include <fcntl.h>
>> -#include <stdlib.h>
>> -#include <stdint.h>
>> +#include <array_length.h>
>> +#include <gnu/lib-names.h>
>>   #include <stdio.h>
>> +#include <stdlib.h>
>>   #include <string.h>
>> -#include <sys/stat.h>
>> -#include <sys/wait.h>
>>   #include <unistd.h>
>>   #include <support/check.h>
>> @@ -36,61 +32,72 @@
>>   static char SETGID_CHILD[] = "setgid-child";
>> -#ifndef test_child
>> -static int
>> -test_child (void)
>> -{
>> -  if (getenv ("MALLOC_CHECK_") != NULL)
>> -    {
>> -      printf ("MALLOC_CHECK_ is still set\n");
>> -      return 1;
>> -    }
>> -
>> -  if (getenv ("MALLOC_MMAP_THRESHOLD_") == NULL)
>> -    {
>> -      printf ("MALLOC_MMAP_THRESHOLD_ lost\n");
>> -      return 1;
>> -    }
>> +#define FILTERED_VALUE   "some-filtered-value"
>> +#define UNFILTERED_VALUE "some-unfiltered-value"
>> -  if (getenv ("LD_HWCAP_MASK") != NULL)
>> -    {
>> -      printf ("LD_HWCAP_MASK still set\n");
>> -      return 1;
>> -    }
>> +struct envvar_t
>> +{
>> +  const char *env;
>> +  const char *value;
>> +};
>> -  return 0;
>> -}
>> -#endif
>> +/* That is not an extensible list of all filtered out environment
>> +   variables.  */
>> +static const struct envvar_t filtered_envvars[] =
>> +{
>> +  { "GLIBC_TUNABLES",          FILTERED_VALUE },
>> +  { "LD_AUDIT",                FILTERED_VALUE },
>> +  { "LD_HWCAP_MASK",           FILTERED_VALUE },
>> +  { "LD_LIBRARY_PATH",         FILTERED_VALUE },
>> +  { "LD_PRELOAD",              FILTERED_VALUE },
>> +  { "LD_PROFILE",              FILTERED_VALUE },
>> +  { "MALLOC_ARENA_MAX",        FILTERED_VALUE },
>> +  { "MALLOC_PERTURB_",         FILTERED_VALUE },
>> +  { "MALLOC_TRACE",            FILTERED_VALUE },
>> +  { "MALLOC_TRIM_THRESHOLD_",  FILTERED_VALUE },
>> +  { "RES_OPTIONS",             FILTERED_VALUE },
>> +};
>> +
>> +static const struct envvar_t unfiltered_envvars[] =
>> +{
>> +  { "LD_BIND_NOW",             "0" },
>> +  { "LD_BIND_NOT",             "1" },
>> +  /* Non longer supported option.  */
>> +  { "LD_ASSUME_KERNEL",        UNFILTERED_VALUE },
>> +};
>> -#ifndef test_parent
>>   static int
>> -test_parent (void)
>> +test_child (void)
>>   {
>> -  if (getenv ("MALLOC_CHECK_") == NULL)
>> -    {
>> -      printf ("MALLOC_CHECK_ lost\n");
>> -      return 1;
>> -    }
>> +  int ret = 0;
>> -  if (getenv ("MALLOC_MMAP_THRESHOLD_") == NULL)
>> +  for (const struct envvar_t *e = filtered_envvars;
>> +       e != array_end (filtered_envvars);
>> +       e++)
>>       {
>> -      printf ("MALLOC_MMAP_THRESHOLD_ lost\n");
>> -      return 1;
>> +      const char *env = getenv (e->env);
>> +      ret |= env != NULL;
>>       }
>> -  if (getenv ("LD_HWCAP_MASK") == NULL)
>> +  for (const struct envvar_t *e = unfiltered_envvars;
>> +       e != array_end (unfiltered_envvars);
>> +       e++)
>>       {
>> -      printf ("LD_HWCAP_MASK lost\n");
>> -      return 1;
>> +      const char *env = getenv (e->env);
>> +      ret |= !(env != NULL && strcmp (env, e->value) == 0);
>>       }
>> -  return 0;
>> +  return ret;
>>   }
>> -#endif
>>   static int
>>   do_test (int argc, char **argv)
>>   {
>> +  /* For dynamic loader, the test requires 
>> --enable-hardcoded-path-in-tests so
>> +     the kernel sets the AT_SECURE on process initialization.  */
>> +  if (argc >= 2 && strstr (argv[1], LD_SO) != 0)
>> +    FAIL_UNSUPPORTED ("dynamic test requires 
>> --enable-hardcoded-path-in-tests");
>> +
>>     /* Setgid child process.  */
>>     if (argc == 2 && strcmp (argv[1], SETGID_CHILD) == 0)
>>       {
>> @@ -104,20 +111,33 @@ do_test (int argc, char **argv)
>>         if (ret != 0)
>>       exit (1);
>> -      exit (EXIT_SUCCESS);
>> +      /* Special return code to make sure that the child executed all 
>> the way
>> +     through.  */
>> +      exit (42);
>>       }
>>     else
>>       {
>> -      if (test_parent () != 0)
>> -    exit (1);
>> +      for (const struct envvar_t *e = filtered_envvars;
>> +       e != array_end (filtered_envvars);
>> +       e++)
>> +    setenv (e->env, e->value, 1);
>> +
>> +      for (const struct envvar_t *e = unfiltered_envvars;
>> +       e != array_end (unfiltered_envvars);
>> +       e++)
>> +    setenv (e->env, e->value, 1);
>>         int status = support_capture_subprogram_self_sgid (SETGID_CHILD);
>>         if (WEXITSTATUS (status) == EXIT_UNSUPPORTED)
>> -    return EXIT_UNSUPPORTED;
>> -
>> -      if (!WIFEXITED (status))
>> -    FAIL_EXIT1 ("Unexpected exit status %d from child process\n", 
>> status);
>> +    exit (EXIT_UNSUPPORTED);
>> +
>> +      if (WEXITSTATUS (status) != 42)
>> +    {
>> +      printf ("    child failed with status %d\n",
>> +          WEXITSTATUS (status));
>> +      support_record_failure ();
>> +    }
>>         return 0;
>>       }
>> diff --git a/sysdeps/generic/unsecvars.h b/sysdeps/generic/unsecvars.h
>> index 81397fb90b..f7ebed60e5 100644
>> --- a/sysdeps/generic/unsecvars.h
>> +++ b/sysdeps/generic/unsecvars.h
>> @@ -18,7 +18,14 @@
>>     "LD_SHOW_AUXV\0"                                  \
>>     "LOCALDOMAIN\0"                                  \
>>     "LOCPATH\0"                                      \
>> +  "MALLOC_ARENA_MAX\0"                                  \
>> +  "MALLOC_ARENA_TEST\0"                                  \
>> +  "MALLOC_MMAP_MAX_\0"                                  \
>> +  "MALLOC_MMAP_THRESHOLD_\0"                              \
>> +  "MALLOC_PERTURB_\0"                                  \
>> +  "MALLOC_TOP_PAD_\0"                                  \
>>     "MALLOC_TRACE\0"                                  \
>> +  "MALLOC_TRIM_THRESHOLD_\0"                              \
>>     "NIS_PATH\0"                                      \
>>     "NLSPATH\0"                                      \
>>     "RESOLV_HOST_CONF\0"                                  \
> 

^ permalink raw reply	[flat|nested] 45+ messages in thread

* Re: [PATCH v2 11/19] elf: Do not duplicate the GLIBC_TUNABLES string
  2023-10-17 13:05 ` [PATCH v2 11/19] elf: Do not duplicate the GLIBC_TUNABLES string Adhemerval Zanella
@ 2023-10-27 11:54   ` Siddhesh Poyarekar
  2023-10-30 18:24     ` Adhemerval Zanella Netto
  0 siblings, 1 reply; 45+ messages in thread
From: Siddhesh Poyarekar @ 2023-10-27 11:54 UTC (permalink / raw)
  To: Adhemerval Zanella, libc-alpha



On 2023-10-17 09:05, Adhemerval Zanella wrote:
> The tunable parsing duplicates the tunable environment variable so it
> null-terminates each one since it simplifies the later parsing. It has
> the drawback of adding another point of failure (__minimal_malloc
> failing), and the memory copy requires tuning the compiler to avoid mem
> operations calls.
> 
> The parsing now tracks the tunable start and its size. The
> dl-tunable-parse.h adds helper functions to help parsing, like a strcmp
> that also checks for size and an iterator for suboptions that are
> comma-separated (used on hwcap parsing by x86, powerpc, and s390x).
> 
> Since the environment variable is allocated on the stack by the kernel,
> it is safe to keep the references to the suboptions for later parsing
> of string tunables (as done by set_hwcaps by multiple architectures).
> 
> Checked on x86_64-linux-gnu, powerpc64le-linux-gnu, and
> aarch64-linux-gnu.
> ---
>   elf/dl-tunables.c                             |  66 +++----
>   elf/dl-tunables.h                             |   6 +-
>   sysdeps/generic/dl-tunables-parse.h           | 128 ++++++++++++++
>   sysdeps/s390/cpu-features.c                   | 167 +++++++-----------
>   .../unix/sysv/linux/aarch64/cpu-features.c    |  38 ++--
>   .../unix/sysv/linux/powerpc/cpu-features.c    |  45 ++---
>   .../sysv/linux/powerpc/tst-hwcap-tunables.c   |   6 +-
>   sysdeps/x86/Makefile                          |   4 +-
>   sysdeps/x86/cpu-tunables.c                    | 118 +++++--------
>   sysdeps/x86/tst-hwcap-tunables.c              | 150 ++++++++++++++++
>   10 files changed, 455 insertions(+), 273 deletions(-)
>   create mode 100644 sysdeps/generic/dl-tunables-parse.h
>   create mode 100644 sysdeps/x86/tst-hwcap-tunables.c
> 
> diff --git a/elf/dl-tunables.c b/elf/dl-tunables.c
> index b39c14694c..869846f9da 100644
> --- a/elf/dl-tunables.c
> +++ b/elf/dl-tunables.c
> @@ -36,28 +36,6 @@
>   #define TUNABLES_INTERNAL 1
>   #include "dl-tunables.h"
>   
> -#include <not-errno.h>
> -
> -static char *
> -tunables_strdup (const char *in)
> -{
> -  size_t i = 0;
> -
> -  while (in[i++] != '\0');
> -  char *out = __minimal_malloc (i + 1);
> -
> -  /* For most of the tunables code, we ignore user errors.  However,
> -     this is a system error - and running out of memory at program
> -     startup should be reported, so we do.  */
> -  if (out == NULL)
> -    _dl_fatal_printf ("failed to allocate memory to process tunables\n");
> -
> -  while (i-- > 0)
> -    out[i] = in[i];
> -
> -  return out;
> -}
> -
>   static char **
>   get_next_env (char **envp, char **name, size_t *namelen, char **val,
>   	      char ***prev_envp)
> @@ -134,14 +112,14 @@ do_tunable_update_val (tunable_t *cur, const tunable_val_t *valp,
>   /* Validate range of the input value and initialize the tunable CUR if it looks
>      good.  */
>   static void
> -tunable_initialize (tunable_t *cur, const char *strval)
> +tunable_initialize (tunable_t *cur, const char *strval, size_t len)
>   {
> -  tunable_val_t val;
> +  tunable_val_t val = { 0 };
>   
>     if (cur->type.type_code != TUNABLE_TYPE_STRING)
>       val.numval = (tunable_num_t) _dl_strtoul (strval, NULL);
>     else
> -    val.strval = strval;
> +    val.strval = (struct tunable_str_t) { strval, len };
>     do_tunable_update_val (cur, &val, NULL, NULL);
>   }
>   
> @@ -158,29 +136,29 @@ struct tunable_toset_t
>   {
>     tunable_t *t;
>     const char *value;
> +  size_t len;
>   };
>   
>   enum { tunables_list_size = array_length (tunable_list) };
>   
>   /* Parse the tunable string VALSTRING and set TUNABLES with the found tunables
> -   and their respectibles values.  VALSTRING is a duplicated values,  where
> -   delimiters ':' are replaced with '\0', so string tunables are null
> -   terminated.
> +   and their respectibles values.  The VALSTRING is parse in place, with the

parsed

> +   tunable start and size recorded in TUNABLES.
>      Return the number of tunables found (including 0 if the string is empty)
>      or -1 if for a ill-formatted definition.  */
>   static int
> -parse_tunables_string (char *valstring, struct tunable_toset_t *tunables)
> +parse_tunables_string (const char *valstring, struct tunable_toset_t *tunables)
>   {
>     if (valstring == NULL || *valstring == '\0')
>       return 0;
>   
> -  char *p = valstring;
> +  const char *p = valstring;
>     bool done = false;
>     int ntunables = 0;
>   
>     while (!done)
>       {
> -      char *name = p;
> +      const char *name = p;
>   
>         /* First, find where the name ends.  */
>         while (*p != '=' && *p != ':' && *p != '\0')
> @@ -202,7 +180,7 @@ parse_tunables_string (char *valstring, struct tunable_toset_t *tunables)
>         /* Skip the ':' or '='.  */
>         p++;
>   
> -      char *value = p;
> +      const char *value = p;
>   
>         while (*p != '=' && *p != ':' && *p != '\0')
>   	p++;
> @@ -211,8 +189,6 @@ parse_tunables_string (char *valstring, struct tunable_toset_t *tunables)
>   	return -1;
>         else if (*p == '\0')
>   	done = true;
> -      else
> -	*p++ = '\0';
>   
>         /* Add the tunable if it exists.  */
>         for (size_t i = 0; i < tunables_list_size; i++)
> @@ -221,7 +197,8 @@ parse_tunables_string (char *valstring, struct tunable_toset_t *tunables)
>   
>   	  if (tunable_is_name (cur->name, name))
>   	    {
> -	      tunables[ntunables++] = (struct tunable_toset_t) { cur, value };
> +	      tunables[ntunables++] =
> +		(struct tunable_toset_t) { cur, value, p - value };
>   	      break;
>   	    }
>   	}
> @@ -231,7 +208,7 @@ parse_tunables_string (char *valstring, struct tunable_toset_t *tunables)
>   }
>   
>   static void
> -parse_tunables (char *valstring)
> +parse_tunables (const char *valstring)
>   {
>     struct tunable_toset_t tunables[tunables_list_size];
>     int ntunables = parse_tunables_string (valstring, tunables);
> @@ -243,7 +220,7 @@ parse_tunables (char *valstring)
>       }
>   
>     for (int i = 0; i < ntunables; i++)
> -    tunable_initialize (tunables[i].t, tunables[i].value);
> +    tunable_initialize (tunables[i].t, tunables[i].value, tunables[i].len);
>   }
>   
>   /* Initialize the tunables list from the environment.  For now we only use the
> @@ -264,9 +241,12 @@ __tunables_init (char **envp)
>     while ((envp = get_next_env (envp, &envname, &len, &envval,
>   			       &prev_envp)) != NULL)
>       {
> +      /* The environment variable is allocated on the stack by the kernel, so
> +	 it is safe to keep the references to the suboptions for later parsing
> +	 of string tunables.  */
>         if (tunable_is_name ("GLIBC_TUNABLES", envname))
>   	{
> -	  parse_tunables (tunables_strdup (envval));
> +	  parse_tunables (envval);
>   	  continue;
>   	}
>   
> @@ -284,7 +264,7 @@ __tunables_init (char **envp)
>   	  /* We have a match.  Initialize and move on to the next line.  */
>   	  if (tunable_is_name (name, envname))
>   	    {
> -	      tunable_initialize (cur, envval);
> +	      tunable_initialize (cur, envval, 0);

Do you want to put an assert in here that the env aliases are only there 
for numeric tunables?  Passing 0 does not seem right for strval tunables.

>   	      break;
>   	    }
>   	}
> @@ -298,7 +278,7 @@ __tunables_print (void)
>       {
>         const tunable_t *cur = &tunable_list[i];
>         if (cur->type.type_code == TUNABLE_TYPE_STRING
> -	  && cur->val.strval == NULL)
> +	  && cur->val.strval.str == NULL)
>   	_dl_printf ("%s:\n", cur->name);
>         else
>   	{
> @@ -324,7 +304,9 @@ __tunables_print (void)
>   			  (size_t) cur->type.max);
>   	      break;
>   	    case TUNABLE_TYPE_STRING:
> -	      _dl_printf ("%s\n", cur->val.strval);
> +	      _dl_printf ("%.*s\n",
> +			  (int) cur->val.strval.len,
> +			  cur->val.strval.str);
>   	      break;
>   	    default:
>   	      __builtin_unreachable ();
> @@ -359,7 +341,7 @@ __tunable_get_val (tunable_id_t id, void *valp, tunable_callback_t callback)
>   	}
>       case TUNABLE_TYPE_STRING:
>   	{
> -	  *((const char **)valp) = cur->val.strval;
> +	  *((struct tunable_str_t **) valp) = &cur->val.strval;
>   	  break;
>   	}
>       default:
> diff --git a/elf/dl-tunables.h b/elf/dl-tunables.h
> index 45c191e021..0e777d7d37 100644
> --- a/elf/dl-tunables.h
> +++ b/elf/dl-tunables.h
> @@ -30,7 +30,11 @@ typedef intmax_t tunable_num_t;
>   typedef union
>   {
>     tunable_num_t numval;
> -  const char *strval;
> +  struct tunable_str_t
> +  {
> +    const char *str;
> +    size_t len;
> +  } strval;
>   } tunable_val_t;
>   
>   typedef void (*tunable_callback_t) (tunable_val_t *);
> diff --git a/sysdeps/generic/dl-tunables-parse.h b/sysdeps/generic/dl-tunables-parse.h
> new file mode 100644
> index 0000000000..a2420f9129
> --- /dev/null
> +++ b/sysdeps/generic/dl-tunables-parse.h
> @@ -0,0 +1,128 @@
> +/* Helper functions to handle tunable strings.
> +   Copyright (C) 2023 Free Software Foundation, Inc.
> +   This file is part of the GNU C Library.
> +
> +   The GNU C Library is free software; you can redistribute it and/or
> +   modify it under the terms of the GNU Lesser General Public
> +   License as published by the Free Software Foundation; either
> +   version 2.1 of the License, or (at your option) any later version.
> +
> +   The GNU C Library is distributed in the hope that it will be useful,
> +   but WITHOUT ANY WARRANTY; without even the implied warranty of
> +   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
> +   Lesser General Public License for more details.
> +
> +   You should have received a copy of the GNU Lesser General Public
> +   License along with the GNU C Library; if not, see
> +   <https://www.gnu.org/licenses/>.  */
> +
> +#ifndef _DL_TUNABLES_PARSE_H
> +#define _DL_TUNABLES_PARSE_H 1
> +
> +#include <string.h>
> +
> +/* Compare the contents of STRVAL with STR of size LEN.  The STR might not
> +   be null-terminated.   */
> +static __always_inline bool
> +tunable_strcmp (const struct tunable_str_t *strval, const char *str,
> +		size_t len)
> +{
> +  return strval->len == len && memcmp (strval->str, str, len) == 0;
> +}
> +#define tunable_strcmp_cte(__tunable, __str) \
> + tunable_strcmp (&__tunable->strval, __str, sizeof (__str) - 1)

What does cte stand for?

> +
> +/*
> +   Helper functions to iterate over a tunable string composed by multiple
> +   suboptions separated by comma.  Each suboptions is return in the form

Maybe enhance this comment like:

   suboptions separated by comma; this is a common pattern for CPU
   feature tunables.

Alternatively, does it make sense to treat this like a list?  That would 
make this abstraction easier to read and reason, e.g.:

   if (tunable_list_contains (&t, opt, opt1_len))
   ...
   if (tunable_list_contains_cte_any (&t, opt1, opt2, ...))

and so on.  You don't actually need the while loop or state maintenance 
then.  In fact, maybe there's a case here to make a LIST tunable type.

> +   of { address, size } (no null terminated).  For instance:
> +
> +     struct tunable_str_comma_t ts;
> +     tunable_str_comma_init (&ts, valp);
> +
> +     struct tunable_str_t t;
> +     while (tunable_str_comma_next (&ts, &t))
> +      {
> +	_dl_printf ("[%s] %.*s (%d)\n",
> +		    __func__,
> +		    (int) tstr.len,
> +		    tstr.str,
> +		    (int) tstr.len);
> +
> +        if (tunable_str_comma_strcmp (&t, opt, opt1_len))
> +	  {
> +	    [...]
> +	  }
> +	else if (tunable_str_comma_strcmp_cte (&t, "opt2"))
> +	  {
> +	    [...]
> +	  }
> +      }
> +*/
> +
> +struct tunable_str_comma_state_t
> +{
> +  const char *p;
> +  size_t plen;
> +  size_t maxplen;
> +};
> +
> +struct tunable_str_comma_t
> +{
> +  const char *str;
> +  size_t len;
> +  bool disable;
> +};
> +
> +static inline void
> +tunable_str_comma_init (struct tunable_str_comma_state_t *state,
> +			tunable_val_t *valp)
> +{
> +  state->p = valp->strval.str;
> +  state->plen = 0;
> +  state->maxplen = valp->strval.len;
> +}
> +
> +static inline bool
> +tunable_str_comma_next (struct tunable_str_comma_state_t *state,
> +			struct tunable_str_comma_t *str)
> +{
> +  if (*state->p == '\0' || state->plen >= state->maxplen)
> +    return false;
> +
> +  const char *c;
> +  for (c = state->p; *c != ','; c++, state->plen++)
> +    if (*c == '\0' || state->plen == state->maxplen)
> +      break;
> +
> +  str->str = state->p;
> +  str->len = c - state->p;
> +
> +  if (str->len > 0)
> +    {
> +      str->disable = *str->str == '-';
> +      if (str->disable)
> +	{
> +	  str->str = str->str + 1;
> +	  str->len = str->len - 1;
> +	}
> +    }
> +
> +  state->p = c + 1;
> +  state->plen++;
> +
> +  return true;
> +}
> +
> +/* Compare the contents of T with STR of size LEN.  The STR might not be
> +   null-terminated.   */
> +static __always_inline bool
> +tunable_str_comma_strcmp (const struct tunable_str_comma_t *t, const char *str,
> +			  size_t len)
> +{
> +  return t->len == len && memcmp (t->str, str, len) == 0;
> +}
> +#define tunable_str_comma_strcmp_cte(__t, __str) \
> +  tunable_str_comma_strcmp (__t, __str, sizeof (__str) - 1)
> +
> +#endif
> diff --git a/sysdeps/s390/cpu-features.c b/sysdeps/s390/cpu-features.c
> index 55449ba07f..8b1466fa7b 100644
> --- a/sysdeps/s390/cpu-features.c
> +++ b/sysdeps/s390/cpu-features.c
> @@ -22,6 +22,7 @@
>   #include <ifunc-memcmp.h>
>   #include <string.h>
>   #include <dl-symbol-redir-ifunc.h>
> +#include <dl-tunables-parse.h>
>   
>   #define S390_COPY_CPU_FEATURES(SRC_PTR, DEST_PTR)	\
>     (DEST_PTR)->hwcap = (SRC_PTR)->hwcap;			\
> @@ -51,33 +52,14 @@ TUNABLE_CALLBACK (set_hwcaps) (tunable_val_t *valp)
>     struct cpu_features cpu_features_curr;
>     S390_COPY_CPU_FEATURES (cpu_features, &cpu_features_curr);
>   
> -  const char *token = valp->strval;
> -  do
> +  struct tunable_str_comma_state_t ts;
> +  tunable_str_comma_init (&ts, valp);
> +
> +  struct tunable_str_comma_t t;
> +  while (tunable_str_comma_next (&ts, &t))
>       {
> -      const char *token_end, *feature;
> -      bool disable;
> -      size_t token_len;
> -      size_t feature_len;
> -
> -      /* Find token separator or end of string.  */
> -      for (token_end = token; *token_end != ','; token_end++)
> -	if (*token_end == '\0')
> -	  break;
> -
> -      /* Determine feature.  */
> -      token_len = token_end - token;
> -      if (*token == '-')
> -	{
> -	  disable = true;
> -	  feature = token + 1;
> -	  feature_len = token_len - 1;
> -	}
> -      else
> -	{
> -	  disable = false;
> -	  feature = token;
> -	  feature_len = token_len;
> -	}
> +      if (t.len == 0)
> +	continue;
>   
>         /* Handle only the features here which are really used in the
>   	 IFUNC-resolvers.  All others are ignored as the values are only used
> @@ -85,85 +67,65 @@ TUNABLE_CALLBACK (set_hwcaps) (tunable_val_t *valp)
>         bool reset_features = false;
>         unsigned long int hwcap_mask = 0UL;
>         unsigned long long stfle_bits0_mask = 0ULL;
> +      bool disable = t.disable;
>   
> -      if ((*feature == 'z' || *feature == 'a'))
> +      if (tunable_str_comma_strcmp_cte (&t, "zEC12")
> +	  || tunable_str_comma_strcmp_cte (&t, "arch10"))
> +	{
> +	  reset_features = true;
> +	  disable = true;
> +	  hwcap_mask = HWCAP_S390_VXRS | HWCAP_S390_VXRS_EXT
> +	    | HWCAP_S390_VXRS_EXT2;
> +	  stfle_bits0_mask = S390_STFLE_MASK_ARCH13_MIE3;
> +	}
> +      else if (tunable_str_comma_strcmp_cte (&t, "z13")
> +	       || tunable_str_comma_strcmp_cte (&t, "arch11"))
> +	{
> +	  reset_features = true;
> +	  disable = true;
> +	  hwcap_mask = HWCAP_S390_VXRS_EXT | HWCAP_S390_VXRS_EXT2;
> +	  stfle_bits0_mask = S390_STFLE_MASK_ARCH13_MIE3;
> +	}
> +      else if (tunable_str_comma_strcmp_cte (&t, "z14")
> +	       || tunable_str_comma_strcmp_cte (&t, "arch12"))
> +	{
> +	  reset_features = true;
> +	  disable = true;
> +	  hwcap_mask = HWCAP_S390_VXRS_EXT2;
> +	  stfle_bits0_mask = S390_STFLE_MASK_ARCH13_MIE3;
> +	}
> +      else if (tunable_str_comma_strcmp_cte (&t, "z15")
> +	       || tunable_str_comma_strcmp_cte (&t, "z16")
> +	       || tunable_str_comma_strcmp_cte (&t, "arch13")
> +	       || tunable_str_comma_strcmp_cte (&t, "arch14"))
>   	{
> -	  if ((feature_len == 5 && *feature == 'z'
> -	       && memcmp (feature, "zEC12", 5) == 0)
> -	      || (feature_len == 6 && *feature == 'a'
> -		  && memcmp (feature, "arch10", 6) == 0))
> -	    {
> -	      reset_features = true;
> -	      disable = true;
> -	      hwcap_mask = HWCAP_S390_VXRS | HWCAP_S390_VXRS_EXT
> -		| HWCAP_S390_VXRS_EXT2;
> -	      stfle_bits0_mask = S390_STFLE_MASK_ARCH13_MIE3;
> -	    }
> -	  else if ((feature_len == 3 && *feature == 'z'
> -		    && memcmp (feature, "z13", 3) == 0)
> -		   || (feature_len == 6 && *feature == 'a'
> -		       && memcmp (feature, "arch11", 6) == 0))
> -	    {
> -	      reset_features = true;
> -	      disable = true;
> -	      hwcap_mask = HWCAP_S390_VXRS_EXT | HWCAP_S390_VXRS_EXT2;
> -	      stfle_bits0_mask = S390_STFLE_MASK_ARCH13_MIE3;
> -	    }
> -	  else if ((feature_len == 3 && *feature == 'z'
> -		    && memcmp (feature, "z14", 3) == 0)
> -		   || (feature_len == 6 && *feature == 'a'
> -		       && memcmp (feature, "arch12", 6) == 0))
> -	    {
> -	      reset_features = true;
> -	      disable = true;
> -	      hwcap_mask = HWCAP_S390_VXRS_EXT2;
> -	      stfle_bits0_mask = S390_STFLE_MASK_ARCH13_MIE3;
> -	    }
> -	  else if ((feature_len == 3 && *feature == 'z'
> -		    && (memcmp (feature, "z15", 3) == 0
> -			|| memcmp (feature, "z16", 3) == 0))
> -		   || (feature_len == 6
> -		       && (memcmp (feature, "arch13", 6) == 0
> -			   || memcmp (feature, "arch14", 6) == 0)))
> -	    {
> -	      /* For z15 or newer we don't have to disable something,
> -		 but we have to reset to the original values.  */
> -	      reset_features = true;
> -	    }
> +	  /* For z15 or newer we don't have to disable something, but we have
> +	     to reset to the original values.  */
> +	  reset_features = true;
>   	}
> -      else if (*feature == 'H')
> +      else if (tunable_str_comma_strcmp_cte (&t, "HWCAP_S390_VXRS"))
>   	{
> -	  if (feature_len == 15
> -	      && memcmp (feature, "HWCAP_S390_VXRS", 15) == 0)
> -	    {
> -	      hwcap_mask = HWCAP_S390_VXRS;
> -	      if (disable)
> -		hwcap_mask |= HWCAP_S390_VXRS_EXT | HWCAP_S390_VXRS_EXT2;
> -	    }
> -	  else if (feature_len == 19
> -		   && memcmp (feature, "HWCAP_S390_VXRS_EXT", 19) == 0)
> -	    {
> -	      hwcap_mask = HWCAP_S390_VXRS_EXT;
> -	      if (disable)
> -		hwcap_mask |= HWCAP_S390_VXRS_EXT2;
> -	      else
> -		hwcap_mask |= HWCAP_S390_VXRS;
> -	    }
> -	  else if (feature_len == 20
> -		   && memcmp (feature, "HWCAP_S390_VXRS_EXT2", 20) == 0)
> -	    {
> -	      hwcap_mask = HWCAP_S390_VXRS_EXT2;
> -	      if (!disable)
> -		hwcap_mask |= HWCAP_S390_VXRS | HWCAP_S390_VXRS_EXT;
> -	    }
> +	  hwcap_mask = HWCAP_S390_VXRS;
> +	  if (t.disable)
> +	    hwcap_mask |= HWCAP_S390_VXRS_EXT | HWCAP_S390_VXRS_EXT2;
>   	}
> -      else if (*feature == 'S')
> +      else if (tunable_str_comma_strcmp_cte (&t, "HWCAP_S390_VXRS_EXT"))
>   	{
> -	  if (feature_len == 10
> -	      && memcmp (feature, "STFLE_MIE3", 10) == 0)
> -	    {
> -	      stfle_bits0_mask = S390_STFLE_MASK_ARCH13_MIE3;
> -	    }
> +	  hwcap_mask = HWCAP_S390_VXRS_EXT;
> +	  if (t.disable)
> +	    hwcap_mask |= HWCAP_S390_VXRS_EXT2;
> +	  else
> +	    hwcap_mask |= HWCAP_S390_VXRS;
> +	}
> +      else if (tunable_str_comma_strcmp_cte (&t, "HWCAP_S390_VXRS_EXT2"))
> +	{
> +	  hwcap_mask = HWCAP_S390_VXRS_EXT2;
> +	  if (!t.disable)
> +	    hwcap_mask |= HWCAP_S390_VXRS | HWCAP_S390_VXRS_EXT;
> +	}
> +      else if (tunable_str_comma_strcmp_cte (&t, "STFLE_MIE3"))
> +	{
> +	  stfle_bits0_mask = S390_STFLE_MASK_ARCH13_MIE3;
>   	}
>   
>         /* Perform the actions determined above.  */
> @@ -187,14 +149,7 @@ TUNABLE_CALLBACK (set_hwcaps) (tunable_val_t *valp)
>   	  else
>   	    cpu_features_curr.stfle_bits[0] |= stfle_bits0_mask;
>   	}
> -
> -      /* Jump over current token ... */
> -      token += token_len;
> -
> -      /* ... and skip token separator for next round.  */
> -      if (*token == ',') token++;
>       }
> -  while (*token != '\0');
>   
>     /* Copy back the features after checking that no unsupported features were
>        enabled by user.  */
> diff --git a/sysdeps/unix/sysv/linux/aarch64/cpu-features.c b/sysdeps/unix/sysv/linux/aarch64/cpu-features.c
> index dc09c1c827..3f1a6bcd62 100644
> --- a/sysdeps/unix/sysv/linux/aarch64/cpu-features.c
> +++ b/sysdeps/unix/sysv/linux/aarch64/cpu-features.c
> @@ -16,10 +16,12 @@
>      License along with the GNU C Library; if not, see
>      <https://www.gnu.org/licenses/>.  */
>   
> +#include <array_length.h>
>   #include <cpu-features.h>
>   #include <sys/auxv.h>
>   #include <elf/dl-hwcaps.h>
>   #include <sys/prctl.h>
> +#include <dl-tunables-parse.h>
>   
>   #define DCZID_DZP_MASK (1 << 4)
>   #define DCZID_BS_MASK (0xf)
> @@ -33,28 +35,32 @@
>   struct cpu_list
>   {
>     const char *name;
> +  size_t len;
>     uint64_t midr;
>   };
>   
> -static struct cpu_list cpu_list[] = {
> -      {"falkor",	 0x510FC000},
> -      {"thunderxt88",	 0x430F0A10},
> -      {"thunderx2t99",   0x431F0AF0},
> -      {"thunderx2t99p1", 0x420F5160},
> -      {"phecda",	 0x680F0000},
> -      {"ares",		 0x411FD0C0},
> -      {"emag",		 0x503F0001},
> -      {"kunpeng920", 	 0x481FD010},
> -      {"a64fx",		 0x460F0010},
> -      {"generic", 	 0x0}
> +static const struct cpu_list cpu_list[] =
> +{
> +#define CPU_LIST_ENTRY(__str, __num) { __str, sizeof (__str) - 1, __num }
> +  CPU_LIST_ENTRY ("falkor",         0x510FC000),
> +  CPU_LIST_ENTRY ("thunderxt88",    0x430F0A10),
> +  CPU_LIST_ENTRY ("thunderx2t99",   0x431F0AF0),
> +  CPU_LIST_ENTRY ("thunderx2t99p1", 0x420F5160),
> +  CPU_LIST_ENTRY ("phecda",         0x680F0000),
> +  CPU_LIST_ENTRY ("ares",           0x411FD0C0),
> +  CPU_LIST_ENTRY ("emag",           0x503F0001),
> +  CPU_LIST_ENTRY ("kunpeng920",     0x481FD010),
> +  CPU_LIST_ENTRY ("a64fx",          0x460F0010),
> +  CPU_LIST_ENTRY ("generic",        0x0),
>   };
>   
>   static uint64_t
> -get_midr_from_mcpu (const char *mcpu)
> +get_midr_from_mcpu (const struct tunable_str_t *mcpu)
>   {
> -  for (int i = 0; i < sizeof (cpu_list) / sizeof (struct cpu_list); i++)
> -    if (strcmp (mcpu, cpu_list[i].name) == 0)
> +  for (int i = 0; i < array_length (cpu_list); i++) {
> +    if (tunable_strcmp (mcpu, cpu_list[i].name, cpu_list[i].len))
>         return cpu_list[i].midr;
> +  }
>   
>     return UINT64_MAX;
>   }
> @@ -65,7 +71,9 @@ init_cpu_features (struct cpu_features *cpu_features)
>     register uint64_t midr = UINT64_MAX;
>   
>     /* Get the tunable override.  */
> -  const char *mcpu = TUNABLE_GET (glibc, cpu, name, const char *, NULL);
> +  const struct tunable_str_t *mcpu = TUNABLE_GET (glibc, cpu, name,
> +						  struct tunable_str_t *,
> +						  NULL);
>     if (mcpu != NULL)
>       midr = get_midr_from_mcpu (mcpu);
>   
> diff --git a/sysdeps/unix/sysv/linux/powerpc/cpu-features.c b/sysdeps/unix/sysv/linux/powerpc/cpu-features.c
> index 7c6e20e702..390b3fd11a 100644
> --- a/sysdeps/unix/sysv/linux/powerpc/cpu-features.c
> +++ b/sysdeps/unix/sysv/linux/powerpc/cpu-features.c
> @@ -20,6 +20,7 @@
>   #include <stdint.h>
>   #include <cpu-features.h>
>   #include <elf/dl-tunables.h>
> +#include <dl-tunables-parse.h>
>   #include <unistd.h>
>   #include <string.h>
>   
> @@ -43,41 +44,26 @@ TUNABLE_CALLBACK (set_hwcaps) (tunable_val_t *valp)
>     struct cpu_features *cpu_features = &GLRO(dl_powerpc_cpu_features);
>     unsigned long int tcbv_hwcap = cpu_features->hwcap;
>     unsigned long int tcbv_hwcap2 = cpu_features->hwcap2;
> -  const char *token = valp->strval;
> -  do
> +
> +  struct tunable_str_comma_state_t ts;
> +  tunable_str_comma_init (&ts, valp);
> +
> +  struct tunable_str_comma_t t;
> +  while (tunable_str_comma_next (&ts, &t))
>       {
> -      const char *token_end, *feature;
> -      bool disable;
> -      size_t token_len, i, feature_len, offset = 0;
> -      /* Find token separator or end of string.  */
> -      for (token_end = token; *token_end != ','; token_end++)
> -	if (*token_end == '\0')
> -	  break;
> +      if (t.len == 0)
> +	continue;
>   
> -      /* Determine feature.  */
> -      token_len = token_end - token;
> -      if (*token == '-')
> -	{
> -	  disable = true;
> -	  feature = token + 1;
> -	  feature_len = token_len - 1;
> -	}
> -      else
> -	{
> -	  disable = false;
> -	  feature = token;
> -	  feature_len = token_len;
> -	}
> -      for (i = 0; i < array_length (hwcap_tunables); ++i)
> +      size_t offset = 0;
> +      for (int i = 0; i < array_length (hwcap_tunables); ++i)
>   	{
>   	  const char *hwcap_name = hwcap_names + offset;
>   	  size_t hwcap_name_len = strlen (hwcap_name);
>   	  /* Check the tunable name on the supported list.  */
> -	  if (hwcap_name_len == feature_len
> -	      && memcmp (feature, hwcap_name, feature_len) == 0)
> +	  if (tunable_str_comma_strcmp (&t, hwcap_name, hwcap_name_len))
>   	    {
>   	      /* Update the hwcap and hwcap2 bits.  */
> -	      if (disable)
> +	      if (t.disable)
>   		{
>   		  /* Id is 1 for hwcap2 tunable.  */
>   		  if (hwcap_tunables[i].id)
> @@ -98,12 +84,7 @@ TUNABLE_CALLBACK (set_hwcaps) (tunable_val_t *valp)
>   	    }
>   	  offset += hwcap_name_len + 1;
>   	}
> -	token += token_len;
> -	/* ... and skip token separator for next round.  */
> -	if (*token == ',')
> -	  token++;
>       }
> -  while (*token != '\0');
>   }
>   
>   static inline void
> diff --git a/sysdeps/unix/sysv/linux/powerpc/tst-hwcap-tunables.c b/sysdeps/unix/sysv/linux/powerpc/tst-hwcap-tunables.c
> index 2631016a3a..049164f841 100644
> --- a/sysdeps/unix/sysv/linux/powerpc/tst-hwcap-tunables.c
> +++ b/sysdeps/unix/sysv/linux/powerpc/tst-hwcap-tunables.c
> @@ -110,7 +110,11 @@ do_test (int argc, char *argv[])
>   	run_test ("-arch_2_06", "__memcpy_power7");
>         if (hwcap & PPC_FEATURE_ARCH_2_05)
>   	run_test ("-arch_2_06,-arch_2_05","__memcpy_power6");
> -      run_test ("-arch_2_06,-arch_2_05,-power5+,-power5,-power4", "__memcpy_power4");
> +      run_test ("-arch_2_06,-arch_2_05,-power5+,-power5,-power4",
> +		"__memcpy_power4");
> +      /* Also run with valid, but empty settings.  */
> +      run_test (",-,-arch_2_06,-arch_2_05,-power5+,-power5,,-power4,-",
> +		"__memcpy_power4");
>       }
>     else
>       {
> diff --git a/sysdeps/x86/Makefile b/sysdeps/x86/Makefile
> index 917c26f116..a64e5f002a 100644
> --- a/sysdeps/x86/Makefile
> +++ b/sysdeps/x86/Makefile
> @@ -12,7 +12,8 @@ CFLAGS-get-cpuid-feature-leaf.o += $(no-stack-protector)
>   
>   tests += tst-get-cpu-features tst-get-cpu-features-static \
>   	 tst-cpu-features-cpuinfo tst-cpu-features-cpuinfo-static \
> -	 tst-cpu-features-supports tst-cpu-features-supports-static
> +	 tst-cpu-features-supports tst-cpu-features-supports-static \
> +	 tst-hwcap-tunables
>   tests-static += tst-get-cpu-features-static \
>   		tst-cpu-features-cpuinfo-static \
>   		tst-cpu-features-supports-static
> @@ -65,6 +66,7 @@ $(objpfx)tst-isa-level-1.out: $(objpfx)tst-isa-level-mod-1-baseline.so \
>   endif
>   tst-ifunc-isa-2-ENV = GLIBC_TUNABLES=glibc.cpu.hwcaps=-SSE4_2,-AVX,-AVX2,-AVX512F
>   tst-ifunc-isa-2-static-ENV = $(tst-ifunc-isa-2-ENV)
> +tst-hwcap-tunables-ARGS = -- $(host-test-program-cmd)
>   endif
>   
>   ifeq ($(subdir),math)
> diff --git a/sysdeps/x86/cpu-tunables.c b/sysdeps/x86/cpu-tunables.c
> index 5697885226..0608209768 100644
> --- a/sysdeps/x86/cpu-tunables.c
> +++ b/sysdeps/x86/cpu-tunables.c
> @@ -24,11 +24,12 @@
>   #include <string.h>
>   #include <cpu-features.h>
>   #include <ldsodefs.h>
> +#include <dl-tunables-parse.h>
>   #include <dl-symbol-redir-ifunc.h>
>   
>   #define CHECK_GLIBC_IFUNC_CPU_OFF(f, cpu_features, name, len)		\
>     _Static_assert (sizeof (#name) - 1 == len, #name " != " #len);	\
> -  if (memcmp (f, #name, len) == 0)					\
> +  if (tunable_str_comma_strcmp_cte (&f, #name))				\
>       {									\
>         CPU_FEATURE_UNSET (cpu_features, name)				\
>         break;								\
> @@ -38,7 +39,7 @@
>      which isn't available.  */
>   #define CHECK_GLIBC_IFUNC_PREFERRED_OFF(f, cpu_features, name, len)	\
>     _Static_assert (sizeof (#name) - 1 == len, #name " != " #len);	\
> -  if (memcmp (f, #name, len) == 0)					\
> +  if (tunable_str_comma_strcmp_cte (&f, #name) == 0)			\
>       {									\
>         cpu_features->preferred[index_arch_##name]			\
>   	&= ~bit_arch_##name;						\
> @@ -46,12 +47,11 @@
>       }
>   
>   /* Enable/disable a preferred feature NAME.  */
> -#define CHECK_GLIBC_IFUNC_PREFERRED_BOTH(f, cpu_features, name,	\
> -					  disable, len)			\
> +#define CHECK_GLIBC_IFUNC_PREFERRED_BOTH(f, cpu_features, name,	len)	\
>     _Static_assert (sizeof (#name) - 1 == len, #name " != " #len);	\
> -  if (memcmp (f, #name, len) == 0)					\
> +  if (tunable_str_comma_strcmp_cte (&f, #name))				\
>       {									\
> -      if (disable)							\
> +      if (f.disable)							\
>   	cpu_features->preferred[index_arch_##name] &= ~bit_arch_##name;	\
>         else								\
>   	cpu_features->preferred[index_arch_##name] |= bit_arch_##name;	\
> @@ -61,11 +61,11 @@
>   /* Enable/disable a preferred feature NAME.  Enable a preferred feature
>      only if the feature NEED is usable.  */
>   #define CHECK_GLIBC_IFUNC_PREFERRED_NEED_BOTH(f, cpu_features, name,	\
> -					       need, disable, len)	\
> +					      need, len)		\
>     _Static_assert (sizeof (#name) - 1 == len, #name " != " #len);	\
> -  if (memcmp (f, #name, len) == 0)					\
> +  if (tunable_str_comma_strcmp_cte (&f, #name))				\
>       {									\
> -      if (disable)							\
> +      if (f.disable)							\
>   	cpu_features->preferred[index_arch_##name] &= ~bit_arch_##name;	\
>         else if (CPU_FEATURE_USABLE_P (cpu_features, need))		\
>   	cpu_features->preferred[index_arch_##name] |= bit_arch_##name;	\
> @@ -93,38 +93,20 @@ TUNABLE_CALLBACK (set_hwcaps) (tunable_val_t *valp)
>        NOTE: the IFUNC selection may change over time.  Please check all
>        multiarch implementations when experimenting.  */
>   
> -  const char *p = valp->strval, *c;
>     struct cpu_features *cpu_features = &GLRO(dl_x86_cpu_features);
> -  size_t len;
>   
> -  do
> -    {
> -      const char *n;
> -      bool disable;
> -      size_t nl;
> -
> -      for (c = p; *c != ','; c++)
> -	if (*c == '\0')
> -	  break;
> +  struct tunable_str_comma_state_t ts;
> +  tunable_str_comma_init (&ts, valp);
>   
> -      len = c - p;
> -      disable = *p == '-';
> -      if (disable)
> -	{
> -	  n = p + 1;
> -	  nl = len - 1;
> -	}
> -      else
> -	{
> -	  n = p;
> -	  nl = len;
> -	}
> -      switch (nl)
> +  struct tunable_str_comma_t n;
> +  while (tunable_str_comma_next (&ts, &n))
> +    {
> +      switch (n.len)
>   	{
>   	default:
>   	  break;
>   	case 3:
> -	  if (disable)
> +	  if (n.disable)
>   	    {
>   	      CHECK_GLIBC_IFUNC_CPU_OFF (n, cpu_features, AVX, 3);
>   	      CHECK_GLIBC_IFUNC_CPU_OFF (n, cpu_features, CX8, 3);
> @@ -135,7 +117,7 @@ TUNABLE_CALLBACK (set_hwcaps) (tunable_val_t *valp)
>   	    }
>   	  break;
>   	case 4:
> -	  if (disable)
> +	  if (n.disable)
>   	    {
>   	      CHECK_GLIBC_IFUNC_CPU_OFF (n, cpu_features, AVX2, 4);
>   	      CHECK_GLIBC_IFUNC_CPU_OFF (n, cpu_features, BMI1, 4);
> @@ -149,7 +131,7 @@ TUNABLE_CALLBACK (set_hwcaps) (tunable_val_t *valp)
>   	    }
>   	  break;
>   	case 5:
> -	  if (disable)
> +	  if (n.disable)
>   	    {
>   	      CHECK_GLIBC_IFUNC_CPU_OFF (n, cpu_features, LZCNT, 5);
>   	      CHECK_GLIBC_IFUNC_CPU_OFF (n, cpu_features, MOVBE, 5);
> @@ -159,12 +141,12 @@ TUNABLE_CALLBACK (set_hwcaps) (tunable_val_t *valp)
>   	    }
>   	  break;
>   	case 6:
> -	  if (disable)
> +	  if (n.disable)
>   	    {
>   	      CHECK_GLIBC_IFUNC_CPU_OFF (n, cpu_features, POPCNT, 6);
>   	      CHECK_GLIBC_IFUNC_CPU_OFF (n, cpu_features, SSE4_1, 6);
>   	      CHECK_GLIBC_IFUNC_CPU_OFF (n, cpu_features, SSE4_2, 6);
> -	      if (memcmp (n, "XSAVEC", 6) == 0)
> +	      if (memcmp (n.str, "XSAVEC", 6) == 0)
>   		{
>   		  /* Update xsave_state_size to XSAVE state size.  */
>   		  cpu_features->xsave_state_size
> @@ -174,14 +156,14 @@ TUNABLE_CALLBACK (set_hwcaps) (tunable_val_t *valp)
>   	    }
>   	  break;
>   	case 7:
> -	  if (disable)
> +	  if (n.disable)
>   	    {
>   	      CHECK_GLIBC_IFUNC_CPU_OFF (n, cpu_features, AVX512F, 7);
>   	      CHECK_GLIBC_IFUNC_CPU_OFF (n, cpu_features, OSXSAVE, 7);
>   	    }
>   	  break;
>   	case 8:
> -	  if (disable)
> +	  if (n.disable)
>   	    {
>   	      CHECK_GLIBC_IFUNC_CPU_OFF (n, cpu_features, AVX512CD, 8);
>   	      CHECK_GLIBC_IFUNC_CPU_OFF (n, cpu_features, AVX512BW, 8);
> @@ -190,86 +172,72 @@ TUNABLE_CALLBACK (set_hwcaps) (tunable_val_t *valp)
>   	      CHECK_GLIBC_IFUNC_CPU_OFF (n, cpu_features, AVX512PF, 8);
>   	      CHECK_GLIBC_IFUNC_CPU_OFF (n, cpu_features, AVX512VL, 8);
>   	    }
> -	  CHECK_GLIBC_IFUNC_PREFERRED_BOTH (n, cpu_features, Slow_BSF,
> -					    disable, 8);
> +	  CHECK_GLIBC_IFUNC_PREFERRED_BOTH (n, cpu_features, Slow_BSF, 8);
>   	  break;
>   	case 11:
>   	    {
> -	      CHECK_GLIBC_IFUNC_PREFERRED_BOTH (n, cpu_features,
> -						Prefer_ERMS,
> -						disable, 11);
> -	      CHECK_GLIBC_IFUNC_PREFERRED_BOTH (n, cpu_features,
> -						Prefer_FSRM,
> -						disable, 11);
> +	      CHECK_GLIBC_IFUNC_PREFERRED_BOTH (n, cpu_features, Prefer_ERMS,
> +						11);
> +	      CHECK_GLIBC_IFUNC_PREFERRED_BOTH (n, cpu_features, Prefer_FSRM,
> +						11);
>   	      CHECK_GLIBC_IFUNC_PREFERRED_NEED_BOTH (n, cpu_features,
>   						     Slow_SSE4_2,
>   						     SSE4_2,
> -						     disable, 11);
> +						     11);
>   	    }
>   	  break;
>   	case 15:
>   	    {
>   	      CHECK_GLIBC_IFUNC_PREFERRED_BOTH (n, cpu_features,
> -						Fast_Rep_String,
> -						disable, 15);
> +						Fast_Rep_String, 15);
>   	    }
>   	  break;
>   	case 16:
>   	    {
>   	      CHECK_GLIBC_IFUNC_PREFERRED_NEED_BOTH
> -		(n, cpu_features, Prefer_No_AVX512, AVX512F,
> -		 disable, 16);
> +		(n, cpu_features, Prefer_No_AVX512, AVX512F, 16);
>   	    }
>   	  break;
>   	case 18:
>   	    {
>   	      CHECK_GLIBC_IFUNC_PREFERRED_BOTH (n, cpu_features,
> -						Fast_Copy_Backward,
> -						disable, 18);
> +						Fast_Copy_Backward, 18);
>   	    }
>   	  break;
>   	case 19:
>   	    {
>   	      CHECK_GLIBC_IFUNC_PREFERRED_BOTH (n, cpu_features,
> -						Fast_Unaligned_Load,
> -						disable, 19);
> +						Fast_Unaligned_Load, 19);
>   	      CHECK_GLIBC_IFUNC_PREFERRED_BOTH (n, cpu_features,
> -						Fast_Unaligned_Copy,
> -						disable, 19);
> +						Fast_Unaligned_Copy, 19);
>   	    }
>   	  break;
>   	case 20:
>   	    {
>   	      CHECK_GLIBC_IFUNC_PREFERRED_NEED_BOTH
> -		(n, cpu_features, Prefer_No_VZEROUPPER, AVX, disable,
> -		 20);
> +		(n, cpu_features, Prefer_No_VZEROUPPER, AVX, 20);
>   	    }
>   	  break;
>   	case 23:
>   	    {
>   	      CHECK_GLIBC_IFUNC_PREFERRED_NEED_BOTH
> -		(n, cpu_features, AVX_Fast_Unaligned_Load, AVX,
> -		 disable, 23);
> +		(n, cpu_features, AVX_Fast_Unaligned_Load, AVX, 23);
>   	    }
>   	  break;
>   	case 24:
>   	    {
>   	      CHECK_GLIBC_IFUNC_PREFERRED_NEED_BOTH
> -		(n, cpu_features, MathVec_Prefer_No_AVX512, AVX512F,
> -		 disable, 24);
> +		(n, cpu_features, MathVec_Prefer_No_AVX512, AVX512F, 24);
>   	    }
>   	  break;
>   	case 26:
>   	    {
>   	      CHECK_GLIBC_IFUNC_PREFERRED_NEED_BOTH
> -		(n, cpu_features, Prefer_PMINUB_for_stringop, SSE2,
> -		 disable, 26);
> +		(n, cpu_features, Prefer_PMINUB_for_stringop, SSE2, 26);
>   	    }
>   	  break;
>   	}
> -      p += len + 1;
>       }
> -  while (*c != '\0');
>   }
>   
>   #if CET_ENABLED
> @@ -277,11 +245,11 @@ attribute_hidden
>   void
>   TUNABLE_CALLBACK (set_x86_ibt) (tunable_val_t *valp)
>   {
> -  if (memcmp (valp->strval, "on", sizeof ("on")) == 0)
> +  if (tunable_strcmp_cte (valp, "on"))
>       GL(dl_x86_feature_control).ibt = cet_always_on;
> -  else if (memcmp (valp->strval, "off", sizeof ("off")) == 0)
> +  else if (tunable_strcmp_cte (valp, "off"))
>       GL(dl_x86_feature_control).ibt = cet_always_off;
> -  else if (memcmp (valp->strval, "permissive", sizeof ("permissive")) == 0)
> +  else if (tunable_strcmp_cte (valp, "permissive"))
>       GL(dl_x86_feature_control).ibt = cet_permissive;
>   }
>   
> @@ -289,11 +257,11 @@ attribute_hidden
>   void
>   TUNABLE_CALLBACK (set_x86_shstk) (tunable_val_t *valp)
>   {
> -  if (memcmp (valp->strval, "on", sizeof ("on")) == 0)
> +  if (tunable_strcmp_cte (valp, "on"))
>       GL(dl_x86_feature_control).shstk = cet_always_on;
> -  else if (memcmp (valp->strval, "off", sizeof ("off")) == 0)
> +  else if (tunable_strcmp_cte (valp, "off"))
>       GL(dl_x86_feature_control).shstk = cet_always_off;
> -  else if (memcmp (valp->strval, "permissive", sizeof ("permissive")) == 0)
> +  else if (tunable_strcmp_cte (valp, "permissive"))
>       GL(dl_x86_feature_control).shstk = cet_permissive;
>   }
>   #endif
> diff --git a/sysdeps/x86/tst-hwcap-tunables.c b/sysdeps/x86/tst-hwcap-tunables.c
> new file mode 100644
> index 0000000000..e9f50ffbe0
> --- /dev/null
> +++ b/sysdeps/x86/tst-hwcap-tunables.c
> @@ -0,0 +1,150 @@
> +/* Tests for powerpc GLIBC_TUNABLES=glibc.cpu.hwcaps filter.
> +   Copyright (C) 2023 Free Software Foundation, Inc.
> +   This file is part of the GNU C Library.
> +
> +   The GNU C Library is free software; you can redistribute it and/or
> +   modify it under the terms of the GNU Lesser General Public
> +   License as published by the Free Software Foundation; either
> +   version 2.1 of the License, or (at your option) any later version.
> +
> +   The GNU C Library is distributed in the hope that it will be useful,
> +   but WITHOUT ANY WARRANTY; without even the implied warranty of
> +   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
> +   Lesser General Public License for more details.
> +
> +   You should have received a copy of the GNU Lesser General Public
> +   License along with the GNU C Library; if not, see
> +   <http://www.gnu.org/licenses/>.  */
> +
> +#pragma GCC optimize ("O0")
> +
> +#include <array_length.h>
> +#include <getopt.h>
> +#include <ifunc-impl-list.h>
> +#include <spawn.h>
> +#include <stdio.h>
> +#include <stdlib.h>
> +#include <string.h>
> +#include <intprops.h>
> +#include <support/check.h>
> +#include <support/support.h>
> +#include <support/xunistd.h>
> +#include <support/capture_subprocess.h>
> +
> +/* Nonzero if the program gets called via `exec'.  */
> +#define CMDLINE_OPTIONS \
> +  { "restart", no_argument, &restart, 1 },
> +static int restart;
> +
> +/* Disable everything.  */
> +static const char *test_1[] =
> +{
> +  "__memcpy_avx512_no_vzeroupper",
> +  "__memcpy_avx512_unaligned",
> +  "__memcpy_avx512_unaligned_erms",
> +  "__memcpy_evex_unaligned",
> +  "__memcpy_evex_unaligned_erms",
> +  "__memcpy_avx_unaligned",
> +  "__memcpy_avx_unaligned_erms",
> +  "__memcpy_avx_unaligned_rtm",
> +  "__memcpy_avx_unaligned_erms_rtm",
> +  "__memcpy_ssse3",
> +};
> +
> +static const struct test_t
> +{
> +  const char *env;
> +  const char *const *funcs;
> +  size_t nfuncs;
> +} tests[] =
> +{
> +  {
> +    /* Disable everything.  */
> +    "-Prefer_ERMS,-Prefer_FSRM,-AVX,-AVX2,-AVX_Usable,-AVX2_Usable,"
> +    "-AVX512F_Usable,-SSE4_1,-SSE4_2,-SSSE3,-Fast_Unaligned_Load,-ERMS,"
> +    "-AVX_Fast_Unaligned_Load",
> +    test_1,
> +    array_length (test_1)
> +  },
> +  {
> +    /* Same as before, but with some empty suboptions.  */
> +    ",-,-Prefer_ERMS,-Prefer_FSRM,-AVX,-AVX2,-AVX_Usable,-AVX2_Usable,"
> +    "-AVX512F_Usable,-SSE4_1,-SSE4_2,,-SSSE3,-Fast_Unaligned_Load,,-,-ERMS,"
> +    "-AVX_Fast_Unaligned_Load,-,",
> +    test_1,
> +    array_length (test_1)
> +  }
> +};
> +
> +/* Called on process re-execution.  */
> +_Noreturn static void
> +handle_restart (int ntest)
> +{
> +  struct libc_ifunc_impl impls[32];
> +  int cnt = __libc_ifunc_impl_list ("memcpy", impls, array_length (impls));
> +  if (cnt == 0)
> +    _exit (EXIT_SUCCESS);
> +  TEST_VERIFY_EXIT (cnt >= 1);
> +  for (int i = 0; i < cnt; i++)
> +    {
> +      for (int f = 0; f < tests[ntest].nfuncs; f++)
> +	{
> +	  if (strcmp (impls[i].name, tests[ntest].funcs[f]) == 0)
> +	    TEST_COMPARE (impls[i].usable, false);
> +	}
> +    }
> +
> +  _exit (EXIT_SUCCESS);
> +}
> +
> +static int
> +do_test (int argc, char *argv[])
> +{
> +  /* We must have either:
> +     - One our fource parameters left if called initially:
> +       + path to ld.so         optional
> +       + "--library-path"      optional
> +       + the library path      optional
> +       + the application name
> +       + the test to check  */
> +
> +  TEST_VERIFY_EXIT (argc == 2 || argc == 5);
> +
> +  if (restart)
> +    handle_restart (atoi (argv[1]));
> +
> +  char nteststr[INT_BUFSIZE_BOUND (int)];
> +
> +  char *spargv[10];
> +  {
> +    int i = 0;
> +    for (; i < argc - 1; i++)
> +      spargv[i] = argv[i + 1];
> +    spargv[i++] = (char *) "--direct";
> +    spargv[i++] = (char *) "--restart";
> +    spargv[i++] = nteststr;
> +    spargv[i] = NULL;
> +  }
> +
> +  for (int i = 0; i < array_length (tests); i++)
> +    {
> +      snprintf (nteststr, sizeof nteststr, "%d", i);
> +
> +      printf ("[%d] Spawned test for %s\n", i, tests[i].env);
> +      char *tunable = xasprintf ("glibc.cpu.hwcaps=%s", tests[i].env);
> +      setenv ("GLIBC_TUNABLES", tunable, 1);
> +
> +      struct support_capture_subprocess result
> +	= support_capture_subprogram (spargv[0], spargv);
> +      support_capture_subprocess_check (&result, "tst-tunables", 0,
> +					sc_allow_stderr);
> +      support_capture_subprocess_free (&result);
> +
> +      free (tunable);
> +    }
> +
> +  return 0;
> +}
> +
> +#define TEST_FUNCTION_ARGV do_test
> +#include <support/test-driver.c>

^ permalink raw reply	[flat|nested] 45+ messages in thread

* Re: [PATCH v2 06/19] elf: Do not parse ill-formatted strings
  2023-10-27 10:25   ` Siddhesh Poyarekar
@ 2023-10-27 12:51     ` Adhemerval Zanella Netto
  0 siblings, 0 replies; 45+ messages in thread
From: Adhemerval Zanella Netto @ 2023-10-27 12:51 UTC (permalink / raw)
  To: Siddhesh Poyarekar, libc-alpha



On 27/10/23 07:25, Siddhesh Poyarekar wrote:
> 
> 
> On 2023-10-17 09:05, Adhemerval Zanella wrote:
>> Instead of ignoring ill-formatted tunable strings, first, check all the
>> tunable definitions are correct and then set each tunable value. It
>> means that partially invalid strings, like "key1=value1:key2=key2=value'
>> or 'key1=value':key2=value2=value2' do not enable 'key1=value1'. It
>> avoids possible user-defined errors in tunable definitions.
>>
>> Checked on x86_64-linux-gnu.
>> ---
> 
> Harsher than 5/19, but fair I guess.  Please send v3 with a tiny nit fixup I've mentioned below.

Ack.

>> +
>> +  return ntunables;
>> +}
>> +
>> +static void
>> +parse_tunables (char *valstring)
>> +{
>> +  struct tunable_toset_t tunables[tunables_list_size];
>> +  int ntunables = parse_tunables_string (valstring, tunables);
>> +  if (ntunables == -1)
>> +    return;
> 
> You don't actually need this; the for loop below will return without doing anything if ntunables == -1.

Ack.

^ permalink raw reply	[flat|nested] 45+ messages in thread

* Re: [PATCH v2 08/19] elf: Emit warning if tunable is ill-formatted
  2023-10-27 10:28   ` Siddhesh Poyarekar
@ 2023-10-27 12:51     ` Adhemerval Zanella Netto
  0 siblings, 0 replies; 45+ messages in thread
From: Adhemerval Zanella Netto @ 2023-10-27 12:51 UTC (permalink / raw)
  To: Siddhesh Poyarekar, libc-alpha



On 27/10/23 07:28, Siddhesh Poyarekar wrote:
> 
> 
> On 2023-10-17 09:05, Adhemerval Zanella wrote:
>> So caller knows that the tunable will be ignored.
>>
>> Checked on x86_64-linux-gnu.
>> ---
>>   elf/dl-tunables.c | 6 +++++-
>>   1 file changed, 5 insertions(+), 1 deletion(-)
>>
>> diff --git a/elf/dl-tunables.c b/elf/dl-tunables.c
>> index 5d4b8c5bc0..b39c14694c 100644
>> --- a/elf/dl-tunables.c
>> +++ b/elf/dl-tunables.c
>> @@ -236,7 +236,11 @@ parse_tunables (char *valstring)
>>     struct tunable_toset_t tunables[tunables_list_size];
>>     int ntunables = parse_tunables_string (valstring, tunables);
>>     if (ntunables == -1)
>> -    return;
>> +    {
>> +      _dl_error_printf (
>> +        "ERROR: ld.so: invalid GLIBC_TUNABLES `%s': ignored.\n", valstring);
> 
> If it's a warning, then we should say WARNING here.

Ack.

> 
>> +      return;
>> +    }
>>       for (int i = 0; i < ntunables; i++)
>>       tunable_initialize (tunables[i].t, tunables[i].value);

^ permalink raw reply	[flat|nested] 45+ messages in thread

* Re: [PATCH v2 12/19] elf: Ignore LD_PROFILE for setuid binaries
  2023-10-17 13:05 ` [PATCH v2 12/19] elf: Ignore LD_PROFILE for setuid binaries Adhemerval Zanella
@ 2023-10-27 14:25   ` Siddhesh Poyarekar
  2023-12-12 10:11     ` Florian Weimer
  0 siblings, 1 reply; 45+ messages in thread
From: Siddhesh Poyarekar @ 2023-10-27 14:25 UTC (permalink / raw)
  To: Adhemerval Zanella, libc-alpha; +Cc: Carlos O'Donell, Florian Weimer

On 2023-10-17 09:05, Adhemerval Zanella wrote:
> Loader does not ignore LD_PROFILE in secure-execution mode (different
> than man-page states [1]), rather it uses a different path
> (/var/profile) and ignore LD_PROFILE_OUTPUT.
> 
> Allowing secure-execution profiling is already a non good security
> boundary, since it enables different code paths and extra OS access by
> the process.  But by ignoring LD_PROFILE_OUTPUT, the resulting profile
> file might also be acceded in a racy manner since the file name does not
> use any process-specific information (such as pid, timing, etc.).
> 
> Another side-effect is it forces lazy binding even on libraries that
> might be with DF_BIND_NOW.
> 
> [1] https://man7.org/linux/man-pages/man8/ld.so.8.html
> ---

I tend to agree.  Carlos, Florian, is profiling of setuid binaries 
something that needs to be supported as compatibility behaviour?  I'm 
inclined to agree with Adhemerval and just rip it out.

Thanks,
Sid

>   elf/Makefile         |  3 +++
>   elf/rtld.c           |  8 +++-----
>   elf/tst-env-setuid.c | 12 +++++++++++-
>   3 files changed, 17 insertions(+), 6 deletions(-)
> 
> diff --git a/elf/Makefile b/elf/Makefile
> index f1cd6e13fa..608bef46f5 100644
> --- a/elf/Makefile
> +++ b/elf/Makefile
> @@ -3021,3 +3021,6 @@ $(objpfx)tst-dlclose-lazy.out: \
>     $(objpfx)tst-dlclose-lazy-mod1.so $(objpfx)tst-dlclose-lazy-mod2.so
>   
>   tst-env-setuid-ARGS = -- $(host-test-program-cmd)
> +
> +# Reuse a module with a SONAME, to specific as the LD_PROFILE.
> +$(objpfx)tst-env-setuid: $(objpfx)tst-sonamemove-runmod2.so
> diff --git a/elf/rtld.c b/elf/rtld.c
> index 51b6d9f326..a09cf2a9df 100644
> --- a/elf/rtld.c
> +++ b/elf/rtld.c
> @@ -361,6 +361,7 @@ struct rtld_global_ro _rtld_global_ro attribute_relro =
>       ._dl_fpu_control = _FPU_DEFAULT,
>       ._dl_pagesize = EXEC_PAGESIZE,
>       ._dl_inhibit_cache = 0,
> +    ._dl_profile_output = "/var/tmp",
>   
>       /* Function pointers.  */
>       ._dl_debug_printf = _dl_debug_printf,
> @@ -2534,10 +2535,6 @@ process_envvars (struct dl_main_state *state)
>     char *envline;
>     char *debug_output = NULL;
>   
> -  /* This is the default place for profiling data file.  */
> -  GLRO(dl_profile_output)
> -    = &"/var/tmp\0/var/profile"[__libc_enable_secure ? 9 : 0];
> -
>     while ((envline = _dl_next_ld_env_entry (&runp)) != NULL)
>       {
>         size_t len = 0;
> @@ -2586,7 +2583,8 @@ process_envvars (struct dl_main_state *state)
>   	    }
>   
>   	  /* Which shared object shall be profiled.  */
> -	  if (memcmp (envline, "PROFILE", 7) == 0 && envline[8] != '\0')
> +	  if (!__libc_enable_secure
> +	      && memcmp (envline, "PROFILE", 7) == 0 && envline[8] != '\0')
>   	    GLRO(dl_profile) = &envline[8];
>   	  break;
>   
> diff --git a/elf/tst-env-setuid.c b/elf/tst-env-setuid.c
> index ba295a6a14..76b8e1fb45 100644
> --- a/elf/tst-env-setuid.c
> +++ b/elf/tst-env-setuid.c
> @@ -34,6 +34,9 @@ static char SETGID_CHILD[] = "setgid-child";
>   
>   #define FILTERED_VALUE   "some-filtered-value"
>   #define UNFILTERED_VALUE "some-unfiltered-value"
> +/* It assumes no other programs is being profile with a library with same
> +   SONAME using the default folder.  */
> +#define PROFILE_LIB      "tst-sonamemove-runmod2.so"
>   
>   struct envvar_t
>   {
> @@ -50,7 +53,7 @@ static const struct envvar_t filtered_envvars[] =
>     { "LD_HWCAP_MASK",           FILTERED_VALUE },
>     { "LD_LIBRARY_PATH",         FILTERED_VALUE },
>     { "LD_PRELOAD",              FILTERED_VALUE },
> -  { "LD_PROFILE",              FILTERED_VALUE },
> +  { "LD_PROFILE",              "tst-sonamemove-runmod2.so" },
>     { "MALLOC_ARENA_MAX",        FILTERED_VALUE },
>     { "MALLOC_PERTURB_",         FILTERED_VALUE },
>     { "MALLOC_TRACE",            FILTERED_VALUE },
> @@ -87,6 +90,13 @@ test_child (void)
>         ret |= !(env != NULL && strcmp (env, e->value) == 0);
>       }
>   
> +  /* Also check if no profile file was created.  */
> +  {
> +    char *profilepath = xasprintf ("/var/tmp/%s.profile", PROFILE_LIB);
> +    ret |= !access (profilepath, R_OK);
> +    free (profilepath);
> +  }
> +
>     return ret;
>   }
>   

^ permalink raw reply	[flat|nested] 45+ messages in thread

* Re: [PATCH v2 15/19] elf: Remove any_debug from dl_main_state
  2023-10-17 13:05 ` [PATCH v2 15/19] elf: Remove any_debug from dl_main_state Adhemerval Zanella
@ 2023-10-27 14:26   ` Siddhesh Poyarekar
  2023-10-30 19:03     ` Adhemerval Zanella Netto
  0 siblings, 1 reply; 45+ messages in thread
From: Siddhesh Poyarekar @ 2023-10-27 14:26 UTC (permalink / raw)
  To: Adhemerval Zanella, libc-alpha



On 2023-10-17 09:05, Adhemerval Zanella wrote:
> Its usage can be implied by the GLRO(dl_debug_mask).
> ---
>   elf/dl-main.h | 3 ---
>   elf/rtld.c    | 6 ++----
>   2 files changed, 2 insertions(+), 7 deletions(-)
> 
> diff --git a/elf/dl-main.h b/elf/dl-main.h
> index 92766d06b4..f876904cb6 100644
> --- a/elf/dl-main.h
> +++ b/elf/dl-main.h
> @@ -97,9 +97,6 @@ struct dl_main_state
>     /* True if program should be also printed for rtld_mode_trace.  */
>     bool mode_trace_program;
>   
> -  /* True if any of the debugging options is enabled.  */
> -  bool any_debug;
> -
>     /* True if information about versions has to be printed.  */
>     bool version_info;
>   };
> diff --git a/elf/rtld.c b/elf/rtld.c
> index 8749ad6288..638b019670 100644
> --- a/elf/rtld.c
> +++ b/elf/rtld.c
> @@ -300,7 +300,6 @@ dl_main_state_init (struct dl_main_state *state)
>     state->glibc_hwcaps_prepend = NULL;
>     state->glibc_hwcaps_mask = NULL;
>     state->mode = rtld_mode_normal;
> -  state->any_debug = false;
>     state->version_info = false;
>   }
>   
> @@ -2481,7 +2480,6 @@ process_dl_debug (struct dl_main_state *state, const char *dl_debug)
>   		&& memcmp (dl_debug, debopts[cnt].name, len) == 0)
>   	      {
>   		GLRO(dl_debug_mask) |= debopts[cnt].mask;
> -		state->any_debug = true;
>   		break;
>   	      }
>   
> @@ -2552,7 +2550,7 @@ process_envvars (struct dl_main_state *state)
>   	{
>   	case 4:
>   	  /* Warning level, verbose or not.  */
> -	  if (!!__libc_enable_secure
> +	  if (!__libc_enable_secure

This probably belongs in 14/19.

>   	      && memcmp (envline, "WARN", 4) == 0)
>   	    GLRO(dl_verbose) = envline[5] != '\0';
>   	  break;
> @@ -2682,7 +2680,7 @@ process_envvars (struct dl_main_state *state)
>     /* If we have to run the dynamic linker in debugging mode and the
>        LD_DEBUG_OUTPUT environment variable is given, we write the debug
>        messages to this file.  */
> -  else if (state->any_debug && debug_output != NULL)
> +  else if (GLRO(dl_debug_mask) != 0 && debug_output != NULL)
>       {
>         const int flags = O_WRONLY | O_APPEND | O_CREAT | O_NOFOLLOW;
>         size_t name_len = strlen (debug_output);

^ permalink raw reply	[flat|nested] 45+ messages in thread

* Re: [PATCH v2 17/19] elf: Add comments on how LD_AUDIT and LD_PRELOAD handle __libc_enable_secure
  2023-10-17 13:05 ` [PATCH v2 17/19] elf: Add comments on how LD_AUDIT and LD_PRELOAD handle __libc_enable_secure Adhemerval Zanella
@ 2023-10-27 14:31   ` Siddhesh Poyarekar
  0 siblings, 0 replies; 45+ messages in thread
From: Siddhesh Poyarekar @ 2023-10-27 14:31 UTC (permalink / raw)
  To: Adhemerval Zanella, libc-alpha



On 2023-10-17 09:05, Adhemerval Zanella wrote:
> To make explicit why __libc_enable_secure is not checked.
> ---

Reviewed-by: Siddhesh Poyarekar <siddhesh@sourceware.org>

>   elf/rtld.c | 9 ++++++++-
>   1 file changed, 8 insertions(+), 1 deletion(-)
> 
> diff --git a/elf/rtld.c b/elf/rtld.c
> index 638b019670..d1017ba9e9 100644
> --- a/elf/rtld.c
> +++ b/elf/rtld.c
> @@ -2563,6 +2563,10 @@ process_envvars (struct dl_main_state *state)
>   	      process_dl_debug (state, &envline[6]);
>   	      break;
>   	    }
> +	  /* For __libc_enable_secure mode, audit pathnames containing slashes
> +	     are ignored.  Also, shared audit objects are only loaded only from
> +	     the standard search directories and only if they have set-user-ID
> +	     mode bit enabled.  */
>   	  if (memcmp (envline, "AUDIT", 5) == 0)
>   	    audit_list_add_string (&state->audit_list, &envline[6]);
>   	  break;
> @@ -2576,7 +2580,10 @@ process_envvars (struct dl_main_state *state)
>   	      break;
>   	    }
>   
> -	  /* List of objects to be preloaded.  */
> +	  /* For __libc_enable_secure mode, preload pathnames containing slashes
> +	     are ignored.  Also, shared objects are only preloaded from the
> +	     standard search directories and only if they have set-user-ID mode
> +	     bit enabled.  */
>   	  if (memcmp (envline, "PRELOAD", 7) == 0)
>   	    {
>   	      state->preloadlist = &envline[8];

^ permalink raw reply	[flat|nested] 45+ messages in thread

* Re: [PATCH v2 03/19] elf: Ignore GLIBC_TUNABLES for setuid/setgid binaries
  2023-10-17 13:05 ` [PATCH v2 03/19] elf: Ignore GLIBC_TUNABLES for setuid/setgid binaries Adhemerval Zanella
  2023-10-18 13:04   ` Siddhesh Poyarekar
@ 2023-10-28  2:14   ` DJ Delorie
  2023-10-30 16:51     ` Adhemerval Zanella Netto
  1 sibling, 1 reply; 45+ messages in thread
From: DJ Delorie @ 2023-10-28  2:14 UTC (permalink / raw)
  To: Adhemerval Zanella; +Cc: libc-alpha, siddhesh


One semi-related question.
Two spelling nits.
One comment nit.
Some suggestions for improving the test cases.

Ok with those addressed.

Reviewed-by: DJ Delorie <dj@redhat.com>

Adhemerval Zanella <adhemerval.zanella@linaro.org> writes:
> diff --git a/elf/Makefile b/elf/Makefile
> index 9176cbf1e3..c824f7dab7 100644
> --- a/elf/Makefile
> +++ b/elf/Makefile
> @@ -263,7 +263,6 @@ tests-static-normal := \
>    tst-dl-iter-static \
>    tst-dst-static \
>    tst-env-setuid \
> -  tst-env-setuid-tunables \
>    tst-getauxval-static \
>    tst-linkall-static \
>    tst-single_threaded-pthread-static \
> @@ -276,10 +275,12 @@ tests-static-normal := \
>  tests-static-internal := \
>    tst-dl-printf-static \
>    tst-dl_find_object-static \
> +  tst-env-setuid-tunables \
>    tst-ptrguard1-static \
>    tst-stackguard1-static \
>    tst-tls1-static \
>    tst-tls1-static-non-pie \
> +  tst-tunables \
>    # tests-static-internal

Ok.

>  CRT-tst-tls1-static-non-pie := $(csu-objpfx)crt1.o
> @@ -2696,6 +2697,8 @@ $(objpfx)tst-glibc-hwcaps-mask.out: \
>  # tst-glibc-hwcaps-cache.
>  $(objpfx)tst-glibc-hwcaps-cache.out: $(objpfx)tst-glibc-hwcaps
>  
> +tst-tunables-ARGS = -- $(host-test-program-cmd)
> +

Ok.

> diff --git a/elf/dl-tunable-types.h b/elf/dl-tunable-types.h
> index c88332657e..62d6d9e629 100644
> --- a/elf/dl-tunable-types.h
> +++ b/elf/dl-tunable-types.h
> @@ -64,16 +64,6 @@ struct _tunable
>    tunable_val_t val;			/* The value.  */
>    bool initialized;			/* Flag to indicate that the tunable is
>  					   initialized.  */
> -  tunable_seclevel_t security_level;	/* Specify the security level for the
> -					   tunable with respect to AT_SECURE
> -					   programs.  See description of
> -					   tunable_seclevel_t to see a
> -					   description of the values.
> -
> -					   Note that even if the tunable is
> -					   read, it may not get used by the
> -					   target module if the value is
> -					   considered unsafe.  */
>    /* Compatibility elements.  */
>    const char env_alias[TUNABLE_ALIAS_MAX]; /* The compatibility environment
>  					   variable name.  */

Ok.  Should there be a related patch to remove "security_level" entries
from dl-tunables.list?  I see support for parsing it in gen-tunables is
removed below, but leaving in the entry may create a false sense of
security.

I see a patch from the 3rd that does this, but it doesn't seem committed
yet.

> diff --git a/elf/dl-tunables.c b/elf/dl-tunables.c
> index 24252af22c..a83bd2b8bc 100644
> --- a/elf/dl-tunables.c
> +++ b/elf/dl-tunables.c
> @@ -154,50 +154,51 @@ __tunable_set_val (tunable_id_t id, tunable_val_t *valp, tunable_num_t *minp,
>    do_tunable_update_val (cur, valp, minp, maxp);
>  }
>  
> -/* Parse the tunable string TUNESTR and adjust it to drop any tunables that may
> -   be unsafe for AT_SECURE processes so that it can be used as the new
> -   environment variable value for GLIBC_TUNABLES.  VALSTRING is the original
> -   environment variable string which we use to make NULL terminated values so
> -   that we don't have to allocate memory again for it.  */
> +/* Parse the tunable string VALSTRING.  VALSTRING is a duplicated values,

Spelling: "a values" should be "a value" ?

> +   where delimiters ':' are replaced with '\0', so string tunables are null
> +   terminated.  */



>  static void
> -parse_tunables (char *tunestr, char *valstring)
> +parse_tunables (char *valstring)
>  {
> -  if (tunestr == NULL || *tunestr == '\0')
> +  if (valstring == NULL || *valstring == '\0')
>      return;

Ok.

> -  char *p = tunestr;
> -  size_t off = 0;
> +  char *p = valstring;
> +  bool done = false;
>  
> -  while (true)
> +  while (!done)
>      {

Ok.

>        char *name = p;
> -      size_t len = 0;
>  
>        /* First, find where the name ends.  */
> -      while (p[len] != '=' && p[len] != ':' && p[len] != '\0')
> -	len++;
> +      while (*p != '=' && *p != ':' && *p != '\0')
> +	p++;

Ok, p points to the end of the tunable name

>        /* If we reach the end of the string before getting a valid name-value
>  	 pair, bail out.  */
> -      if (p[len] == '\0')
> +      if (*p == '\0')
>  	break;

Missing value, ok.

>        /* We did not find a valid name-value pair before encountering the
>  	 colon.  */
> -      if (p[len]== ':')
> +      if (*p == ':')
>  	{
> -	  p += len + 1;
> +	  p++;
>  	  continue;
>  	}

Missing value, ok.

> -      p += len + 1;
> +      /* Skip the ':' or '='.  */
> +      p++;

Nit: This can't skip the ':' because we know there isn't one (above).

p now points to the start of the value.  Ok.

> -      /* Take the value from the valstring since we need to NULL terminate it.  */
> -      char *value = &valstring[p - tunestr];
> -      len = 0;
> +      const char *value = p;

"value" points to value, "valstring" still points to name.  Ok.

> -      while (p[len] != ':' && p[len] != '\0')
> -	len++;
> +      while (*p != ':' && *p != '\0')
> +	p++;
> +
> +      if (*p == '\0')
> +	done = true;
> +      else
> +	*p++ = '\0';

Ok.

> @@ -206,50 +207,11 @@ parse_tunables (char *tunestr, char *valstring)
>  
>  	  if (tunable_is_name (cur->name, name))
>  	    {
> -	      /* If we are in a secure context (AT_SECURE) then ignore the
> -		 tunable unless it is explicitly marked as secure.  Tunable
> -		 values take precedence over their envvar aliases.  We write
> -		 the tunables that are not SXID_ERASE back to TUNESTR, thus
> -		 dropping all SXID_ERASE tunables and any invalid or
> -		 unrecognized tunables.  */
> -	      if (__libc_enable_secure)
> -		{
> -		  if (cur->security_level != TUNABLE_SECLEVEL_SXID_ERASE)
> -		    {
> -		      if (off > 0)
> -			tunestr[off++] = ':';
> -
> -		      const char *n = cur->name;
> -
> -		      while (*n != '\0')
> -			tunestr[off++] = *n++;
> -
> -		      tunestr[off++] = '=';
> -
> -		      for (size_t j = 0; j < len; j++)
> -			tunestr[off++] = value[j];
> -		    }
> -
> -		  if (cur->security_level != TUNABLE_SECLEVEL_NONE)
> -		    break;
> -		}
> -
> -	      value[len] = '\0';
>  	      tunable_initialize (cur, value);
>  	      break;
>  	    }
>  	}

Ok.

> -
> -      /* We reached the end while processing the tunable string.  */
> -      if (p[len] == '\0')
> -	break;
> -
> -      p += len + 1;
>      }

Ok.  The "done" replaces this.

> -
> -  /* Terminate tunestr before we leave.  */
> -  if (__libc_enable_secure)
> -    tunestr[off] = '\0';
>  }

Ok.

> @@ -263,16 +225,16 @@ __tunables_init (char **envp)
>    size_t len = 0;
>    char **prev_envp = envp;
>  
> +  /* Ignore tunables for AT_SECURE programs.  */
> +  if (__libc_enable_secure)
> +    return;
> +

Ok.

>    while ((envp = get_next_env (envp, &envname, &len, &envval,
>  			       &prev_envp)) != NULL)
>      {
>        if (tunable_is_name ("GLIBC_TUNABLES", envname))
>  	{
> -	  char *new_env = tunables_strdup (envname);
> -	  if (new_env != NULL)
> -	    parse_tunables (new_env + len + 1, envval);
> -	  /* Put in the updated envval.  */
> -	  *prev_envp = new_env;
> +	  parse_tunables (tunables_strdup (envval));
>  	  continue;
>  	}

We don't free these, but that's OK.

> @@ -290,39 +252,6 @@ __tunables_init (char **envp)
>  	  /* We have a match.  Initialize and move on to the next line.  */
>  	  if (tunable_is_name (name, envname))
>  	    {
> -	      /* For AT_SECURE binaries, we need to check the security settings of
> -		 the tunable and decide whether we read the value and also whether
> -		 we erase the value so that child processes don't inherit them in
> -		 the environment.  */
> -	      if (__libc_enable_secure)
> -		{
> -		  if (cur->security_level == TUNABLE_SECLEVEL_SXID_ERASE)
> -		    {
> -		      /* Erase the environment variable.  */
> -		      char **ep = prev_envp;
> -
> -		      while (*ep != NULL)
> -			{
> -			  if (tunable_is_name (name, *ep))
> -			    {
> -			      char **dp = ep;
> -
> -			      do
> -				dp[0] = dp[1];
> -			      while (*dp++);
> -			    }
> -			  else
> -			    ++ep;
> -			}
> -		      /* Reset the iterator so that we read the environment again
> -			 from the point we erased.  */
> -		      envp = prev_envp;
> -		    }
> -
> -		  if (cur->security_level != TUNABLE_SECLEVEL_NONE)
> -		    continue;
> -		}
> -
>  	      tunable_initialize (cur, envval);
>  	      break;
>  	    }

Ok.

> diff --git a/elf/dl-tunables.list b/elf/dl-tunables.list
> index 695ba7192e..471895af7b 100644
> --- a/elf/dl-tunables.list
> +++ b/elf/dl-tunables.list
> @@ -21,14 +21,6 @@
>  # minval: Optional minimum acceptable value
>  # maxval: Optional maximum acceptable value
>  # env_alias: An alias environment variable
> -# security_level: Specify security level of the tunable for AT_SECURE binaries.
> -# 		  Valid values are:
> -#
> -# 	     SXID_ERASE: (default) Do not read and do not pass on to
> -# 	     child processes.
> -# 	     SXID_IGNORE: Do not read, but retain for non-AT_SECURE
> -# 	     subprocesses.
> -# 	     NONE: Read all the time.

Ok.

> @@ -158,7 +150,6 @@ glibc {
>        type: INT_32
>        minval: 0
>        maxval: 255
> -      security_level: SXID_IGNORE
>      }
>    }

I assume this is based on some other patch which removes the rest, so ok.

> diff --git a/elf/tst-env-setuid-tunables.c b/elf/tst-env-setuid-tunables.c
> index 2603007b7b..ca02dbba58 100644
> --- a/elf/tst-env-setuid-tunables.c
> +++ b/elf/tst-env-setuid-tunables.c
> @@ -15,14 +15,10 @@
>     License along with the GNU C Library; if not, see
>     <https://www.gnu.org/licenses/>.  */
>  
> -/* Verify that tunables correctly filter out unsafe tunables like
> -   glibc.malloc.check and glibc.malloc.mmap_threshold but also retain
> -   glibc.malloc.mmap_threshold in an unprivileged child.  */
> -
> -#define _LIBC 1
> -#include "config.h"
> -#undef _LIBC
> +/* Verify that GLIBC_TUNABLES is kept unchanged but no tunable is actually
> +   enabled for AT_SECURE processes.  */

Ok.

> +#include <dl-tunables.h>

Ok.

> @@ -40,7 +36,7 @@
>  #include <support/test-driver.h>
>  #include <support/capture_subprocess.h>
>  
> -const char *teststrings[] =
> +static const char *teststrings[] =

Ok.

> @@ -74,6 +70,23 @@ test_child (int off)
>      ret = 0;
>    fflush (stdout);
>  
> +  /* Also check if the set tunables are effectively unchanged.  */
> +  int32_t check = TUNABLE_GET_FULL (glibc, malloc, check, int32_t, NULL);
> +  size_t mmap_threshold = TUNABLE_GET_FULL (glibc, malloc, mmap_threshold,
> +					    size_t, NULL);
> +  int32_t perturb = TUNABLE_GET_FULL (glibc, malloc, perturb, int32_t, NULL);
> +
> +  printf ("    [%d] glibc.malloc.check=%d\n", off, check);
> +  fflush (stdout);
> +  printf ("    [%d] glibc.malloc.mmap_threshold=%zu\n", off, mmap_threshold);
> +  fflush (stdout);
> +  printf ("    [%d] glibc.malloc.perturb=%d\n", off, perturb);
> +  fflush (stdout);
> +
> +  ret |= check != 0;
> +  ret |= mmap_threshold != 0;
> +  ret |= perturb != 0;
> +

Ok.

> diff --git a/elf/tst-tunables.c b/elf/tst-tunables.c
> new file mode 100644
> index 0000000000..57cf532fc4
> --- /dev/null
> +++ b/elf/tst-tunables.c
> @@ -0,0 +1,244 @@
> +/* Check GLIBC_TUNABLES parsing.
> +   Copyright (C) 2023 Free Software Foundation, Inc.
> +   This file is part of the GNU C Library.
> +
> +   The GNU C Library is free software; you can redistribute it and/or
> +   modify it under the terms of the GNU Lesser General Public
> +   License as published by the Free Software Foundation; either
> +   version 2.1 of the License, or (at your option) any later version.
> +
> +   The GNU C Library is distributed in the hope that it will be useful,
> +   but WITHOUT ANY WARRANTY; without even the implied warranty of
> +   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
> +   Lesser General Public License for more details.
> +
> +   You should have received a copy of the GNU Lesser General Public
> +   License along with the GNU C Library; if not, see
> +   <https://www.gnu.org/licenses/>.  */
> +
> +#include <array_length.h>
> +#include <dl-tunables.h>
> +#include <getopt.h>
> +#include <intprops.h>
> +#include <stdint.h>
> +#include <stdlib.h>
> +#include <support/capture_subprocess.h>
> +#include <support/check.h>

Ok.

> +static int restart;
> +#define CMDLINE_OPTIONS \
> +  { "restart", no_argument, &restart, 1 },

Ok.

> +static const struct test_t
> +{
> +  const char *env;
> +  int32_t expected_malloc_check;
> +  size_t expected_mmap_threshold;
> +  int32_t expected_perturb;
> +} tests[] =
> +{
> +  /* Expected tunable format.  */
> +  {
> +    "glibc.malloc.check=2",
> +    2,
> +    0,
> +    0,
> +  },
> +  {
> +    "glibc.malloc.check=2:glibc.malloc.mmap_threshold=4096",
> +    2,
> +    4096,
> +    0,
> +  },
> +  /* Empty tunable are ignored.  */
> +  {
> +    "glibc.malloc.check=2::glibc.malloc.mmap_threshold=4096",
> +    2,
> +    4096,
> +    0,
> +  },
> +  /* As well empty values.  */
> +  {
> +    "glibc.malloc.check=:glibc.malloc.mmap_threshold=4096",
> +    0,
> +    4096,
> +    0,
> +  },
> +  /* Tunable are processed from left to right, so last one is the one set.  */
> +  {
> +    "glibc.malloc.check=1:glibc.malloc.check=2",
> +    2,
> +    0,
> +    0,
> +  },
> +  {
> +    "glibc.malloc.check=2:glibc.malloc.check=2:glibc.malloc.mmap_threshold=4096",

Suggest different values for check, so you know which is used.

> +    2,
> +    4096,
> +    0,
> +  },
> +  {
> +    "glibc.malloc.check=2:glibc.malloc.mmap_threshold=4096:glibc.malloc.check=2",

Likewise.

> +    2,
> +    4096,
> +    0,
> +  },
> +  /* 0x800 is larger than tunable maxval (0xff), so the tunable is unchanged.  */
> +  {
> +    "glibc.malloc.perturb=0x800",
> +    0,
> +    0,
> +    0,
> +  },
> +  {
> +    "glibc.malloc.perturb=0x55",
> +    0,
> +    0,
> +    0x55,
> +  },
> +  /* Out of range values are just ignored.  */
> +  {
> +    "glibc.malloc.perturb=0x800:glibc.malloc.mmap_threshold=4096",
> +    0,
> +    4096,
> +    0,
> +  },
> +  /* Invalid keys are ignored.  */
> +  {
> +    ":glibc.malloc.garbage=2:glibc.malloc.check=1",
> +    1,
> +    0,
> +    0,
> +  },
> +  {
> +    "glibc.malloc.perturb=0x800:not_valid.malloc.check=2:glibc.malloc.mmap_threshold=4096",
> +    0,
> +    4096,
> +    0,
> +  },
> +  {
> +    "glibc.not_valid.check=2:glibc.malloc.mmap_threshold=4096",
> +    0,
> +    4096,
> +    0,
> +  },
> +  {
> +    "not_valid.malloc.check=2:glibc.malloc.mmap_threshold=4096",
> +    0,
> +    4096,
> +    0,
> +  },
> +  /* Invalid subkeys are ignored.  */
> +  {
> +    "glibc.malloc.garbage=2:glibc.maoc.mmap_threshold=4096:glibc.malloc.check=2",
> +    2,
> +    0,
> +    0,
> +  },
> +  {
> +    "glibc.malloc.check=4:glibc.malloc.garbage=2:glibc.maoc.mmap_threshold=4096",
> +    0,
> +    0,
> +    0,
> +  },
> +  {
> +    "not_valid.malloc.check=2",
> +    0,
> +    0,
> +    0,
> +  },
> +  {
> +    "glibc.not_valid.check=2",
> +    0,
> +    0,
> +    0,
> +  },
> +  /* An ill-formatted tunable in the for key=key=value will considere the
> +     value as 'key=value' (which can not be parsed as an integer).  */
> +  {
> +    "glibc.malloc.mmap_threshold=glibc.malloc.mmap_threshold=4096",
> +    0,
> +    0,
> +    0,
> +  },
> +  /* The ill-formatted tunable is also skipped.  */
> +  {
> +    "glibc.malloc.mmap_threshold=glibc.malloc.mmap_threshold=4096:glibc.malloc.check=2",
> +    2,
> +    0,
> +    0,
> +  },
> +  /* For an integer tunable, parse will stop on non number character.  */
> +  {
> +    "glibc.malloc.check=2=2",
> +    2,

Better to put different numbers here, so you know which is parsed, but ok.

> +    0,
> +    0,
> +  },
> +  {
> +    "glibc.malloc.check=2=2:glibc.malloc.mmap_threshold=4096",
> +    2,
> +    4096,
> +    0,
> +  }
> +};

Ok.

> +static int
> +handle_restart (int i)
> +{
> +  TEST_COMPARE (tests[i].expected_malloc_check,
> +		TUNABLE_GET_FULL (glibc, malloc, check, int32_t, NULL));
> +  TEST_COMPARE (tests[i].expected_mmap_threshold,
> +		TUNABLE_GET_FULL (glibc, malloc, mmap_threshold, size_t, NULL));
> +  TEST_COMPARE (tests[i].expected_perturb,
> +		TUNABLE_GET_FULL (glibc, malloc, perturb, int32_t, NULL));
> +  return 0;
> +}

Ok.

> +static int
> +do_test (int argc, char *argv[])
> +{
> +  /* We must have either:
> +     - One our fource parameters left if called initially:

Spelling?

> +       + path to ld.so         optional
> +       + "--library-path"      optional
> +       + the library path      optional
> +       + the application name
> +       + the test to check  */
> +
> +  TEST_VERIFY_EXIT (argc == 2 || argc == 5);

Ok.

> +  if (restart)
> +    return handle_restart (atoi (argv[1]));

Set by --restart, ok.

> +  char nteststr[INT_BUFSIZE_BOUND (int)];

Ok.

> +  char *spargv[10];
> +  {
> +    int i = 0;
> +    for (; i < argc - 1; i++)
> +      spargv[i] = argv[i + 1];
> +    spargv[i++] = (char *) "--direct";
> +    spargv[i++] = (char *) "--restart";
> +    spargv[i++] = nteststr;
> +    spargv[i] = NULL;
> +  }

No overflow prevention, but this is internal, so ok.

> +  for (int i = 0; i < array_length (tests); i++)
> +    {
> +      snprintf (nteststr, sizeof nteststr, "%d", i);
> +
> +      printf ("[%d] Spawned test for %s\n", i, tests[i].env);
> +      setenv ("GLIBC_TUNABLES", tests[i].env, 1);
> +      struct support_capture_subprocess result
> +	= support_capture_subprogram (spargv[0], spargv);
> +      support_capture_subprocess_check (&result, "tst-tunables", 0,
> +					sc_allow_stderr);
> +      support_capture_subprocess_free (&result);
> +    }
> +
> +  return 0;
> +}

Ok.

> +#define TEST_FUNCTION_ARGV do_test
> +#include <support/test-driver.c>

Ok.

> diff --git a/manual/README.tunables b/manual/README.tunables
> index 605ddd78cd..72ae00dc02 100644
> --- a/manual/README.tunables
> +++ b/manual/README.tunables
> @@ -59,15 +59,6 @@ The list of allowed attributes are:
>  
>  - env_alias:		An alias environment variable
>  
> -- security_level:	Specify security level of the tunable for AT_SECURE
> -			binaries.  Valid values are:
> -
> -			SXID_ERASE: (default) Do not read and do not pass on to
> -			child processes.
> -			SXID_IGNORE: Do not read, but retain for non-AT_SECURE
> -			child processes.
> -			NONE: Read all the time.
> -

Ok.

> diff --git a/scripts/gen-tunables.awk b/scripts/gen-tunables.awk
> index d6de100df0..1e9d6b534e 100644
> --- a/scripts/gen-tunables.awk
> +++ b/scripts/gen-tunables.awk
> @@ -61,9 +61,6 @@ $1 == "}" {
>      if (!env_alias[top_ns,ns,tunable]) {
>        env_alias[top_ns,ns,tunable] = "{0}"
>      }
> -    if (!security_level[top_ns,ns,tunable]) {
> -      security_level[top_ns,ns,tunable] = "SXID_ERASE"
> -    }

Ok.

> @@ -118,17 +115,6 @@ $1 == "}" {
>      if (len > max_alias_len)
>        max_alias_len = len
>    }
> -  else if (attr == "security_level") {
> -    if (val == "SXID_ERASE" || val == "SXID_IGNORE" || val == "NONE") {
> -      security_level[top_ns,ns,tunable] = val
> -    }
> -    else {
> -      printf("Line %d: Invalid value (%s) for security_level: %s, ", NR, val,
> -	     $0)
> -      print("Allowed values are 'SXID_ERASE', 'SXID_IGNORE', or 'NONE'")
> -      exit 1
> -    }
> -  }

Ok.

> @@ -177,9 +163,9 @@ END {
>      n = indices[2];
>      m = indices[3];
>      printf ("  {TUNABLE_NAME_S(%s, %s, %s)", t, n, m)
> -    printf (", {TUNABLE_TYPE_%s, %s, %s}, {%s}, false, TUNABLE_SECLEVEL_%s, %s},\n",
> +    printf (", {TUNABLE_TYPE_%s, %s, %s}, {%s}, false, %s},\n",
>  	    types[t,n,m], minvals[t,n,m], maxvals[t,n,m],
> -	    default_val[t,n,m], security_level[t,n,m], env_alias[t,n,m]);
> +	    default_val[t,n,m], env_alias[t,n,m]);
>    }

Ok.

> diff --git a/sysdeps/x86_64/64/dl-tunables.list b/sysdeps/x86_64/64/dl-tunables.list
> index 0aab52e662..54a216a461 100644
> --- a/sysdeps/x86_64/64/dl-tunables.list
> +++ b/sysdeps/x86_64/64/dl-tunables.list
> @@ -23,7 +23,6 @@ glibc {
>        minval: 0
>        maxval: 1
>        env_alias: LD_PREFER_MAP_32BIT_EXEC
> -      security_level: SXID_IGNORE
>      }
>    }
>  }

Ok.


^ permalink raw reply	[flat|nested] 45+ messages in thread

* Re: [PATCH v2 04/19] elf: Add all malloc tunable to unsecvars
  2023-10-17 13:05 ` [PATCH v2 04/19] elf: Add all malloc tunable to unsecvars Adhemerval Zanella
  2023-10-18 13:04   ` Siddhesh Poyarekar
@ 2023-10-28  3:45   ` DJ Delorie
  1 sibling, 0 replies; 45+ messages in thread
From: DJ Delorie @ 2023-10-28  3:45 UTC (permalink / raw)
  To: Adhemerval Zanella; +Cc: libc-alpha, siddhesh


LGTM.

Reviewed-by: DJ Delorie <dj@redhat.com>

Adhemerval Zanella <adhemerval.zanella@linaro.org> writes:
> diff --git a/elf/Makefile b/elf/Makefile
> index c824f7dab7..f1cd6e13fa 100644
> --- a/elf/Makefile
> +++ b/elf/Makefile
> @@ -262,7 +262,7 @@ tests-static-normal := \
>    tst-array5-static \
>    tst-dl-iter-static \
>    tst-dst-static \
> -  tst-env-setuid \
> +  tst-env-setuid-static \
>    tst-getauxval-static \
>    tst-linkall-static \
>    tst-single_threaded-pthread-static \
> @@ -305,6 +305,7 @@ tests := \
>    tst-array5 \
>    tst-auxv \
>    tst-dl-hash \
> +  tst-env-setuid \
>    tst-leaks1 \
>    tst-stringtable \
>    tst-tls9 \

Ok.

> @@ -2467,9 +2468,6 @@ $(objpfx)tst-nodelete-dlclose: $(objpfx)tst-nodelete-dlclose-dso.so
>  $(objpfx)tst-nodelete-dlclose.out: $(objpfx)tst-nodelete-dlclose-dso.so \
>  				   $(objpfx)tst-nodelete-dlclose-plugin.so
>  
> -tst-env-setuid-ENV = MALLOC_CHECK_=2 MALLOC_MMAP_THRESHOLD_=4096 \
> -		     LD_HWCAP_MASK=0x1
> -

Ok.

> @@ -3021,3 +3019,5 @@ LDFLAGS-tst-dlclose-lazy-mod1.so = -Wl,-z,lazy,--no-as-needed
>  $(objpfx)tst-dlclose-lazy-mod1.so: $(objpfx)tst-dlclose-lazy-mod2.so
>  $(objpfx)tst-dlclose-lazy.out: \
>    $(objpfx)tst-dlclose-lazy-mod1.so $(objpfx)tst-dlclose-lazy-mod2.so
> +
> +tst-env-setuid-ARGS = -- $(host-test-program-cmd)

Ok.

> diff --git a/elf/tst-env-setuid-static.c b/elf/tst-env-setuid-static.c
> new file mode 100644
> index 0000000000..0d88ae88b9
> --- /dev/null
> +++ b/elf/tst-env-setuid-static.c
> @@ -0,0 +1 @@
> +#include "tst-env-setuid.c"

Ok.

> diff --git a/elf/tst-env-setuid.c b/elf/tst-env-setuid.c
> index 032ab44be2..ba295a6a14 100644
> --- a/elf/tst-env-setuid.c
> +++ b/elf/tst-env-setuid.c
> @@ -15,18 +15,14 @@
>     License along with the GNU C Library; if not, see
>     <https://www.gnu.org/licenses/>.  */
>  
> -/* Verify that tunables correctly filter out unsafe environment variables like
> -   MALLOC_CHECK_ and MALLOC_MMAP_THRESHOLD_ but also retain
> -   MALLOC_MMAP_THRESHOLD_ in an unprivileged child.  */
> +/* Verify that correctly filter out unsafe environment variables defined
> +   in unsecvars.h.  */

Ok.

> -#include <errno.h>
> -#include <fcntl.h>
> -#include <stdlib.h>
> -#include <stdint.h>
> +#include <array_length.h>
> +#include <gnu/lib-names.h>
>  #include <stdio.h>
> +#include <stdlib.h>
>  #include <string.h>
> -#include <sys/stat.h>
> -#include <sys/wait.h>
>  #include <unistd.h>

Ok.

>  #include <support/check.h>
> @@ -36,61 +32,72 @@
>  
>  static char SETGID_CHILD[] = "setgid-child";
>  
> +#define FILTERED_VALUE   "some-filtered-value"
> +#define UNFILTERED_VALUE "some-unfiltered-value"

Ok.

> +struct envvar_t
> +{
> +  const char *env;
> +  const char *value;
> +};

Ok.

> -#ifndef test_child
> -static int
> -test_child (void)
> -{
> -  if (getenv ("MALLOC_CHECK_") != NULL)
> -    {
> -      printf ("MALLOC_CHECK_ is still set\n");
> -      return 1;
> -    }
> -
> -  if (getenv ("MALLOC_MMAP_THRESHOLD_") == NULL)
> -    {
> -      printf ("MALLOC_MMAP_THRESHOLD_ lost\n");
> -      return 1;
> -    }
>  
> -  if (getenv ("LD_HWCAP_MASK") != NULL)
> -    {
> -      printf ("LD_HWCAP_MASK still set\n");
> -      return 1;
> -    }
> -  return 0;
> -}
> -#endif

Ok.

> +/* That is not an extensible list of all filtered out environment
> +   variables.  */
> +static const struct envvar_t filtered_envvars[] =
> +{
> +  { "GLIBC_TUNABLES",          FILTERED_VALUE },
> +  { "LD_AUDIT",                FILTERED_VALUE },
> +  { "LD_HWCAP_MASK",           FILTERED_VALUE },
> +  { "LD_LIBRARY_PATH",         FILTERED_VALUE },
> +  { "LD_PRELOAD",              FILTERED_VALUE },
> +  { "LD_PROFILE",              FILTERED_VALUE },
> +  { "MALLOC_ARENA_MAX",        FILTERED_VALUE },
> +  { "MALLOC_PERTURB_",         FILTERED_VALUE },
> +  { "MALLOC_TRACE",            FILTERED_VALUE },
> +  { "MALLOC_TRIM_THRESHOLD_",  FILTERED_VALUE },
> +  { "RES_OPTIONS",             FILTERED_VALUE },
> +};
> +
> +static const struct envvar_t unfiltered_envvars[] =
> +{
> +  { "LD_BIND_NOW",             "0" },
> +  { "LD_BIND_NOT",             "1" },
> +  /* Non longer supported option.  */
> +  { "LD_ASSUME_KERNEL",        UNFILTERED_VALUE },
> +};

Ok.

> -#ifndef test_parent
>  static int
> -test_parent (void)
> +test_child (void)

Ok.

>  {
> -  if (getenv ("MALLOC_CHECK_") == NULL)
> -    {
> -      printf ("MALLOC_CHECK_ lost\n");
> -      return 1;
> -    }

Ok.

> +  int ret = 0;
>  
> -  if (getenv ("MALLOC_MMAP_THRESHOLD_") == NULL)
> +  for (const struct envvar_t *e = filtered_envvars;
> +       e != array_end (filtered_envvars);
> +       e++)
>      {
> -      printf ("MALLOC_MMAP_THRESHOLD_ lost\n");
> -      return 1;
> +      const char *env = getenv (e->env);
> +      ret |= env != NULL;
>      }

Ok.  Diff is not our friend here ;-)

Checking for vars which should be NOT set.

> -  if (getenv ("LD_HWCAP_MASK") == NULL)
> +  for (const struct envvar_t *e = unfiltered_envvars;
> +       e != array_end (unfiltered_envvars);
> +       e++)
>      {
> -      printf ("LD_HWCAP_MASK lost\n");
> -      return 1;
> +      const char *env = getenv (e->env);
> +      ret |= !(env != NULL && strcmp (env, e->value) == 0);
>      }

Ok.  Checking for vars which should be set.

> -  return 0;
> +  return ret;
>  }
> -#endif

Ok.

>  static int
>  do_test (int argc, char **argv)
>  {
> +  /* For dynamic loader, the test requires --enable-hardcoded-path-in-tests so
> +     the kernel sets the AT_SECURE on process initialization.  */
> +  if (argc >= 2 && strstr (argv[1], LD_SO) != 0)
> +    FAIL_UNSUPPORTED ("dynamic test requires --enable-hardcoded-path-in-tests");
> +

Does not test for argc < 2 but this is internal so OK.

> @@ -104,20 +111,33 @@ do_test (int argc, char **argv)
>        if (ret != 0)
>  	exit (1);
>  
> -      exit (EXIT_SUCCESS);
> +      /* Special return code to make sure that the child executed all the way
> +	 through.  */
> +      exit (42);
>      }

Ok.

>    else
>      {
> -      if (test_parent () != 0)
> -	exit (1);
> +      for (const struct envvar_t *e = filtered_envvars;
> +	   e != array_end (filtered_envvars);
> +	   e++)
> +	setenv (e->env, e->value, 1);
> +
> +      for (const struct envvar_t *e = unfiltered_envvars;
> +	   e != array_end (unfiltered_envvars);
> +	   e++)
> +	setenv (e->env, e->value, 1);

Ok.

>        int status = support_capture_subprogram_self_sgid (SETGID_CHILD);
>  
>        if (WEXITSTATUS (status) == EXIT_UNSUPPORTED)
> -	return EXIT_UNSUPPORTED;
> -
> -      if (!WIFEXITED (status))
> -	FAIL_EXIT1 ("Unexpected exit status %d from child process\n", status);
> +	exit (EXIT_UNSUPPORTED);

Ok.

> +      if (WEXITSTATUS (status) != 42)
> +	{
> +	  printf ("    child failed with status %d\n",
> +		  WEXITSTATUS (status));
> +	  support_record_failure ();
> +	}

Ok.

>        return 0;
>      }

Ok.

> diff --git a/sysdeps/generic/unsecvars.h b/sysdeps/generic/unsecvars.h
> index 81397fb90b..f7ebed60e5 100644
> --- a/sysdeps/generic/unsecvars.h
> +++ b/sysdeps/generic/unsecvars.h
> @@ -18,7 +18,14 @@
>    "LD_SHOW_AUXV\0"							      \
>    "LOCALDOMAIN\0"							      \
>    "LOCPATH\0"								      \
> +  "MALLOC_ARENA_MAX\0"							      \
> +  "MALLOC_ARENA_TEST\0"							      \
> +  "MALLOC_MMAP_MAX_\0"							      \
> +  "MALLOC_MMAP_THRESHOLD_\0"						      \
> +  "MALLOC_PERTURB_\0"							      \
> +  "MALLOC_TOP_PAD_\0"							      \
>    "MALLOC_TRACE\0"							      \
> +  "MALLOC_TRIM_THRESHOLD_\0"						      \
>    "NIS_PATH\0"								      \
>    "NLSPATH\0"								      \
>    "RESOLV_HOST_CONF\0"							      \

Ok.


^ permalink raw reply	[flat|nested] 45+ messages in thread

* Re: [PATCH v2 03/19] elf: Ignore GLIBC_TUNABLES for setuid/setgid binaries
  2023-10-28  2:14   ` DJ Delorie
@ 2023-10-30 16:51     ` Adhemerval Zanella Netto
  0 siblings, 0 replies; 45+ messages in thread
From: Adhemerval Zanella Netto @ 2023-10-30 16:51 UTC (permalink / raw)
  To: DJ Delorie; +Cc: libc-alpha, siddhesh



On 27/10/23 23:14, DJ Delorie wrote:
> 
> One semi-related question.
> Two spelling nits.
> One comment nit.
> Some suggestions for improving the test cases.
> 
> Ok with those addressed.

Thanks!

> 
> Reviewed-by: DJ Delorie <dj@redhat.com>
> 
> Adhemerval Zanella <adhemerval.zanella@linaro.org> writes:
>> diff --git a/elf/Makefile b/elf/Makefile
>> index 9176cbf1e3..c824f7dab7 100644
>> --- a/elf/Makefile
>> +++ b/elf/Makefile
>> @@ -263,7 +263,6 @@ tests-static-normal := \
>>    tst-dl-iter-static \
>>    tst-dst-static \
>>    tst-env-setuid \
>> -  tst-env-setuid-tunables \
>>    tst-getauxval-static \
>>    tst-linkall-static \
>>    tst-single_threaded-pthread-static \
>> @@ -276,10 +275,12 @@ tests-static-normal := \
>>  tests-static-internal := \
>>    tst-dl-printf-static \
>>    tst-dl_find_object-static \
>> +  tst-env-setuid-tunables \
>>    tst-ptrguard1-static \
>>    tst-stackguard1-static \
>>    tst-tls1-static \
>>    tst-tls1-static-non-pie \
>> +  tst-tunables \
>>    # tests-static-internal
> 
> Ok.
> 
>>  CRT-tst-tls1-static-non-pie := $(csu-objpfx)crt1.o
>> @@ -2696,6 +2697,8 @@ $(objpfx)tst-glibc-hwcaps-mask.out: \
>>  # tst-glibc-hwcaps-cache.
>>  $(objpfx)tst-glibc-hwcaps-cache.out: $(objpfx)tst-glibc-hwcaps
>>  
>> +tst-tunables-ARGS = -- $(host-test-program-cmd)
>> +
> 
> Ok.
> 
>> diff --git a/elf/dl-tunable-types.h b/elf/dl-tunable-types.h
>> index c88332657e..62d6d9e629 100644
>> --- a/elf/dl-tunable-types.h
>> +++ b/elf/dl-tunable-types.h
>> @@ -64,16 +64,6 @@ struct _tunable
>>    tunable_val_t val;			/* The value.  */
>>    bool initialized;			/* Flag to indicate that the tunable is
>>  					   initialized.  */
>> -  tunable_seclevel_t security_level;	/* Specify the security level for the
>> -					   tunable with respect to AT_SECURE
>> -					   programs.  See description of
>> -					   tunable_seclevel_t to see a
>> -					   description of the values.
>> -
>> -					   Note that even if the tunable is
>> -					   read, it may not get used by the
>> -					   target module if the value is
>> -					   considered unsafe.  */
>>    /* Compatibility elements.  */
>>    const char env_alias[TUNABLE_ALIAS_MAX]; /* The compatibility environment
>>  					   variable name.  */
> 
> Ok.  Should there be a related patch to remove "security_level" entries
> from dl-tunables.list?  I see support for parsing it in gen-tunables is
> removed below, but leaving in the entry may create a false sense of
> security.
> 
> I see a patch from the 3rd that does this, but it doesn't seem committed
> yet.

It was an onverlook from my patch, I send a newer version with
security_level removed from all tunables list files.

> 
>> diff --git a/elf/dl-tunables.c b/elf/dl-tunables.c
>> index 24252af22c..a83bd2b8bc 100644
>> --- a/elf/dl-tunables.c
>> +++ b/elf/dl-tunables.c
>> @@ -154,50 +154,51 @@ __tunable_set_val (tunable_id_t id, tunable_val_t *valp, tunable_num_t *minp,
>>    do_tunable_update_val (cur, valp, minp, maxp);
>>  }
>>  
>> -/* Parse the tunable string TUNESTR and adjust it to drop any tunables that may
>> -   be unsafe for AT_SECURE processes so that it can be used as the new
>> -   environment variable value for GLIBC_TUNABLES.  VALSTRING is the original
>> -   environment variable string which we use to make NULL terminated values so
>> -   that we don't have to allocate memory again for it.  */
>> +/* Parse the tunable string VALSTRING.  VALSTRING is a duplicated values,
> 
> Spelling: "a values" should be "a value" ?

Ack.

> 
>> +   where delimiters ':' are replaced with '\0', so string tunables are null
>> +   terminated.  */
> 
> 
> 
>>  static void
>> -parse_tunables (char *tunestr, char *valstring)
>> +parse_tunables (char *valstring)
>>  {
>> -  if (tunestr == NULL || *tunestr == '\0')
>> +  if (valstring == NULL || *valstring == '\0')
>>      return;
> 
> Ok.
> 
>> -  char *p = tunestr;
>> -  size_t off = 0;
>> +  char *p = valstring;
>> +  bool done = false;
>>  
>> -  while (true)
>> +  while (!done)
>>      {
> 
> Ok.
> 
>>        char *name = p;
>> -      size_t len = 0;
>>  
>>        /* First, find where the name ends.  */
>> -      while (p[len] != '=' && p[len] != ':' && p[len] != '\0')
>> -	len++;
>> +      while (*p != '=' && *p != ':' && *p != '\0')
>> +	p++;
> 
> Ok, p points to the end of the tunable name
> 
>>        /* If we reach the end of the string before getting a valid name-value
>>  	 pair, bail out.  */
>> -      if (p[len] == '\0')
>> +      if (*p == '\0')
>>  	break;
> 
> Missing value, ok.
> 
>>        /* We did not find a valid name-value pair before encountering the
>>  	 colon.  */
>> -      if (p[len]== ':')
>> +      if (*p == ':')
>>  	{
>> -	  p += len + 1;
>> +	  p++;
>>  	  continue;
>>  	}
> 
> Missing value, ok.
> 
>> -      p += len + 1;
>> +      /* Skip the ':' or '='.  */
>> +      p++;
> 
> Nit: This can't skip the ':' because we know there isn't one (above).
> 
> p now points to the start of the value.  Ok.

Ack, I will fix the comment.

> 
>> -      /* Take the value from the valstring since we need to NULL terminate it.  */
>> -      char *value = &valstring[p - tunestr];
>> -      len = 0;
>> +      const char *value = p;
> 
> "value" points to value, "valstring" still points to name.  Ok.
> 
>> -      while (p[len] != ':' && p[len] != '\0')
>> -	len++;
>> +      while (*p != ':' && *p != '\0')
>> +	p++;
>> +
>> +      if (*p == '\0')
>> +	done = true;
>> +      else
>> +	*p++ = '\0';
> 
> Ok.
> 
>> @@ -206,50 +207,11 @@ parse_tunables (char *tunestr, char *valstring)
>>  
>>  	  if (tunable_is_name (cur->name, name))
>>  	    {
>> -	      /* If we are in a secure context (AT_SECURE) then ignore the
>> -		 tunable unless it is explicitly marked as secure.  Tunable
>> -		 values take precedence over their envvar aliases.  We write
>> -		 the tunables that are not SXID_ERASE back to TUNESTR, thus
>> -		 dropping all SXID_ERASE tunables and any invalid or
>> -		 unrecognized tunables.  */
>> -	      if (__libc_enable_secure)
>> -		{
>> -		  if (cur->security_level != TUNABLE_SECLEVEL_SXID_ERASE)
>> -		    {
>> -		      if (off > 0)
>> -			tunestr[off++] = ':';
>> -
>> -		      const char *n = cur->name;
>> -
>> -		      while (*n != '\0')
>> -			tunestr[off++] = *n++;
>> -
>> -		      tunestr[off++] = '=';
>> -
>> -		      for (size_t j = 0; j < len; j++)
>> -			tunestr[off++] = value[j];
>> -		    }
>> -
>> -		  if (cur->security_level != TUNABLE_SECLEVEL_NONE)
>> -		    break;
>> -		}
>> -
>> -	      value[len] = '\0';
>>  	      tunable_initialize (cur, value);
>>  	      break;
>>  	    }
>>  	}
> 
> Ok.
> 
>> -
>> -      /* We reached the end while processing the tunable string.  */
>> -      if (p[len] == '\0')
>> -	break;
>> -
>> -      p += len + 1;
>>      }
> 
> Ok.  The "done" replaces this.
> 
>> -
>> -  /* Terminate tunestr before we leave.  */
>> -  if (__libc_enable_secure)
>> -    tunestr[off] = '\0';
>>  }
> 
> Ok.
> 
>> @@ -263,16 +225,16 @@ __tunables_init (char **envp)
>>    size_t len = 0;
>>    char **prev_envp = envp;
>>  
>> +  /* Ignore tunables for AT_SECURE programs.  */
>> +  if (__libc_enable_secure)
>> +    return;
>> +
> 
> Ok.
> 
>>    while ((envp = get_next_env (envp, &envname, &len, &envval,
>>  			       &prev_envp)) != NULL)
>>      {
>>        if (tunable_is_name ("GLIBC_TUNABLES", envname))
>>  	{
>> -	  char *new_env = tunables_strdup (envname);
>> -	  if (new_env != NULL)
>> -	    parse_tunables (new_env + len + 1, envval);
>> -	  /* Put in the updated envval.  */
>> -	  *prev_envp = new_env;
>> +	  parse_tunables (tunables_strdup (envval));
>>  	  continue;
>>  	}
> 
> We don't free these, but that's OK.
> 
>> @@ -290,39 +252,6 @@ __tunables_init (char **envp)
>>  	  /* We have a match.  Initialize and move on to the next line.  */
>>  	  if (tunable_is_name (name, envname))
>>  	    {
>> -	      /* For AT_SECURE binaries, we need to check the security settings of
>> -		 the tunable and decide whether we read the value and also whether
>> -		 we erase the value so that child processes don't inherit them in
>> -		 the environment.  */
>> -	      if (__libc_enable_secure)
>> -		{
>> -		  if (cur->security_level == TUNABLE_SECLEVEL_SXID_ERASE)
>> -		    {
>> -		      /* Erase the environment variable.  */
>> -		      char **ep = prev_envp;
>> -
>> -		      while (*ep != NULL)
>> -			{
>> -			  if (tunable_is_name (name, *ep))
>> -			    {
>> -			      char **dp = ep;
>> -
>> -			      do
>> -				dp[0] = dp[1];
>> -			      while (*dp++);
>> -			    }
>> -			  else
>> -			    ++ep;
>> -			}
>> -		      /* Reset the iterator so that we read the environment again
>> -			 from the point we erased.  */
>> -		      envp = prev_envp;
>> -		    }
>> -
>> -		  if (cur->security_level != TUNABLE_SECLEVEL_NONE)
>> -		    continue;
>> -		}
>> -
>>  	      tunable_initialize (cur, envval);
>>  	      break;
>>  	    }
> 
> Ok.
> 
>> diff --git a/elf/dl-tunables.list b/elf/dl-tunables.list
>> index 695ba7192e..471895af7b 100644
>> --- a/elf/dl-tunables.list
>> +++ b/elf/dl-tunables.list
>> @@ -21,14 +21,6 @@
>>  # minval: Optional minimum acceptable value
>>  # maxval: Optional maximum acceptable value
>>  # env_alias: An alias environment variable
>> -# security_level: Specify security level of the tunable for AT_SECURE binaries.
>> -# 		  Valid values are:
>> -#
>> -# 	     SXID_ERASE: (default) Do not read and do not pass on to
>> -# 	     child processes.
>> -# 	     SXID_IGNORE: Do not read, but retain for non-AT_SECURE
>> -# 	     subprocesses.
>> -# 	     NONE: Read all the time.
> 
> Ok.
> 
>> @@ -158,7 +150,6 @@ glibc {
>>        type: INT_32
>>        minval: 0
>>        maxval: 255
>> -      security_level: SXID_IGNORE
>>      }
>>    }
> 
> I assume this is based on some other patch which removes the rest, so ok.
> 
>> diff --git a/elf/tst-env-setuid-tunables.c b/elf/tst-env-setuid-tunables.c
>> index 2603007b7b..ca02dbba58 100644
>> --- a/elf/tst-env-setuid-tunables.c
>> +++ b/elf/tst-env-setuid-tunables.c
>> @@ -15,14 +15,10 @@
>>     License along with the GNU C Library; if not, see
>>     <https://www.gnu.org/licenses/>.  */
>>  
>> -/* Verify that tunables correctly filter out unsafe tunables like
>> -   glibc.malloc.check and glibc.malloc.mmap_threshold but also retain
>> -   glibc.malloc.mmap_threshold in an unprivileged child.  */
>> -
>> -#define _LIBC 1
>> -#include "config.h"
>> -#undef _LIBC
>> +/* Verify that GLIBC_TUNABLES is kept unchanged but no tunable is actually
>> +   enabled for AT_SECURE processes.  */
> 
> Ok.
> 
>> +#include <dl-tunables.h>
> 
> Ok.
> 
>> @@ -40,7 +36,7 @@
>>  #include <support/test-driver.h>
>>  #include <support/capture_subprocess.h>
>>  
>> -const char *teststrings[] =
>> +static const char *teststrings[] =
> 
> Ok.
> 
>> @@ -74,6 +70,23 @@ test_child (int off)
>>      ret = 0;
>>    fflush (stdout);
>>  
>> +  /* Also check if the set tunables are effectively unchanged.  */
>> +  int32_t check = TUNABLE_GET_FULL (glibc, malloc, check, int32_t, NULL);
>> +  size_t mmap_threshold = TUNABLE_GET_FULL (glibc, malloc, mmap_threshold,
>> +					    size_t, NULL);
>> +  int32_t perturb = TUNABLE_GET_FULL (glibc, malloc, perturb, int32_t, NULL);
>> +
>> +  printf ("    [%d] glibc.malloc.check=%d\n", off, check);
>> +  fflush (stdout);
>> +  printf ("    [%d] glibc.malloc.mmap_threshold=%zu\n", off, mmap_threshold);
>> +  fflush (stdout);
>> +  printf ("    [%d] glibc.malloc.perturb=%d\n", off, perturb);
>> +  fflush (stdout);
>> +
>> +  ret |= check != 0;
>> +  ret |= mmap_threshold != 0;
>> +  ret |= perturb != 0;
>> +
> 
> Ok.
> 
>> diff --git a/elf/tst-tunables.c b/elf/tst-tunables.c
>> new file mode 100644
>> index 0000000000..57cf532fc4
>> --- /dev/null
>> +++ b/elf/tst-tunables.c
>> @@ -0,0 +1,244 @@
>> +/* Check GLIBC_TUNABLES parsing.
>> +   Copyright (C) 2023 Free Software Foundation, Inc.
>> +   This file is part of the GNU C Library.
>> +
>> +   The GNU C Library is free software; you can redistribute it and/or
>> +   modify it under the terms of the GNU Lesser General Public
>> +   License as published by the Free Software Foundation; either
>> +   version 2.1 of the License, or (at your option) any later version.
>> +
>> +   The GNU C Library is distributed in the hope that it will be useful,
>> +   but WITHOUT ANY WARRANTY; without even the implied warranty of
>> +   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
>> +   Lesser General Public License for more details.
>> +
>> +   You should have received a copy of the GNU Lesser General Public
>> +   License along with the GNU C Library; if not, see
>> +   <https://www.gnu.org/licenses/>.  */
>> +
>> +#include <array_length.h>
>> +#include <dl-tunables.h>
>> +#include <getopt.h>
>> +#include <intprops.h>
>> +#include <stdint.h>
>> +#include <stdlib.h>
>> +#include <support/capture_subprocess.h>
>> +#include <support/check.h>
> 
> Ok.
> 
>> +static int restart;
>> +#define CMDLINE_OPTIONS \
>> +  { "restart", no_argument, &restart, 1 },
> 
> Ok.
> 
>> +static const struct test_t
>> +{
>> +  const char *env;
>> +  int32_t expected_malloc_check;
>> +  size_t expected_mmap_threshold;
>> +  int32_t expected_perturb;
>> +} tests[] =
>> +{
>> +  /* Expected tunable format.  */
>> +  {
>> +    "glibc.malloc.check=2",
>> +    2,
>> +    0,
>> +    0,
>> +  },
>> +  {
>> +    "glibc.malloc.check=2:glibc.malloc.mmap_threshold=4096",
>> +    2,
>> +    4096,
>> +    0,
>> +  },
>> +  /* Empty tunable are ignored.  */
>> +  {
>> +    "glibc.malloc.check=2::glibc.malloc.mmap_threshold=4096",
>> +    2,
>> +    4096,
>> +    0,
>> +  },
>> +  /* As well empty values.  */
>> +  {
>> +    "glibc.malloc.check=:glibc.malloc.mmap_threshold=4096",
>> +    0,
>> +    4096,
>> +    0,
>> +  },
>> +  /* Tunable are processed from left to right, so last one is the one set.  */
>> +  {
>> +    "glibc.malloc.check=1:glibc.malloc.check=2",
>> +    2,
>> +    0,
>> +    0,
>> +  },
>> +  {
>> +    "glibc.malloc.check=2:glibc.malloc.check=2:glibc.malloc.mmap_threshold=4096",
> 
> Suggest different values for check, so you know which is used.

Ack.

> 
>> +    2,
>> +    4096,
>> +    0,
>> +  },
>> +  {
>> +    "glibc.malloc.check=2:glibc.malloc.mmap_threshold=4096:glibc.malloc.check=2",
> 
> Likewise.

Ack.

> 
>> +    2,
>> +    4096,
>> +    0,
>> +  },
>> +  /* 0x800 is larger than tunable maxval (0xff), so the tunable is unchanged.  */
>> +  {
>> +    "glibc.malloc.perturb=0x800",
>> +    0,
>> +    0,
>> +    0,
>> +  },
>> +  {
>> +    "glibc.malloc.perturb=0x55",
>> +    0,
>> +    0,
>> +    0x55,
>> +  },
>> +  /* Out of range values are just ignored.  */
>> +  {
>> +    "glibc.malloc.perturb=0x800:glibc.malloc.mmap_threshold=4096",
>> +    0,
>> +    4096,
>> +    0,
>> +  },
>> +  /* Invalid keys are ignored.  */
>> +  {
>> +    ":glibc.malloc.garbage=2:glibc.malloc.check=1",
>> +    1,
>> +    0,
>> +    0,
>> +  },
>> +  {
>> +    "glibc.malloc.perturb=0x800:not_valid.malloc.check=2:glibc.malloc.mmap_threshold=4096",
>> +    0,
>> +    4096,
>> +    0,
>> +  },
>> +  {
>> +    "glibc.not_valid.check=2:glibc.malloc.mmap_threshold=4096",
>> +    0,
>> +    4096,
>> +    0,
>> +  },
>> +  {
>> +    "not_valid.malloc.check=2:glibc.malloc.mmap_threshold=4096",
>> +    0,
>> +    4096,
>> +    0,
>> +  },
>> +  /* Invalid subkeys are ignored.  */
>> +  {
>> +    "glibc.malloc.garbage=2:glibc.maoc.mmap_threshold=4096:glibc.malloc.check=2",
>> +    2,
>> +    0,
>> +    0,
>> +  },
>> +  {
>> +    "glibc.malloc.check=4:glibc.malloc.garbage=2:glibc.maoc.mmap_threshold=4096",
>> +    0,
>> +    0,
>> +    0,
>> +  },
>> +  {
>> +    "not_valid.malloc.check=2",
>> +    0,
>> +    0,
>> +    0,
>> +  },
>> +  {
>> +    "glibc.not_valid.check=2",
>> +    0,
>> +    0,
>> +    0,
>> +  },
>> +  /* An ill-formatted tunable in the for key=key=value will considere the
>> +     value as 'key=value' (which can not be parsed as an integer).  */
>> +  {
>> +    "glibc.malloc.mmap_threshold=glibc.malloc.mmap_threshold=4096",
>> +    0,
>> +    0,
>> +    0,
>> +  },
>> +  /* The ill-formatted tunable is also skipped.  */
>> +  {
>> +    "glibc.malloc.mmap_threshold=glibc.malloc.mmap_threshold=4096:glibc.malloc.check=2",
>> +    2,
>> +    0,
>> +    0,
>> +  },
>> +  /* For an integer tunable, parse will stop on non number character.  */
>> +  {
>> +    "glibc.malloc.check=2=2",
>> +    2,
> 
> Better to put different numbers here, so you know which is parsed, but ok.
> 
>> +    0,
>> +    0,
>> +  },
>> +  {
>> +    "glibc.malloc.check=2=2:glibc.malloc.mmap_threshold=4096",
>> +    2,
>> +    4096,
>> +    0,
>> +  }
>> +};
> 
> Ok.
> 
>> +static int
>> +handle_restart (int i)
>> +{
>> +  TEST_COMPARE (tests[i].expected_malloc_check,
>> +		TUNABLE_GET_FULL (glibc, malloc, check, int32_t, NULL));
>> +  TEST_COMPARE (tests[i].expected_mmap_threshold,
>> +		TUNABLE_GET_FULL (glibc, malloc, mmap_threshold, size_t, NULL));
>> +  TEST_COMPARE (tests[i].expected_perturb,
>> +		TUNABLE_GET_FULL (glibc, malloc, perturb, int32_t, NULL));
>> +  return 0;
>> +}
> 
> Ok.
> 
>> +static int
>> +do_test (int argc, char *argv[])
>> +{
>> +  /* We must have either:
>> +     - One our fource parameters left if called initially:
> 
> Spelling?
> 
>> +       + path to ld.so         optional
>> +       + "--library-path"      optional
>> +       + the library path      optional
>> +       + the application name
>> +       + the test to check  */
>> +
>> +  TEST_VERIFY_EXIT (argc == 2 || argc == 5);
> 
> Ok.
> 
>> +  if (restart)
>> +    return handle_restart (atoi (argv[1]));
> 
> Set by --restart, ok.
> 
>> +  char nteststr[INT_BUFSIZE_BOUND (int)];
> 
> Ok.
> 
>> +  char *spargv[10];
>> +  {
>> +    int i = 0;
>> +    for (; i < argc - 1; i++)
>> +      spargv[i] = argv[i + 1];
>> +    spargv[i++] = (char *) "--direct";
>> +    spargv[i++] = (char *) "--restart";
>> +    spargv[i++] = nteststr;
>> +    spargv[i] = NULL;
>> +  }
> 
> No overflow prevention, but this is internal, so ok.
> 
>> +  for (int i = 0; i < array_length (tests); i++)
>> +    {
>> +      snprintf (nteststr, sizeof nteststr, "%d", i);
>> +
>> +      printf ("[%d] Spawned test for %s\n", i, tests[i].env);
>> +      setenv ("GLIBC_TUNABLES", tests[i].env, 1);
>> +      struct support_capture_subprocess result
>> +	= support_capture_subprogram (spargv[0], spargv);
>> +      support_capture_subprocess_check (&result, "tst-tunables", 0,
>> +					sc_allow_stderr);
>> +      support_capture_subprocess_free (&result);
>> +    }
>> +
>> +  return 0;
>> +}
> 
> Ok.
> 
>> +#define TEST_FUNCTION_ARGV do_test
>> +#include <support/test-driver.c>
> 
> Ok.
> 
>> diff --git a/manual/README.tunables b/manual/README.tunables
>> index 605ddd78cd..72ae00dc02 100644
>> --- a/manual/README.tunables
>> +++ b/manual/README.tunables
>> @@ -59,15 +59,6 @@ The list of allowed attributes are:
>>  
>>  - env_alias:		An alias environment variable
>>  
>> -- security_level:	Specify security level of the tunable for AT_SECURE
>> -			binaries.  Valid values are:
>> -
>> -			SXID_ERASE: (default) Do not read and do not pass on to
>> -			child processes.
>> -			SXID_IGNORE: Do not read, but retain for non-AT_SECURE
>> -			child processes.
>> -			NONE: Read all the time.
>> -
> 
> Ok.
> 
>> diff --git a/scripts/gen-tunables.awk b/scripts/gen-tunables.awk
>> index d6de100df0..1e9d6b534e 100644
>> --- a/scripts/gen-tunables.awk
>> +++ b/scripts/gen-tunables.awk
>> @@ -61,9 +61,6 @@ $1 == "}" {
>>      if (!env_alias[top_ns,ns,tunable]) {
>>        env_alias[top_ns,ns,tunable] = "{0}"
>>      }
>> -    if (!security_level[top_ns,ns,tunable]) {
>> -      security_level[top_ns,ns,tunable] = "SXID_ERASE"
>> -    }
> 
> Ok.
> 
>> @@ -118,17 +115,6 @@ $1 == "}" {
>>      if (len > max_alias_len)
>>        max_alias_len = len
>>    }
>> -  else if (attr == "security_level") {
>> -    if (val == "SXID_ERASE" || val == "SXID_IGNORE" || val == "NONE") {
>> -      security_level[top_ns,ns,tunable] = val
>> -    }
>> -    else {
>> -      printf("Line %d: Invalid value (%s) for security_level: %s, ", NR, val,
>> -	     $0)
>> -      print("Allowed values are 'SXID_ERASE', 'SXID_IGNORE', or 'NONE'")
>> -      exit 1
>> -    }
>> -  }
> 
> Ok.
> 
>> @@ -177,9 +163,9 @@ END {
>>      n = indices[2];
>>      m = indices[3];
>>      printf ("  {TUNABLE_NAME_S(%s, %s, %s)", t, n, m)
>> -    printf (", {TUNABLE_TYPE_%s, %s, %s}, {%s}, false, TUNABLE_SECLEVEL_%s, %s},\n",
>> +    printf (", {TUNABLE_TYPE_%s, %s, %s}, {%s}, false, %s},\n",
>>  	    types[t,n,m], minvals[t,n,m], maxvals[t,n,m],
>> -	    default_val[t,n,m], security_level[t,n,m], env_alias[t,n,m]);
>> +	    default_val[t,n,m], env_alias[t,n,m]);
>>    }
> 
> Ok.
> 
>> diff --git a/sysdeps/x86_64/64/dl-tunables.list b/sysdeps/x86_64/64/dl-tunables.list
>> index 0aab52e662..54a216a461 100644
>> --- a/sysdeps/x86_64/64/dl-tunables.list
>> +++ b/sysdeps/x86_64/64/dl-tunables.list
>> @@ -23,7 +23,6 @@ glibc {
>>        minval: 0
>>        maxval: 1
>>        env_alias: LD_PREFER_MAP_32BIT_EXEC
>> -      security_level: SXID_IGNORE
>>      }
>>    }
>>  }
> 
> Ok.
> 

^ permalink raw reply	[flat|nested] 45+ messages in thread

* Re: [PATCH v2 11/19] elf: Do not duplicate the GLIBC_TUNABLES string
  2023-10-27 11:54   ` Siddhesh Poyarekar
@ 2023-10-30 18:24     ` Adhemerval Zanella Netto
  0 siblings, 0 replies; 45+ messages in thread
From: Adhemerval Zanella Netto @ 2023-10-30 18:24 UTC (permalink / raw)
  To: Siddhesh Poyarekar, libc-alpha



On 27/10/23 08:54, Siddhesh Poyarekar wrote:
> 
> 
> On 2023-10-17 09:05, Adhemerval Zanella wrote:
>> The tunable parsing duplicates the tunable environment variable so it
>> null-terminates each one since it simplifies the later parsing. It has
>> the drawback of adding another point of failure (__minimal_malloc
>> failing), and the memory copy requires tuning the compiler to avoid mem
>> operations calls.
>>
>> The parsing now tracks the tunable start and its size. The
>> dl-tunable-parse.h adds helper functions to help parsing, like a strcmp
>> that also checks for size and an iterator for suboptions that are
>> comma-separated (used on hwcap parsing by x86, powerpc, and s390x).
>>
>> Since the environment variable is allocated on the stack by the kernel,
>> it is safe to keep the references to the suboptions for later parsing
>> of string tunables (as done by set_hwcaps by multiple architectures).
>>
>> Checked on x86_64-linux-gnu, powerpc64le-linux-gnu, and
>> aarch64-linux-gnu.
>> ---
>>   elf/dl-tunables.c                             |  66 +++----
>>   elf/dl-tunables.h                             |   6 +-
>>   sysdeps/generic/dl-tunables-parse.h           | 128 ++++++++++++++
>>   sysdeps/s390/cpu-features.c                   | 167 +++++++-----------
>>   .../unix/sysv/linux/aarch64/cpu-features.c    |  38 ++--
>>   .../unix/sysv/linux/powerpc/cpu-features.c    |  45 ++---
>>   .../sysv/linux/powerpc/tst-hwcap-tunables.c   |   6 +-
>>   sysdeps/x86/Makefile                          |   4 +-
>>   sysdeps/x86/cpu-tunables.c                    | 118 +++++--------
>>   sysdeps/x86/tst-hwcap-tunables.c              | 150 ++++++++++++++++
>>   10 files changed, 455 insertions(+), 273 deletions(-)
>>   create mode 100644 sysdeps/generic/dl-tunables-parse.h
>>   create mode 100644 sysdeps/x86/tst-hwcap-tunables.c
>>
>> diff --git a/elf/dl-tunables.c b/elf/dl-tunables.c
>> index b39c14694c..869846f9da 100644
>> --- a/elf/dl-tunables.c
>> +++ b/elf/dl-tunables.c
>> @@ -36,28 +36,6 @@
>>   #define TUNABLES_INTERNAL 1
>>   #include "dl-tunables.h"
>>   -#include <not-errno.h>
>> -
>> -static char *
>> -tunables_strdup (const char *in)
>> -{
>> -  size_t i = 0;
>> -
>> -  while (in[i++] != '\0');
>> -  char *out = __minimal_malloc (i + 1);
>> -
>> -  /* For most of the tunables code, we ignore user errors.  However,
>> -     this is a system error - and running out of memory at program
>> -     startup should be reported, so we do.  */
>> -  if (out == NULL)
>> -    _dl_fatal_printf ("failed to allocate memory to process tunables\n");
>> -
>> -  while (i-- > 0)
>> -    out[i] = in[i];
>> -
>> -  return out;
>> -}
>> -
>>   static char **
>>   get_next_env (char **envp, char **name, size_t *namelen, char **val,
>>             char ***prev_envp)
>> @@ -134,14 +112,14 @@ do_tunable_update_val (tunable_t *cur, const tunable_val_t *valp,
>>   /* Validate range of the input value and initialize the tunable CUR if it looks
>>      good.  */
>>   static void
>> -tunable_initialize (tunable_t *cur, const char *strval)
>> +tunable_initialize (tunable_t *cur, const char *strval, size_t len)
>>   {
>> -  tunable_val_t val;
>> +  tunable_val_t val = { 0 };
>>       if (cur->type.type_code != TUNABLE_TYPE_STRING)
>>       val.numval = (tunable_num_t) _dl_strtoul (strval, NULL);
>>     else
>> -    val.strval = strval;
>> +    val.strval = (struct tunable_str_t) { strval, len };
>>     do_tunable_update_val (cur, &val, NULL, NULL);
>>   }
>>   @@ -158,29 +136,29 @@ struct tunable_toset_t
>>   {
>>     tunable_t *t;
>>     const char *value;
>> +  size_t len;
>>   };
>>     enum { tunables_list_size = array_length (tunable_list) };
>>     /* Parse the tunable string VALSTRING and set TUNABLES with the found tunables
>> -   and their respectibles values.  VALSTRING is a duplicated values,  where
>> -   delimiters ':' are replaced with '\0', so string tunables are null
>> -   terminated.
>> +   and their respectibles values.  The VALSTRING is parse in place, with the
> 
> parsed

Ack.

> 
>> +   tunable start and size recorded in TUNABLES.
>>      Return the number of tunables found (including 0 if the string is empty)
>>      or -1 if for a ill-formatted definition.  */
>>   static int
>> -parse_tunables_string (char *valstring, struct tunable_toset_t *tunables)
>> +parse_tunables_string (const char *valstring, struct tunable_toset_t *tunables)
>>   {
>>     if (valstring == NULL || *valstring == '\0')
>>       return 0;
>>   -  char *p = valstring;
>> +  const char *p = valstring;
>>     bool done = false;
>>     int ntunables = 0;
>>       while (!done)
>>       {
>> -      char *name = p;
>> +      const char *name = p;
>>           /* First, find where the name ends.  */
>>         while (*p != '=' && *p != ':' && *p != '\0')
>> @@ -202,7 +180,7 @@ parse_tunables_string (char *valstring, struct tunable_toset_t *tunables)
>>         /* Skip the ':' or '='.  */
>>         p++;
>>   -      char *value = p;
>> +      const char *value = p;
>>           while (*p != '=' && *p != ':' && *p != '\0')
>>       p++;
>> @@ -211,8 +189,6 @@ parse_tunables_string (char *valstring, struct tunable_toset_t *tunables)
>>       return -1;
>>         else if (*p == '\0')
>>       done = true;
>> -      else
>> -    *p++ = '\0';
>>           /* Add the tunable if it exists.  */
>>         for (size_t i = 0; i < tunables_list_size; i++)
>> @@ -221,7 +197,8 @@ parse_tunables_string (char *valstring, struct tunable_toset_t *tunables)
>>           if (tunable_is_name (cur->name, name))
>>           {
>> -          tunables[ntunables++] = (struct tunable_toset_t) { cur, value };
>> +          tunables[ntunables++] =
>> +        (struct tunable_toset_t) { cur, value, p - value };
>>             break;
>>           }
>>       }
>> @@ -231,7 +208,7 @@ parse_tunables_string (char *valstring, struct tunable_toset_t *tunables)
>>   }
>>     static void
>> -parse_tunables (char *valstring)
>> +parse_tunables (const char *valstring)
>>   {
>>     struct tunable_toset_t tunables[tunables_list_size];
>>     int ntunables = parse_tunables_string (valstring, tunables);
>> @@ -243,7 +220,7 @@ parse_tunables (char *valstring)
>>       }
>>       for (int i = 0; i < ntunables; i++)
>> -    tunable_initialize (tunables[i].t, tunables[i].value);
>> +    tunable_initialize (tunables[i].t, tunables[i].value, tunables[i].len);
>>   }
>>     /* Initialize the tunables list from the environment.  For now we only use the
>> @@ -264,9 +241,12 @@ __tunables_init (char **envp)
>>     while ((envp = get_next_env (envp, &envname, &len, &envval,
>>                      &prev_envp)) != NULL)
>>       {
>> +      /* The environment variable is allocated on the stack by the kernel, so
>> +     it is safe to keep the references to the suboptions for later parsing
>> +     of string tunables.  */
>>         if (tunable_is_name ("GLIBC_TUNABLES", envname))
>>       {
>> -      parse_tunables (tunables_strdup (envval));
>> +      parse_tunables (envval);
>>         continue;
>>       }
>>   @@ -284,7 +264,7 @@ __tunables_init (char **envp)
>>         /* We have a match.  Initialize and move on to the next line.  */
>>         if (tunable_is_name (name, envname))
>>           {
>> -          tunable_initialize (cur, envval);
>> +          tunable_initialize (cur, envval, 0);
> 
> Do you want to put an assert in here that the env aliases are only there for numeric tunables?  Passing 0 does not seem right for strval tunables.

It should be 'tunable_initialize (cur, envval, len)' in fact, since we
can keep a pointer to the kernel allocated environment variable to
subsequent TUNABLE_CALLBACK queries.

I will also add extra aliases checks on tst-tunebles (unfortunately 
we don't have any tunable aliases to string ones).

> 
>>             break;
>>           }
>>       }
>> @@ -298,7 +278,7 @@ __tunables_print (void)
>>       {
>>         const tunable_t *cur = &tunable_list[i];
>>         if (cur->type.type_code == TUNABLE_TYPE_STRING
>> -      && cur->val.strval == NULL)
>> +      && cur->val.strval.str == NULL)
>>       _dl_printf ("%s:\n", cur->name);
>>         else
>>       {
>> @@ -324,7 +304,9 @@ __tunables_print (void)
>>                 (size_t) cur->type.max);
>>             break;
>>           case TUNABLE_TYPE_STRING:
>> -          _dl_printf ("%s\n", cur->val.strval);
>> +          _dl_printf ("%.*s\n",
>> +              (int) cur->val.strval.len,
>> +              cur->val.strval.str);
>>             break;
>>           default:
>>             __builtin_unreachable ();
>> @@ -359,7 +341,7 @@ __tunable_get_val (tunable_id_t id, void *valp, tunable_callback_t callback)
>>       }
>>       case TUNABLE_TYPE_STRING:
>>       {
>> -      *((const char **)valp) = cur->val.strval;
>> +      *((struct tunable_str_t **) valp) = &cur->val.strval;
>>         break;
>>       }
>>       default:
>> diff --git a/elf/dl-tunables.h b/elf/dl-tunables.h
>> index 45c191e021..0e777d7d37 100644
>> --- a/elf/dl-tunables.h
>> +++ b/elf/dl-tunables.h
>> @@ -30,7 +30,11 @@ typedef intmax_t tunable_num_t;
>>   typedef union
>>   {
>>     tunable_num_t numval;
>> -  const char *strval;
>> +  struct tunable_str_t
>> +  {
>> +    const char *str;
>> +    size_t len;
>> +  } strval;
>>   } tunable_val_t;
>>     typedef void (*tunable_callback_t) (tunable_val_t *);
>> diff --git a/sysdeps/generic/dl-tunables-parse.h b/sysdeps/generic/dl-tunables-parse.h
>> new file mode 100644
>> index 0000000000..a2420f9129
>> --- /dev/null
>> +++ b/sysdeps/generic/dl-tunables-parse.h
>> @@ -0,0 +1,128 @@
>> +/* Helper functions to handle tunable strings.
>> +   Copyright (C) 2023 Free Software Foundation, Inc.
>> +   This file is part of the GNU C Library.
>> +
>> +   The GNU C Library is free software; you can redistribute it and/or
>> +   modify it under the terms of the GNU Lesser General Public
>> +   License as published by the Free Software Foundation; either
>> +   version 2.1 of the License, or (at your option) any later version.
>> +
>> +   The GNU C Library is distributed in the hope that it will be useful,
>> +   but WITHOUT ANY WARRANTY; without even the implied warranty of
>> +   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
>> +   Lesser General Public License for more details.
>> +
>> +   You should have received a copy of the GNU Lesser General Public
>> +   License along with the GNU C Library; if not, see
>> +   <https://www.gnu.org/licenses/>.  */
>> +
>> +#ifndef _DL_TUNABLES_PARSE_H
>> +#define _DL_TUNABLES_PARSE_H 1
>> +
>> +#include <string.h>
>> +
>> +/* Compare the contents of STRVAL with STR of size LEN.  The STR might not
>> +   be null-terminated.   */
>> +static __always_inline bool
>> +tunable_strcmp (const struct tunable_str_t *strval, const char *str,
>> +        size_t len)
>> +{
>> +  return strval->len == len && memcmp (strval->str, str, len) == 0;
>> +}
>> +#define tunable_strcmp_cte(__tunable, __str) \
>> + tunable_strcmp (&__tunable->strval, __str, sizeof (__str) - 1)
> 
> What does cte stand for?

'constant' and it is meant for string literals (where sizeof works correctly).
I tried to come up some macros/builtin shenanigans to check if the input is
a string literal to either call sizeof of strlen, but in the end I could not
find a reliable way and ended up with this.

> 
>> +
>> +/*
>> +   Helper functions to iterate over a tunable string composed by multiple
>> +   suboptions separated by comma.  Each suboptions is return in the form
> 
> Maybe enhance this comment like:
> 
>   suboptions separated by comma; this is a common pattern for CPU
>   feature tunables.

Ack.

> 
> Alternatively, does it make sense to treat this like a list?  That would make this abstraction easier to read and reason, e.g.:
> 
>   if (tunable_list_contains (&t, opt, opt1_len))
>   ...
>   if (tunable_list_contains_cte_any (&t, opt1, opt2, ...))
> 
> and so on.  You don't actually need the while loop or state maintenance then.  In fact, maybe there's a case here to make a LIST tunable type.

A see two problems with a list: usually each suboption requires an action
by the architecture.  So 

  if (tunable_list_contains_cte_any (&t, opt1, opt2, ...))

will required also

     if (tunable_list_contains (&t1, opt1))
       // do something

And so on.

Another issues is each architecture callback will need to check whether each
option is set or not; meaning that tunable_list_contains will require to
return a list of unbonded returns values (to indicate whether the suboption
is enabled or not).

So I think an iterator-like seems to be the best option here.

> 
>> +   of { address, size } (no null terminated).  For instance:
>> +
>> +     struct tunable_str_comma_t ts;
>> +     tunable_str_comma_init (&ts, valp);
>> +
>> +     struct tunable_str_t t;
>> +     while (tunable_str_comma_next (&ts, &t))
>> +      {
>> +    _dl_printf ("[%s] %.*s (%d)\n",
>> +            __func__,
>> +            (int) tstr.len,
>> +            tstr.str,
>> +            (int) tstr.len);
>> +
>> +        if (tunable_str_comma_strcmp (&t, opt, opt1_len))
>> +      {
>> +        [...]
>> +      }
>> +    else if (tunable_str_comma_strcmp_cte (&t, "opt2"))
>> +      {
>> +        [...]
>> +      }
>> +      }
>> +*/
>> +
>> +struct tunable_str_comma_state_t
>> +{
>> +  const char *p;
>> +  size_t plen;
>> +  size_t maxplen;
>> +};
>> +
>> +struct tunable_str_comma_t
>> +{
>> +  const char *str;
>> +  size_t len;
>> +  bool disable;
>> +};
>> +
>> +static inline void
>> +tunable_str_comma_init (struct tunable_str_comma_state_t *state,
>> +            tunable_val_t *valp)
>> +{
>> +  state->p = valp->strval.str;
>> +  state->plen = 0;
>> +  state->maxplen = valp->strval.len;
>> +}
>> +
>> +static inline bool
>> +tunable_str_comma_next (struct tunable_str_comma_state_t *state,
>> +            struct tunable_str_comma_t *str)
>> +{
>> +  if (*state->p == '\0' || state->plen >= state->maxplen)
>> +    return false;
>> +
>> +  const char *c;
>> +  for (c = state->p; *c != ','; c++, state->plen++)
>> +    if (*c == '\0' || state->plen == state->maxplen)
>> +      break;
>> +
>> +  str->str = state->p;
>> +  str->len = c - state->p;
>> +
>> +  if (str->len > 0)
>> +    {
>> +      str->disable = *str->str == '-';
>> +      if (str->disable)
>> +    {
>> +      str->str = str->str + 1;
>> +      str->len = str->len - 1;
>> +    }
>> +    }
>> +
>> +  state->p = c + 1;
>> +  state->plen++;
>> +
>> +  return true;
>> +}
>> +
>> +/* Compare the contents of T with STR of size LEN.  The STR might not be
>> +   null-terminated.   */
>> +static __always_inline bool
>> +tunable_str_comma_strcmp (const struct tunable_str_comma_t *t, const char *str,
>> +              size_t len)
>> +{
>> +  return t->len == len && memcmp (t->str, str, len) == 0;
>> +}
>> +#define tunable_str_comma_strcmp_cte(__t, __str) \
>> +  tunable_str_comma_strcmp (__t, __str, sizeof (__str) - 1)
>> +
>> +#endif
>> diff --git a/sysdeps/s390/cpu-features.c b/sysdeps/s390/cpu-features.c
>> index 55449ba07f..8b1466fa7b 100644
>> --- a/sysdeps/s390/cpu-features.c
>> +++ b/sysdeps/s390/cpu-features.c
>> @@ -22,6 +22,7 @@
>>   #include <ifunc-memcmp.h>
>>   #include <string.h>
>>   #include <dl-symbol-redir-ifunc.h>
>> +#include <dl-tunables-parse.h>
>>     #define S390_COPY_CPU_FEATURES(SRC_PTR, DEST_PTR)    \
>>     (DEST_PTR)->hwcap = (SRC_PTR)->hwcap;            \
>> @@ -51,33 +52,14 @@ TUNABLE_CALLBACK (set_hwcaps) (tunable_val_t *valp)
>>     struct cpu_features cpu_features_curr;
>>     S390_COPY_CPU_FEATURES (cpu_features, &cpu_features_curr);
>>   -  const char *token = valp->strval;
>> -  do
>> +  struct tunable_str_comma_state_t ts;
>> +  tunable_str_comma_init (&ts, valp);
>> +
>> +  struct tunable_str_comma_t t;
>> +  while (tunable_str_comma_next (&ts, &t))
>>       {
>> -      const char *token_end, *feature;
>> -      bool disable;
>> -      size_t token_len;
>> -      size_t feature_len;
>> -
>> -      /* Find token separator or end of string.  */
>> -      for (token_end = token; *token_end != ','; token_end++)
>> -    if (*token_end == '\0')
>> -      break;
>> -
>> -      /* Determine feature.  */
>> -      token_len = token_end - token;
>> -      if (*token == '-')
>> -    {
>> -      disable = true;
>> -      feature = token + 1;
>> -      feature_len = token_len - 1;
>> -    }
>> -      else
>> -    {
>> -      disable = false;
>> -      feature = token;
>> -      feature_len = token_len;
>> -    }
>> +      if (t.len == 0)
>> +    continue;
>>           /* Handle only the features here which are really used in the
>>        IFUNC-resolvers.  All others are ignored as the values are only used
>> @@ -85,85 +67,65 @@ TUNABLE_CALLBACK (set_hwcaps) (tunable_val_t *valp)
>>         bool reset_features = false;
>>         unsigned long int hwcap_mask = 0UL;
>>         unsigned long long stfle_bits0_mask = 0ULL;
>> +      bool disable = t.disable;
>>   -      if ((*feature == 'z' || *feature == 'a'))
>> +      if (tunable_str_comma_strcmp_cte (&t, "zEC12")
>> +      || tunable_str_comma_strcmp_cte (&t, "arch10"))
>> +    {
>> +      reset_features = true;
>> +      disable = true;
>> +      hwcap_mask = HWCAP_S390_VXRS | HWCAP_S390_VXRS_EXT
>> +        | HWCAP_S390_VXRS_EXT2;
>> +      stfle_bits0_mask = S390_STFLE_MASK_ARCH13_MIE3;
>> +    }
>> +      else if (tunable_str_comma_strcmp_cte (&t, "z13")
>> +           || tunable_str_comma_strcmp_cte (&t, "arch11"))
>> +    {
>> +      reset_features = true;
>> +      disable = true;
>> +      hwcap_mask = HWCAP_S390_VXRS_EXT | HWCAP_S390_VXRS_EXT2;
>> +      stfle_bits0_mask = S390_STFLE_MASK_ARCH13_MIE3;
>> +    }
>> +      else if (tunable_str_comma_strcmp_cte (&t, "z14")
>> +           || tunable_str_comma_strcmp_cte (&t, "arch12"))
>> +    {
>> +      reset_features = true;
>> +      disable = true;
>> +      hwcap_mask = HWCAP_S390_VXRS_EXT2;
>> +      stfle_bits0_mask = S390_STFLE_MASK_ARCH13_MIE3;
>> +    }
>> +      else if (tunable_str_comma_strcmp_cte (&t, "z15")
>> +           || tunable_str_comma_strcmp_cte (&t, "z16")
>> +           || tunable_str_comma_strcmp_cte (&t, "arch13")
>> +           || tunable_str_comma_strcmp_cte (&t, "arch14"))
>>       {
>> -      if ((feature_len == 5 && *feature == 'z'
>> -           && memcmp (feature, "zEC12", 5) == 0)
>> -          || (feature_len == 6 && *feature == 'a'
>> -          && memcmp (feature, "arch10", 6) == 0))
>> -        {
>> -          reset_features = true;
>> -          disable = true;
>> -          hwcap_mask = HWCAP_S390_VXRS | HWCAP_S390_VXRS_EXT
>> -        | HWCAP_S390_VXRS_EXT2;
>> -          stfle_bits0_mask = S390_STFLE_MASK_ARCH13_MIE3;
>> -        }
>> -      else if ((feature_len == 3 && *feature == 'z'
>> -            && memcmp (feature, "z13", 3) == 0)
>> -           || (feature_len == 6 && *feature == 'a'
>> -               && memcmp (feature, "arch11", 6) == 0))
>> -        {
>> -          reset_features = true;
>> -          disable = true;
>> -          hwcap_mask = HWCAP_S390_VXRS_EXT | HWCAP_S390_VXRS_EXT2;
>> -          stfle_bits0_mask = S390_STFLE_MASK_ARCH13_MIE3;
>> -        }
>> -      else if ((feature_len == 3 && *feature == 'z'
>> -            && memcmp (feature, "z14", 3) == 0)
>> -           || (feature_len == 6 && *feature == 'a'
>> -               && memcmp (feature, "arch12", 6) == 0))
>> -        {
>> -          reset_features = true;
>> -          disable = true;
>> -          hwcap_mask = HWCAP_S390_VXRS_EXT2;
>> -          stfle_bits0_mask = S390_STFLE_MASK_ARCH13_MIE3;
>> -        }
>> -      else if ((feature_len == 3 && *feature == 'z'
>> -            && (memcmp (feature, "z15", 3) == 0
>> -            || memcmp (feature, "z16", 3) == 0))
>> -           || (feature_len == 6
>> -               && (memcmp (feature, "arch13", 6) == 0
>> -               || memcmp (feature, "arch14", 6) == 0)))
>> -        {
>> -          /* For z15 or newer we don't have to disable something,
>> -         but we have to reset to the original values.  */
>> -          reset_features = true;
>> -        }
>> +      /* For z15 or newer we don't have to disable something, but we have
>> +         to reset to the original values.  */
>> +      reset_features = true;
>>       }
>> -      else if (*feature == 'H')
>> +      else if (tunable_str_comma_strcmp_cte (&t, "HWCAP_S390_VXRS"))
>>       {
>> -      if (feature_len == 15
>> -          && memcmp (feature, "HWCAP_S390_VXRS", 15) == 0)
>> -        {
>> -          hwcap_mask = HWCAP_S390_VXRS;
>> -          if (disable)
>> -        hwcap_mask |= HWCAP_S390_VXRS_EXT | HWCAP_S390_VXRS_EXT2;
>> -        }
>> -      else if (feature_len == 19
>> -           && memcmp (feature, "HWCAP_S390_VXRS_EXT", 19) == 0)
>> -        {
>> -          hwcap_mask = HWCAP_S390_VXRS_EXT;
>> -          if (disable)
>> -        hwcap_mask |= HWCAP_S390_VXRS_EXT2;
>> -          else
>> -        hwcap_mask |= HWCAP_S390_VXRS;
>> -        }
>> -      else if (feature_len == 20
>> -           && memcmp (feature, "HWCAP_S390_VXRS_EXT2", 20) == 0)
>> -        {
>> -          hwcap_mask = HWCAP_S390_VXRS_EXT2;
>> -          if (!disable)
>> -        hwcap_mask |= HWCAP_S390_VXRS | HWCAP_S390_VXRS_EXT;
>> -        }
>> +      hwcap_mask = HWCAP_S390_VXRS;
>> +      if (t.disable)
>> +        hwcap_mask |= HWCAP_S390_VXRS_EXT | HWCAP_S390_VXRS_EXT2;
>>       }
>> -      else if (*feature == 'S')
>> +      else if (tunable_str_comma_strcmp_cte (&t, "HWCAP_S390_VXRS_EXT"))
>>       {
>> -      if (feature_len == 10
>> -          && memcmp (feature, "STFLE_MIE3", 10) == 0)
>> -        {
>> -          stfle_bits0_mask = S390_STFLE_MASK_ARCH13_MIE3;
>> -        }
>> +      hwcap_mask = HWCAP_S390_VXRS_EXT;
>> +      if (t.disable)
>> +        hwcap_mask |= HWCAP_S390_VXRS_EXT2;
>> +      else
>> +        hwcap_mask |= HWCAP_S390_VXRS;
>> +    }
>> +      else if (tunable_str_comma_strcmp_cte (&t, "HWCAP_S390_VXRS_EXT2"))
>> +    {
>> +      hwcap_mask = HWCAP_S390_VXRS_EXT2;
>> +      if (!t.disable)
>> +        hwcap_mask |= HWCAP_S390_VXRS | HWCAP_S390_VXRS_EXT;
>> +    }
>> +      else if (tunable_str_comma_strcmp_cte (&t, "STFLE_MIE3"))
>> +    {
>> +      stfle_bits0_mask = S390_STFLE_MASK_ARCH13_MIE3;
>>       }
>>           /* Perform the actions determined above.  */
>> @@ -187,14 +149,7 @@ TUNABLE_CALLBACK (set_hwcaps) (tunable_val_t *valp)
>>         else
>>           cpu_features_curr.stfle_bits[0] |= stfle_bits0_mask;
>>       }
>> -
>> -      /* Jump over current token ... */
>> -      token += token_len;
>> -
>> -      /* ... and skip token separator for next round.  */
>> -      if (*token == ',') token++;
>>       }
>> -  while (*token != '\0');
>>       /* Copy back the features after checking that no unsupported features were
>>        enabled by user.  */
>> diff --git a/sysdeps/unix/sysv/linux/aarch64/cpu-features.c b/sysdeps/unix/sysv/linux/aarch64/cpu-features.c
>> index dc09c1c827..3f1a6bcd62 100644
>> --- a/sysdeps/unix/sysv/linux/aarch64/cpu-features.c
>> +++ b/sysdeps/unix/sysv/linux/aarch64/cpu-features.c
>> @@ -16,10 +16,12 @@
>>      License along with the GNU C Library; if not, see
>>      <https://www.gnu.org/licenses/>.  */
>>   +#include <array_length.h>
>>   #include <cpu-features.h>
>>   #include <sys/auxv.h>
>>   #include <elf/dl-hwcaps.h>
>>   #include <sys/prctl.h>
>> +#include <dl-tunables-parse.h>
>>     #define DCZID_DZP_MASK (1 << 4)
>>   #define DCZID_BS_MASK (0xf)
>> @@ -33,28 +35,32 @@
>>   struct cpu_list
>>   {
>>     const char *name;
>> +  size_t len;
>>     uint64_t midr;
>>   };
>>   -static struct cpu_list cpu_list[] = {
>> -      {"falkor",     0x510FC000},
>> -      {"thunderxt88",     0x430F0A10},
>> -      {"thunderx2t99",   0x431F0AF0},
>> -      {"thunderx2t99p1", 0x420F5160},
>> -      {"phecda",     0x680F0000},
>> -      {"ares",         0x411FD0C0},
>> -      {"emag",         0x503F0001},
>> -      {"kunpeng920",      0x481FD010},
>> -      {"a64fx",         0x460F0010},
>> -      {"generic",      0x0}
>> +static const struct cpu_list cpu_list[] =
>> +{
>> +#define CPU_LIST_ENTRY(__str, __num) { __str, sizeof (__str) - 1, __num }
>> +  CPU_LIST_ENTRY ("falkor",         0x510FC000),
>> +  CPU_LIST_ENTRY ("thunderxt88",    0x430F0A10),
>> +  CPU_LIST_ENTRY ("thunderx2t99",   0x431F0AF0),
>> +  CPU_LIST_ENTRY ("thunderx2t99p1", 0x420F5160),
>> +  CPU_LIST_ENTRY ("phecda",         0x680F0000),
>> +  CPU_LIST_ENTRY ("ares",           0x411FD0C0),
>> +  CPU_LIST_ENTRY ("emag",           0x503F0001),
>> +  CPU_LIST_ENTRY ("kunpeng920",     0x481FD010),
>> +  CPU_LIST_ENTRY ("a64fx",          0x460F0010),
>> +  CPU_LIST_ENTRY ("generic",        0x0),
>>   };
>>     static uint64_t
>> -get_midr_from_mcpu (const char *mcpu)
>> +get_midr_from_mcpu (const struct tunable_str_t *mcpu)
>>   {
>> -  for (int i = 0; i < sizeof (cpu_list) / sizeof (struct cpu_list); i++)
>> -    if (strcmp (mcpu, cpu_list[i].name) == 0)
>> +  for (int i = 0; i < array_length (cpu_list); i++) {
>> +    if (tunable_strcmp (mcpu, cpu_list[i].name, cpu_list[i].len))
>>         return cpu_list[i].midr;
>> +  }
>>       return UINT64_MAX;
>>   }
>> @@ -65,7 +71,9 @@ init_cpu_features (struct cpu_features *cpu_features)
>>     register uint64_t midr = UINT64_MAX;
>>       /* Get the tunable override.  */
>> -  const char *mcpu = TUNABLE_GET (glibc, cpu, name, const char *, NULL);
>> +  const struct tunable_str_t *mcpu = TUNABLE_GET (glibc, cpu, name,
>> +                          struct tunable_str_t *,
>> +                          NULL);
>>     if (mcpu != NULL)
>>       midr = get_midr_from_mcpu (mcpu);
>>   diff --git a/sysdeps/unix/sysv/linux/powerpc/cpu-features.c b/sysdeps/unix/sysv/linux/powerpc/cpu-features.c
>> index 7c6e20e702..390b3fd11a 100644
>> --- a/sysdeps/unix/sysv/linux/powerpc/cpu-features.c
>> +++ b/sysdeps/unix/sysv/linux/powerpc/cpu-features.c
>> @@ -20,6 +20,7 @@
>>   #include <stdint.h>
>>   #include <cpu-features.h>
>>   #include <elf/dl-tunables.h>
>> +#include <dl-tunables-parse.h>
>>   #include <unistd.h>
>>   #include <string.h>
>>   @@ -43,41 +44,26 @@ TUNABLE_CALLBACK (set_hwcaps) (tunable_val_t *valp)
>>     struct cpu_features *cpu_features = &GLRO(dl_powerpc_cpu_features);
>>     unsigned long int tcbv_hwcap = cpu_features->hwcap;
>>     unsigned long int tcbv_hwcap2 = cpu_features->hwcap2;
>> -  const char *token = valp->strval;
>> -  do
>> +
>> +  struct tunable_str_comma_state_t ts;
>> +  tunable_str_comma_init (&ts, valp);
>> +
>> +  struct tunable_str_comma_t t;
>> +  while (tunable_str_comma_next (&ts, &t))
>>       {
>> -      const char *token_end, *feature;
>> -      bool disable;
>> -      size_t token_len, i, feature_len, offset = 0;
>> -      /* Find token separator or end of string.  */
>> -      for (token_end = token; *token_end != ','; token_end++)
>> -    if (*token_end == '\0')
>> -      break;
>> +      if (t.len == 0)
>> +    continue;
>>   -      /* Determine feature.  */
>> -      token_len = token_end - token;
>> -      if (*token == '-')
>> -    {
>> -      disable = true;
>> -      feature = token + 1;
>> -      feature_len = token_len - 1;
>> -    }
>> -      else
>> -    {
>> -      disable = false;
>> -      feature = token;
>> -      feature_len = token_len;
>> -    }
>> -      for (i = 0; i < array_length (hwcap_tunables); ++i)
>> +      size_t offset = 0;
>> +      for (int i = 0; i < array_length (hwcap_tunables); ++i)
>>       {
>>         const char *hwcap_name = hwcap_names + offset;
>>         size_t hwcap_name_len = strlen (hwcap_name);
>>         /* Check the tunable name on the supported list.  */
>> -      if (hwcap_name_len == feature_len
>> -          && memcmp (feature, hwcap_name, feature_len) == 0)
>> +      if (tunable_str_comma_strcmp (&t, hwcap_name, hwcap_name_len))
>>           {
>>             /* Update the hwcap and hwcap2 bits.  */
>> -          if (disable)
>> +          if (t.disable)
>>           {
>>             /* Id is 1 for hwcap2 tunable.  */
>>             if (hwcap_tunables[i].id)
>> @@ -98,12 +84,7 @@ TUNABLE_CALLBACK (set_hwcaps) (tunable_val_t *valp)
>>           }
>>         offset += hwcap_name_len + 1;
>>       }
>> -    token += token_len;
>> -    /* ... and skip token separator for next round.  */
>> -    if (*token == ',')
>> -      token++;
>>       }
>> -  while (*token != '\0');
>>   }
>>     static inline void
>> diff --git a/sysdeps/unix/sysv/linux/powerpc/tst-hwcap-tunables.c b/sysdeps/unix/sysv/linux/powerpc/tst-hwcap-tunables.c
>> index 2631016a3a..049164f841 100644
>> --- a/sysdeps/unix/sysv/linux/powerpc/tst-hwcap-tunables.c
>> +++ b/sysdeps/unix/sysv/linux/powerpc/tst-hwcap-tunables.c
>> @@ -110,7 +110,11 @@ do_test (int argc, char *argv[])
>>       run_test ("-arch_2_06", "__memcpy_power7");
>>         if (hwcap & PPC_FEATURE_ARCH_2_05)
>>       run_test ("-arch_2_06,-arch_2_05","__memcpy_power6");
>> -      run_test ("-arch_2_06,-arch_2_05,-power5+,-power5,-power4", "__memcpy_power4");
>> +      run_test ("-arch_2_06,-arch_2_05,-power5+,-power5,-power4",
>> +        "__memcpy_power4");
>> +      /* Also run with valid, but empty settings.  */
>> +      run_test (",-,-arch_2_06,-arch_2_05,-power5+,-power5,,-power4,-",
>> +        "__memcpy_power4");
>>       }
>>     else
>>       {
>> diff --git a/sysdeps/x86/Makefile b/sysdeps/x86/Makefile
>> index 917c26f116..a64e5f002a 100644
>> --- a/sysdeps/x86/Makefile
>> +++ b/sysdeps/x86/Makefile
>> @@ -12,7 +12,8 @@ CFLAGS-get-cpuid-feature-leaf.o += $(no-stack-protector)
>>     tests += tst-get-cpu-features tst-get-cpu-features-static \
>>        tst-cpu-features-cpuinfo tst-cpu-features-cpuinfo-static \
>> -     tst-cpu-features-supports tst-cpu-features-supports-static
>> +     tst-cpu-features-supports tst-cpu-features-supports-static \
>> +     tst-hwcap-tunables
>>   tests-static += tst-get-cpu-features-static \
>>           tst-cpu-features-cpuinfo-static \
>>           tst-cpu-features-supports-static
>> @@ -65,6 +66,7 @@ $(objpfx)tst-isa-level-1.out: $(objpfx)tst-isa-level-mod-1-baseline.so \
>>   endif
>>   tst-ifunc-isa-2-ENV = GLIBC_TUNABLES=glibc.cpu.hwcaps=-SSE4_2,-AVX,-AVX2,-AVX512F
>>   tst-ifunc-isa-2-static-ENV = $(tst-ifunc-isa-2-ENV)
>> +tst-hwcap-tunables-ARGS = -- $(host-test-program-cmd)
>>   endif
>>     ifeq ($(subdir),math)
>> diff --git a/sysdeps/x86/cpu-tunables.c b/sysdeps/x86/cpu-tunables.c
>> index 5697885226..0608209768 100644
>> --- a/sysdeps/x86/cpu-tunables.c
>> +++ b/sysdeps/x86/cpu-tunables.c
>> @@ -24,11 +24,12 @@
>>   #include <string.h>
>>   #include <cpu-features.h>
>>   #include <ldsodefs.h>
>> +#include <dl-tunables-parse.h>
>>   #include <dl-symbol-redir-ifunc.h>
>>     #define CHECK_GLIBC_IFUNC_CPU_OFF(f, cpu_features, name, len)        \
>>     _Static_assert (sizeof (#name) - 1 == len, #name " != " #len);    \
>> -  if (memcmp (f, #name, len) == 0)                    \
>> +  if (tunable_str_comma_strcmp_cte (&f, #name))                \
>>       {                                    \
>>         CPU_FEATURE_UNSET (cpu_features, name)                \
>>         break;                                \
>> @@ -38,7 +39,7 @@
>>      which isn't available.  */
>>   #define CHECK_GLIBC_IFUNC_PREFERRED_OFF(f, cpu_features, name, len)    \
>>     _Static_assert (sizeof (#name) - 1 == len, #name " != " #len);    \
>> -  if (memcmp (f, #name, len) == 0)                    \
>> +  if (tunable_str_comma_strcmp_cte (&f, #name) == 0)            \
>>       {                                    \
>>         cpu_features->preferred[index_arch_##name]            \
>>       &= ~bit_arch_##name;                        \
>> @@ -46,12 +47,11 @@
>>       }
>>     /* Enable/disable a preferred feature NAME.  */
>> -#define CHECK_GLIBC_IFUNC_PREFERRED_BOTH(f, cpu_features, name,    \
>> -                      disable, len)            \
>> +#define CHECK_GLIBC_IFUNC_PREFERRED_BOTH(f, cpu_features, name,    len)    \
>>     _Static_assert (sizeof (#name) - 1 == len, #name " != " #len);    \
>> -  if (memcmp (f, #name, len) == 0)                    \
>> +  if (tunable_str_comma_strcmp_cte (&f, #name))                \
>>       {                                    \
>> -      if (disable)                            \
>> +      if (f.disable)                            \
>>       cpu_features->preferred[index_arch_##name] &= ~bit_arch_##name;    \
>>         else                                \
>>       cpu_features->preferred[index_arch_##name] |= bit_arch_##name;    \
>> @@ -61,11 +61,11 @@
>>   /* Enable/disable a preferred feature NAME.  Enable a preferred feature
>>      only if the feature NEED is usable.  */
>>   #define CHECK_GLIBC_IFUNC_PREFERRED_NEED_BOTH(f, cpu_features, name,    \
>> -                           need, disable, len)    \
>> +                          need, len)        \
>>     _Static_assert (sizeof (#name) - 1 == len, #name " != " #len);    \
>> -  if (memcmp (f, #name, len) == 0)                    \
>> +  if (tunable_str_comma_strcmp_cte (&f, #name))                \
>>       {                                    \
>> -      if (disable)                            \
>> +      if (f.disable)                            \
>>       cpu_features->preferred[index_arch_##name] &= ~bit_arch_##name;    \
>>         else if (CPU_FEATURE_USABLE_P (cpu_features, need))        \
>>       cpu_features->preferred[index_arch_##name] |= bit_arch_##name;    \
>> @@ -93,38 +93,20 @@ TUNABLE_CALLBACK (set_hwcaps) (tunable_val_t *valp)
>>        NOTE: the IFUNC selection may change over time.  Please check all
>>        multiarch implementations when experimenting.  */
>>   -  const char *p = valp->strval, *c;
>>     struct cpu_features *cpu_features = &GLRO(dl_x86_cpu_features);
>> -  size_t len;
>>   -  do
>> -    {
>> -      const char *n;
>> -      bool disable;
>> -      size_t nl;
>> -
>> -      for (c = p; *c != ','; c++)
>> -    if (*c == '\0')
>> -      break;
>> +  struct tunable_str_comma_state_t ts;
>> +  tunable_str_comma_init (&ts, valp);
>>   -      len = c - p;
>> -      disable = *p == '-';
>> -      if (disable)
>> -    {
>> -      n = p + 1;
>> -      nl = len - 1;
>> -    }
>> -      else
>> -    {
>> -      n = p;
>> -      nl = len;
>> -    }
>> -      switch (nl)
>> +  struct tunable_str_comma_t n;
>> +  while (tunable_str_comma_next (&ts, &n))
>> +    {
>> +      switch (n.len)
>>       {
>>       default:
>>         break;
>>       case 3:
>> -      if (disable)
>> +      if (n.disable)
>>           {
>>             CHECK_GLIBC_IFUNC_CPU_OFF (n, cpu_features, AVX, 3);
>>             CHECK_GLIBC_IFUNC_CPU_OFF (n, cpu_features, CX8, 3);
>> @@ -135,7 +117,7 @@ TUNABLE_CALLBACK (set_hwcaps) (tunable_val_t *valp)
>>           }
>>         break;
>>       case 4:
>> -      if (disable)
>> +      if (n.disable)
>>           {
>>             CHECK_GLIBC_IFUNC_CPU_OFF (n, cpu_features, AVX2, 4);
>>             CHECK_GLIBC_IFUNC_CPU_OFF (n, cpu_features, BMI1, 4);
>> @@ -149,7 +131,7 @@ TUNABLE_CALLBACK (set_hwcaps) (tunable_val_t *valp)
>>           }
>>         break;
>>       case 5:
>> -      if (disable)
>> +      if (n.disable)
>>           {
>>             CHECK_GLIBC_IFUNC_CPU_OFF (n, cpu_features, LZCNT, 5);
>>             CHECK_GLIBC_IFUNC_CPU_OFF (n, cpu_features, MOVBE, 5);
>> @@ -159,12 +141,12 @@ TUNABLE_CALLBACK (set_hwcaps) (tunable_val_t *valp)
>>           }
>>         break;
>>       case 6:
>> -      if (disable)
>> +      if (n.disable)
>>           {
>>             CHECK_GLIBC_IFUNC_CPU_OFF (n, cpu_features, POPCNT, 6);
>>             CHECK_GLIBC_IFUNC_CPU_OFF (n, cpu_features, SSE4_1, 6);
>>             CHECK_GLIBC_IFUNC_CPU_OFF (n, cpu_features, SSE4_2, 6);
>> -          if (memcmp (n, "XSAVEC", 6) == 0)
>> +          if (memcmp (n.str, "XSAVEC", 6) == 0)
>>           {
>>             /* Update xsave_state_size to XSAVE state size.  */
>>             cpu_features->xsave_state_size
>> @@ -174,14 +156,14 @@ TUNABLE_CALLBACK (set_hwcaps) (tunable_val_t *valp)
>>           }
>>         break;
>>       case 7:
>> -      if (disable)
>> +      if (n.disable)
>>           {
>>             CHECK_GLIBC_IFUNC_CPU_OFF (n, cpu_features, AVX512F, 7);
>>             CHECK_GLIBC_IFUNC_CPU_OFF (n, cpu_features, OSXSAVE, 7);
>>           }
>>         break;
>>       case 8:
>> -      if (disable)
>> +      if (n.disable)
>>           {
>>             CHECK_GLIBC_IFUNC_CPU_OFF (n, cpu_features, AVX512CD, 8);
>>             CHECK_GLIBC_IFUNC_CPU_OFF (n, cpu_features, AVX512BW, 8);
>> @@ -190,86 +172,72 @@ TUNABLE_CALLBACK (set_hwcaps) (tunable_val_t *valp)
>>             CHECK_GLIBC_IFUNC_CPU_OFF (n, cpu_features, AVX512PF, 8);
>>             CHECK_GLIBC_IFUNC_CPU_OFF (n, cpu_features, AVX512VL, 8);
>>           }
>> -      CHECK_GLIBC_IFUNC_PREFERRED_BOTH (n, cpu_features, Slow_BSF,
>> -                        disable, 8);
>> +      CHECK_GLIBC_IFUNC_PREFERRED_BOTH (n, cpu_features, Slow_BSF, 8);
>>         break;
>>       case 11:
>>           {
>> -          CHECK_GLIBC_IFUNC_PREFERRED_BOTH (n, cpu_features,
>> -                        Prefer_ERMS,
>> -                        disable, 11);
>> -          CHECK_GLIBC_IFUNC_PREFERRED_BOTH (n, cpu_features,
>> -                        Prefer_FSRM,
>> -                        disable, 11);
>> +          CHECK_GLIBC_IFUNC_PREFERRED_BOTH (n, cpu_features, Prefer_ERMS,
>> +                        11);
>> +          CHECK_GLIBC_IFUNC_PREFERRED_BOTH (n, cpu_features, Prefer_FSRM,
>> +                        11);
>>             CHECK_GLIBC_IFUNC_PREFERRED_NEED_BOTH (n, cpu_features,
>>                                Slow_SSE4_2,
>>                                SSE4_2,
>> -                             disable, 11);
>> +                             11);
>>           }
>>         break;
>>       case 15:
>>           {
>>             CHECK_GLIBC_IFUNC_PREFERRED_BOTH (n, cpu_features,
>> -                        Fast_Rep_String,
>> -                        disable, 15);
>> +                        Fast_Rep_String, 15);
>>           }
>>         break;
>>       case 16:
>>           {
>>             CHECK_GLIBC_IFUNC_PREFERRED_NEED_BOTH
>> -        (n, cpu_features, Prefer_No_AVX512, AVX512F,
>> -         disable, 16);
>> +        (n, cpu_features, Prefer_No_AVX512, AVX512F, 16);
>>           }
>>         break;
>>       case 18:
>>           {
>>             CHECK_GLIBC_IFUNC_PREFERRED_BOTH (n, cpu_features,
>> -                        Fast_Copy_Backward,
>> -                        disable, 18);
>> +                        Fast_Copy_Backward, 18);
>>           }
>>         break;
>>       case 19:
>>           {
>>             CHECK_GLIBC_IFUNC_PREFERRED_BOTH (n, cpu_features,
>> -                        Fast_Unaligned_Load,
>> -                        disable, 19);
>> +                        Fast_Unaligned_Load, 19);
>>             CHECK_GLIBC_IFUNC_PREFERRED_BOTH (n, cpu_features,
>> -                        Fast_Unaligned_Copy,
>> -                        disable, 19);
>> +                        Fast_Unaligned_Copy, 19);
>>           }
>>         break;
>>       case 20:
>>           {
>>             CHECK_GLIBC_IFUNC_PREFERRED_NEED_BOTH
>> -        (n, cpu_features, Prefer_No_VZEROUPPER, AVX, disable,
>> -         20);
>> +        (n, cpu_features, Prefer_No_VZEROUPPER, AVX, 20);
>>           }
>>         break;
>>       case 23:
>>           {
>>             CHECK_GLIBC_IFUNC_PREFERRED_NEED_BOTH
>> -        (n, cpu_features, AVX_Fast_Unaligned_Load, AVX,
>> -         disable, 23);
>> +        (n, cpu_features, AVX_Fast_Unaligned_Load, AVX, 23);
>>           }
>>         break;
>>       case 24:
>>           {
>>             CHECK_GLIBC_IFUNC_PREFERRED_NEED_BOTH
>> -        (n, cpu_features, MathVec_Prefer_No_AVX512, AVX512F,
>> -         disable, 24);
>> +        (n, cpu_features, MathVec_Prefer_No_AVX512, AVX512F, 24);
>>           }
>>         break;
>>       case 26:
>>           {
>>             CHECK_GLIBC_IFUNC_PREFERRED_NEED_BOTH
>> -        (n, cpu_features, Prefer_PMINUB_for_stringop, SSE2,
>> -         disable, 26);
>> +        (n, cpu_features, Prefer_PMINUB_for_stringop, SSE2, 26);
>>           }
>>         break;
>>       }
>> -      p += len + 1;
>>       }
>> -  while (*c != '\0');
>>   }
>>     #if CET_ENABLED
>> @@ -277,11 +245,11 @@ attribute_hidden
>>   void
>>   TUNABLE_CALLBACK (set_x86_ibt) (tunable_val_t *valp)
>>   {
>> -  if (memcmp (valp->strval, "on", sizeof ("on")) == 0)
>> +  if (tunable_strcmp_cte (valp, "on"))
>>       GL(dl_x86_feature_control).ibt = cet_always_on;
>> -  else if (memcmp (valp->strval, "off", sizeof ("off")) == 0)
>> +  else if (tunable_strcmp_cte (valp, "off"))
>>       GL(dl_x86_feature_control).ibt = cet_always_off;
>> -  else if (memcmp (valp->strval, "permissive", sizeof ("permissive")) == 0)
>> +  else if (tunable_strcmp_cte (valp, "permissive"))
>>       GL(dl_x86_feature_control).ibt = cet_permissive;
>>   }
>>   @@ -289,11 +257,11 @@ attribute_hidden
>>   void
>>   TUNABLE_CALLBACK (set_x86_shstk) (tunable_val_t *valp)
>>   {
>> -  if (memcmp (valp->strval, "on", sizeof ("on")) == 0)
>> +  if (tunable_strcmp_cte (valp, "on"))
>>       GL(dl_x86_feature_control).shstk = cet_always_on;
>> -  else if (memcmp (valp->strval, "off", sizeof ("off")) == 0)
>> +  else if (tunable_strcmp_cte (valp, "off"))
>>       GL(dl_x86_feature_control).shstk = cet_always_off;
>> -  else if (memcmp (valp->strval, "permissive", sizeof ("permissive")) == 0)
>> +  else if (tunable_strcmp_cte (valp, "permissive"))
>>       GL(dl_x86_feature_control).shstk = cet_permissive;
>>   }
>>   #endif
>> diff --git a/sysdeps/x86/tst-hwcap-tunables.c b/sysdeps/x86/tst-hwcap-tunables.c
>> new file mode 100644
>> index 0000000000..e9f50ffbe0
>> --- /dev/null
>> +++ b/sysdeps/x86/tst-hwcap-tunables.c
>> @@ -0,0 +1,150 @@
>> +/* Tests for powerpc GLIBC_TUNABLES=glibc.cpu.hwcaps filter.
>> +   Copyright (C) 2023 Free Software Foundation, Inc.
>> +   This file is part of the GNU C Library.
>> +
>> +   The GNU C Library is free software; you can redistribute it and/or
>> +   modify it under the terms of the GNU Lesser General Public
>> +   License as published by the Free Software Foundation; either
>> +   version 2.1 of the License, or (at your option) any later version.
>> +
>> +   The GNU C Library is distributed in the hope that it will be useful,
>> +   but WITHOUT ANY WARRANTY; without even the implied warranty of
>> +   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
>> +   Lesser General Public License for more details.
>> +
>> +   You should have received a copy of the GNU Lesser General Public
>> +   License along with the GNU C Library; if not, see
>> +   <http://www.gnu.org/licenses/>.  */
>> +
>> +#pragma GCC optimize ("O0")
>> +
>> +#include <array_length.h>
>> +#include <getopt.h>
>> +#include <ifunc-impl-list.h>
>> +#include <spawn.h>
>> +#include <stdio.h>
>> +#include <stdlib.h>
>> +#include <string.h>
>> +#include <intprops.h>
>> +#include <support/check.h>
>> +#include <support/support.h>
>> +#include <support/xunistd.h>
>> +#include <support/capture_subprocess.h>
>> +
>> +/* Nonzero if the program gets called via `exec'.  */
>> +#define CMDLINE_OPTIONS \
>> +  { "restart", no_argument, &restart, 1 },
>> +static int restart;
>> +
>> +/* Disable everything.  */
>> +static const char *test_1[] =
>> +{
>> +  "__memcpy_avx512_no_vzeroupper",
>> +  "__memcpy_avx512_unaligned",
>> +  "__memcpy_avx512_unaligned_erms",
>> +  "__memcpy_evex_unaligned",
>> +  "__memcpy_evex_unaligned_erms",
>> +  "__memcpy_avx_unaligned",
>> +  "__memcpy_avx_unaligned_erms",
>> +  "__memcpy_avx_unaligned_rtm",
>> +  "__memcpy_avx_unaligned_erms_rtm",
>> +  "__memcpy_ssse3",
>> +};
>> +
>> +static const struct test_t
>> +{
>> +  const char *env;
>> +  const char *const *funcs;
>> +  size_t nfuncs;
>> +} tests[] =
>> +{
>> +  {
>> +    /* Disable everything.  */
>> +    "-Prefer_ERMS,-Prefer_FSRM,-AVX,-AVX2,-AVX_Usable,-AVX2_Usable,"
>> +    "-AVX512F_Usable,-SSE4_1,-SSE4_2,-SSSE3,-Fast_Unaligned_Load,-ERMS,"
>> +    "-AVX_Fast_Unaligned_Load",
>> +    test_1,
>> +    array_length (test_1)
>> +  },
>> +  {
>> +    /* Same as before, but with some empty suboptions.  */
>> +    ",-,-Prefer_ERMS,-Prefer_FSRM,-AVX,-AVX2,-AVX_Usable,-AVX2_Usable,"
>> +    "-AVX512F_Usable,-SSE4_1,-SSE4_2,,-SSSE3,-Fast_Unaligned_Load,,-,-ERMS,"
>> +    "-AVX_Fast_Unaligned_Load,-,",
>> +    test_1,
>> +    array_length (test_1)
>> +  }
>> +};
>> +
>> +/* Called on process re-execution.  */
>> +_Noreturn static void
>> +handle_restart (int ntest)
>> +{
>> +  struct libc_ifunc_impl impls[32];
>> +  int cnt = __libc_ifunc_impl_list ("memcpy", impls, array_length (impls));
>> +  if (cnt == 0)
>> +    _exit (EXIT_SUCCESS);
>> +  TEST_VERIFY_EXIT (cnt >= 1);
>> +  for (int i = 0; i < cnt; i++)
>> +    {
>> +      for (int f = 0; f < tests[ntest].nfuncs; f++)
>> +    {
>> +      if (strcmp (impls[i].name, tests[ntest].funcs[f]) == 0)
>> +        TEST_COMPARE (impls[i].usable, false);
>> +    }
>> +    }
>> +
>> +  _exit (EXIT_SUCCESS);
>> +}
>> +
>> +static int
>> +do_test (int argc, char *argv[])
>> +{
>> +  /* We must have either:
>> +     - One our fource parameters left if called initially:
>> +       + path to ld.so         optional
>> +       + "--library-path"      optional
>> +       + the library path      optional
>> +       + the application name
>> +       + the test to check  */
>> +
>> +  TEST_VERIFY_EXIT (argc == 2 || argc == 5);
>> +
>> +  if (restart)
>> +    handle_restart (atoi (argv[1]));
>> +
>> +  char nteststr[INT_BUFSIZE_BOUND (int)];
>> +
>> +  char *spargv[10];
>> +  {
>> +    int i = 0;
>> +    for (; i < argc - 1; i++)
>> +      spargv[i] = argv[i + 1];
>> +    spargv[i++] = (char *) "--direct";
>> +    spargv[i++] = (char *) "--restart";
>> +    spargv[i++] = nteststr;
>> +    spargv[i] = NULL;
>> +  }
>> +
>> +  for (int i = 0; i < array_length (tests); i++)
>> +    {
>> +      snprintf (nteststr, sizeof nteststr, "%d", i);
>> +
>> +      printf ("[%d] Spawned test for %s\n", i, tests[i].env);
>> +      char *tunable = xasprintf ("glibc.cpu.hwcaps=%s", tests[i].env);
>> +      setenv ("GLIBC_TUNABLES", tunable, 1);
>> +
>> +      struct support_capture_subprocess result
>> +    = support_capture_subprogram (spargv[0], spargv);
>> +      support_capture_subprocess_check (&result, "tst-tunables", 0,
>> +                    sc_allow_stderr);
>> +      support_capture_subprocess_free (&result);
>> +
>> +      free (tunable);
>> +    }
>> +
>> +  return 0;
>> +}
>> +
>> +#define TEST_FUNCTION_ARGV do_test
>> +#include <support/test-driver.c>

^ permalink raw reply	[flat|nested] 45+ messages in thread

* Re: [PATCH v2 15/19] elf: Remove any_debug from dl_main_state
  2023-10-27 14:26   ` Siddhesh Poyarekar
@ 2023-10-30 19:03     ` Adhemerval Zanella Netto
  0 siblings, 0 replies; 45+ messages in thread
From: Adhemerval Zanella Netto @ 2023-10-30 19:03 UTC (permalink / raw)
  To: Siddhesh Poyarekar, libc-alpha



On 27/10/23 11:26, Siddhesh Poyarekar wrote:
> 
> 
> On 2023-10-17 09:05, Adhemerval Zanella wrote:
>> Its usage can be implied by the GLRO(dl_debug_mask).
>> ---
>>   elf/dl-main.h | 3 ---
>>   elf/rtld.c    | 6 ++----
>>   2 files changed, 2 insertions(+), 7 deletions(-)
>>
>> diff --git a/elf/dl-main.h b/elf/dl-main.h
>> index 92766d06b4..f876904cb6 100644
>> --- a/elf/dl-main.h
>> +++ b/elf/dl-main.h
>> @@ -97,9 +97,6 @@ struct dl_main_state
>>     /* True if program should be also printed for rtld_mode_trace.  */
>>     bool mode_trace_program;
>>   -  /* True if any of the debugging options is enabled.  */
>> -  bool any_debug;
>> -
>>     /* True if information about versions has to be printed.  */
>>     bool version_info;
>>   };
>> diff --git a/elf/rtld.c b/elf/rtld.c
>> index 8749ad6288..638b019670 100644
>> --- a/elf/rtld.c
>> +++ b/elf/rtld.c
>> @@ -300,7 +300,6 @@ dl_main_state_init (struct dl_main_state *state)
>>     state->glibc_hwcaps_prepend = NULL;
>>     state->glibc_hwcaps_mask = NULL;
>>     state->mode = rtld_mode_normal;
>> -  state->any_debug = false;
>>     state->version_info = false;
>>   }
>>   @@ -2481,7 +2480,6 @@ process_dl_debug (struct dl_main_state *state, const char *dl_debug)
>>           && memcmp (dl_debug, debopts[cnt].name, len) == 0)
>>             {
>>           GLRO(dl_debug_mask) |= debopts[cnt].mask;
>> -        state->any_debug = true;
>>           break;
>>             }
>>   @@ -2552,7 +2550,7 @@ process_envvars (struct dl_main_state *state)
>>       {
>>       case 4:
>>         /* Warning level, verbose or not.  */
>> -      if (!!__libc_enable_secure
>> +      if (!__libc_enable_secure
> 
> This probably belongs in 14/19.
> 

Indeed.

>>             && memcmp (envline, "WARN", 4) == 0)
>>           GLRO(dl_verbose) = envline[5] != '\0';
>>         break;
>> @@ -2682,7 +2680,7 @@ process_envvars (struct dl_main_state *state)
>>     /* If we have to run the dynamic linker in debugging mode and the
>>        LD_DEBUG_OUTPUT environment variable is given, we write the debug
>>        messages to this file.  */
>> -  else if (state->any_debug && debug_output != NULL)
>> +  else if (GLRO(dl_debug_mask) != 0 && debug_output != NULL)
>>       {
>>         const int flags = O_WRONLY | O_APPEND | O_CREAT | O_NOFOLLOW;
>>         size_t name_len = strlen (debug_output);

^ permalink raw reply	[flat|nested] 45+ messages in thread

* Re: [PATCH v2 12/19] elf: Ignore LD_PROFILE for setuid binaries
  2023-10-27 14:25   ` Siddhesh Poyarekar
@ 2023-12-12 10:11     ` Florian Weimer
  0 siblings, 0 replies; 45+ messages in thread
From: Florian Weimer @ 2023-12-12 10:11 UTC (permalink / raw)
  To: Siddhesh Poyarekar; +Cc: Adhemerval Zanella, libc-alpha, Carlos O'Donell

* Siddhesh Poyarekar:

> On 2023-10-17 09:05, Adhemerval Zanella wrote:
>> Loader does not ignore LD_PROFILE in secure-execution mode (different
>> than man-page states [1]), rather it uses a different path
>> (/var/profile) and ignore LD_PROFILE_OUTPUT.
>> Allowing secure-execution profiling is already a non good security
>> boundary, since it enables different code paths and extra OS access by
>> the process.  But by ignoring LD_PROFILE_OUTPUT, the resulting profile
>> file might also be acceded in a racy manner since the file name does not
>> use any process-specific information (such as pid, timing, etc.).
>> Another side-effect is it forces lazy binding even on libraries that
>> might be with DF_BIND_NOW.
>> [1] https://man7.org/linux/man-pages/man8/ld.so.8.html
>> ---
>
> I tend to agree.  Carlos, Florian, is profiling of setuid binaries
> something that needs to be supported as compatibility behaviour?  I'm
> inclined to agree with Adhemerval and just rip it out.

It's not something we need to support.  LD_PROFILE does not seem to be
regularly used for profiling anyway.

Thanks,
Florian


^ permalink raw reply	[flat|nested] 45+ messages in thread

end of thread, other threads:[~2023-12-12 10:11 UTC | newest]

Thread overview: 45+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2023-10-17 13:05 [PATCH v2 00/19] Improve loader environment variable handling Adhemerval Zanella
2023-10-17 13:05 ` [PATCH v2 01/19] elf: Remove /etc/suid-debug support Adhemerval Zanella
2023-10-18 12:31   ` Siddhesh Poyarekar
2023-10-18 18:27     ` Adhemerval Zanella Netto
2023-10-17 13:05 ` [PATCH v2 02/19] elf: Add GLIBC_TUNABLES to unsecvars Adhemerval Zanella
2023-10-18 12:52   ` Siddhesh Poyarekar
2023-10-17 13:05 ` [PATCH v2 03/19] elf: Ignore GLIBC_TUNABLES for setuid/setgid binaries Adhemerval Zanella
2023-10-18 13:04   ` Siddhesh Poyarekar
2023-10-27 10:38     ` Siddhesh Poyarekar
2023-10-28  2:14   ` DJ Delorie
2023-10-30 16:51     ` Adhemerval Zanella Netto
2023-10-17 13:05 ` [PATCH v2 04/19] elf: Add all malloc tunable to unsecvars Adhemerval Zanella
2023-10-18 13:04   ` Siddhesh Poyarekar
2023-10-27 10:38     ` Siddhesh Poyarekar
2023-10-28  3:45   ` DJ Delorie
2023-10-17 13:05 ` [PATCH v2 05/19] elf: Do not process invalid tunable format Adhemerval Zanella
2023-10-18 13:42   ` Siddhesh Poyarekar
2023-10-17 13:05 ` [PATCH v2 06/19] elf: Do not parse ill-formatted strings Adhemerval Zanella
2023-10-27 10:25   ` Siddhesh Poyarekar
2023-10-27 12:51     ` Adhemerval Zanella Netto
2023-10-17 13:05 ` [PATCH v2 07/19] elf: Fix _dl_debug_vdprintf to work before self-relocation Adhemerval Zanella
2023-10-27 10:27   ` Siddhesh Poyarekar
2023-10-17 13:05 ` [PATCH v2 08/19] elf: Emit warning if tunable is ill-formatted Adhemerval Zanella
2023-10-27 10:28   ` Siddhesh Poyarekar
2023-10-27 12:51     ` Adhemerval Zanella Netto
2023-10-17 13:05 ` [PATCH v2 09/19] x86: Use dl-symbol-redir-ifunc.h on cpu-tunables Adhemerval Zanella
2023-10-27 10:32   ` Siddhesh Poyarekar
2023-10-17 13:05 ` [PATCH v2 10/19] s390: " Adhemerval Zanella
2023-10-27 10:34   ` Siddhesh Poyarekar
2023-10-17 13:05 ` [PATCH v2 11/19] elf: Do not duplicate the GLIBC_TUNABLES string Adhemerval Zanella
2023-10-27 11:54   ` Siddhesh Poyarekar
2023-10-30 18:24     ` Adhemerval Zanella Netto
2023-10-17 13:05 ` [PATCH v2 12/19] elf: Ignore LD_PROFILE for setuid binaries Adhemerval Zanella
2023-10-27 14:25   ` Siddhesh Poyarekar
2023-12-12 10:11     ` Florian Weimer
2023-10-17 13:05 ` [PATCH v2 13/19] elf: Remove LD_PROFILE for static binaries Adhemerval Zanella
2023-10-17 13:05 ` [PATCH v2 14/19] elf: Ignore loader debug env vars for setuid Adhemerval Zanella
2023-10-17 13:05 ` [PATCH v2 15/19] elf: Remove any_debug from dl_main_state Adhemerval Zanella
2023-10-27 14:26   ` Siddhesh Poyarekar
2023-10-30 19:03     ` Adhemerval Zanella Netto
2023-10-17 13:05 ` [PATCH v2 16/19] elf: Ignore LD_LIBRARY_PATH and debug env var for setuid for static Adhemerval Zanella
2023-10-17 13:05 ` [PATCH v2 17/19] elf: Add comments on how LD_AUDIT and LD_PRELOAD handle __libc_enable_secure Adhemerval Zanella
2023-10-27 14:31   ` Siddhesh Poyarekar
2023-10-17 13:05 ` [PATCH v2 18/19] elf: Ignore LD_BIND_NOW and LD_BIND_NOT for setuid binaries Adhemerval Zanella
2023-10-17 13:05 ` [PATCH v2 19/19] elf: Refactor process_envvars Adhemerval Zanella

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).