public inbox for libc-alpha@sourceware.org
 help / color / mirror / Atom feed
From: Siddhesh Poyarekar <siddhesh@sourceware.org>
To: "H.J. Lu" <hjl.tools@gmail.com>, Carlos O'Donell <carlos@redhat.com>
Cc: Florian Weimer <fweimer@redhat.com>,
	GNU C Library <libc-alpha@sourceware.org>
Subject: Re: [PATCH] ld.so: Handle read-only dynamic section gracefully [BZ #28340]
Date: Thu, 16 Sep 2021 22:15:30 +0530	[thread overview]
Message-ID: <ec58a4ac-7414-c06d-d77a-42e00aaccf94@sourceware.org> (raw)
In-Reply-To: <CAMe9rOo4EHRy7DeoQpB=KCC86DY4Q79k8qkoBE_p_qdOXE_14A@mail.gmail.com>

On 9/16/21 8:48 PM, H.J. Lu wrote:
> On Thu, Sep 16, 2021 at 7:11 AM Carlos O'Donell <carlos@redhat.com> wrote:
>>
>> On 9/16/21 00:48, Florian Weimer via Libc-alpha wrote:
>>> * H. J. Lu:
>>>
>>>> There is nothing wrong with read-only dynamic segment.
>>>
>>> A relocated DYNAMIC array is part of the ABI for !DL_RO_DYN_SECTION.
>>> ELF requires that DT_STRTAB is present.  DT_STRTAB needs relocation.
>>> This means that for !DL_RO_DYN_SECTION, the dynamic segment cannot be in
>>> a read-ony LOAD segment for a valid ELF file.
>>
>> I agree strongly with this position.
>>
>> Even with PT_GNU_RELRO, we must only go in one direction from RW -> RO (to avoid
>> other security issues e.g. hardening not loosening the restrictions).
>>
>> In theory the vDSO is invalid.
>>
>> In practice it is a DL_RO_DYN_SECTION DSO but selected dynamically at runtime
>> rather than statically at compile time for the target.

Actually it isn't.  The dynamic section in the vdso is already relocated 
by the kernel when it's mapped in.  DL_RO_DYN_SECTION DSOs are not 
relocated because of which any references to pointers written in the 
.dynamic section need to be relocated.

> 
> This patch makes DL_RO_DYN_SECTION semi-dynamic.  We can also simply
> remove DL_RO_DYN_SECTION.
> 
> > From dc8d0fbc17024696cfa9e1ed8df2ecee4bad4696 Mon Sep 17 00:00:00 2001
> From: "H.J. Lu" <hjl.tools@gmail.com>
> Date: Thu, 16 Sep 2021 08:15:29 -0700
> Subject: [PATCH] ld.so: Change DL_RO_DYN_SECTION to semi-dynamic
> 
> ---
>  elf/dl-load.c              |  1 +
>  elf/dl-reloc-static-pie.c  | 10 ++++++++++
>  elf/get-dynamic-info.h     |  4 +---
>  elf/rtld.c                 |  2 ++
>  include/link.h             |  1 +
>  sysdeps/generic/ldsodefs.h | 12 +++++++-----
>  6 files changed, 22 insertions(+), 8 deletions(-)
> 
> diff --git a/elf/dl-load.c b/elf/dl-load.c
> index 650e4edc35..292e921292 100644
> --- a/elf/dl-load.c
> +++ b/elf/dl-load.c
> @@ -1149,6 +1149,7 @@ _dl_map_object_from_fd (const char *name, const char *origname, int fd,
>  		 such a segment to avoid a crash later.  */
>  	      l->l_ld = (void *) ph->p_vaddr;
>  	      l->l_ldnum = ph->p_memsz / sizeof (ElfW(Dyn));
> +	      l->l_ld_readonly = (ph->p_flags & PF_W) == 0;

One concern Florian had was that the read-only decision should be made 
based on the LOAD segment flags and not the DYNAMIC segment flags.  It 
works for vdso since those permissions are in sync as far as write is 
concerned (RX and R for LOAD and DYNAMIC respectively) but if we want to 
write to .dynamic when LOAD is RW (but DYNAMIC isn't for some reason) 
then we should look for the LOAD segment that overlaps with this.

>  	    }
>  	  break;
>  
> diff --git a/elf/dl-reloc-static-pie.c b/elf/dl-reloc-static-pie.c
> index d5bd2f31e9..d5fbeb137c 100644
> --- a/elf/dl-reloc-static-pie.c
> +++ b/elf/dl-reloc-static-pie.c
> @@ -40,6 +40,16 @@ _dl_relocate_static_pie (void)
>  
>    /* Read our own dynamic section and fill in the info array.  */
>    main_map->l_ld = ((void *) main_map->l_addr + elf_machine_dynamic ());
> +
> +  const ElfW(Phdr) *ph, *phdr = GL(dl_phdr);
> +  size_t phnum = GL(dl_phnum);
> +  for (ph = phdr; ph < &phdr[phnum]; ++ph)
> +    if (ph->p_type == PT_DYNAMIC)
> +      {
> +	main_map->l_ld_readonly = (ph->p_flags & PF_W) == 0;
> +	break;
> +      }
> +
>    elf_get_dynamic_info (main_map, NULL);

Same question here.

>  
>  # ifdef ELF_MACHINE_BEFORE_RTLD_RELOC
> diff --git a/elf/get-dynamic-info.h b/elf/get-dynamic-info.h
> index d8ec32377d..02dc281ec5 100644
> --- a/elf/get-dynamic-info.h
> +++ b/elf/get-dynamic-info.h
> @@ -71,9 +71,8 @@ elf_get_dynamic_info (struct link_map *l, ElfW(Dyn) *temp)
>  
>  #define DL_RO_DYN_TEMP_CNT	8
>  
> -#ifndef DL_RO_DYN_SECTION
>    /* Don't adjust .dynamic unnecessarily.  */
> -  if (l->l_addr != 0)
> +  if (l->l_addr != 0 && !l->l_ld_readonly)

This still has the concern that applications will incorrectly assume 
that these entries are relocated.  If we want to continue to load DSOs 
with read-only DYNAMIC, shouldn't we just allocate memory (like we do 
for setup_vdso, but dynamically per DSO) and write the relocations there 
instead?

>      {
>        ElfW(Addr) l_addr = l->l_addr;
>        int cnt = 0;
> @@ -109,7 +108,6 @@ elf_get_dynamic_info (struct link_map *l, ElfW(Dyn) *temp)
>  # undef ADJUST_DYN_INFO
>        assert (cnt <= DL_RO_DYN_TEMP_CNT);
>      }
> -#endif
>    if (info[DT_PLTREL] != NULL)
>      {
>  #if ELF_MACHINE_NO_RELA
> diff --git a/elf/rtld.c b/elf/rtld.c
> index 878e6480f4..c7818586a2 100644
> --- a/elf/rtld.c
> +++ b/elf/rtld.c
> @@ -463,6 +463,7 @@ _dl_start_final (void *arg, struct dl_start_final_info *info)
>  #ifndef DONT_USE_BOOTSTRAP_MAP
>    GL(dl_rtld_map).l_addr = info->l.l_addr;
>    GL(dl_rtld_map).l_ld = info->l.l_ld;
> +  GL(dl_rtld_map).l_ld_readonly = info->l.l_ld_readonly;
>    memcpy (GL(dl_rtld_map).l_info, info->l.l_info,
>  	  sizeof GL(dl_rtld_map).l_info);
>    GL(dl_rtld_map).l_mach = info->l.l_mach;
> @@ -1468,6 +1469,7 @@ dl_main (const ElfW(Phdr) *phdr,
>  	/* This tells us where to find the dynamic section,
>  	   which tells us everything we need to do.  */
>  	main_map->l_ld = (void *) main_map->l_addr + ph->p_vaddr;
> +	main_map->l_ld_readonly = (ph->p_flags & PF_W) == 0;
>  	break;
>        case PT_INTERP:
>  	/* This "interpreter segment" was used by the program loader to
> diff --git a/include/link.h b/include/link.h
> index 4af16cb596..963a9f0147 100644
> --- a/include/link.h
> +++ b/include/link.h
> @@ -205,6 +205,7 @@ struct link_map
>      unsigned int l_free_initfini:1; /* Nonzero if l_initfini can be
>  				       freed, ie. not allocated with
>  				       the dummy malloc in ld.so.  */
> +    unsigned int l_ld_readonly;	/* Nonzero if dynamic section is readonly.  */
>  
>      /* NODELETE status of the map.  Only valid for maps of type
>         lt_loaded.  Lazy binding sets l_nodelete_active directly,
> diff --git a/sysdeps/generic/ldsodefs.h b/sysdeps/generic/ldsodefs.h
> index fd67871f4b..3710d7ea79 100644
> --- a/sysdeps/generic/ldsodefs.h
> +++ b/sysdeps/generic/ldsodefs.h
> @@ -69,17 +69,19 @@ __BEGIN_DECLS
>     `ElfW(TYPE)' is used in place of `Elf32_TYPE' or `Elf64_TYPE'.  */
>  #define ELFW(type)	_ElfW (ELF, __ELF_NATIVE_CLASS, type)
>  
> +#ifndef DL_RO_DYN_SECTION
> +# define DL_RO_DYN_SECTION 0
> +#endif
> +
>  /* All references to the value of l_info[DT_PLTGOT],
>    l_info[DT_STRTAB], l_info[DT_SYMTAB], l_info[DT_RELA],
>    l_info[DT_REL], l_info[DT_JMPREL], and l_info[VERSYMIDX (DT_VERSYM)]
>    have to be accessed via the D_PTR macro.  The macro is needed since for
>    most architectures the entry is already relocated - but for some not
>    and we need to relocate at access time.  */
> -#ifdef DL_RO_DYN_SECTION
> -# define D_PTR(map, i) ((map)->i->d_un.d_ptr + (map)->l_addr)
> -#else
> -# define D_PTR(map, i) (map)->i->d_un.d_ptr
> -#endif
> +#define D_PTR(map, i) \
> +  ((map)->i->d_un.d_ptr \
> +   + (DL_RO_DYN_SECTION || (map)->l_ld_readonly ? (map)->l_addr : 0))

OK for vDSO since it doesn't get marked as readonly in setup_vdso.

>  
>  /* Result of the lookup functions and how to retrieve the base address.  */
>  typedef struct link_map *lookup_t;
> -- 
> 2.31.1

  reply	other threads:[~2021-09-16 16:45 UTC|newest]

Thread overview: 41+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2021-09-14 19:09 Siddhesh Poyarekar
2021-09-14 19:15 ` Florian Weimer
2021-09-15  1:14   ` Siddhesh Poyarekar
2021-09-15 14:35 ` H.J. Lu
2021-09-15 15:42   ` Siddhesh Poyarekar
2021-09-15 16:13     ` H.J. Lu
2021-09-15 16:24       ` Siddhesh Poyarekar
2021-09-15 16:34         ` H.J. Lu
2021-09-16  1:43           ` Siddhesh Poyarekar
2021-09-16  2:23             ` H.J. Lu
2021-09-16  3:46               ` Siddhesh Poyarekar
2021-09-16  4:26                 ` H.J. Lu
2021-09-16  4:28                   ` Siddhesh Poyarekar
2021-09-16  4:30                     ` H.J. Lu
2021-09-16  4:48               ` Florian Weimer
2021-09-16  5:36                 ` Siddhesh Poyarekar
2021-09-16  5:46                   ` Florian Weimer
2021-09-16  6:04                     ` Siddhesh Poyarekar
2021-09-16 14:11                 ` Carlos O'Donell
2021-09-16 15:18                   ` H.J. Lu
2021-09-16 16:45                     ` Siddhesh Poyarekar [this message]
2021-09-16 17:38                       ` H.J. Lu
2021-09-16 17:58                         ` Siddhesh Poyarekar
2021-09-16 22:11                           ` H.J. Lu
2021-09-17  2:47                             ` Siddhesh Poyarekar
2021-09-17  2:59                               ` H.J. Lu
2021-09-17  3:36                                 ` Siddhesh Poyarekar
2021-09-17  3:42                                   ` H.J. Lu
2021-09-17  3:44                                     ` Siddhesh Poyarekar
2021-09-17  3:44                                   ` Florian Weimer
2021-09-17  3:51                                     ` Siddhesh Poyarekar
2021-09-16 18:03                         ` Florian Weimer
2021-09-16 22:14                           ` H.J. Lu
2021-09-17  2:58                           ` Siddhesh Poyarekar
2021-09-17  3:46                             ` Florian Weimer
2021-09-17  4:00                               ` Florian Weimer
2021-09-17  4:12                                 ` [PATCH] ld.so: Remove DL_RO_DYN_SECTION H.J. Lu
2021-09-17  6:54                                   ` David Abdurachmanov
2021-09-17  9:01                                   ` Siddhesh Poyarekar
2021-09-17 15:40                                     ` H.J. Lu
2021-10-14 12:36                               ` [PATCH] ld.so: Handle read-only dynamic section gracefully [BZ #28340] Maciej W. Rozycki

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=ec58a4ac-7414-c06d-d77a-42e00aaccf94@sourceware.org \
    --to=siddhesh@sourceware.org \
    --cc=carlos@redhat.com \
    --cc=fweimer@redhat.com \
    --cc=hjl.tools@gmail.com \
    --cc=libc-alpha@sourceware.org \
    /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).