elf/rtld: Count skipped environment variables for enable_secure

When using the glibc.rtld.enable_secure tunable we need to keep track of
the count of environment variables we skip due to __libc_enable_secure
being set and adjust the auxv section of the stack.  This fixes an
assertion when running ld.so directly with glibc.rtld.enable_secure set.
Add a testcase that ensures the assert is not hit.

elf/rtld.c:1324   assert (auxv == sp + 1);

Reviewed-by: Adhemerval Zanella  <adhemerval.zanella@linaro.org>
This commit is contained in:
Joe Simmons-Talbott 2024-04-16 20:31:42 +00:00
parent 14e56bd4ce
commit 59974938fe
3 changed files with 66 additions and 8 deletions

View File

@ -315,6 +315,7 @@ tests := \
tst-leaks1 \ tst-leaks1 \
tst-stringtable \ tst-stringtable \
tst-tls9 \ tst-tls9 \
tst-tunables-enable_secure-env \
# tests # tests
tests-internal := \ tests-internal := \
@ -1188,6 +1189,7 @@ tests-special += \
$(objpfx)tst-trace3.out \ $(objpfx)tst-trace3.out \
$(objpfx)tst-trace4.out \ $(objpfx)tst-trace4.out \
$(objpfx)tst-trace5.out \ $(objpfx)tst-trace5.out \
$(objpfx)tst-tunables-enable_secure-env.out \
$(objpfx)tst-unused-dep-cmp.out \ $(objpfx)tst-unused-dep-cmp.out \
$(objpfx)tst-unused-dep.out \ $(objpfx)tst-unused-dep.out \
# tests-special # tests-special
@ -2215,6 +2217,14 @@ $(objpfx)tst-unused-dep-cmp.out: $(objpfx)tst-unused-dep.out
cmp $< /dev/null > $@; \ cmp $< /dev/null > $@; \
$(evaluate-test) $(evaluate-test)
$(objpfx)tst-tunables-enable_secure-env.out: $(objpfx)tst-tunables-enable_secure-env
$(test-wrapper-env) \
GLIBC_TUNABLES=glibc.rtld.enable_secure=1 \
$(rtld-prefix) \
$< > $@; \
$(evaluate-test)
$(objpfx)tst-audit11.out: $(objpfx)tst-auditmod11.so $(objpfx)tst-audit11mod1.so $(objpfx)tst-audit11.out: $(objpfx)tst-auditmod11.so $(objpfx)tst-audit11mod1.so
tst-audit11-ENV = LD_AUDIT=$(objpfx)tst-auditmod11.so tst-audit11-ENV = LD_AUDIT=$(objpfx)tst-auditmod11.so
$(objpfx)tst-audit11mod1.so: $(objpfx)tst-audit11mod2.so $(objpfx)tst-audit11mod1.so: $(objpfx)tst-audit11mod2.so

View File

