[PATCH v5 1/3] elf: Add GNU_PROPERTY_MEMORY_SEAL gnu property

H.J. Lu hjl.tools@gmail.com
Thu Jan 2 21:48:28 GMT 2025


On Fri, Jan 3, 2025 at 3:20 AM Adhemerval Zanella
<adhemerval.zanella@linaro.org> wrote:
>
> The GNU_PROPERTY_MEMORY_SEAL gnu property is a way to mark binaries
> to be memory sealed by the loader, to avoid further changes of
> PT_LOAD segments (such as unmapping or change permission flags).
> This is done along with Linux kernel (the mseal syscall [1]), and
> C runtime supports to instruct the kernel on the correct time during
> program startup (for instance, after RELRO handling).  This support
> is added along the glibc support to handle the new gnu property [2].
>
> This is a opt-in security features, like other security hardening
> ones like NX-stack or RELRO.
>
> 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://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=8be7258aad44b5e25977a98db136f677fa6f4370
> [2] https://sourceware.org/pipermail/libc-alpha/2024-September/160291.html
>
> Change-Id: Id47fadabecd24be0e83cff45653f7ce9a900ecf4
> ---
>  bfd/elf-properties.c                  | 85 +++++++++++++++++++++------
>  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, 235 insertions(+), 18 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 61f01bf5380..23634a9d9c9 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)
> @@ -254,6 +268,7 @@ elf_merge_gnu_properties (struct bfd_link_info *info, bfd *abfd, bfd *bbfd,
>        /* FALLTHROUGH */
>
>      case GNU_PROPERTY_NO_COPY_ON_PROTECTED:
> +    case GNU_PROPERTY_MEMORY_SEAL:
>        /* Return TRUE if APROP is NULL to indicate that BPROP should
>          be added to ABFD.  */
>        return aprop == NULL;
> @@ -607,6 +622,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 +698,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,6 +716,31 @@ _bfd_elf_link_setup_gnu_properties (struct bfd_link_info *info)
>           |= GNU_PROPERTY_1_NEEDED_INDIRECT_EXTERN_ACCESS;
>      }
>
> +  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
> +       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;
> diff --git a/bfd/elfxx-x86.c b/bfd/elfxx-x86.c
> index 60e81f518a7..7c164e9c131 100644
> --- a/bfd/elfxx-x86.c
> +++ b/bfd/elfxx-x86.c
> @@ -4892,7 +4892,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 4f8f879cf91..0f29b65fe30 100644
> --- a/binutils/readelf.c
> +++ b/binutils/readelf.c
> @@ -21476,6 +21476,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 85e7da84406..ae451075996 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 6077db7a8b7..2e2c486bf8d 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 4a19f5a7d5d..5d5fec4aed3 100644
> --- a/ld/NEWS
> +++ b/ld/NEWS
> @@ -33,6 +33,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 4d431995d2e..9a14eae749e 100644
> --- a/ld/emultempl/elf.em
> +++ b/ld/emultempl/elf.em
> @@ -1083,6 +1083,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 da714a20855..d3d9f32d8f3 100644
> --- a/ld/ld.texi
> +++ b/ld/ld.texi
> @@ -1590,6 +1590,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 e63c4310c6e..9cf1a9a033b 100644
> --- a/ld/lexsup.c
> +++ b/ld/lexsup.c
> @@ -2265,6 +2265,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
> --
> 2.43.0
>

As the GNU property owner, it looks OK to me.  Please apply if
there are no objections from other binutils maintainers after a few
days.

Thanks.

-- 
H.J.


More information about the Binutils mailing list