]> git.ipfire.org Git - thirdparty/binutils-gdb.git/commitdiff
Disable eh_frame optimisation if code detected in .eh_frame
authorAlan Modra <amodra@gmail.com>
Wed, 3 Sep 2025 00:24:50 +0000 (09:54 +0930)
committerAlan Modra <amodra@gmail.com>
Wed, 3 Sep 2025 01:52:06 +0000 (11:22 +0930)
Fuzzers stress the assembler in ways no sane programmer would ever do.
One x86 oss-fuzz testcase (cleaned up a litte) was:
 .sect .debug_frame
 call x
 .long x,0
 .space 1
 .long 0,0
The call insn leaves the frag data corresponding to a CIE
uninitialised until later in assembly, leading to reports of
uninitialised data access in ehopt.c:check_eh_frame.

Hack around this problem by noticing an insn has been assembled in
dwarf2_emit_insn.  The existing frag has_code can't be used as that
leads to alignment complaints, so add a new segment_info flag.

* subsegs.h (struct segment_info_struct): Move bss and hadone
later.  Rename hadone to stab_seen.  Add insn_seen bitfield.
* dwarf2dbg.c (dwarf2_emit_insn): Set insn_seen.
* ehopt.c (check_eh_frame): Disable optimisation if insn_seen.
* stabs.c (s_stab_generic): Adjust for hadone rename.

gas/dwarf2dbg.c
gas/ehopt.c
gas/stabs.c
gas/subsegs.h

index 84c9661df9a6f81303928eaff4ab5e99051e6e54..f1df564b3d95fd876e4e7f9a18587e84cb724500 100644 (file)
@@ -1072,6 +1072,8 @@ dwarf2_emit_insn (int size)
 {
   struct dwarf2_line_info loc;
 
+  seg_info (now_seg)->insn_seen = 1;
+
   if (debug_type != DEBUG_DWARF2
       ? !dwarf2_loc_directive_seen
       : !seen_at_least_1_file ())
index 5a9d9d641219b5a158d55e5cd1a33ce302a8931b..a8f450bb5ec8d4093441f59497f3b289e229e9a4 100644 (file)
@@ -306,25 +306,24 @@ check_eh_frame (expressionS *exp, unsigned int *pnbytes)
   switch (d->state)
     {
     case state_idle:
-      if (*pnbytes == 4)
+      /* This might be the size of the CIE or FDE.  We want to know
+        the size so that we don't accidentally optimize across an FDE
+        boundary.  We recognize the size in one of two forms: a
+        symbol which will later be defined as a difference, or a
+        subtraction of two symbols.  Either way, we can tell when we
+        are at the end of the FDE because the symbol becomes defined
+        (in the case of a subtraction, the end symbol, from which the
+        start symbol is being subtracted).  Other ways of describing
+        the size will not be optimized.  */
+      if (*pnbytes == 4
+         && !seg_info (now_seg)->insn_seen
+         && (exp->X_op == O_symbol || exp->X_op == O_subtract)
+         && !S_IS_DEFINED (exp->X_add_symbol))
        {
-         /* This might be the size of the CIE or FDE.  We want to know
-            the size so that we don't accidentally optimize across an FDE
-            boundary.  We recognize the size in one of two forms: a
-            symbol which will later be defined as a difference, or a
-            subtraction of two symbols.  Either way, we can tell when we
-            are at the end of the FDE because the symbol becomes defined
-            (in the case of a subtraction, the end symbol, from which the
-            start symbol is being subtracted).  Other ways of describing
-            the size will not be optimized.  */
-         if ((exp->X_op == O_symbol || exp->X_op == O_subtract)
-             && ! S_IS_DEFINED (exp->X_add_symbol))
-           {
-             d->state = state_saw_size;
-             d->size_end_sym = exp->X_add_symbol;
-             if (!d->cie_info.f)
-               d->cie_info.f = frag_now;
-           }
+         d->state = state_saw_size;
+         d->size_end_sym = exp->X_add_symbol;
+         if (!d->cie_info.f)
+           d->cie_info.f = frag_now;
        }
       break;
 
index 12b1267ff84d9a1a42b73675af34f24ebdb09792..49e27403f897431c37fa7010d00a96d96f045cdd 100644 (file)
@@ -229,14 +229,14 @@ s_stab_generic (int what,
        obstack_free (&notes, stab_secname);
 
       subseg_set (stab, 0);
-      if (!seg_info (stab)->hadone)
+      if (!seg_info (stab)->stab_seen)
        {
          bfd_set_section_flags (stab,
                                 SEC_READONLY | SEC_RELOC | SEC_DEBUGGING);
 #ifdef INIT_STAB_SECTION
          INIT_STAB_SECTION (stab, stabstr);
 #endif
-         seg_info (stab)->hadone = 1;
+         seg_info (stab)->stab_seen = 1;
        }
     }
   else if (freenames)
index 7d960d3d6718b94f640110bcb7d8471ffb84fd93..473bd0f3ebab966e299a80d865f04bb7ed018b0b 100644 (file)
@@ -62,16 +62,9 @@ typedef struct frchain frchainS;
    frag chain, even if it contains no (complete) frags.  */
 extern frchainS *frchain_now;
 
-typedef struct segment_info_struct {
+typedef struct segment_info_struct
+{
   frchainS *frchainP;
-  unsigned int hadone : 1;
-
-  /* This field is set if this is a .bss section which does not really
-     have any contents.  Once upon a time a .bss section did not have
-     any frags, but that is no longer true.  This field prevent the
-     SEC_HAS_CONTENTS flag from being set for the section even if
-     there are frags.  */
-  unsigned int bss : 1;
 
   /* Fixups for this segment.  This is only valid after the frchains
      are run together.  */
@@ -85,13 +78,28 @@ typedef struct segment_info_struct {
   /* Used by dwarf2dbg.c for this section's line table entries.  */
   void *dwarf2_line_seg;
 
-  union {
+  /* This field is set if this is a .bss section which does not really
+     have any contents.  Once upon a time a .bss section did not have
+     any frags, but that is no longer true.  This field prevent the
+     SEC_HAS_CONTENTS flag from being set for the section even if
+     there are frags.  */
+  unsigned int bss : 1;
+
+  /* Set whenever dwarf2_emit_insn is called, and used to disable
+     .eh_frame and .debug_frame optimisation.  This is an anti-fuzzer
+     measure.  */
+  unsigned int insn_seen : 1;
+
+  /* Used by the stabs code.  */
+  unsigned int stab_seen : 1;
+
+  union
+  {
     /* Current size of section holding stabs strings.  */
     unsigned long stab_string_size;
     /* Initial frag for ELF.  */
     char *p;
-  }
-  stabu;
+  } stabu;
 
 #ifdef NEED_LITERAL_POOL
   unsigned long literal_pool_size;