Re: [PATCH] elf: Support multiple PT_GNU_RELRO segments

Florian Weimer <[email protected]>
Newsgroups gmane.comp.lib.glibc.alpha
Message-ID <[email protected]>
* Adhemerval Zanella:

> diff --git a/elf/tst-relro-multi-notes.S b/elf/tst-relro-multi-notes.S
> new file mode 100644
> index 00000000000..c66ad8e0baf
> --- /dev/null
> +++ b/elf/tst-relro-multi-notes.S
> @@ -0,0 +1,46 @@

> +/* Assembly is used because compilers emit SHT_PROGBITS for
> +   __attribute__ ((section)) definitions and gas might warns when its
> +   name-based heuristic retypes .note.* sections; and  note sections
> +   must not have the SHF_WRITE flag for the same reason.  */

typo: might warn[]

> diff --git a/elf/tst-relro-symbols.py b/elf/tst-relro-symbols.py
> index ffbe9958fed..7633a8dfb84 100644
> --- a/elf/tst-relro-symbols.py
> +++ b/elf/tst-relro-symbols.py
> @@ -32,25 +32,31 @@ sys.path.append(os.path.join(

These changes could go in first.

> -def find_relro(path: str, img: glibcelf.Image) -> (int, int):
> -    """Discover the address range of the PT_GNU_RELRO segment."""
> +def find_relro(path: str, img: glibcelf.Image) -> list:
> +    """Discover the address ranges of the PT_GNU_RELRO segments."""
> +    regions = []
>      for phdr in img.phdrs():
>          if phdr.p_type == glibcelf.Pt.PT_GNU_RELRO:
>              # The computation is not entirely accurate because
>              # _dl_protect_relro in elf/dl-reloc.c rounds both the
>              # start end and downwards using the run-time page size.
> +            regions.append((phdr.p_vaddr, phdr.p_vaddr + phdr.p_memsz))

That's:

    regions = [(phdr.p_vaddr, phdr.p_vaddr + phdr.p_memsz)
               for phdr in img.phdrs()]

> diff --git a/scripts/tst-elf-edit.py b/scripts/tst-elf-edit.py
> index 07fa7e90f55..4c5e73e2f0e 100644
> --- a/scripts/tst-elf-edit.py
> +++ b/scripts/tst-elf-edit.py
> @@ -47,7 +47,11 @@ ET_EXEC=2
>  ET_DYN=3
>  
>  PT_LOAD=1
> +PT_NOTE=4
>  PT_TLS=7
> +PT_GNU_RELRO=0x6474e552
> +
> +PF_W=2
>  
>  def elf_types_fmts(e_ident):
>      endian = '<' if e_ident[EI_DATA] == ELFDATA2LSB else '>'
> @@ -156,6 +160,34 @@ def elf_edit_maximize_tls_size(phdr, elfclass):
>      else:
>          phdr.p_memsz = 1 << 63
>  
> +def elf_edit_note_to_relro(f, e_ident, ehdr, expected):
> +    phdrs = []
> +    for i in range(0, ehdr.e_phnum):
> +        phdr = Elf_Phdr(e_ident)
> +        f.seek(ehdr.e_phoff + i * phdr.len)
> +        phdr.read(f)
> +        phdrs.append(phdr)
> +
> +    wr_loads = [(p.p_vaddr, p.p_vaddr + p.p_memsz) for p in phdrs
> +                if p.p_type == PT_LOAD and (p.p_flags & PF_W) != 0]
> +
> +    converted = 0
> +    for i, phdr in enumerate(phdrs):
> +        if phdr.p_type != PT_NOTE:
> +            continue
> +        if not any(lo <= phdr.p_vaddr < hi for lo, hi in wr_loads):
> +            continue
> +        phdr.p_type = PT_GNU_RELRO
> +        # Match the alignment the linker uses for PT_GNU_RELRO.
> +        phdr.p_align = 1
> +        f.seek(ehdr.e_phoff + i * phdr.len)
> +        phdr.write(f)
> +        converted += 1

I think it would slightly less hackish if we looked at the note
*contents* to trigger the conversion.  As in, the note itself could be
a marker that indicates that it should be turned into PT_GNU_RELRO.
I think this is more robust if the toolchain generates its own notes.

> diff --git a/sysdeps/generic/ldsodefs.h b/sysdeps/generic/ldsodefs.h
> index 305ca6e0df2..0d5f2a2c96d 100644
> --- a/sysdeps/generic/ldsodefs.h
> +++ b/sysdeps/generic/ldsodefs.h

> @@ -1037,6 +1038,24 @@ void _dl_relocate_object_no_relro (struct link_map *map,
>  /* Protect PT_GNU_RELRO area.  */
>  extern void _dl_protect_relro (struct link_map *map) attribute_hidden;
>  
> +struct dl_relro_range
> +{
> +  ElfW(Addr) start;
> +  ElfW(Addr) end;
> +};
> +
> +/* Compute the range for the PT_GNU_RELRO segment PH of map L.  */
> +static inline struct dl_relro_range
> +_dl_relro_range (const struct link_map *l, const ElfW(Phdr) *ph)
> +{
> +  return (struct dl_relro_range)
> +    {
> +      .start = ALIGN_DOWN (l->l_addr + ph->p_vaddr, GLRO(dl_pagesize)),
> +      .end = ALIGN_DOWN (l->l_addr + ph->p_vaddr + ph->p_memsz,
> +			 GLRO(dl_pagesize)),
> +    };
> +}

I think we should finally add a comment why it's using ALIGN_DOWN
twice.  It's even possible that with multi-RELRO, this becomes
downright buggy on systems with multiple page sizes.
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.