diff mbox series

[v2,4/4] elf: Make glibc.rtld.enable_secure ignore alias environment variables

Message ID 20240502163716.1107975-5-adhemerval.zanella@linaro.org
State New
Headers show
Series More tunable fixes | expand

Commit Message

Adhemerval Zanella Netto May 2, 2024, 4:35 p.m. UTC
Tunable with environment variables aliases are also ignored if
glibc.rtld.enable_secure is enabled.  The tunable parsing is also
optimized a bit, where the loop that checks each environment variable
only checks for the tunables with aliases instead of all tables.

Checked on aarch64-linux-gnu and x86_64-linux-gnu.
---
 elf/dl-tunables.c                |  36 ++++++---
 elf/tst-tunables-enable_secure.c | 133 +++++++++++++++++++++++++++----
 scripts/gen-tunables.awk         |  16 +++-
 3 files changed, 161 insertions(+), 24 deletions(-)

Comments

Siddhesh Poyarekar May 3, 2024, 3:30 p.m. UTC | #1
On 2024-05-02 12:35, Adhemerval Zanella wrote:
> Tunable with environment variables aliases are also ignored if
> glibc.rtld.enable_secure is enabled.  The tunable parsing is also
> optimized a bit, where the loop that checks each environment variable
> only checks for the tunables with aliases instead of all tables.
> 
> Checked on aarch64-linux-gnu and x86_64-linux-gnu.
> ---
>   elf/dl-tunables.c                |  36 ++++++---
>   elf/tst-tunables-enable_secure.c | 133 +++++++++++++++++++++++++++----
>   scripts/gen-tunables.awk         |  16 +++-
>   3 files changed, 161 insertions(+), 24 deletions(-)
> 
> diff --git a/elf/dl-tunables.c b/elf/dl-tunables.c
> index 63cf8c7ab5..e87d0628b2 100644
> --- a/elf/dl-tunables.c
> +++ b/elf/dl-tunables.c
> @@ -300,6 +300,9 @@ __tunables_init (char **envp)
>     if (__libc_enable_secure)
>       return;
>   
> +  enum { tunable_num_env_alias = array_length (tunable_env_alias_list) };
> +  struct tunable_toset_t tunables_env_alias[tunable_num_env_alias] = { 0 };
> +
>     while ((envp = get_next_env (envp, &envname, &envval, &prev_envp)) != NULL)
>       {
>         /* The environment variable is allocated on the stack by the kernel, so
> @@ -311,29 +314,44 @@ __tunables_init (char **envp)
>   	  continue;
>   	}
>   
> -      for (int i = 0; i < tunables_list_size; i++)
> +      for (int i = 0; i < tunable_num_env_alias; i++)
>   	{
> -	  tunable_t *cur = &tunable_list[i];
> +	  tunable_t *cur = &tunable_list[tunable_env_alias_list[i]];
> +	  const char *name = cur->env_alias;
>   
> -	  /* Skip over tunables that have either been set already or should be
> -	     skipped.  */
> -	  if (cur->initialized || cur->env_alias[0] == '\0')
> +	  if (name[0] == '\0')
>   	    continue;

You don't skip over initialized ones here because you're going to have 
to check for that later anyway, in case GLIBC_TUNABLES came in after any 
of the aliases.  OK.

>   
> -	  const char *name = cur->env_alias;
> -
> -	  /* We have a match.  Initialize and move on to the next line.  */
>   	  if (tunable_is_name (name, envname))
>   	    {
>   	      size_t envvallen = 0;
>   	      /* The environment variable is always null-terminated.  */
>   	      for (const char *p = envval; *p != '\0'; p++, envvallen++);
>   
> -	      tunable_initialize (cur, envval, envvallen);
> +	      tunables_env_alias[i] =
> +		(struct tunable_toset_t) { cur, envval, envvallen };
>   	      break;
>   	    }
>   	}
>       }
> +
> +  /* Check if glibc.rtld.enable_secure was set and skip over the environment
> +     variables aliases.  */
> +  if (__libc_enable_secure)
> +    return;
> +
> +  for (int i = 0; i < tunable_num_env_alias; i++)
> +    {
> +      /* Skip over tunables that have either been set.  */

either been set or already initialized?

> +      if (tunables_env_alias[i].t == NULL
> +	  || tunables_env_alias[i].t->initialized)
> +	continue;
> +
> +      if (!tunable_initialize (tunables_env_alias[i].t,
> +			       tunables_env_alias[i].value,
> +			       tunables_env_alias[i].len))
> +	parse_tunable_print_error (&tunables_env_alias[i]);
> +    }
>   }
>   
>   void
> diff --git a/elf/tst-tunables-enable_secure.c b/elf/tst-tunables-enable_secure.c
> index f5db1c84e9..a0da0fd179 100644
> --- a/elf/tst-tunables-enable_secure.c
> +++ b/elf/tst-tunables-enable_secure.c
> @@ -17,6 +17,7 @@
>      <https://www.gnu.org/licenses/>.  */
>   
>   #include <array_length.h>
> +#define TUNABLES_INTERNAL 1

