diff mbox series

[v3,1/3] elf: Add GNU_PROPERTY_MEMORY_SEAL gnu property

Message ID 20241016170435.1404114-2-adhemerval.zanella@linaro.org
State New
Headers show
Series elf: Add GNU_PROPERTY_MEMORY_SEAL gnu property | expand

Commit Message

Adhemerval Zanella Netto Oct. 16, 2024, 5:01 p.m. UTC
On a glibc recent proposal [1] to add Linux mseal support [2],
Mike Hommey raised that this feature might potentially break Firefox
on Linux.  The issue is Firefox is built with DT_RELR support, and
post-processed with a tool to both remove the GLIBC_ABI_DT_RELR
dependency and instrument the binaries to apply the relocation
themselves so they can deploy Firefox regardless if loader supports
DT_RELR or not (some more details at [3]).

To accomplish it, the instrumentation mimics the dynamic loader and
temporarily undo the RELRO segment to be able to apply those
relocations, and redo it afterward.  This will break if mseal is
applied as default.

The GNU_PROPERTY_MEMORY_SEAL gnu property is a way to mark such
objects to no be sealed by glibc.  When linked with
-Wl,-z,noseal (the default), glibc will not seal either the binary or
the shared library.

The new property is ignored if present on ET_REL objects, and only
added on ET_EXEC/ET_DYN if the linker option is used.  A gnu property
is used instead of DT_FLAGS_1 flag to allow memory sealing to work
with ET_EXEC without PT_DYNAMIC support (at least on glibc some ports
still do no support static-pie).

[1] https://sourceware.org/pipermail/libc-alpha/2024-June/157359.html
[2] https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=8be7258aad44b5e25977a98db136f677fa6f4370
[3] https://sourceware.org/pipermail/libc-alpha/2024-June/157668.html
[4] https://glandium.org/blog/?p=4297
---
 bfd/elf-properties.c                  | 100 ++++++++++++++++++++------
 bfd/elfxx-x86.c                       |   3 +-
 binutils/readelf.c                    |   6 ++
 include/bfdlink.h                     |   3 +
 include/elf/common.h                  |   1 +
 ld/NEWS                               |   3 +
 ld/emultempl/elf.em                   |   4 ++
 ld/ld.texi                            |   8 +++
 ld/lexsup.c                           |   4 ++
 ld/testsuite/ld-elf/property-seal-1.d |  16 +++++
 ld/testsuite/ld-elf/property-seal-1.s |  11 +++
 ld/testsuite/ld-elf/property-seal-2.d |  17 +++++
 ld/testsuite/ld-elf/property-seal-3.d |  16 +++++
 ld/testsuite/ld-elf/property-seal-4.d |  16 +++++
 ld/testsuite/ld-elf/property-seal-5.d |  15 ++++
 ld/testsuite/ld-elf/property-seal-6.d |  16 +++++
 ld/testsuite/ld-elf/property-seal-7.d |  14 ++++
 ld/testsuite/ld-elf/property-seal-8.d |  15 ++++
 18 files changed, 246 insertions(+), 22 deletions(-)
 create mode 100644 ld/testsuite/ld-elf/property-seal-1.d
 create mode 100644 ld/testsuite/ld-elf/property-seal-1.s
 create mode 100644 ld/testsuite/ld-elf/property-seal-2.d
 create mode 100644 ld/testsuite/ld-elf/property-seal-3.d
 create mode 100644 ld/testsuite/ld-elf/property-seal-4.d
 create mode 100644 ld/testsuite/ld-elf/property-seal-5.d
 create mode 100644 ld/testsuite/ld-elf/property-seal-6.d
 create mode 100644 ld/testsuite/ld-elf/property-seal-7.d
 create mode 100644 ld/testsuite/ld-elf/property-seal-8.d

Comments

H.J. Lu Oct. 16, 2024, 9:39 p.m. UTC | #1
On Thu, Oct 17, 2024, 1:04 AM Adhemerval Zanella <
adhemerval.zanella@linaro.org> wrote:

