public inbox for libc-alpha@sourceware.org
 help / color / mirror / Atom feed
From: Adhemerval Zanella <adhemerval.zanella@linaro.org>
To: Carlos O'Donell <carlos@redhat.com>,
	libc-alpha@sourceware.org, jma14 <jma14@rice.edu>
Cc: John Mellor-Crummey <johnmc@rice.edu>, Ben Woodard <woodard@redhat.com>
Subject: Re: [PATCH v12 2/4] elf: Fix initial-exec TLS access on audit modules (BZ #28096)
Date: Tue, 1 Feb 2022 10:35:36 -0300	[thread overview]
Message-ID: <76182b38-d288-1cc5-5f09-2ad99a2509fb@linaro.org> (raw)
In-Reply-To: <cfedab40-b9f8-1bf0-507e-47288fc71a8d@redhat.com>



On 01/02/2022 02:29, Carlos O'Donell wrote:
> On 1/25/22 13:36, Adhemerval Zanella wrote:
>> For audit modules and dependencies with initial-exec TLS, we can not
>> set the initial TLS image on default loader initialization because it
>> would already be set by the audit setup.  However, subsequent thread
>> creation would need to follow the default behaviour.
> 
> Correct, there is a circular dependency, we need the initial TLS image setup for
> the auditor before the loader does the setup.
> 
>> This patch fixes it by setting l_auditing link_map field not only
>> for the audit modules, but also for all its dependencies.  This is
>> used on _dl_allocate_tls_init to avoid the static TLS initialization
>> only at loading time.
> 
> s/only at loading/at load/g

Ack.

> 
>> Checked on x86_64-linux-gnu, i686-linux-gnu, and aarch64-linux-gnu.
> 
> Tested on x86_64 and i686 and it looks good for glibc 2.35.
> 
> Please post v13 with fixes and I'll ACK that as release manager.
> 
> 
>> ---
>>  elf/Makefile               |  8 ++++
>>  elf/dl-tls.c               | 13 +++++--
>>  elf/rtld.c                 |  2 +-
>>  elf/tst-audit21.c          | 42 ++++++++++++++++++++
>>  elf/tst-auditmod21a.c      | 80 ++++++++++++++++++++++++++++++++++++++
>>  elf/tst-auditmod21b.c      | 22 +++++++++++
>>  nptl/allocatestack.c       |  2 +-
>>  sysdeps/generic/ldsodefs.h |  2 +-
>>  8 files changed, 165 insertions(+), 6 deletions(-)
>>  create mode 100644 elf/tst-audit21.c
>>  create mode 100644 elf/tst-auditmod21a.c
>>  create mode 100644 elf/tst-auditmod21b.c
>>
>> diff --git a/elf/Makefile b/elf/Makefile
>> index a3b4468593..7d01b71f6a 100644
>> --- a/elf/Makefile
>> +++ b/elf/Makefile
>> @@ -376,6 +376,7 @@ tests += \
>>    tst-audit18 \
>>    tst-audit19b \
>>    tst-audit20 \
>> +  tst-audit21 \
> 
> OK. Test.
> 
>>    tst-audit22 \
>>    tst-audit23 \
>>    tst-auditmany \
>> @@ -696,6 +697,8 @@ modules-names = \
>>    tst-auditmod19a \
>>    tst-auditmod19b \
>>    tst-auditmod20 \
>> +  tst-auditmod21a \
>> +  tst-auditmod21b \
> 
> OK. Two audit modules.
> 
>>    tst-auditmod22 \
>>    tst-auditmod23 \
>>    tst-auxvalmod \
>> @@ -2136,6 +2139,11 @@ tst-audit19b-ARGS = -- $(host-test-program-cmd)
>>  $(objpfx)tst-audit20.out: $(objpfx)tst-auditmod20.so
>>  tst-audit20-ENV = LD_AUDIT=$(objpfx)tst-auditmod20.so
>>  
>> +$(objpfx)tst-audit21: $(shared-thread-library)
>> +$(objpfx)tst-audit21.out: $(objpfx)tst-auditmod21a.so
>> +$(objpfx)tst-auditmod21a.so: $(objpfx)tst-auditmod21b.so
> 
> OK. A deps B.
> 
>> +tst-audit21-ENV = LD_AUDIT=$(objpfx)tst-auditmod21a.so
> 
> OK. New test is audited by A, which deps B.
> 
>> +
>>  $(objpfx)tst-audit22.out: $(objpfx)tst-auditmod22.so
>>  tst-audit22-ARGS = -- $(host-test-program-cmd)
>>  
>> diff --git a/elf/dl-tls.c b/elf/dl-tls.c
>> index 8ba70c9a9d..ccbe39660b 100644
>> --- a/elf/dl-tls.c
>> +++ b/elf/dl-tls.c
>> @@ -520,7 +520,7 @@ _dl_resize_dtv (dtv_t *dtv, size_t max_modid)
>>  
>>  
> 
> This needs a comment explaining bool init_tls.
> 
> /* Allocate initial TLS.  RESULT should be a non-NULL pointer to storage
>    for the TLS space.  The DTV may be resized, and so this function may
>    call malloc to allocate that space.  The loader's GL(dl_load_tls_lock)
>    is taken when manipulating global TLS-related data in the laoder.

Ack.

> 
>>  void *
>> -_dl_allocate_tls_init (void *result)
>> +_dl_allocate_tls_init (void *result, bool init_tls)
> 
> OK. This is called by a reinitialization of re-used stacks. It is called
> by normal _dl_allocate_tls, which is called when a thread allocates a DTV.
> 
>>  {
>>    if (result == NULL)
>>      /* The memory allocation failed.  */
>> @@ -593,7 +593,14 @@ _dl_allocate_tls_init (void *result)
>>  	     some platforms use in static programs requires it.  */
>>  	  dtv[map->l_tls_modid].pointer.val = dest;
>>  
>> -	  /* Copy the initialization image and clear the BSS part.  */
>> +	  /* Copy the initialization image and clear the BSS part.  For
>> +	     audit modules or depedencies with initial-exec TLS, we can not
> 
> s/depedencies/dependencies/g

Ack.

> 
>> +	     set the initial TLS image on default loader initialization
>> +	     because it would already be set by the audit setup.  However,
>> +	     subsequent thread creation would need to follow the default
>> +	     behaviour.   */
>> +	  if (map->l_ns != LM_ID_BASE && !init_tls)
>> +	    continue;
> 
> OK. The tricky part here is that this works because GL(dl_tls_dtv_slotinfo_list)
> is a *GLOBAL* list that is irrespective of linker namespace. So when we come to setup
> the static TLS for the thread via rtld we will redo the already done setup for the libc
> in the non-main-namespace.

Yeap, that is the main issue indeed.

> 
>>  	  memset (__mempcpy (dest, map->l_tls_initimage,
>>  			     map->l_tls_initimage_size), '\0',
>>  		  map->l_tls_blocksize - map->l_tls_initimage_size);
>> @@ -620,7 +627,7 @@ _dl_allocate_tls (void *mem)
>>  {
>>    return _dl_allocate_tls_init (mem == NULL
>>  				? _dl_allocate_tls_storage ()
>> -				: allocate_dtv (mem));
>> +				: allocate_dtv (mem), true);
> 
> OK. Correct, when calling via _dl_allocate_tls we always want to initialize TLS.
> 
>>  }
>>  rtld_hidden_def (_dl_allocate_tls)
>>  
>> diff --git a/elf/rtld.c b/elf/rtld.c
>> index 8d233f77be..10436f7034 100644
>> --- a/elf/rtld.c
>> +++ b/elf/rtld.c
>> @@ -2462,7 +2462,7 @@ dl_main (const ElfW(Phdr) *phdr,
>>       into the main thread's TLS area, which we allocated above.
>>       Note: thread-local variables must only be accessed after completing
>>       the next step.  */
>> -  _dl_allocate_tls_init (tcbp);
>> +  _dl_allocate_tls_init (tcbp, false);
> 
> OK. When doing this step we have already potentially initialized everything
> via init_tls(naudit) earlier, but "false" is only useful if we are in the
> non-base namespace.
> 
>>  
>>    /* And finally install it for the main thread.  */
>>    if (! tls_init_tp_called)
>> diff --git a/elf/tst-audit21.c b/elf/tst-audit21.c
>> new file mode 100644
>> index 0000000000..41446a2107
>> --- /dev/null
>> +++ b/elf/tst-audit21.c
>> @@ -0,0 +1,42 @@
>> +/* Check DT_AUDIT with static TLS.
> 
> s/DT_AUDIT/LD_AUDIT/g

Ack.

> 
>> +   Copyright (C) 2022 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 <ctype.h>
>> +#include <support/xthread.h>
>> +#include <support/check.h>
>> +
>> +static volatile __thread int out __attribute__ ((tls_model ("initial-exec")));
> 
> OK. out has size int.
> 
>> +
>> +static void *
>> +tf (void *arg)
>> +{
>> +  TEST_COMPARE (out, 0);
>> +  out = isspace (' ');
> 
> OK. Uses ctype data, which uses tls also.
> 
>> +  return NULL;
>> +}
>> +
>> +int main (int argc, char *argv[])
>> +{
>> +  TEST_COMPARE (out, 0);
>> +  out = isspace (' ');
>> +
>> +  pthread_t t = xpthread_create (NULL, tf, NULL);
> 
> OK. In the new thread look at the value.
> 
>> +  xpthread_join (t);
>> +
>> +  return 0;
>> +}
>> diff --git a/elf/tst-auditmod21a.c b/elf/tst-auditmod21a.c
>> new file mode 100644
>> index 0000000000..4c0f256969
>> --- /dev/null
>> +++ b/elf/tst-auditmod21a.c
>> @@ -0,0 +1,80 @@
>> +/* Check DT_AUDIT with static TLS.
> 
> s/DT_AUDIT/LD_AUDIT/g

Ack.

> 
>> +   Copyright (C) 2022 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 <ctype.h>
>> +#include <stdlib.h>
>> +#include <link.h>
>> +
>> +#define tls_ie __attribute__ ((tls_model ("initial-exec")))
>> +
>> +__thread int tls_var0 tls_ie;
>> +__thread int tls_var1 tls_ie = 0x10;
> 
> OK. Two TLS IE mode vars one with a non-zero intializer.
> 
>> +
>> +/* Defined at tst-auditmod21b.so  */
>> +extern __thread int tls_var2;
>> +extern __thread int tls_var3;
>> +
>> +static volatile int out;
>> +
>> +static void
>> +call_libc (void)
>> +{
>> +  /* isspace access the initial-exec glibc TLS variables, which are
> 
> s/access/accesses/g

Ack.

> 
>> +     setup in glibc initialization.  */
>> +  out = isspace (' ');
>> +}
>> +
>> +unsigned int
>> +la_version (unsigned int v)
>> +{
> 
> OK. la_version is called to initialize this module, so it's after all the
> early tls initialization done by the loader.
> 
>> +  tls_var0 = 0x1;
>> +  if (tls_var1 != 0x10)
>> +    abort ();
>> +  tls_var1 = 0x20;
>> +
>> +  tls_var2 = 0x2;
>> +  if (tls_var3 != 0x20)
>> +    abort ();
>> +  tls_var3 = 0x40;
>> +
>> +  call_libc ();
>> +
>> +  return LAV_CURRENT;
>> +}
>> +
>> +unsigned int
>> +la_objopen (struct link_map* map, Lmid_t lmid, uintptr_t* cookie)
>> +{
>> +  call_libc ();
>> +  *cookie = (uintptr_t) map;
>> +  return 0;
>> +}
>> +
>> +void
>> +la_activity (uintptr_t* cookie, unsigned int flag)
>> +{
> 
> OK. By the time we have activity our TLS will have been overwritten.
> 
>> +  if (tls_var0 != 0x1 || tls_var1 != 0x20)
>> +    abort ();
>> +  call_libc ();
>> +}
>> +
>> +void
>> +la_preinit (uintptr_t* cookie)
>> +{
>> +  call_libc ();
> 
> OK. Trigger libc tls usage after all DSOs are loaded but before main.
> 
>> +}
>> diff --git a/elf/tst-auditmod21b.c b/elf/tst-auditmod21b.c
>> new file mode 100644
>> index 0000000000..69705cf75a
>> --- /dev/null
>> +++ b/elf/tst-auditmod21b.c
>> @@ -0,0 +1,22 @@
>> +/* Check DT_AUDIT with static TLS.
> 
> s/DT_AUDIT/LD_AUDIT/g

Ack.

> 
>> +   Copyright (C) 2022 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/>.  */
>> +
>> +#define tls_ie __attribute__ ((tls_model ("initial-exec")))
>> +
>> +__thread int tls_var2 tls_ie;
>> +__thread int tls_var3 tls_ie = 0x20;
> 
> OK.
> 
>> diff --git a/nptl/allocatestack.c b/nptl/allocatestack.c
>> index 3fb085f9a1..34a33164ff 100644
>> --- a/nptl/allocatestack.c
>> +++ b/nptl/allocatestack.c
>> @@ -138,7 +138,7 @@ get_cached_stack (size_t *sizep, void **memp)
>>    memset (dtv, '\0', (dtv[-1].counter + 1) * sizeof (dtv_t));
>>  
>>    /* Re-initialize the TLS.  */
>> -  _dl_allocate_tls_init (TLS_TPADJ (result));
>> +  _dl_allocate_tls_init (TLS_TPADJ (result), true);
> 
> OK. Yes, a thread needs to reinitialize this for the new stack.
> 
>>  
>>    return result;
>>  }
>> diff --git a/sysdeps/generic/ldsodefs.h b/sysdeps/generic/ldsodefs.h
>> index f6b2b415a6..97061bdf9f 100644
>> --- a/sysdeps/generic/ldsodefs.h
>> +++ b/sysdeps/generic/ldsodefs.h
>> @@ -1282,7 +1282,7 @@ extern void _dl_allocate_static_tls (struct link_map *map) attribute_hidden;
>>  /* These are internal entry points to the two halves of _dl_allocate_tls,
>>     only used within rtld.c itself at startup time.  */
>>  extern void *_dl_allocate_tls_storage (void) attribute_hidden;
>> -extern void *_dl_allocate_tls_init (void *);
>> +extern void *_dl_allocate_tls_init (void *, bool);
> 
> OK.
> 
>>  rtld_hidden_proto (_dl_allocate_tls_init)
>>  
>>  /* Deallocate memory allocated with _dl_allocate_tls.  */
> 
> 

  reply	other threads:[~2022-02-01 13:35 UTC|newest]