OK, I noticed this in patch 1 but it didn't strike me then; this macro 
will result in a new array tunable_list being initialized in the binary. 
  The tunable references themselves should go via __tunable_get_val and 
hence refer to the structure in ld.so, so this array only serves as a 
copy to get the total size.  I suppose it's not much for a test case, 
but do you think it's worth adding a comment here, and also in 
tst-tunables.c?  That way it could be easier to weed out any unforeseen 
problems with these tests in future.

>   #include <dl-tunables.h>
>   #include <getopt.h>
>   #include <intprops.h>
> @@ -34,6 +35,8 @@ static int restart;
>   static const struct test_t
>   {
>     const char *env;
> +  const char *extraenv;
> +  bool check_multiple;
>     int32_t expected_malloc_check;
>     int32_t expected_enable_secure;
>   } tests[] =
> @@ -41,39 +44,124 @@ static const struct test_t
>     /* Expected tunable format.  */
>     /* Tunables should be ignored if enable_secure is set. */
>     {
> -    "glibc.malloc.check=2:glibc.rtld.enable_secure=1",
> +    "GLIBC_TUNABLES=glibc.malloc.check=2:glibc.rtld.enable_secure=1",
> +    NULL,
> +    false,
>       0,
>       1,
>     },
>     /* Tunables should be ignored if enable_secure is set. */
>     {
> -    "glibc.rtld.enable_secure=1:glibc.malloc.check=2",
> +    "GLIBC_TUNABLES=glibc.rtld.enable_secure=1:glibc.malloc.check=2",
> +    NULL,
> +    false,
>       0,
>       1,
>     },
>     /* Tunables should be set if enable_secure is unset. */
>     {
> -    "glibc.rtld.enable_secure=0:glibc.malloc.check=2",
> +    "GLIBC_TUNABLES=glibc.rtld.enable_secure=0:glibc.malloc.check=2",

Same question as in v1, should we issue a warning for enable_secure=0? 
We really should never be able to set it to anything other than 1. 
Which also means that a bunch of these enable_secure=0 tests are 
probably redundant.

> +    NULL,
> +    false,
>       2,
>       0,
>     },
> +  /* Tunables should be ignored if enable_secure is set. */
> +  {
> +    "GLIBC_TUNABLES=glibc.malloc.check=2:glibc.rtld.enable_secure=1",
> +    "MALLOC_CHECK_=2",
> +    false,
> +    0,
> +    1,
> +  },
> +  /* Same as before, but with enviroment alias prior GLIBC_TUNABLES.  */
> +  {
> +    "MALLOC_CHECK_=2",
> +    "GLIBC_TUNABLES=glibc.malloc.check=2:glibc.rtld.enable_secure=1",
> +    false,
> +    0,
> +    1,
> +  },
> +  /* Tunables should be ignored if enable_secure is set. */
> +  {
> +    "GLIBC_TUNABLES=glibc.rtld.enable_secure=1:glibc.malloc.check=2",
> +    "MALLOC_CHECK_=2",
> +    false,
> +    0,
> +    1,
> +  },
> +  {
> +    "MALLOC_CHECK_=2",
> +    "GLIBC_TUNABLES=glibc.rtld.enable_secure=1:glibc.malloc.check=2",
> +    false,
> +    0,
> +    1,
> +  },
> +  /* Tunables should be set if enable_secure is unset. */
> +  {
> +    "GLIBC_TUNABLES=glibc.rtld.enable_secure=0:glibc.malloc.check=2",
> +    /* Tunable have precedence over the environment variable.  */
> +    "MALLOC_CHECK_=1",
> +    false,
> +    2,
> +    0,
> +  },
> +  {
> +    "MALLOC_CHECK_=1",
> +    "GLIBC_TUNABLES=glibc.rtld.enable_secure=0:glibc.malloc.check=2",
> +    /* Tunable have precedence over the environment variable.  */
> +    false,
> +    2,
> +    0,
> +  },
> +  /* Tunables should be set if enable_secure is unset. */
> +  {
> +    "GLIBC_TUNABLES=glibc.rtld.enable_secure=0",
> +    /* Tunable have precedence over the environment variable.  */
> +    "MALLOC_CHECK_=1",
> +    false,
> +    1,
> +    0,
> +  },
> +  /* Tunables should be set if enable_secure is unset. */
> +  {
> +    "GLIBC_TUNABLES=glibc.rtld.enable_secure=0",
> +    /* Tunable have precedence over the environment variable.  */
> +    "MALLOC_CHECK_=1",
> +    false,
> +    1,
> +    0,
> +  },
> +  /* Check with tunables environment variable alias set multiple times.  */
> +  {
> +    "GLIBC_TUNABLES=glibc.rtld.enable_secure=1:glibc.malloc.check=2",
> +    "MALLOC_CHECK_=2",
> +    true,
> +    0,
> +    1,
> +  },
> +  /* Tunables should be set if enable_secure is unset. */
> +  {
> +    "GLIBC_TUNABLES=glibc.rtld.enable_secure=0",
> +    /* Tunable have precedence over the environment variable.  */
> +    "MALLOC_CHECK_=1",
> +    true,
> +    1,
> +    0,
> +  },
>   };
>   
>   static int
>   handle_restart (int i)
>   {
>     if (tests[i].expected_enable_secure == 1)
> -    {
> -      TEST_COMPARE (1, __libc_enable_secure);
> -    }
> +    TEST_COMPARE (1, __libc_enable_secure);
>     else
> -    {
> -      TEST_COMPARE (tests[i].expected_malloc_check,
> -		    TUNABLE_GET_FULL (glibc, malloc, check, int32_t, NULL));
> -      TEST_COMPARE (tests[i].expected_enable_secure,
> -		    TUNABLE_GET_FULL (glibc, rtld, enable_secure, int32_t,
> -		    NULL));
> -    }
> +    TEST_COMPARE (tests[i].expected_enable_secure,
> +		  TUNABLE_GET_FULL (glibc, rtld, enable_secure, int32_t,
> +				     NULL));
> +  TEST_COMPARE (tests[i].expected_malloc_check,
> +		TUNABLE_GET_FULL (glibc, malloc, check, int32_t, NULL));
>     return 0;
>   }
>   
> @@ -106,14 +194,31 @@ do_test (int argc, char *argv[])
>       spargv[i] = NULL;
>     }
>   
> +  enum { tunable_num_env_alias = array_length (tunable_env_alias_list) };
> +
>     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);
> +
> +      char *envp[2 + tunable_num_env_alias + 1] =
> +      {
> +	(char *) tests[i].env,
> +	(char *) tests[i].extraenv,
> +	NULL,
> +      };
> +      if (tests[i].check_multiple)
> +	{
> +	  int j;
> +	  for (j=0; j < tunable_num_env_alias; j++)
> +	    envp[j + 2] = (char *) tests[i].extraenv;
> +	  envp[j + 2] = NULL;
> +	}
> +
>         struct support_capture_subprocess result
> -	= support_capture_subprogram (spargv[0], spargv, NULL);
> +	= support_capture_subprogram (spargv[0], spargv, envp);
>         support_capture_subprocess_check (&result, "tst-tunables-enable_secure",
>   		                        0, sc_allow_stderr);
>         support_capture_subprocess_free (&result);
> diff --git a/scripts/gen-tunables.awk b/scripts/gen-tunables.awk
> index 9f5336381e..fc3b41376f 100644
> --- a/scripts/gen-tunables.awk
> +++ b/scripts/gen-tunables.awk
> @@ -156,7 +156,7 @@ END {
>     print "# define TUNABLE_ALIAS_MAX " (max_alias_len + 1)
>     print "# include \"dl-tunable-types.h\""
>     # Finally, the tunable list.
> -  print "static tunable_t tunable_list[] attribute_relro = {"
> +  print "static tunable_t tunable_list[] attribute_relro __attribute_used__ = {"

Why is this needed?  Doesn't array_length ensures at least one "use"?

>     for (tnm in types) {
>       split (tnm, indices, SUBSEP);
>       t = indices[1];
> @@ -168,5 +168,19 @@ END {
>   	    default_val[t,n,m], env_alias[t,n,m]);
>     }
>     print "};"
> +
> +  # Map of tunable with environment variables aliases used during parsing.  */
> +  print "\nstatic const tunable_id_t tunable_env_alias_list[] ="
> +  printf "{\n"
> +  for (tnm in types) {
> +    split (tnm, indices, SUBSEP);
> +    t = indices[1];
> +    n = indices[2];
> +    m = indices[3];
> +    if (env_alias[t,n,m] != "{0}") {
> +      printf ("  TUNABLE_ENUM_NAME(%s, %s, %s),\n", t, n, m);
> +    }
> +  }
> +  printf "};\n"
>     print "#endif"
>   }
Adhemerval Zanella Netto May 6, 2024, 2:10 p.m. UTC | #2
On 03/05/24 12:30, Siddhesh Poyarekar wrote:
> On 2024-05-02 12:35, Adhemerval Zanella wrote:
>> Tunable with environment variables aliases are also ignored if
>> glibc.rtld.enable_secure is enabled.  The tunable parsing is also
>> optimized a bit, where the loop that checks each environment variable
>> only checks for the tunables with aliases instead of all tables.
>>
>> Checked on aarch64-linux-gnu and x86_64-linux-gnu.
>> ---
>>   elf/dl-tunables.c                |  36 ++++++---
>>   elf/tst-tunables-enable_secure.c | 133 +++++++++++++++++++++++++++----
>>   scripts/gen-tunables.awk         |  16 +++-
>>   3 files changed, 161 insertions(+), 24 deletions(-)
>>
>> diff --git a/elf/dl-tunables.c b/elf/dl-tunables.c
>> index 63cf8c7ab5..e87d0628b2 100644
>> --- a/elf/dl-tunables.c
>> +++ b/elf/dl-tunables.c
>> @@ -300,6 +300,9 @@ __tunables_init (char **envp)
>>     if (__libc_enable_secure)
>>       return;
>>   +  enum { tunable_num_env_alias = array_length (tunable_env_alias_list) };
>> +  struct tunable_toset_t tunables_env_alias[tunable_num_env_alias] = { 0 };
>> +
>>     while ((envp = get_next_env (envp, &envname, &envval, &prev_envp)) != NULL)
>>       {
>>         /* The environment variable is allocated on the stack by the kernel, so
>> @@ -311,29 +314,44 @@ __tunables_init (char **envp)
>>         continue;
>>       }
>>   -      for (int i = 0; i < tunables_list_size; i++)
>> +      for (int i = 0; i < tunable_num_env_alias; i++)
>>       {
>> -      tunable_t *cur = &tunable_list[i];
>> +      tunable_t *cur = &tunable_list[tunable_env_alias_list[i]];
>> +      const char *name = cur->env_alias;
>>   -      /* Skip over tunables that have either been set already or should be
>> -         skipped.  */
>> -      if (cur->initialized || cur->env_alias[0] == '\0')
>> +      if (name[0] == '\0')
>>           continue;
> 
> You don't skip over initialized ones here because you're going to have to check for that later anyway, in case GLIBC_TUNABLES came in after any of the aliases.  OK.
> 
>>   -      const char *name = cur->env_alias;
>> -
>> -      /* We have a match.  Initialize and move on to the next line.  */
>>         if (tunable_is_name (name, envname))
>>           {
>>             size_t envvallen = 0;
>>             /* The environment variable is always null-terminated.  */
>>             for (const char *p = envval; *p != '\0'; p++, envvallen++);
>>   -          tunable_initialize (cur, envval, envvallen);
>> +          tunables_env_alias[i] =
>> +        (struct tunable_toset_t) { cur, envval, envvallen };
>>             break;
>>           }
>>       }
>>       }
>> +
>> +  /* Check if glibc.rtld.enable_secure was set and skip over the environment
>> +     variables aliases.  */
>> +  if (__libc_enable_secure)
>> +    return;
>> +
>> +  for (int i = 0; i < tunable_num_env_alias; i++)
>> +    {
>> +      /* Skip over tunables that have either been set.  */
> 
> either been set or already initialized?

Ack.

> 
>> +      if (tunables_env_alias[i].t == NULL
>> +      || tunables_env_alias[i].t->initialized)
>> +    continue;
>> +
>> +      if (!tunable_initialize (tunables_env_alias[i].t,
>> +                   tunables_env_alias[i].value,
>> +                   tunables_env_alias[i].len))
>> +    parse_tunable_print_error (&tunables_env_alias[i]);
>> +    }
>>   }
>>     void
>> diff --git a/elf/tst-tunables-enable_secure.c b/elf/tst-tunables-enable_secure.c
>> index f5db1c84e9..a0da0fd179 100644
>> --- a/elf/tst-tunables-enable_secure.c
>> +++ b/elf/tst-tunables-enable_secure.c
>> @@ -17,6 +17,7 @@
>>      <https://www.gnu.org/licenses/>.  */
>>     #include <array_length.h>
>> +#define TUNABLES_INTERNAL 1
> 
> OK, I noticed this in patch 1 but it didn't strike me then; this macro will result in a new array tunable_list being initialized in the binary.  The tunable references themselves should go via __tunable_get_val and hence refer to the structure in ld.so, so this array only serves as a copy to get the total size.  I suppose it's not much for a test case, but do you think it's worth adding a comment here, and also in tst-tunables.c?  That way it could be easier to weed out any unforeseen problems with these tests in future.

