Message ID | 20240930200822.1669666-2-adhemerval.zanella@linaro.org |
---|---|
State | New |
Headers | show |
Series | elf: Add GNU_PROPERTY_MEMORY_SEAL gnu property | expand |
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?
On 30.09.2024 22:08, Adhemerval Zanella 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. What's wrong with DT_* in ET_EXEC type files? The first random executables I looked at (as-new and ld-new) have a fair set of DT_*, so I don't see why a note would need using instead. As per H.J.'s question, a note would also pose a possible issue (as to how to deal with it) when found in an object file. Jan
* Jan Beulich: > On 30.09.2024 22:08, Adhemerval Zanella 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. > > What's wrong with DT_* in ET_EXEC type files? The first random executables > I looked at (as-new and ld-new) have a fair set of DT_*, so I don't see > why a note would need using instead. As per H.J.'s question, a note would > also pose a possible issue (as to how to deal with it) when found in an > object file. Do we even need a new flag? Could we reuse DF_1_NODELETE? Thanks, Florian
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.
On 01/10/24 03:45, Jan Beulich wrote: > On 30.09.2024 22:08, Adhemerval Zanella 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. > > What's wrong with DT_* in ET_EXEC type files? The first random executables > I looked at (as-new and ld-new) have a fair set of DT_*, so I don't see > why a note would need using instead. As per H.J.'s question, a note would > also pose a possible issue (as to how to deal with it) when found in an > object file. I had the impression that this was not really required for ET_EXEC, but it seems I am wrong. I will change to use a DT_FLAGS_1, similar to DF_GNU_1_UNIQUE. > > Do we even need a new flag? Could we reuse DF_1_NODELETE? I would prefer to use a new flag, I don't want to mix different runtime support with the same flag (it might be a source of confusion).
On 03/10/24 09:43, Adhemerval Zanella Netto wrote: > > > On 01/10/24 03:45, Jan Beulich wrote: >> On 30.09.2024 22:08, Adhemerval Zanella 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. >> >> What's wrong with DT_* in ET_EXEC type files? The first random executables >> I looked at (as-new and ld-new) have a fair set of DT_*, so I don't see >> why a note would need using instead. As per H.J.'s question, a note would >> also pose a possible issue (as to how to deal with it) when found in an >> object file. > > I had the impression that this was not really required for ET_EXEC, but it > seems I am wrong. I will change to use a DT_FLAGS_1, similar to DF_GNU_1_UNIQUE. > > In fact the main problem with DT_FLAGS_1 is it requires a dynamic section and it might be tricky to get this with for non-pie static binaries (since a lot of fields are mandatory). We can just make it supported for PT_DYNAMIC, although with GNU attribute it is easier to avoid this constraint.
On 03/10/24 11:00, Adhemerval Zanella Netto wrote: > > > On 03/10/24 09:43, Adhemerval Zanella Netto wrote: >> >> >> On 01/10/24 03:45, Jan Beulich wrote: >>> On 30.09.2024 22:08, Adhemerval Zanella 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. >>> >>> What's wrong with DT_* in ET_EXEC type files? The first random executables >>> I looked at (as-new and ld-new) have a fair set of DT_*, so I don't see >>> why a note would need using instead. As per H.J.'s question, a note would >>> also pose a possible issue (as to how to deal with it) when found in an >>> object file. >> >> I had the impression that this was not really required for ET_EXEC, but it >> seems I am wrong. I will change to use a DT_FLAGS_1, similar to DF_GNU_1_UNIQUE. >> >> > > In fact the main problem with DT_FLAGS_1 is it requires a dynamic section and > it might be tricky to get this with for non-pie static binaries (since a lot > of fields are mandatory). We can just make it supported for PT_DYNAMIC, although > with GNU attribute it is easier to avoid this constraint. So I am more inclined to keep using the current strategy of a gnu property to avoid the PT_DYNAMIC constraint (specially because some glibc ports still do not have static-pie support). Do you see any problem with this approach?
On Wed, Oct 9, 2024 at 9:48 PM Adhemerval Zanella Netto <adhemerval.zanella@linaro.org> wrote: > > > > On 03/10/24 11:00, Adhemerval Zanella Netto wrote: > > > > > > On 03/10/24 09:43, Adhemerval Zanella Netto wrote: > >> > >> > >> On 01/10/24 03:45, Jan Beulich wrote: > >>> On 30.09.2024 22:08, Adhemerval Zanella 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. > >>> > >>> What's wrong with DT_* in ET_EXEC type files? The first random executables > >>> I looked at (as-new and ld-new) have a fair set of DT_*, so I don't see > >>> why a note would need using instead. As per H.J.'s question, a note would > >>> also pose a possible issue (as to how to deal with it) when found in an > >>> object file. > >> > >> I had the impression that this was not really required for ET_EXEC, but it > >> seems I am wrong. I will change to use a DT_FLAGS_1, similar to DF_GNU_1_UNIQUE. > >> > >> > > > > In fact the main problem with DT_FLAGS_1 is it requires a dynamic section and > > it might be tricky to get this with for non-pie static binaries (since a lot > > of fields are mandatory). We can just make it supported for PT_DYNAMIC, although > > with GNU attribute it is easier to avoid this constraint. > > > So I am more inclined to keep using the current strategy of a gnu property > to avoid the PT_DYNAMIC constraint (specially because some glibc ports > still do not have static-pie support). Do you see any problem with this > approach? Please add linker tests to verify that GNU_PROPERTY_MEMORY_SEAL in input objects, .o and .so, are ignored. Tests should cover all input objects with GNU_PROPERTY_MEMORY_SEAL and some have GNU_PROPERTY_MEMORY_SEAL. You should check the behaviors of old and non-bfd linkers.
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