Thread overview: 18+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2022-01-25 18:36 [PATCH v12 0/4] Multiple rtld-audit fixes Adhemerval Zanella
2022-01-25 18:36 ` [PATCH v12 1/4] elf: Add la_activity during application exit Adhemerval Zanella
2022-01-26 11:42   ` Florian Weimer
2022-01-26 12:18     ` Adhemerval Zanella
2022-01-26 12:25       ` Florian Weimer
2022-02-01  4:21   ` Carlos O'Donell
2022-02-01 13:30     ` Adhemerval Zanella
2022-01-25 18:36 ` [PATCH v12 2/4] elf: Fix initial-exec TLS access on audit modules (BZ #28096) Adhemerval Zanella
2022-02-01  5:29   ` Carlos O'Donell
2022-02-01 13:35     ` Adhemerval Zanella [this message]
2022-01-25 18:36 ` [PATCH v12 3/4] elf: Issue la_symbind for bind-now (BZ #23734) Adhemerval Zanella
2022-02-01  6:06   ` Carlos O'Donell
2022-02-01 13:47     ` Adhemerval Zanella
2022-01-25 18:37 ` [PATCH v12 4/4] elf: Fix runtime linker auditing on aarch64 (BZ #26643) Adhemerval Zanella
2022-02-01  6:19   ` Carlos O'Donell
2022-02-01 14:11     ` Adhemerval Zanella
2022-02-01  6:45 ` [PATCH v12 0/4] Multiple rtld-audit fixes Carlos O'Donell
2022-02-01  7:53   ` John Mellor-Crummey

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=76182b38-d288-1cc5-5f09-2ad99a2509fb@linaro.org \
    --to=adhemerval.zanella@linaro.org \
    --cc=carlos@redhat.com \
    --cc=jma14@rice.edu \
    --cc=johnmc@rice.edu \
    --cc=libc-alpha@sourceware.org \
    --cc=woodard@redhat.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
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).