Ok, I will add a comment why this is required.

> 
>>   #include <dl-tunables.h>
>>   #include <getopt.h>
>>   #include <intprops.h>
>> @@ -34,6 +35,8 @@ static int restart;
>>   static const struct test_t
>>   {
>>     const char *env;
>> +  const char *extraenv;
>> +  bool check_multiple;
>>     int32_t expected_malloc_check;
>>     int32_t expected_enable_secure;
>>   } tests[] =
>> @@ -41,39 +44,124 @@ static const struct test_t
>>     /* Expected tunable format.  */
>>     /* Tunables should be ignored if enable_secure is set. */
>>     {
>> -    "glibc.malloc.check=2:glibc.rtld.enable_secure=1",
>> +    "GLIBC_TUNABLES=glibc.malloc.check=2:glibc.rtld.enable_secure=1",
>> +    NULL,
>> +    false,
>>       0,
>>       1,
>>     },
>>     /* Tunables should be ignored if enable_secure is set. */
>>     {
>> -    "glibc.rtld.enable_secure=1:glibc.malloc.check=2",
>> +    "GLIBC_TUNABLES=glibc.rtld.enable_secure=1:glibc.malloc.check=2",
>> +    NULL,
>> +    false,
>>       0,
>>       1,
>>     },
>>     /* Tunables should be set if enable_secure is unset. */
>>     {
>> -    "glibc.rtld.enable_secure=0:glibc.malloc.check=2",
>> +    "GLIBC_TUNABLES=glibc.rtld.enable_secure=0:glibc.malloc.check=2",
> 
> Same question as in v1, should we issue a warning for enable_secure=0? We really should never be able to set it to anything other than 1. Which also means that a bunch of these enable_secure=0 tests are probably redundant.

I am not sure if adding another knob for an specific tunable, specially one that
is only aimed for debug, would yield much gain here.  The enable_secure=0 is just
the default value, similar to setting any other tunable to its default value.

> 
>> +    NULL,
>> +    false,
>>       2,
>>       0,
>>     },
>> +  /* Tunables should be ignored if enable_secure is set. */
>> +  {
>> +    "GLIBC_TUNABLES=glibc.malloc.check=2:glibc.rtld.enable_secure=1",
>> +    "MALLOC_CHECK_=2",
>> +    false,
>> +    0,
>> +    1,
>> +  },
>> +  /* Same as before, but with enviroment alias prior GLIBC_TUNABLES.  */
>> +  {
>> +    "MALLOC_CHECK_=2",
>> +    "GLIBC_TUNABLES=glibc.malloc.check=2:glibc.rtld.enable_secure=1",
>> +    false,
>> +    0,
>> +    1,
>> +  },
>> +  /* Tunables should be ignored if enable_secure is set. */
>> +  {
>> +    "GLIBC_TUNABLES=glibc.rtld.enable_secure=1:glibc.malloc.check=2",
>> +    "MALLOC_CHECK_=2",
>> +    false,
>> +    0,
>> +    1,
>> +  },
>> +  {
>> +    "MALLOC_CHECK_=2",
>> +    "GLIBC_TUNABLES=glibc.rtld.enable_secure=1:glibc.malloc.check=2",
>> +    false,
>> +    0,
>> +    1,
>> +  },
>> +  /* Tunables should be set if enable_secure is unset. */
>> +  {
>> +    "GLIBC_TUNABLES=glibc.rtld.enable_secure=0:glibc.malloc.check=2",
>> +    /* Tunable have precedence over the environment variable.  */
>> +    "MALLOC_CHECK_=1",
>> +    false,
>> +    2,
>> +    0,
>> +  },
>> +  {
>> +    "MALLOC_CHECK_=1",
>> +    "GLIBC_TUNABLES=glibc.rtld.enable_secure=0:glibc.malloc.check=2",
>> +    /* Tunable have precedence over the environment variable.  */
>> +    false,
>> +    2,
>> +    0,
>> +  },
>> +  /* Tunables should be set if enable_secure is unset. */
>> +  {
>> +    "GLIBC_TUNABLES=glibc.rtld.enable_secure=0",
>> +    /* Tunable have precedence over the environment variable.  */
>> +    "MALLOC_CHECK_=1",
>> +    false,
>> +    1,
>> +    0,
>> +  },
>> +  /* Tunables should be set if enable_secure is unset. */
>> +  {
>> +    "GLIBC_TUNABLES=glibc.rtld.enable_secure=0",
>> +    /* Tunable have precedence over the environment variable.  */
>> +    "MALLOC_CHECK_=1",
>> +    false,
>> +    1,
>> +    0,
>> +  },
>> +  /* Check with tunables environment variable alias set multiple times.  */
>> +  {
>> +    "GLIBC_TUNABLES=glibc.rtld.enable_secure=1:glibc.malloc.check=2",
>> +    "MALLOC_CHECK_=2",
>> +    true,
>> +    0,
>> +    1,
>> +  },
>> +  /* Tunables should be set if enable_secure is unset. */
>> +  {
>> +    "GLIBC_TUNABLES=glibc.rtld.enable_secure=0",
>> +    /* Tunable have precedence over the environment variable.  */
>> +    "MALLOC_CHECK_=1",
>> +    true,
>> +    1,
>> +    0,
>> +  },
>>   };
>>     static int
>>   handle_restart (int i)
>>   {
>>     if (tests[i].expected_enable_secure == 1)
>> -    {
>> -      TEST_COMPARE (1, __libc_enable_secure);
>> -    }
>> +    TEST_COMPARE (1, __libc_enable_secure);
>>     else
>> -    {
>> -      TEST_COMPARE (tests[i].expected_malloc_check,
>> -            TUNABLE_GET_FULL (glibc, malloc, check, int32_t, NULL));
>> -      TEST_COMPARE (tests[i].expected_enable_secure,
>> -            TUNABLE_GET_FULL (glibc, rtld, enable_secure, int32_t,
>> -            NULL));
>> -    }
>> +    TEST_COMPARE (tests[i].expected_enable_secure,
>> +          TUNABLE_GET_FULL (glibc, rtld, enable_secure, int32_t,
>> +                     NULL));
>> +  TEST_COMPARE (tests[i].expected_malloc_check,
>> +        TUNABLE_GET_FULL (glibc, malloc, check, int32_t, NULL));
>>     return 0;
>>   }
>>   @@ -106,14 +194,31 @@ do_test (int argc, char *argv[])
>>       spargv[i] = NULL;
>>     }
>>   +  enum { tunable_num_env_alias = array_length (tunable_env_alias_list) };
>> +
>>     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);
>> +
>> +      char *envp[2 + tunable_num_env_alias + 1] =
>> +      {
>> +    (char *) tests[i].env,
>> +    (char *) tests[i].extraenv,
>> +    NULL,
>> +      };
>> +      if (tests[i].check_multiple)
>> +    {
>> +      int j;
>> +      for (j=0; j < tunable_num_env_alias; j++)
>> +        envp[j + 2] = (char *) tests[i].extraenv;
>> +      envp[j + 2] = NULL;
>> +    }
>> +
>>         struct support_capture_subprocess result
>> -    = support_capture_subprogram (spargv[0], spargv, NULL);
>> +    = support_capture_subprogram (spargv[0], spargv, envp);
>>         support_capture_subprocess_check (&result, "tst-tunables-enable_secure",
>>                                   0, sc_allow_stderr);
>>         support_capture_subprocess_free (&result);
>> diff --git a/scripts/gen-tunables.awk b/scripts/gen-tunables.awk
>> index 9f5336381e..fc3b41376f 100644
>> --- a/scripts/gen-tunables.awk
>> +++ b/scripts/gen-tunables.awk
>> @@ -156,7 +156,7 @@ END {
>>     print "# define TUNABLE_ALIAS_MAX " (max_alias_len + 1)
>>     print "# include \"dl-tunable-types.h\""
>>     # Finally, the tunable list.
>> -  print "static tunable_t tunable_list[] attribute_relro = {"
>> +  print "static tunable_t tunable_list[] attribute_relro __attribute_used__ = {"
> 
> Why is this needed?  Doesn't array_length ensures at least one "use"?

The array_length is used for only for tunable_env_alias_list on
elf/tst-tunables-enable_secure.c.

> 
>>     for (tnm in types) {
>>       split (tnm, indices, SUBSEP);
>>       t = indices[1];
>> @@ -168,5 +168,19 @@ END {
>>           default_val[t,n,m], env_alias[t,n,m]);
>>     }
>>     print "};"
>> +
>> +  # Map of tunable with environment variables aliases used during parsing.  */
>> +  print "\nstatic const tunable_id_t tunable_env_alias_list[] ="
>> +  printf "{\n"
>> +  for (tnm in types) {
>> +    split (tnm, indices, SUBSEP);
>> +    t = indices[1];
>> +    n = indices[2];
>> +    m = indices[3];
>> +    if (env_alias[t,n,m] != "{0}") {
>> +      printf ("  TUNABLE_ENUM_NAME(%s, %s, %s),\n", t, n, m);
>> +    }
>> +  }
>> +  printf "};\n"
>>     print "#endif"
>>   }
diff mbox series

