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

Adhemerval Zanella Netto adhemerval.zanella@linaro.org
Thu Oct 3 12:22:53 GMT 2024



On 30/09/24 17:52, H.J. Lu wrote:
> On Tue, Oct 1, 2024 at 4:09 AM Adhemerval Zanella
> <adhemerval.zanella@linaro.org> wrote:
>>
>> Along with -Wl,memory-seal/-Wl,nomemory-seal options to ld.bfd.
>>
>> The new attribute indicates that an ET_EXEC or ET_DYN ELF object should
>> be memory-sealed if the loader supports it. Memory sealing is useful as
>> a hardening mechanism to avoid either remapping the memory segments or
>> changing the memory protection segments layout by the dynamic loader
>> (for instance, the RELRO hardening). The Linux 6.10
>> (8be7258aad44b5e25977a98db136f677fa6f4370) added the mseal syscall
>> accomplishes it.
>>
>> A GNU property is used instead of a new dynamic section tag (like the
>> one proposed for DT_GNU_FLAGS_1) because the memory sealing should be
>> selectable for ET_EXEC and not only for ET_DYN. It also fits new opt-in
>> security features like x86 CET or AArch64 BTI.
>> ---
>>  bfd/elf-properties.c                  | 72 ++++++++++++++++++++-------
>>  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 | 15 ++++++
>>  ld/testsuite/ld-elf/property-seal-2.d | 14 ++++++
>>  11 files changed, 115 insertions(+), 18 deletions(-)
>>  create mode 100644 ld/testsuite/ld-elf/property-seal-1.d
>>  create mode 100644 ld/testsuite/ld-elf/property-seal-2.d
>>
>> diff --git a/bfd/elf-properties.c b/bfd/elf-properties.c
>> index ee8bd37f2bd..17c89d9e876 100644
>> --- a/bfd/elf-properties.c
>> +++ b/bfd/elf-properties.c
>> @@ -177,6 +177,9 @@ _bfd_elf_parse_gnu_properties (bfd *abfd, Elf_Internal_Note *note)
>>               prop->pr_kind = property_number;
>>               goto next;
>>
>> +           case GNU_PROPERTY_MEMORY_SEAL:
>> +             goto next;
>> +
>>             default:
>>               if ((type >= GNU_PROPERTY_UINT32_AND_LO
>>                    && type <= GNU_PROPERTY_UINT32_AND_HI)
>> @@ -258,6 +261,9 @@ 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:
>> +      return aprop == NULL;
>> +
>>      default:
>>        updated = false;
>>        if (pr_type >= GNU_PROPERTY_UINT32_OR_LO
>> @@ -607,6 +613,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 +689,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 +707,27 @@ _bfd_elf_link_setup_gnu_properties (struct bfd_link_info *info)
>>           |= GNU_PROPERTY_1_NEEDED_INDIRECT_EXTERN_ACCESS;
>>      }
>>
>> +  if (info->memory_seal && elf_bfd != NULL)
>> +    {
>> +      /* 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;
>> +    }
>> +
>>    /* 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 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..0b92a8eb6eb
>> --- /dev/null
>> +++ b/ld/testsuite/ld-elf/property-seal-1.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-2.d b/ld/testsuite/ld-elf/property-seal-2.d
>> new file mode 100644
>> index 00000000000..12339e83ebd
>> --- /dev/null
>> +++ b/ld/testsuite/ld-elf/property-seal-2.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
>> --
>> 2.34.1
>>
> 
> Is GNU_PROPERTY_MEMORY_SEAL only generated by linker?
> What happens when an input relocatable object file contains
> GNU_PROPERTY_MEMORY_SEAL?
> 

Yes, I didn't see a strong reason to add a compiler flag to enable it.  Similar
to RELR, it makes mode sense to be a linker decision instead of a per object
one. And the flag is just ignored if present in a ET_REL.

I am not sure if this is be best approach, I am thinking in revising to use a
DT_FLAGS_1 instead.


More information about the Binutils mailing list