@ -155,7 +155,7 @@ static void dl_main_state_init (struct dl_main_state *state);
Since all of them start with `LD_' we are a bit smarter while finding Since all of them start with `LD_' we are a bit smarter while finding
all the entries. */ all the entries. */
extern char **_environ attribute_hidden; extern char **_environ attribute_hidden;
static void process_envvars (struct dl_main_state *state); static int process_envvars (struct dl_main_state *state);
int _dl_argc attribute_relro attribute_hidden; int _dl_argc attribute_relro attribute_hidden;
char **_dl_argv attribute_relro = NULL; char **_dl_argv attribute_relro = NULL;
@ -1289,7 +1289,7 @@ rtld_setup_main_map (struct link_map *main_map)
_dl_argv and _dl_argc accordingly. Those arguments are removed from _dl_argv and _dl_argc accordingly. Those arguments are removed from
argv here. */ argv here. */
static void static void
_dl_start_args_adjust (int skip_args) _dl_start_args_adjust (int skip_args, int skip_env)
{ {
void **sp = (void **) (_dl_argv - skip_args - 1); void **sp = (void **) (_dl_argv - skip_args - 1);
void **p = sp + skip_args; void **p = sp + skip_args;
@ -1321,7 +1321,7 @@ _dl_start_args_adjust (int skip_args)
while (*p != NULL); while (*p != NULL);
#ifdef HAVE_AUX_VECTOR #ifdef HAVE_AUX_VECTOR
void **auxv = (void **) GLRO(dl_auxv) - skip_args; void **auxv = (void **) GLRO(dl_auxv) - skip_args - skip_env;
GLRO(dl_auxv) = (ElfW(auxv_t) *) auxv; /* Aliasing violation. */ GLRO(dl_auxv) = (ElfW(auxv_t) *) auxv; /* Aliasing violation. */
assert (auxv == sp + 1); assert (auxv == sp + 1);
@ -1352,6 +1352,7 @@ dl_main (const ElfW(Phdr) *phdr,
unsigned int i; unsigned int i;
bool rtld_is_main = false; bool rtld_is_main = false;
void *tcbp = NULL; void *tcbp = NULL;
int skip_env = 0;
struct dl_main_state state; struct dl_main_state state;
dl_main_state_init (&state); dl_main_state_init (&state);
@ -1365,7 +1366,7 @@ dl_main (const ElfW(Phdr) *phdr,
#endif #endif
/* Process the environment variable which control the behaviour. */ /* Process the environment variable which control the behaviour. */
process_envvars (&state); skip_env = process_envvars (&state);
#ifndef HAVE_INLINED_SYSCALLS #ifndef HAVE_INLINED_SYSCALLS
/* Set up a flag which tells we are just starting. */ /* Set up a flag which tells we are just starting. */
@ -1630,7 +1631,7 @@ dl_main (const ElfW(Phdr) *phdr,
_dl_argv[0] = argv0; _dl_argv[0] = argv0;
/* Adjust arguments for the application entry point. */ /* Adjust arguments for the application entry point. */
_dl_start_args_adjust (_dl_argv - orig_argv); _dl_start_args_adjust (_dl_argv - orig_argv, skip_env);
} }
else else
{ {
@ -2534,11 +2535,12 @@ a filename can be specified using the LD_DEBUG_OUTPUT environment variable.\n");
} }
} }
static void static int
process_envvars_secure (struct dl_main_state *state) process_envvars_secure (struct dl_main_state *state)
{ {
char **runp = _environ; char **runp = _environ;
char *envline; char *envline;
int skip_env = 0;
while ((envline = _dl_next_ld_env_entry (&runp)) != NULL) while ((envline = _dl_next_ld_env_entry (&runp)) != NULL)
{ {
@ -2580,6 +2582,14 @@ process_envvars_secure (struct dl_main_state *state)
const char *nextp = UNSECURE_ENVVARS; const char *nextp = UNSECURE_ENVVARS;
do do
{ {
/* Keep track of the number of environment variables that were set in
the environment and are unset below. Use getenv() which returns
non-NULL if the variable is set in the environment. This count is
needed if we need to adjust the location of the AUX vector on the
stack when running ld.so directly. */
if (getenv (nextp) != NULL)
skip_env++;
unsetenv (nextp); unsetenv (nextp);
nextp = strchr (nextp, '\0') + 1; nextp = strchr (nextp, '\0') + 1;
} }
@ -2592,6 +2602,8 @@ process_envvars_secure (struct dl_main_state *state)
|| state->mode != rtld_mode_normal || state->mode != rtld_mode_normal
|| state->version_info) || state->version_info)
_exit (5); _exit (5);
return skip_env;
} }
static void static void
@ -2745,13 +2757,16 @@ process_envvars_default (struct dl_main_state *state)
} }
} }
static void static int
process_envvars (struct dl_main_state *state) process_envvars (struct dl_main_state *state)
{ {
int skip_env = 0;
if (__glibc_unlikely (__libc_enable_secure)) if (__glibc_unlikely (__libc_enable_secure))
process_envvars_secure (state); skip_env += process_envvars_secure (state);
else else
process_envvars_default (state); process_envvars_default (state);
return skip_env;
} }
#if HP_TIMING_INLINE #if HP_TIMING_INLINE

View File

@ -0,0 +1,33 @@
/* Check enable_secure tunable handles removed ENV variables without
assertions.
Copyright (C) 2024 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 <support/capture_subprocess.h>
#include <support/check.h>
static int
do_test (int argc, char *argv[])
{
/* Ensure that no assertions are hit when a dynamically linked application
runs. This test requires that GLIBC_TUNABLES=glibc.rtld.enable_secure=1
is set. */
return 0;
}
#define TEST_FUNCTION_ARGV do_test
#include <support/test-driver.c>