Patch

diff --git a/elf/dl-tunables.c b/elf/dl-tunables.c
index 63cf8c7ab5..e87d0628b2 100644
--- a/elf/dl-tunables.c
+++ b/elf/dl-tunables.c
@@ -300,6 +300,9 @@  __tunables_init (char **envp)
   if (__libc_enable_secure)
     return;
 
+  enum { tunable_num_env_alias = array_length (tunable_env_alias_list) };
+  struct tunable_toset_t tunables_env_alias[tunable_num_env_alias] = { 0 };
+
   while ((envp = get_next_env (envp, &envname, &envval, &prev_envp)) != NULL)
     {
       /* The environment variable is allocated on the stack by the kernel, so
@@ -311,29 +314,44 @@  __tunables_init (char **envp)
 	  continue;
 	}
 
-      for (int i = 0; i < tunables_list_size; i++)
+      for (int i = 0; i < tunable_num_env_alias; i++)
 	{
-	  tunable_t *cur = &tunable_list[i];
+	  tunable_t *cur = &tunable_list[tunable_env_alias_list[i]];
+	  const char *name = cur->env_alias;
 
-	  /* Skip over tunables that have either been set already or should be
-	     skipped.  */
-	  if (cur->initialized || cur->env_alias[0] == '\0')
+	  if (name[0] == '\0')
 	    continue;
 
-	  const char *name = cur->env_alias;
-
-	  /* We have a match.  Initialize and move on to the next line.  */
 	  if (tunable_is_name (name, envname))
 	    {
 	      size_t envvallen = 0;
 	      /* The environment variable is always null-terminated.  */
 	      for (const char *p = envval; *p != '\0'; p++, envvallen++);
 
-	      tunable_initialize (cur, envval, envvallen);
+	      tunables_env_alias[i] =
+		(struct tunable_toset_t) { cur, envval, envvallen };
 	      break;
 	    }
 	}
     }
