[PATCH] elf/rtld: Fix auxiliary vector for enable_secure

Adhemerval Zanella Netto adhemerval.zanella@linaro.org
Fri Jun 28 13:22:40 GMT 2024



On 28/06/24 10:01, Stefan Liebler wrote:
> On 27.06.24 22:40, Adhemerval Zanella Netto wrote:
>>
>>
>> On 25/06/24 06:17, Stefan Liebler wrote:
>>> Starting with commit
>>> 59974938fe1f4add843f5325f78e2a7ccd8db853
>>> elf/rtld: Count skipped environment variables for enable_secure
>>>
>>> The new testcase elf/tst-tunables-enable_secure-env segfaults on s390 (31bit).
>>> There _start parses the auxiliary vector for some additional checks.
>>>
>>> Therefore it skips over the zeros after the environment variables ...
>>> 0x7fffac20:	0x7fffbd17	0x7fffbd32	0x7fffbd69	0x00000000
>>> ------------------------------------------------^^^last environment variable
>>>
>>> ... and then it parses the auxiliary vector and stops at AT_NULL.
>>> 0x7fffac30:	0x00000000	0x00000021	0x00000000	0x00000000
>>> --------------------------------^^^AT_SYSINFO_EHDR--------------^^^AT_NULL
>>> ----------------^^^newp-----------------------------------------^^^oldp
>>> Afterwards it tries to access AT_PHDR which points to somewhere and segfaults.
>>>
>>> Due to not incorporating the skip_env variable in the computation of oldp
>>> when shuffling down the auxv in rtld.c, it just copies one entry with AT_NULL
>>> and value 0x00000021 and stops the loop.  In reality we have skipped
>>> GLIBC_TUNABLES environment variable (=> skip_env=1). Thus we should copy from
>>> here:
>>> 0x7fffac40:	0x00000021	0x7ffff000	0x00000010	0x007fffff
>>> ----------------^^^fixed-oldp
>>>
>>> This patch fixes the computation of oldp when shuffling down auxiliary vector.
>>> It also adds some checks in the testcase.  Those checks also fail on
>>> s390x (64bit) and x86_64 without the fix.
>>> ---
>>>  elf/rtld.c                           |  2 +-
>>>  elf/tst-tunables-enable_secure-env.c | 19 +++++++++++++++++++
>>>  2 files changed, 20 insertions(+), 1 deletion(-)
>>>
>>> diff --git a/elf/rtld.c b/elf/rtld.c
>>> index e9525ea987..883c651d48 100644
>>> --- a/elf/rtld.c
>>> +++ b/elf/rtld.c
>>> @@ -1325,7 +1325,7 @@ _dl_start_args_adjust (int skip_args, int skip_env)
>>>  
>>>    /* Shuffle auxv down. */
>>>    ElfW(auxv_t) ax;
>>> -  char *oldp = (char *) (p + 1);
>>> +  char *oldp = (char *) (p + 1 + skip_env);
>>>    char *newp = (char *) (sp + 1);
>>>    do
>>>      {
>>
>> I don't think this is correct, running as the testcase it does show the expected value:
>>
> LD_SHOW_AUXV=1 can't be used to show this issue.
> 
> In process_envvars(), process_envvars_secure() is called and e.g.
> GLIBC_TUNABLES variable is removed with unsetenv(). The skipped/removed
> variables are counted in skip_env. Note that unsetenv() itself removes
> the pointer on the stack by moving the later ones to the front.
> Then in process_envvars_default(), _dl_show_auxv() is called if
> LD_SHOW_AUXV=1 is set. At this time, the auxv is fine.
> 
> Lateron, in _dl_start_args_adjust(), the arguments,
> environment-variables and the auxiliary-vector are shuffled down as
> "elf/ld-linux-x86-64.so.2" "--library-path" "..." arguments are removed.
> 
> argv/envp are shuffled down until the NULL-pointers are found on stack:
>   /* Shuffle argv down.  */
>   do
>     *++sp = *++p;
>   while (*p != NULL);
> ...
>   /* Shuffle envp down.  */
>   do
>     *++sp = *++p;
>   while (*p != NULL);
> 
> Then auxv is shuffled down. The code assumes that there is one
> NULL-pointer between envp and auxv.
>   /* Shuffle auxv down. */
>   ElfW(auxv_t) ax;
>   char *oldp = (char *) (p + 1);
>   char *newp = (char *) (sp + 1);
>   do
>     {
>       memcpy (&ax, oldp, sizeof (ax));
>       memcpy (newp, &ax, sizeof (ax));
>       oldp += sizeof (ax);
>       newp += sizeof (ax);
>     }
>   while (ax.a_type != AT_NULL);
> 
> But GLIBC_TUNABLES was removed with unsetenv() and thus there are
> skip_env=1 additional pointers between envp and auxv.
> 
> With skip_env==1, oldp points to the original NULL-pointer between envp
> and auxv at startup of ld.so. Thus only one auxv-entry is in place - the
> AT_NULL-one. (Also for skip_env >1, oldp points to a NULL-pointer)
> 
> If you want, add "_dl_show_auxv ();" after the call of
> "_dl_start_args_adjust (_dl_argv - orig_argv, skip_env);" But you won't
> get an output!

Sorry if I was not clearn, I was not relying on the _dl_show_auxv but rather
I changed tst-tunables-enable_secure-env to loop over a list of AT_ entries
with getauxval.  I used LD_SHOW_AUXV=1 as an example, but I does fail if you
set any filtered variable from sysdeps/generic/unsecvars.h:

GLIBC_TUNABLES=glibc.rtld.enable_secure=1 \
   LD_BIND_NOT=1 \
   elf/ld-linux-x86-64.so.2 --library-path ... elf/tst-tunables-enable_secure-env --direct
AT_EXECFD:            0
AT_EXECFN:            (null)
AT_PHDR:              0x0
AT_PHENT:             0
AT_PHNUM:             0
AT_PAGESZ:            0
AT_BASE:              0x0
AT_FLAGS:             0x0
AT_ENTRY:             0x0
AT_NOTELF:            0
AT_UID:               0
AT_EUID:              0
AT_GID:               0
AT_EGID:              0
AT_PLATFORM:          (null)
AT_HWCAP:             2
AT_CLKTCK:            0
AT_FPUCW:             0
AT_DCACHEBSIZE:       0x0
AT_ICACHEBSIZE:       0x0
AT_UCACHEBSIZE:       0x0
AT_IGNOREPPC(null)
AT_SECURE:            0
AT_BASE_PLATFORM:     (null)
AT_SYSINFO:           0x0
AT_SYSINFO_EHDR:      0x0
AT_RANDOM:            0x0
AT_HWCAP2:            0x2
AT_HWCAP3:            0x0
AT_HWCAP4:            0x0
AT_MINSIGSTKSZ:       0
AT_L1I_CACHESIZE:     0
AT_L1I_CACHEGEOMETRY: 0x0
AT_L1D_CACHESIZE:     0
AT_L1D_CACHEGEOMETRY: 0x0
AT_L2_CACHESIZE:      0
AT_L2_CACHEGEOMETRY:  0x0
AT_L3_CACHESIZE:      0
AT_L3_CACHEGEOMETRY:  0x0

> 
> The called executable (elf/tst-tunables-enable_secure-env) does not get
> any auxv-entries via getauxval(). Okay, only the special handled ones:
> AT_HWCAP/AT_HWCAP2.

Well, it does if only set GLIBC_TUNABLES=glibc.rtld.enable_secure=1, but
the auxv will only contain AT_HWCAP if you set something from the filtered
environments variables.  I am not sure if this is really a consistent
behavior.

> 
>> $ GLIBC_TUNABLES=glibc.rtld.enable_secure=1 \
>>   elf/ld-linux-x86-64.so.2 --library-path ... elf/tst-tunables-enable_secure-env --direct
>> AT_EXECFD:            0
>> AT_EXECFN:            [...]/elf/tst-tunables-enable_secure-env
>> AT_PHDR:              0x7e7f608b4040
>> AT_PHENT:             56
>> AT_PHNUM:             13
>> AT_PAGESZ:            4096
>> AT_BASE:              0x0
>> AT_FLAGS:             0x0
>> AT_ENTRY:             0x7e7f608b6760
>> AT_NOTELF:            0
>> AT_UID:               1000
>> AT_EUID:              1000
>> AT_GID:               1000
>> AT_EGID:              1000
>> AT_PLATFORM:          x86_64
>> AT_HWCAP:             2
>> AT_CLKTCK:            100
>> AT_FPUCW:             0
>> AT_DCACHEBSIZE:       0x0
>> AT_ICACHEBSIZE:       0x0
>> AT_UCACHEBSIZE:       0x0
>> AT_IGNOREPPC(null)
>> AT_SECURE:            0
>> AT_BASE_PLATFORM:     (null)
>> AT_SYSINFO:           0x0
>> AT_SYSINFO_EHDR:      0x0
>> AT_RANDOM:            0x7ffe90102d29
>> AT_HWCAP2:            0x2
>> AT_HWCAP3:            0x0
>> AT_HWCAP4:            0x0
>> AT_MINSIGSTKSZ:       3376
>> AT_L1I_CACHESIZE:     0
>> AT_L1I_CACHEGEOMETRY: 0x0
>> AT_L1D_CACHESIZE:     0
>> AT_L1D_CACHEGEOMETRY: 0x0
>> AT_L2_CACHESIZE:      0
>> AT_L2_CACHEGEOMETRY:  0x0
>> AT_L3_CACHESIZE:      0
>> AT_L3_CACHEGEOMETRY:  0x0
>>
>> However if I add environment that should be filtered:
>>
>>
>> $ GLIBC_TUNABLES=glibc.rtld.enable_secure=1 \
>>   LD_SHOW_AUXV=1 \
>>   elf/ld-linux-x86-64.so.2 --library-path ... elf/tst-tunables-enable_secure-env --direct
>>
>> AT_EXECFD:            0
>> AT_EXECFN:            (null)
>> AT_PHDR:              0x0
>> AT_PHENT:             0
>> AT_PHNUM:             0
>> AT_PAGESZ:            0
>> AT_BASE:              0x0
>> AT_FLAGS:             0x0
>> AT_ENTRY:             0x0
>> AT_NOTELF:            0
>> AT_UID:               0
>> AT_EUID:              0
>> AT_GID:               0
>> AT_EGID:              0
>> AT_PLATFORM:          (null)
>> AT_HWCAP:             2
>> AT_CLKTCK:            0
>> AT_FPUCW:             0
>> AT_DCACHEBSIZE:       0x0
>> AT_ICACHEBSIZE:       0x0
>> AT_UCACHEBSIZE:       0x0
>> AT_IGNOREPPC(null)
>> AT_SECURE:            0
>> AT_BASE_PLATFORM:     (null)
>> AT_SYSINFO:           0x0
>> AT_SYSINFO_EHDR:      0x0
>> AT_RANDOM:            0x0
>> AT_HWCAP2:            0x2
>> AT_HWCAP3:            0x0
>> AT_HWCAP4:            0x0
>> AT_MINSIGSTKSZ:       0
>> AT_L1I_CACHESIZE:     0
>> AT_L1I_CACHEGEOMETRY: 0x0
>> AT_L1D_CACHESIZE:     0
>> AT_L1D_CACHEGEOMETRY: 0x0
>> AT_L2_CACHESIZE:      0
>> AT_L2_CACHEGEOMETRY:  0x0
>> AT_L3_CACHESIZE:      0
>> AT_L3_CACHEGEOMETRY:  0x0
>>
>>> diff --git a/elf/tst-tunables-enable_secure-env.c b/elf/tst-tunables-enable_secure-env.c
>>> index 24e846f299..c78e113c21 100644
>>> --- a/elf/tst-tunables-enable_secure-env.c
>>> +++ b/elf/tst-tunables-enable_secure-env.c
>>> @@ -17,8 +17,12 @@
>>>     License along with the GNU C Library; if not, see
>>>     <https://www.gnu.org/licenses/>.  */
>>>  
>>> +#include <stdlib.h>
>>>  #include <support/capture_subprocess.h>
>>>  #include <support/check.h>
>>> +#ifdef __linux__
>>> + #include <sys/auxv.h>
>>> +#endif
>>>  
>>>  static int
>>>  do_test (int argc, char *argv[])
>>> @@ -26,6 +30,21 @@ 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. */
>>> +
>>> +  /* The environment variable GLIBC_TUNABLES is skipped for secure
>>> +     execution.  */
>>> +  TEST_VERIFY (getenv ("GLIBC_TUNABLES") == NULL);
>>> +
>>> +#ifdef __linux__
>>> +  /* On linux, the auxiliary vector is located after the environment variables.
>>> +     Check that some of them are available as those are also shuffled down by
>>> +     ld.so.  */
>>> +  TEST_VERIFY (getauxval (AT_PHDR) != 0);
>>> +  TEST_VERIFY (getauxval (AT_PHENT) != 0);
>>> +  TEST_VERIFY (getauxval (AT_PHNUM) != 0);
>>> +  TEST_VERIFY (getauxval (AT_ENTRY) != 0);
>>> +#endif
>>> +
>>>    return 0;
>>>  }
>>>  
> 


More information about the Libc-alpha mailing list