[binutils-gdb] Disable eh_frame optimisation if code detected in .eh_frame

Alan Modra amodra@sourceware.org
Wed Sep 3 05:38:11 GMT 2025


https://sourceware.org/git/gitweb.cgi?p=binutils-gdb.git;h=98583463abed11c7659028c0d68a9619fed5f559

commit 98583463abed11c7659028c0d68a9619fed5f559
Author: Alan Modra <amodra@gmail.com>
Date:   Wed Sep 3 09:54:50 2025 +0930

    Disable eh_frame optimisation if code detected in .eh_frame
    
    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.

Diff:
---
 gas/dwarf2dbg.c |  2 ++
 gas/ehopt.c     | 35 +++++++++++++++++------------------
 gas/stabs.c     |  4 ++--
 gas/subsegs.h   | 32 ++++++++++++++++++++------------
 4 files changed, 41 insertions(+), 32 deletions(-)

diff --git a/gas/dwarf2dbg.c b/gas/dwarf2dbg.c
index 84c9661df9a..f1df564b3d9 100644
--- a/gas/dwarf2dbg.c
+++ b/gas/dwarf2dbg.c
@@ -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 ())
diff --git a/gas/ehopt.c b/gas/ehopt.c
index 5a9d9d64121..a8f450bb5ec 100644
--- a/gas/ehopt.c
+++ b/gas/ehopt.c
@@ -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;
 
diff --git a/gas/stabs.c b/gas/stabs.c
index 12b1267ff84..49e27403f89 100644
--- a/gas/stabs.c
+++ b/gas/stabs.c
@@ -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)
diff --git a/gas/subsegs.h b/gas/subsegs.h
index 7d960d3d671..473bd0f3eba 100644
--- a/gas/subsegs.h
+++ b/gas/subsegs.h
@@ -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;


More information about the Binutils-cvs mailing list