+
+  /* Check if glibc.rtld.enable_secure was set and skip over the environment
+     variables aliases.  */
+  if (__libc_enable_secure)
+    return;
+
+  for (int i = 0; i < tunable_num_env_alias; i++)
+    {
+      /* Skip over tunables that have either been set.  */
+      if (tunables_env_alias[i].t == NULL
+	  || tunables_env_alias[i].t->initialized)
+	continue;
+
+      if (!tunable_initialize (tunables_env_alias[i].t,
+			       tunables_env_alias[i].value,
+			       tunables_env_alias[i].len))
+	parse_tunable_print_error (&tunables_env_alias[i]);
+    }
 }
 
 void
diff --git a/elf/tst-tunables-enable_secure.c b/elf/tst-tunables-enable_secure.c
index f5db1c84e9..a0da0fd179 100644
--- a/elf/tst-tunables-enable_secure.c
+++ b/elf/tst-tunables-enable_secure.c
@@ -17,6 +17,7 @@ 
    <https://www.gnu.org/licenses/>.  */
 
 #include <array_length.h>
+#define TUNABLES_INTERNAL 1
 #include <dl-tunables.h>
 #include <getopt.h>
 #include <intprops.h>
@@ -34,6 +35,8 @@  static int restart;
 static const struct test_t
 {
   const char *env;
+  const char *extraenv;
+  bool check_multiple;
   int32_t expected_malloc_check;
   int32_t expected_enable_secure;
 } tests[] =
