]> git.ipfire.org Git - thirdparty/binutils-gdb.git/commitdiff
elf: Add GNU_PROPERTY_MEMORY_SEAL gnu property
authorAdhemerval Zanella <adhemerval.zanella@linaro.org>
Wed, 14 Aug 2024 17:04:55 +0000 (17:04 +0000)
committerAdhemerval Zanella <adhemerval.zanella@linaro.org>
Tue, 14 Jan 2025 16:18:43 +0000 (13:18 -0300)
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

18 files changed:
bfd/elf-properties.c
bfd/elfxx-x86.c
binutils/readelf.c
include/bfdlink.h
include/elf/common.h
ld/NEWS
ld/emultempl/elf.em
ld/ld.texi
ld/lexsup.c
ld/testsuite/ld-elf/property-seal-1.d [new file with mode: 0644]
ld/testsuite/ld-elf/property-seal-1.s [new file with mode: 0644]
ld/testsuite/ld-elf/property-seal-2.d [new file with mode: 0644]
ld/testsuite/ld-elf/property-seal-3.d [new file with mode: 0644]
ld/testsuite/ld-elf/property-seal-4.d [new file with mode: 0644]
ld/testsuite/ld-elf/property-seal-5.d [new file with mode: 0644]
ld/testsuite/ld-elf/property-seal-6.d [new file with mode: 0644]
ld/testsuite/ld-elf/property-seal-7.d [new file with mode: 0644]
ld/testsuite/ld-elf/property-seal-8.d [new file with mode: 0644]

index 61f01bf53806292c7b1fb1cf389eba18f228c56d..23634a9d9c911aa54cddbaa50e87c4feeb019e85 100644 (file)
@@ -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;
index 60e81f518a7990d78de33f80c2b5a3c98a67eacf..7c164e9c131de1a323c60a58a128e0b4dec68d7b 100644 (file)
@@ -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)
index 949395483bbefb5036c0cb071723448c9fa42130..694d8267efaa2176ecad2b459f48f2e4a2f53046 100644 (file)
@@ -21477,6 +21477,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)
index 85e7da844065f13b723b2fdf458c4060e749ae66..ae451075996498eb2c20ea28180dd67489724583 100644 (file)
@@ -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;
index b0b54d87ab4aaaf916f2492d1d7c38e70f7dd6e1..fd032d1e03ed5b19f84fda4a8f1a9ecb14867de3 100644 (file)
 /* 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 4a19f5a7d5d516a2b6692f1c49e39191ba9401cf..5d5fec4aed38921e62d692d9fb4983bf945fc6fc 100644 (file)
--- 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
index 0bad25d3a46e81c68e4c9ea2269727721f118cb1..8492251ff3390ceebb16b4d5368ce246cb664acd 100644 (file)
@@ -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
index d1787453f57f69b8039540ecf17e89957feb93c2..fc16bbb01f1b588a96c27883bfed33656fe0664f 100644 (file)
@@ -1602,6 +1602,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
index e63c4310c6ef1d959c9ffbc1812b3019e1859784..9cf1a9a033b90565a7df57e0d2c5cd9a541caff6 100644 (file)
@@ -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 (file)
index 0000000..a0b1fee
--- /dev/null
@@ -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 (file)
index 0000000..aa28a3d
--- /dev/null
@@ -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 (file)
index 0000000..ebdaa62
--- /dev/null
@@ -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 (file)
index 0000000..969729e
--- /dev/null
@@ -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 (file)
index 0000000..3dd990d
--- /dev/null
@@ -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 (file)
index 0000000..0b92a8e
--- /dev/null
@@ -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 (file)
index 0000000..725911a
--- /dev/null
@@ -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 (file)
index 0000000..12339e8
--- /dev/null
@@ -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 (file)
index 0000000..0c4c4e4
--- /dev/null
@@ -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