> On a glibc recent proposal [1] to add Linux mseal support [2],
> Mike Hommey raised that this feature might potentially break Firefox
> on Linux.  The issue is Firefox is built with DT_RELR support, and
> post-processed with a tool to both remove the GLIBC_ABI_DT_RELR
> dependency and instrument the binaries to apply the relocation
> themselves so they can deploy Firefox regardless if loader supports
> DT_RELR or not (some more details at [3]).
>
> To accomplish it, the instrumentation mimics the dynamic loader and
> temporarily undo the RELRO segment to be able to apply those
> relocations, and redo it afterward.  This will break if mseal is
> applied as default.
>
> The GNU_PROPERTY_MEMORY_SEAL gnu property is a way to mark such
> objects to no be sealed by glibc.  When linked with
> -Wl,-z,noseal (the default), glibc will not seal either the binary or
> the shared library.
>
> The new property is ignored if present on ET_REL objects, and only
> added on ET_EXEC/ET_DYN if the linker option is used.  A gnu property
> is used instead of DT_FLAGS_1 flag to allow memory sealing to work
> with ET_EXEC without PT_DYNAMIC support (at least on glibc some ports
> still do no support static-pie).
>
> [1] https://sourceware.org/pipermail/libc-alpha/2024-June/157359.html
> [2]
> https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=8be7258aad44b5e25977a98db136f677fa6f4370
> [3] https://sourceware.org/pipermail/libc-alpha/2024-June/157668.html
> [4] https://glandium.org/blog/?p=4297
> ---
>  bfd/elf-properties.c                  | 100 ++++++++++++++++++++------
>  bfd/elfxx-x86.c                       |   3 +-
>  binutils/readelf.c                    |   6 ++
>  include/bfdlink.h                     |   3 +
>  include/elf/common.h                  |   1 +
>  ld/NEWS                               |   3 +
>  ld/emultempl/elf.em                   |   4 ++
>  ld/ld.texi                            |   8 +++
>  ld/lexsup.c                           |   4 ++
>  ld/testsuite/ld-elf/property-seal-1.d |  16 +++++
>  ld/testsuite/ld-elf/property-seal-1.s |  11 +++
>  ld/testsuite/ld-elf/property-seal-2.d |  17 +++++
>  ld/testsuite/ld-elf/property-seal-3.d |  16 +++++
>  ld/testsuite/ld-elf/property-seal-4.d |  16 +++++
>  ld/testsuite/ld-elf/property-seal-5.d |  15 ++++
>  ld/testsuite/ld-elf/property-seal-6.d |  16 +++++
>  ld/testsuite/ld-elf/property-seal-7.d |  14 ++++
>  ld/testsuite/ld-elf/property-seal-8.d |  15 ++++
>  18 files changed, 246 insertions(+), 22 deletions(-)
>  create mode 100644 ld/testsuite/ld-elf/property-seal-1.d
>  create mode 100644 ld/testsuite/ld-elf/property-seal-1.s
>  create mode 100644 ld/testsuite/ld-elf/property-seal-2.d
>  create mode 100644 ld/testsuite/ld-elf/property-seal-3.d
>  create mode 100644 ld/testsuite/ld-elf/property-seal-4.d
>  create mode 100644 ld/testsuite/ld-elf/property-seal-5.d
>  create mode 100644 ld/testsuite/ld-elf/property-seal-6.d
>  create mode 100644 ld/testsuite/ld-elf/property-seal-7.d
>  create mode 100644 ld/testsuite/ld-elf/property-seal-8.d
>
> diff --git a/bfd/elf-properties.c b/bfd/elf-properties.c
> index ee8bd37f2bd..c6acdb60ba2 100644
> --- a/bfd/elf-properties.c
> +++ b/bfd/elf-properties.c
> @@ -177,6 +177,20 @@ _bfd_elf_parse_gnu_properties (bfd *abfd,
> Elf_Internal_Note *note)
>               prop->pr_kind = property_number;
>               goto next;
>
> +           case GNU_PROPERTY_MEMORY_SEAL:
> +             if (datasz != 0)
> +               {
> +                 _bfd_error_handler
> +                   (_("warning: %pB: corrupt memory sealing size: 0x%x"),
> +                    abfd, datasz);
> +                 /* Clear all properties.  */
> +                 elf_properties (abfd) = NULL;
> +                 return false;
> +               }
> +             prop = _bfd_elf_get_property (abfd, type, datasz);
> +             prop->pr_kind = property_number;
> +             goto next;
> +
>             default:
>               if ((type >= GNU_PROPERTY_UINT32_AND_LO
>                    && type <= GNU_PROPERTY_UINT32_AND_HI)
> @@ -258,6 +272,12 @@ elf_merge_gnu_properties (struct bfd_link_info *info,
> bfd *abfd, bfd *bbfd,
>          be added to ABFD.  */
>        return aprop == NULL;
>
> +    case GNU_PROPERTY_MEMORY_SEAL:
> +      /* Memory seal is controlled only by the linker.  */
> +      if (aprop != NULL)
> +       aprop->pr_kind = property_remove;
> +      return true;

+
>      default:
>        updated = false;
>        if (pr_type >= GNU_PROPERTY_UINT32_OR_LO
> @@ -607,6 +627,33 @@ elf_write_gnu_properties (struct bfd_link_info *info,
>      }
>  }
>
> +static asection *
> +_bfd_elf_link_create_gnu_property_sec (struct bfd_link_info *info, bfd
> *elf_bfd,

+                                      unsigned int elfclass)
> +{
> +  asection *sec;
> +
> +  sec = bfd_make_section_with_flags (elf_bfd,
> +                                    NOTE_GNU_PROPERTY_SECTION_NAME,
> +                                    (SEC_ALLOC
> +                                     | SEC_LOAD
> +                                     | SEC_IN_MEMORY
> +                                     | SEC_READONLY
> +                                     | SEC_HAS_CONTENTS
> +                                     | SEC_DATA));
> +  if (sec == NULL)
> +    info->callbacks->einfo (_("%F%P: failed to create GNU property
> section\n"));
> +
> +  if (!bfd_set_section_alignment (sec,
> +                                 elfclass == ELFCLASS64 ? 3 : 2))
> +    info->callbacks->einfo (_("%F%pA: failed to align section\n"),
> +                           sec);
> +
> +  elf_section_type (sec) = SHT_NOTE;
> +  return sec;
> +}
> +
> +
>  /* Set up GNU properties.  Return the first relocatable ELF input with
>     GNU properties if found.  Otherwise, return NULL.  */
>
> @@ -656,23 +703,7 @@ _bfd_elf_link_setup_gnu_properties (struct
> bfd_link_info *info)
>        /* Support -z indirect-extern-access.  */
>        if (first_pbfd == NULL)
>         {
> -         sec = bfd_make_section_with_flags (elf_bfd,
> -
> NOTE_GNU_PROPERTY_SECTION_NAME,
> -                                            (SEC_ALLOC
> -                                             | SEC_LOAD
> -                                             | SEC_IN_MEMORY
> -                                             | SEC_READONLY
> -                                             | SEC_HAS_CONTENTS
> -                                             | SEC_DATA));
> -         if (sec == NULL)
> -           info->callbacks->einfo (_("%F%P: failed to create GNU property
> section\n"));
> -
> -         if (!bfd_set_section_alignment (sec,
> -                                         elfclass == ELFCLASS64 ? 3 : 2))
> -           info->callbacks->einfo (_("%F%pA: failed to align section\n"),
> -                                   sec);
> -
> -         elf_section_type (sec) = SHT_NOTE;
> +         sec = _bfd_elf_link_create_gnu_property_sec (info, elf_bfd,
> elfclass);
>           first_pbfd = elf_bfd;
>           has_properties = true;
>         }
> @@ -690,10 +721,6 @@ _bfd_elf_link_setup_gnu_properties (struct
> bfd_link_info *info)
>           |= GNU_PROPERTY_1_NEEDED_INDIRECT_EXTERN_ACCESS;
>      }
>
> -  /* Do nothing if there is no .note.gnu.property section.  */
> -  if (!has_properties)
> -    return NULL;
>

This change looks odd.  Now we merge properties even when
there is no property.

-
>    /* Merge .note.gnu.property sections.  */
>    info->callbacks->minfo (_("\n"));
>    info->callbacks->minfo (_("Merging program properties\n"));
> @@ -737,6 +764,37 @@ _bfd_elf_link_setup_gnu_properties (struct
> bfd_link_info *info)
>           }
>        }
>
> +  if (elf_bfd != NULL)
> +    {
> +      if (info->memory_seal)
> +       {
> +         /* Support -z no-memory-seal.  */
> +         if (first_pbfd == NULL)
> +           {
> +             sec = _bfd_elf_link_create_gnu_property_sec (info, elf_bfd,
> elfclass);
> +             first_pbfd = elf_bfd;
> +             has_properties = true;
> +           }
> +
> +         p = _bfd_elf_get_property (first_pbfd, GNU_PROPERTY_MEMORY_SEAL,
> 0);
> +         if (p->pr_kind == property_unknown)
>

What are possible kind values? Do we have tests to cover them?

+           {
> +             /* Create GNU_PROPERTY_NO_MEMORY_SEAL.  */
> +             p->u.number = GNU_PROPERTY_MEMORY_SEAL;
> +             p->pr_kind = property_number;
> +           }
> +         else
> +           p->u.number |= GNU_PROPERTY_MEMORY_SEAL;
> +       }
> +      else
> +       elf_find_and_remove_property (&elf_properties (elf_bfd),
> +                                     GNU_PROPERTY_MEMORY_SEAL, true);
>

Shouldn't the input seal property have been removed already?

+    }
> +
> +  /* Do nothing if there is no .note.gnu.property section.  */
> +  if (!has_properties)
> +    return NULL;
> +
>    /* Rewrite .note.gnu.property section so that GNU properties are
>       always sorted by type even if input GNU properties aren't sorted.  */
>    if (first_pbfd != NULL)
> diff --git a/bfd/elfxx-x86.c b/bfd/elfxx-x86.c
> index dd951b91f50..8a4405c8a79 100644
> --- a/bfd/elfxx-x86.c
> +++ b/bfd/elfxx-x86.c
> @@ -4815,7 +4815,8 @@ _bfd_x86_elf_link_fixup_gnu_properties
>    for (p = *listp; p; p = p->next)
>      {
>        unsigned int type = p->property.pr_type;
> -      if (type == GNU_PROPERTY_X86_COMPAT_ISA_1_USED
> +      if (type == GNU_PROPERTY_MEMORY_SEAL
> +         || type == GNU_PROPERTY_X86_COMPAT_ISA_1_USED
>           || type == GNU_PROPERTY_X86_COMPAT_ISA_1_NEEDED
>           || (type >= GNU_PROPERTY_X86_UINT32_AND_LO
>               && type <= GNU_PROPERTY_X86_UINT32_AND_HI)
> diff --git a/binutils/readelf.c b/binutils/readelf.c
> index 0f8dc1b9716..bf25425bb8d 100644
> --- a/binutils/readelf.c
> +++ b/binutils/readelf.c
> @@ -21464,6 +21464,12 @@ print_gnu_property_note (Filedata * filedata,
> Elf_Internal_Note * pnote)
>                 printf (_("<corrupt length: %#x> "), datasz);
>               goto next;
>
> +           case GNU_PROPERTY_MEMORY_SEAL:
> +             printf ("memory seal ");
> +             if (datasz)
> +               printf (_("<corrupt length: %#x> "), datasz);
> +             goto next;
> +
>             default:
>               if ((type >= GNU_PROPERTY_UINT32_AND_LO
>                    && type <= GNU_PROPERTY_UINT32_AND_HI)
> diff --git a/include/bfdlink.h b/include/bfdlink.h
> index f802ec627ef..8b9e391e6ff 100644
> --- a/include/bfdlink.h
> +++ b/include/bfdlink.h
> @@ -429,6 +429,9 @@ struct bfd_link_info
>    /* TRUE if only one read-only, non-code segment should be created.  */
>    unsigned int one_rosegment: 1;
>
> +  /* TRUE if GNU_PROPERTY_MEMORY_SEAL should be generated.  */
> +  unsigned int memory_seal: 1;
> +
>    /* Nonzero if .eh_frame_hdr section and PT_GNU_EH_FRAME ELF segment
>       should be created.  1 for DWARF2 tables, 2 for compact tables.  */
>    unsigned int eh_frame_hdr_type: 2;
> diff --git a/include/elf/common.h b/include/elf/common.h
> index c9920e7731a..8938e2f4754 100644
> --- a/include/elf/common.h
> +++ b/include/elf/common.h
> @@ -890,6 +890,7 @@
>  /* Values used in GNU .note.gnu.property notes (NT_GNU_PROPERTY_TYPE_0).
> */
>  #define GNU_PROPERTY_STACK_SIZE                        1
>  #define GNU_PROPERTY_NO_COPY_ON_PROTECTED      2
> +#define GNU_PROPERTY_MEMORY_SEAL               3
>
>  /* A 4-byte unsigned integer property: A bit is set if it is set in all
>     relocatable inputs.  */
> diff --git a/ld/NEWS b/ld/NEWS
> index 1f14dd6bc77..4a28592fa32 100644
> --- a/ld/NEWS
> +++ b/ld/NEWS
> @@ -23,6 +23,9 @@ Changes in 2.43:
>
>  * Add -plugin-save-temps to store plugin intermediate files permanently.
>
> +* Add -z memory-seal/-z nomemory-seal options to ELF linker to mark the
> +  object to memory sealed.
> +
>  Changes in 2.42:
>
>  * Add -z mark-plt/-z nomark-plt options to x86-64 ELF linker to mark PLT
> diff --git a/ld/emultempl/elf.em b/ld/emultempl/elf.em
> index 2e865728587..ccd43531237 100644
> --- a/ld/emultempl/elf.em
> +++ b/ld/emultempl/elf.em
> @@ -1075,6 +1075,10 @@ fragment <<EOF
>         link_info.combreloc = false;
>        else if (strcmp (optarg, "nocopyreloc") == 0)
>         link_info.nocopyreloc = true;
> +      else if (strcmp (optarg, "memory-seal") == 0)
> +       link_info.memory_seal = true;
> +      else if (strcmp (optarg, "nomemory-seal") == 0)
> +       link_info.memory_seal = false;
>  EOF
>  if test -n "$COMMONPAGESIZE"; then
>  fragment <<EOF
> diff --git a/ld/ld.texi b/ld/ld.texi
> index 90182c436ec..b8957d3027e 100644
> --- a/ld/ld.texi
> +++ b/ld/ld.texi
> @@ -1591,6 +1591,14 @@ Disable relocation overflow check.  This can be
> used to disable
>  relocation overflow check if there will be no dynamic relocation
>  overflow at run-time.  Supported for x86_64.
>
> +@item memory-seal
> +@item nomemory-seal
> +Instruct the executable or shared library that the all PT_LOAD segments
> +should be sealed to avoid further manipulation (such as changing the
> +protection flags, the segment size, or remove the mapping).
> +This is a security hardening that requires system support.  This
> +generates GNU_PROPERTY_MEMORY_SEAL in .note.gnu.property section
> +
>  @item now
>  When generating an executable or shared library, mark it to tell the
>  dynamic linker to resolve all symbols when the program is started, or
> diff --git a/ld/lexsup.c b/ld/lexsup.c
> index 8982073bc91..d82d3631a09 100644
> --- a/ld/lexsup.c
> +++ b/ld/lexsup.c
> @@ -2271,6 +2271,10 @@ elf_shlib_list_options (FILE *file)
>        fprintf (file, _("\
>    -z textoff                  Don't treat DT_TEXTREL in output as
> error\n"));
>      }
> +  fprintf (file, _("\
> +  -z memory-seal              Mark object be memory sealed\n"));
> +  fprintf (file, _("\
> +  -z nomemory-seal            Don't mark oject to be memory sealed
> (default)\n"));
>  }
>
>  static void
> diff --git a/ld/testsuite/ld-elf/property-seal-1.d
> b/ld/testsuite/ld-elf/property-seal-1.d
> new file mode 100644
> index 00000000000..a0b1feedf31
> --- /dev/null
> +++ b/ld/testsuite/ld-elf/property-seal-1.d
> @@ -0,0 +1,16 @@
> +# Check if a GNU_PROPERTY_MEMORY_SEAL on a ET_REL is not replicated on
> +# ET_DYN.
> +#source: property-seal-1.s
> +#as: --generate-missing-build-notes=no
> +#ld: -shared
> +#readelf: -n
> +#xfail: ![check_shared_lib_support]
> +#notarget: am33_2.0-*-* hppa*-*-hpux* mn10300-*-*
> +# Assembly source file for the HPPA assembler is renamed and modifed by
> +# sed.  mn10300 has relocations in .note.gnu.property section which
> +# elf_parse_notes doesn't support.
> +
> +#failif
> +#...
> +Displaying notes found in: .note.gnu.property
>

Please check if there no seal property instead. This applies to
all such tests.

+#pass
> diff --git a/ld/testsuite/ld-elf/property-seal-1.s
> b/ld/testsuite/ld-elf/property-seal-1.s
> new file mode 100644
> index 00000000000..aa28a3d0516
> --- /dev/null
> +++ b/ld/testsuite/ld-elf/property-seal-1.s
> @@ -0,0 +1,11 @@
> +       .section ".note.gnu.property", "a"
> +       .p2align ALIGN
> +       .long 1f - 0f           /* name length */
> +       .long 3f - 2f           /* data length */
> +       .long 5                 /* note type */
> +0:     .asciz "GNU"            /* vendor name */
> +1:
> +       .p2align ALIGN
> +2:     .long 3                 /* pr_type.  */
> +       .long 0                 /* pr_datasz.  */
> +3:
> diff --git a/ld/testsuite/ld-elf/property-seal-2.d
> b/ld/testsuite/ld-elf/property-seal-2.d
> new file mode 100644
> index 00000000000..ebdaa623e0c
> --- /dev/null
> +++ b/ld/testsuite/ld-elf/property-seal-2.d
> @@ -0,0 +1,17 @@
> +# Check if a GNU_PROPERTY_MEMORY_SEAL on a ET_REL is not replicated on
> +# ET_DYN.
> +#source: empty.s
> +#source: property-seal-1.s
> +#as: --generate-missing-build-notes=no
> +#ld: -shared
> +#readelf: -n
> +#xfail: ![check_shared_lib_support]
> +#notarget: am33_2.0-*-* hppa*-*-hpux* mn10300-*-*
> +# Assembly source file for the HPPA assembler is renamed and modifed by
> +# sed.  mn10300 has relocations in .note.gnu.property section which
> +# elf_parse_notes doesn't support.
> +
> +#failif
> +#...
> +Displaying notes found in: .note.gnu.property
> +#pass
> diff --git a/ld/testsuite/ld-elf/property-seal-3.d
> b/ld/testsuite/ld-elf/property-seal-3.d
> new file mode 100644
> index 00000000000..969729ee0f4
> --- /dev/null
> +++ b/ld/testsuite/ld-elf/property-seal-3.d
> @@ -0,0 +1,16 @@
> +# Check if a GNU_PROPERTY_MEMORY_SEAL on a ET_REL is not replicated on
> +# ET_EXEC.
> +#source: property-seal-1.s
> +#as: --generate-missing-build-notes=no
> +#ld: -e _start
> +#warning: .*: warning: cannot find entry symbol .*
> +#readelf: -n
> +#notarget: am33_2.0-*-* hppa*-*-hpux* mn10300-*-*
> +# Assembly source file for the HPPA assembler is renamed and modifed by
> +# sed.  mn10300 has relocations in .note.gnu.property section which
> +# elf_parse_notes doesn't support.
> +
> +#failif
> +#...
> +Displaying notes found in: .note.gnu.property
> +#pass
> diff --git a/ld/testsuite/ld-elf/property-seal-4.d
> b/ld/testsuite/ld-elf/property-seal-4.d
> new file mode 100644
> index 00000000000..3dd990dd68a
> --- /dev/null
> +++ b/ld/testsuite/ld-elf/property-seal-4.d
> @@ -0,0 +1,16 @@
> +# Check if a GNU_PROPERTY_MEMORY_SEAL on a ET_REL is not replicated on
> +# ET_EXEC.
> +#source: empty.s
> +#source: property-seal-1.s
> +#as: --generate-missing-build-notes=no
> +#ld: -e _start
> +#readelf: -n
> +#notarget: am33_2.0-*-* hppa*-*-hpux* mn10300-*-*
> +# Assembly source file for the HPPA assembler is renamed and modifed by
> +# sed.  mn10300 has relocations in .note.gnu.property section which
> +# elf_parse_notes doesn't support.
> +
> +#failif
> +#...
> +Displaying notes found in: .note.gnu.property
> +#pass
> diff --git a/ld/testsuite/ld-elf/property-seal-5.d
> b/ld/testsuite/ld-elf/property-seal-5.d
> new file mode 100644
> index 00000000000..0b92a8eb6eb
> --- /dev/null
> +++ b/ld/testsuite/ld-elf/property-seal-5.d
> @@ -0,0 +1,15 @@
> +#source: empty.s
> +#ld: -shared -z memory-seal
> +#readelf: -n
> +#xfail: ![check_shared_lib_support]
> +#notarget: am33_2.0-*-* hppa*-*-hpux* mn10300-*-*
> +# Assembly source file for the HPPA assembler is renamed and modifed by
> +# sed.  mn10300 has relocations in .note.gnu.property section which
> +# elf_parse_notes doesn't support.
> +
> +#...
> +Displaying notes found in: .note.gnu.property
> +[      ]+Owner[        ]+Data size[    ]+Description
> +  GNU                  0x[0-9a-f]+     NT_GNU_PROPERTY_TYPE_0
> +      Properties: memory seal


There may be other properties.  X86 assembler
may be configured to generate instruction
info property note by default.
This applies to other such tests.


> +#pass
> diff --git a/ld/testsuite/ld-elf/property-seal-6.d
> b/ld/testsuite/ld-elf/property-seal-6.d
> new file mode 100644
> index 00000000000..725911acae7
> --- /dev/null
> +++ b/ld/testsuite/ld-elf/property-seal-6.d
> @@ -0,0 +1,16 @@
> +#source: empty.s
> +#source: property-seal-1.s
> +#ld: -shared -z memory-seal
> +#readelf: -n
> +#xfail: ![check_shared_lib_support]
> +#notarget: am33_2.0-*-* hppa*-*-hpux* mn10300-*-*
> +# Assembly source file for the HPPA assembler is renamed and modifed by
> +# sed.  mn10300 has relocations in .note.gnu.property section which
> +# elf_parse_notes doesn't support.
> +
> +#...
> +Displaying notes found in: .note.gnu.property
> +[      ]+Owner[        ]+Data size[    ]+Description
> +  GNU                  0x[0-9a-f]+     NT_GNU_PROPERTY_TYPE_0
> +      Properties: memory seal
> +#pass
> diff --git a/ld/testsuite/ld-elf/property-seal-7.d
> b/ld/testsuite/ld-elf/property-seal-7.d
> new file mode 100644
> index 00000000000..12339e83ebd
> --- /dev/null
> +++ b/ld/testsuite/ld-elf/property-seal-7.d
> @@ -0,0 +1,14 @@
> +#source: empty.s
> +#ld: -z memory-seal
> +#readelf: -n
> +#notarget: am33_2.0-*-* hppa*-*-hpux* mn10300-*-*
> +# Assembly source file for the HPPA assembler is renamed and modifed by
> +# sed.  mn10300 has relocations in .note.gnu.property section which
> +# elf_parse_notes doesn't support.
> +
> +#...
> +Displaying notes found in: .note.gnu.property
> +[      ]+Owner[        ]+Data size[    ]+Description
> +  GNU                  0x[0-9a-f]+     NT_GNU_PROPERTY_TYPE_0
> +      Properties: memory seal
> +#pass
> diff --git a/ld/testsuite/ld-elf/property-seal-8.d
> b/ld/testsuite/ld-elf/property-seal-8.d
> new file mode 100644
> index 00000000000..0c4c4e4907e
> --- /dev/null
> +++ b/ld/testsuite/ld-elf/property-seal-8.d
> @@ -0,0 +1,15 @@
> +#source: empty.s
> +#source: property-seal-1.s
> +#ld: -z memory-seal
> +#readelf: -n
> +#notarget: am33_2.0-*-* hppa*-*-hpux* mn10300-*-*
> +# Assembly source file for the HPPA assembler is renamed and modifed by
> +# sed.  mn10300 has relocations in .note.gnu.property section which
> +# elf_parse_notes doesn't support.
> +
> +#...
> +Displaying notes found in: .note.gnu.property
> +[      ]+Owner[        ]+Data size[    ]+Description
> +  GNU                  0x[0-9a-f]+     NT_GNU_PROPERTY_TYPE_0
> +      Properties: memory seal
> +#pass
> --
> 2.43.0
>
>
> H.J.
diff mbox series

Patch

diff --git a/bfd/elf-properties.c b/bfd/elf-properties.c
index ee8bd37f2bd..c6acdb60ba2 100644
--- a/bfd/elf-properties.c
+++ b/bfd/elf-properties.c
@@ -177,6 +177,20 @@  _bfd_elf_parse_gnu_properties (bfd *abfd, Elf_Internal_Note *note)
 	      prop->pr_kind = property_number;
 	      goto next;
 
+	    case GNU_PROPERTY_MEMORY_SEAL:
+	      if (datasz != 0)
+		{
+		  _bfd_error_handler
+		    (_("warning: %pB: corrupt memory sealing size: 0x%x"),
+		     abfd, datasz);
+		  /* Clear all properties.  */
+		  elf_properties (abfd) = NULL;
+		  return false;
+		}
+	      prop = _bfd_elf_get_property (abfd, type, datasz);
+	      prop->pr_kind = property_number;
+	      goto next;
+
 	    default:
 	      if ((type >= GNU_PROPERTY_UINT32_AND_LO
 		   && type <= GNU_PROPERTY_UINT32_AND_HI)
@@ -258,6 +272,12 @@  elf_merge_gnu_properties (struct bfd_link_info *info, bfd *abfd, bfd *bbfd,
 	 be added to ABFD.  */
       return aprop == NULL;
 
+    case GNU_PROPERTY_MEMORY_SEAL:
+      /* Memory seal is controlled only by the linker.  */
+      if (aprop != NULL)
+	aprop->pr_kind = property_remove;
+      return true;
+
     default:
       updated = false;
       if (pr_type >= GNU_PROPERTY_UINT32_OR_LO
@@ -607,6 +627,33 @@  elf_write_gnu_properties (struct bfd_link_info *info,
     }
 }
 
+static asection *
+_bfd_elf_link_create_gnu_property_sec (struct bfd_link_info *info, bfd *elf_bfd,
+				       unsigned int elfclass)
+{
+  asection *sec;
+
+  sec = bfd_make_section_with_flags (elf_bfd,
+				     NOTE_GNU_PROPERTY_SECTION_NAME,
+				     (SEC_ALLOC
+				      | SEC_LOAD
+				      | SEC_IN_MEMORY
+				      | SEC_READONLY
+				      | SEC_HAS_CONTENTS
+				      | SEC_DATA));
+  if (sec == NULL)
+    info->callbacks->einfo (_("%F%P: failed to create GNU property section\n"));
+
+  if (!bfd_set_section_alignment (sec,
+				  elfclass == ELFCLASS64 ? 3 : 2))
+    info->callbacks->einfo (_("%F%pA: failed to align section\n"),
+			    sec);
+
+  elf_section_type (sec) = SHT_NOTE;
+  return sec;
+}
+
+
 /* Set up GNU properties.  Return the first relocatable ELF input with
    GNU properties if found.  Otherwise, return NULL.  */
 
@@ -656,23 +703,7 @@  _bfd_elf_link_setup_gnu_properties (struct bfd_link_info *info)
       /* Support -z indirect-extern-access.  */
       if (first_pbfd == NULL)
 	{
-	  sec = bfd_make_section_with_flags (elf_bfd,
-					     NOTE_GNU_PROPERTY_SECTION_NAME,
-					     (SEC_ALLOC
-					      | SEC_LOAD
-					      | SEC_IN_MEMORY
-					      | SEC_READONLY
-					      | SEC_HAS_CONTENTS
-					      | SEC_DATA));
-	  if (sec == NULL)
-	    info->callbacks->einfo (_("%F%P: failed to create GNU property section\n"));
-
-	  if (!bfd_set_section_alignment (sec,
-					  elfclass == ELFCLASS64 ? 3 : 2))
-	    info->callbacks->einfo (_("%F%pA: failed to align section\n"),
-				    sec);
-
-	  elf_section_type (sec) = SHT_NOTE;
+	  sec = _bfd_elf_link_create_gnu_property_sec (info, elf_bfd, elfclass);
 	  first_pbfd = elf_bfd;
 	  has_properties = true;
 	}
@@ -690,10 +721,6 @@  _bfd_elf_link_setup_gnu_properties (struct bfd_link_info *info)
 	  |= GNU_PROPERTY_1_NEEDED_INDIRECT_EXTERN_ACCESS;
     }
 
-  /* Do nothing if there is no .note.gnu.property section.  */
-  if (!has_properties)
-    return NULL;
-
   /* Merge .note.gnu.property sections.  */
   info->callbacks->minfo (_("\n"));
   info->callbacks->minfo (_("Merging program properties\n"));
@@ -737,6 +764,37 @@  _bfd_elf_link_setup_gnu_properties (struct bfd_link_info *info)
 	  }
       }
 
+  if (elf_bfd != NULL)
+    {
+      if (info->memory_seal)
+	{
+	  /* Support -z no-memory-seal.  */
+	  if (first_pbfd == NULL)
+	    {
+	      sec = _bfd_elf_link_create_gnu_property_sec (info, elf_bfd, elfclass);
+	      first_pbfd = elf_bfd;
+	      has_properties = true;
+	    }
+
+	  p = _bfd_elf_get_property (first_pbfd, GNU_PROPERTY_MEMORY_SEAL, 0);
+	  if (p->pr_kind == property_unknown)
+	    {
+	      /* Create GNU_PROPERTY_NO_MEMORY_SEAL.  */
+	      p->u.number = GNU_PROPERTY_MEMORY_SEAL;
+	      p->pr_kind = property_number;
+	    }
+	  else
+	    p->u.number |= GNU_PROPERTY_MEMORY_SEAL;
+	}
+      else
+	elf_find_and_remove_property (&elf_properties (elf_bfd),
+				      GNU_PROPERTY_MEMORY_SEAL, true);
+    }
+
+  /* Do nothing if there is no .note.gnu.property section.  */
+  if (!has_properties)
+    return NULL;
+
   /* Rewrite .note.gnu.property section so that GNU properties are
      always sorted by type even if input GNU properties aren't sorted.  */
   if (first_pbfd != NULL)
diff --git a/bfd/elfxx-x86.c b/bfd/elfxx-x86.c
index dd951b91f50..8a4405c8a79 100644
--- a/bfd/elfxx-x86.c
+++ b/bfd/elfxx-x86.c
@@ -4815,7 +4815,8 @@  _bfd_x86_elf_link_fixup_gnu_properties
   for (p = *listp; p; p = p->next)
     {
       unsigned int type = p->property.pr_type;
-      if (type == GNU_PROPERTY_X86_COMPAT_ISA_1_USED
+      if (type == GNU_PROPERTY_MEMORY_SEAL
+	  || type == GNU_PROPERTY_X86_COMPAT_ISA_1_USED
 	  || type == GNU_PROPERTY_X86_COMPAT_ISA_1_NEEDED
 	  || (type >= GNU_PROPERTY_X86_UINT32_AND_LO
 	      && type <= GNU_PROPERTY_X86_UINT32_AND_HI)
diff --git a/binutils/readelf.c b/binutils/readelf.c
index 0f8dc1b9716..bf25425bb8d 100644
--- a/binutils/readelf.c
+++ b/binutils/readelf.c
@@ -21464,6 +21464,12 @@  print_gnu_property_note (Filedata * filedata, Elf_Internal_Note * pnote)
 		printf (_("<corrupt length: %#x> "), datasz);
 	      goto next;
 
+	    case GNU_PROPERTY_MEMORY_SEAL:
+	      printf ("memory seal ");
+	      if (datasz)
+		printf (_("<corrupt length: %#x> "), datasz);
+	      goto next;
+
 	    default:
 	      if ((type >= GNU_PROPERTY_UINT32_AND_LO
 		   && type <= GNU_PROPERTY_UINT32_AND_HI)
diff --git a/include/bfdlink.h b/include/bfdlink.h
index f802ec627ef..8b9e391e6ff 100644
--- a/include/bfdlink.h
+++ b/include/bfdlink.h
@@ -429,6 +429,9 @@  struct bfd_link_info
   /* TRUE if only one read-only, non-code segment should be created.  */
   unsigned int one_rosegment: 1;
 
+  /* TRUE if GNU_PROPERTY_MEMORY_SEAL should be generated.  */
+  unsigned int memory_seal: 1;
+
   /* Nonzero if .eh_frame_hdr section and PT_GNU_EH_FRAME ELF segment
      should be created.  1 for DWARF2 tables, 2 for compact tables.  */
   unsigned int eh_frame_hdr_type: 2;
diff --git a/include/elf/common.h b/include/elf/common.h
index c9920e7731a..8938e2f4754 100644
--- a/include/elf/common.h
+++ b/include/elf/common.h
@@ -890,6 +890,7 @@ 
 /* Values used in GNU .note.gnu.property notes (NT_GNU_PROPERTY_TYPE_0).  */
 #define GNU_PROPERTY_STACK_SIZE			1
 #define GNU_PROPERTY_NO_COPY_ON_PROTECTED	2
+#define GNU_PROPERTY_MEMORY_SEAL		3
 
 /* A 4-byte unsigned integer property: A bit is set if it is set in all
    relocatable inputs.  */
diff --git a/ld/NEWS b/ld/NEWS
index 1f14dd6bc77..4a28592fa32 100644
--- a/ld/NEWS
+++ b/ld/NEWS
@@ -23,6 +23,9 @@  Changes in 2.43:
 
 * Add -plugin-save-temps to store plugin intermediate files permanently.
 
+* Add -z memory-seal/-z nomemory-seal options to ELF linker to mark the
+  object to memory sealed.
+
 Changes in 2.42:
 
 * Add -z mark-plt/-z nomark-plt options to x86-64 ELF linker to mark PLT
diff --git a/ld/emultempl/elf.em b/ld/emultempl/elf.em
index 2e865728587..ccd43531237 100644
--- a/ld/emultempl/elf.em
+++ b/ld/emultempl/elf.em
@@ -1075,6 +1075,10 @@  fragment <<EOF
 	link_info.combreloc = false;
       else if (strcmp (optarg, "nocopyreloc") == 0)
 	link_info.nocopyreloc = true;
+      else if (strcmp (optarg, "memory-seal") == 0)
+       link_info.memory_seal = true;
+      else if (strcmp (optarg, "nomemory-seal") == 0)
+       link_info.memory_seal = false;
 EOF
 if test -n "$COMMONPAGESIZE"; then
 fragment <<EOF
diff --git a/ld/ld.texi b/ld/ld.texi
index 90182c436ec..b8957d3027e 100644
--- a/ld/ld.texi
+++ b/ld/ld.texi
@@ -1591,6 +1591,14 @@  Disable relocation overflow check.  This can be used to disable
 relocation overflow check if there will be no dynamic relocation
 overflow at run-time.  Supported for x86_64.
 
+@item memory-seal
+@item nomemory-seal
+Instruct the executable or shared library that the all PT_LOAD segments
+should be sealed to avoid further manipulation (such as changing the
+protection flags, the segment size, or remove the mapping).
+This is a security hardening that requires system support.  This
+generates GNU_PROPERTY_MEMORY_SEAL in .note.gnu.property section
+
 @item now
 When generating an executable or shared library, mark it to tell the
 dynamic linker to resolve all symbols when the program is started, or
diff --git a/ld/lexsup.c b/ld/lexsup.c
index 8982073bc91..d82d3631a09 100644
--- a/ld/lexsup.c
+++ b/ld/lexsup.c
@@ -2271,6 +2271,10 @@  elf_shlib_list_options (FILE *file)
       fprintf (file, _("\
   -z textoff                  Don't treat DT_TEXTREL in output as error\n"));
     }
+  fprintf (file, _("\
+  -z memory-seal              Mark object be memory sealed\n"));
+  fprintf (file, _("\
+  -z nomemory-seal            Don't mark oject to be memory sealed (default)\n"));
 }
 
 static void
diff --git a/ld/testsuite/ld-elf/property-seal-1.d b/ld/testsuite/ld-elf/property-seal-1.d
new file mode 100644
index 00000000000..a0b1feedf31
--- /dev/null
+++ b/ld/testsuite/ld-elf/property-seal-1.d
@@ -0,0 +1,16 @@ 
+# Check if a GNU_PROPERTY_MEMORY_SEAL on a ET_REL is not replicated on
+# ET_DYN.
+#source: property-seal-1.s
+#as: --generate-missing-build-notes=no
+#ld: -shared
+#readelf: -n
+#xfail: ![check_shared_lib_support]
+#notarget: am33_2.0-*-* hppa*-*-hpux* mn10300-*-*
+# Assembly source file for the HPPA assembler is renamed and modifed by
+# sed.  mn10300 has relocations in .note.gnu.property section which
+# elf_parse_notes doesn't support.
+
+#failif
+#...
+Displaying notes found in: .note.gnu.property
+#pass
diff --git a/ld/testsuite/ld-elf/property-seal-1.s b/ld/testsuite/ld-elf/property-seal-1.s
new file mode 100644
index 00000000000..aa28a3d0516
--- /dev/null
+++ b/ld/testsuite/ld-elf/property-seal-1.s
@@ -0,0 +1,11 @@ 
+	.section ".note.gnu.property", "a"
+	.p2align ALIGN
+	.long 1f - 0f		/* name length */
+	.long 3f - 2f		/* data length */
+	.long 5			/* note type */
+0:	.asciz "GNU"		/* vendor name */
+1:
+	.p2align ALIGN
+2:	.long 3			/* pr_type.  */
+	.long 0			/* pr_datasz.  */
+3:
diff --git a/ld/testsuite/ld-elf/property-seal-2.d b/ld/testsuite/ld-elf/property-seal-2.d
new file mode 100644
index 00000000000..ebdaa623e0c
--- /dev/null
+++ b/ld/testsuite/ld-elf/property-seal-2.d
@@ -0,0 +1,17 @@ 
+# Check if a GNU_PROPERTY_MEMORY_SEAL on a ET_REL is not replicated on
+# ET_DYN.
+#source: empty.s
+#source: property-seal-1.s
+#as: --generate-missing-build-notes=no
+#ld: -shared
+#readelf: -n
+#xfail: ![check_shared_lib_support]
+#notarget: am33_2.0-*-* hppa*-*-hpux* mn10300-*-*
+# Assembly source file for the HPPA assembler is renamed and modifed by
+# sed.  mn10300 has relocations in .note.gnu.property section which
+# elf_parse_notes doesn't support.
+
+#failif
+#...
+Displaying notes found in: .note.gnu.property
+#pass
diff --git a/ld/testsuite/ld-elf/property-seal-3.d b/ld/testsuite/ld-elf/property-seal-3.d
new file mode 100644
index 00000000000..969729ee0f4
--- /dev/null
+++ b/ld/testsuite/ld-elf/property-seal-3.d
@@ -0,0 +1,16 @@ 
+# Check if a GNU_PROPERTY_MEMORY_SEAL on a ET_REL is not replicated on
+# ET_EXEC.
+#source: property-seal-1.s
+#as: --generate-missing-build-notes=no
+#ld: -e _start
+#warning: .*: warning: cannot find entry symbol .*
+#readelf: -n
+#notarget: am33_2.0-*-* hppa*-*-hpux* mn10300-*-*
+# Assembly source file for the HPPA assembler is renamed and modifed by
+# sed.  mn10300 has relocations in .note.gnu.property section which
+# elf_parse_notes doesn't support.
+
+#failif
+#...
+Displaying notes found in: .note.gnu.property
+#pass
diff --git a/ld/testsuite/ld-elf/property-seal-4.d b/ld/testsuite/ld-elf/property-seal-4.d
new file mode 100644
index 00000000000..3dd990dd68a
--- /dev/null
+++ b/ld/testsuite/ld-elf/property-seal-4.d
@@ -0,0 +1,16 @@ 
+# Check if a GNU_PROPERTY_MEMORY_SEAL on a ET_REL is not replicated on
+# ET_EXEC.
+#source: empty.s
+#source: property-seal-1.s
+#as: --generate-missing-build-notes=no
+#ld: -e _start
+#readelf: -n
+#notarget: am33_2.0-*-* hppa*-*-hpux* mn10300-*-*
+# Assembly source file for the HPPA assembler is renamed and modifed by
+# sed.  mn10300 has relocations in .note.gnu.property section which
+# elf_parse_notes doesn't support.
+
+#failif
+#...
+Displaying notes found in: .note.gnu.property
+#pass
diff --git a/ld/testsuite/ld-elf/property-seal-5.d b/ld/testsuite/ld-elf/property-seal-5.d
new file mode 100644
index 00000000000..0b92a8eb6eb
--- /dev/null
+++ b/ld/testsuite/ld-elf/property-seal-5.d
@@ -0,0 +1,15 @@ 
+#source: empty.s
+#ld: -shared -z memory-seal
+#readelf: -n
+#xfail: ![check_shared_lib_support]
+#notarget: am33_2.0-*-* hppa*-*-hpux* mn10300-*-*
+# Assembly source file for the HPPA assembler is renamed and modifed by
+# sed.  mn10300 has relocations in .note.gnu.property section which
+# elf_parse_notes doesn't support.
+
+#...
+Displaying notes found in: .note.gnu.property
+[ 	]+Owner[ 	]+Data size[ 	]+Description
+  GNU                  0x[0-9a-f]+	NT_GNU_PROPERTY_TYPE_0
+      Properties: memory seal 
+#pass
diff --git a/ld/testsuite/ld-elf/property-seal-6.d b/ld/testsuite/ld-elf/property-seal-6.d
new file mode 100644
index 00000000000..725911acae7
--- /dev/null
+++ b/ld/testsuite/ld-elf/property-seal-6.d
@@ -0,0 +1,16 @@ 
+#source: empty.s
+#source: property-seal-1.s
+#ld: -shared -z memory-seal
+#readelf: -n
+#xfail: ![check_shared_lib_support]
+#notarget: am33_2.0-*-* hppa*-*-hpux* mn10300-*-*
+# Assembly source file for the HPPA assembler is renamed and modifed by
+# sed.  mn10300 has relocations in .note.gnu.property section which
+# elf_parse_notes doesn't support.
+
+#...
+Displaying notes found in: .note.gnu.property
+[ 	]+Owner[ 	]+Data size[ 	]+Description
+  GNU                  0x[0-9a-f]+	NT_GNU_PROPERTY_TYPE_0
+      Properties: memory seal 
+#pass
diff --git a/ld/testsuite/ld-elf/property-seal-7.d b/ld/testsuite/ld-elf/property-seal-7.d
new file mode 100644
index 00000000000..12339e83ebd
--- /dev/null
+++ b/ld/testsuite/ld-elf/property-seal-7.d
@@ -0,0 +1,14 @@ 
+#source: empty.s
+#ld: -z memory-seal
+#readelf: -n
+#notarget: am33_2.0-*-* hppa*-*-hpux* mn10300-*-*
+# Assembly source file for the HPPA assembler is renamed and modifed by
+# sed.  mn10300 has relocations in .note.gnu.property section which
+# elf_parse_notes doesn't support.
+
+#...
+Displaying notes found in: .note.gnu.property
+[ 	]+Owner[ 	]+Data size[ 	]+Description
+  GNU                  0x[0-9a-f]+	NT_GNU_PROPERTY_TYPE_0
+      Properties: memory seal 
+#pass
diff --git a/ld/testsuite/ld-elf/property-seal-8.d b/ld/testsuite/ld-elf/property-seal-8.d
new file mode 100644
index 00000000000..0c4c4e4907e
--- /dev/null
+++ b/ld/testsuite/ld-elf/property-seal-8.d
@@ -0,0 +1,15 @@ 
+#source: empty.s
+#source: property-seal-1.s
+#ld: -z memory-seal
+#readelf: -n
+#notarget: am33_2.0-*-* hppa*-*-hpux* mn10300-*-*
+# Assembly source file for the HPPA assembler is renamed and modifed by
+# sed.  mn10300 has relocations in .note.gnu.property section which
+# elf_parse_notes doesn't support.
+
+#...
+Displaying notes found in: .note.gnu.property
+[ 	]+Owner[ 	]+Data size[ 	]+Description
+  GNU                  0x[0-9a-f]+	NT_GNU_PROPERTY_TYPE_0
+      Properties: memory seal 
+#pass