@@ -41,39 +44,124 @@  static const struct test_t
   /* Expected tunable format.  */
   /* Tunables should be ignored if enable_secure is set. */
   {
-    "glibc.malloc.check=2:glibc.rtld.enable_secure=1",
+    "GLIBC_TUNABLES=glibc.malloc.check=2:glibc.rtld.enable_secure=1",
+    NULL,
+    false,
     0,
     1,
   },
   /* Tunables should be ignored if enable_secure is set. */
   {
-    "glibc.rtld.enable_secure=1:glibc.malloc.check=2",
+    "GLIBC_TUNABLES=glibc.rtld.enable_secure=1:glibc.malloc.check=2",
+    NULL,
+    false,
     0,
     1,
   },
   /* Tunables should be set if enable_secure is unset. */
   {
-    "glibc.rtld.enable_secure=0:glibc.malloc.check=2",
+    "GLIBC_TUNABLES=glibc.rtld.enable_secure=0:glibc.malloc.check=2",
+    NULL,
+    false,
     2,
     0,
   },
+  /* Tunables should be ignored if enable_secure is set. */
+  {
+    "GLIBC_TUNABLES=glibc.malloc.check=2:glibc.rtld.enable_secure=1",
+    "MALLOC_CHECK_=2",
+    false,
+    0,
+    1,
+  },
+  /* Same as before, but with enviroment alias prior GLIBC_TUNABLES.  */
+  {
+    "MALLOC_CHECK_=2",
+    "GLIBC_TUNABLES=glibc.malloc.check=2:glibc.rtld.enable_secure=1",
+    false,
+    0,
+    1,
+  },
+  /* Tunables should be ignored if enable_secure is set. */
+  {
+    "GLIBC_TUNABLES=glibc.rtld.enable_secure=1:glibc.malloc.check=2",
+    "MALLOC_CHECK_=2",
+    false,
+    0,
+    1,
+  },
+  {
+    "MALLOC_CHECK_=2",
+    "GLIBC_TUNABLES=glibc.rtld.enable_secure=1:glibc.malloc.check=2",
+    false,
+    0,
+    1,
+  },
+  /* Tunables should be set if enable_secure is unset. */
+  {
+    "GLIBC_TUNABLES=glibc.rtld.enable_secure=0:glibc.malloc.check=2",
+    /* Tunable have precedence over the environment variable.  */
+    "MALLOC_CHECK_=1",
+    false,
+    2,
+    0,
+  },
+  {
+    "MALLOC_CHECK_=1",
+    "GLIBC_TUNABLES=glibc.rtld.enable_secure=0:glibc.malloc.check=2",
+    /* Tunable have precedence over the environment variable.  */
+    false,
+    2,
+    0,
+  },
+  /* Tunables should be set if enable_secure is unset. */
+  {
+    "GLIBC_TUNABLES=glibc.rtld.enable_secure=0",
+    /* Tunable have precedence over the environment variable.  */
+    "MALLOC_CHECK_=1",
+    false,
+    1,
+    0,
+  },
+  /* Tunables should be set if enable_secure is unset. */
+  {
+    "GLIBC_TUNABLES=glibc.rtld.enable_secure=0",
+    /* Tunable have precedence over the environment variable.  */
+    "MALLOC_CHECK_=1",
+    false,
+    1,
+    0,
+  },
+  /* Check with tunables environment variable alias set multiple times.  */
+  {
+    "GLIBC_TUNABLES=glibc.rtld.enable_secure=1:glibc.malloc.check=2",
+    "MALLOC_CHECK_=2",
+    true,
+    0,
+    1,
+  },
+  /* Tunables should be set if enable_secure is unset. */
+  {
+    "GLIBC_TUNABLES=glibc.rtld.enable_secure=0",
+    /* Tunable have precedence over the environment variable.  */
+    "MALLOC_CHECK_=1",
+    true,
+    1,
+    0,
+  },
 };
 
 static int
 handle_restart (int i)
 {
   if (tests[i].expected_enable_secure == 1)
-    {
-      TEST_COMPARE (1, __libc_enable_secure);
-    }
+    TEST_COMPARE (1, __libc_enable_secure);
   else
-    {
-      TEST_COMPARE (tests[i].expected_malloc_check,
-		    TUNABLE_GET_FULL (glibc, malloc, check, int32_t, NULL));
-      TEST_COMPARE (tests[i].expected_enable_secure,
-		    TUNABLE_GET_FULL (glibc, rtld, enable_secure, int32_t,
-		    NULL));
-    }
+    TEST_COMPARE (tests[i].expected_enable_secure,
+		  TUNABLE_GET_FULL (glibc, rtld, enable_secure, int32_t,
+				     NULL));
+  TEST_COMPARE (tests[i].expected_malloc_check,
+		TUNABLE_GET_FULL (glibc, malloc, check, int32_t, NULL));
   return 0;
 }
 
