From: Alan Modra Date: Wed, 3 Sep 2025 00:24:50 +0000 (+0930) Subject: Disable eh_frame optimisation if code detected in .eh_frame X-Git-Tag: gdb-17-branchpoint~74 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=98583463abed11c7659028c0d68a9619fed5f559;p=thirdparty%2Fbinutils-gdb.git 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 --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 (¬es, 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;