@@ -106,14 +194,31 @@  do_test (int argc, char *argv[])
     spargv[i] = NULL;
   }
 
+  enum { tunable_num_env_alias = array_length (tunable_env_alias_list) };
+
   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);
+
+      char *envp[2 + tunable_num_env_alias + 1] =
+      {
+	(char *) tests[i].env,
+	(char *) tests[i].extraenv,
+	NULL,
+      };
+      if (tests[i].check_multiple)
+	{
+	  int j;
+	  for (j=0; j < tunable_num_env_alias; j++)
+	    envp[j + 2] = (char *) tests[i].extraenv;
+	  envp[j + 2] = NULL;
+	}
+
       struct support_capture_subprocess result
-	= support_capture_subprogram (spargv[0], spargv, NULL);
+	= support_capture_subprogram (spargv[0], spargv, envp);
       support_capture_subprocess_check (&result, "tst-tunables-enable_secure",
 		                        0, sc_allow_stderr);
       support_capture_subprocess_free (&result);
diff --git a/scripts/gen-tunables.awk b/scripts/gen-tunables.awk
index 9f5336381e..fc3b41376f 100644
--- a/scripts/gen-tunables.awk
+++ b/scripts/gen-tunables.awk
@@ -156,7 +156,7 @@  END {
   print "# define TUNABLE_ALIAS_MAX " (max_alias_len + 1)
   print "# include \"dl-tunable-types.h\""
   # Finally, the tunable list.
-  print "static tunable_t tunable_list[] attribute_relro = {"
+  print "static tunable_t tunable_list[] attribute_relro __attribute_used__ = {"
   for (tnm in types) {
     split (tnm, indices, SUBSEP);
     t = indices[1];
@@ -168,5 +168,19 @@  END {
 	    default_val[t,n,m], env_alias[t,n,m]);
   }
   print "};"
+
+  # Map of tunable with environment variables aliases used during parsing.  */
+  print "\nstatic const tunable_id_t tunable_env_alias_list[] ="
+  printf "{\n"
+  for (tnm in types) {
+    split (tnm, indices, SUBSEP);
+    t = indices[1];
+    n = indices[2];
+    m = indices[3];
+    if (env_alias[t,n,m] != "{0}") {
+      printf ("  TUNABLE_ENUM_NAME(%s, %s, %s),\n", t, n, m);
+    }
+  }
+  printf "};\n"
   print "#endif"
 }