diff options
author | Alan Modra <amodra@gmail.com> | 2025-09-03 09:54:50 +0930 |
---|---|---|
committer | Alan Modra <amodra@gmail.com> | 2025-09-03 11:22:06 +0930 |
commit | 98583463abed11c7659028c0d68a9619fed5f559 (patch) | |
tree | e5cad908c8c99fae1527de2381f49214577bf12f | |
parent | 180075d14fab9f75eaf679589f9d175fb5448d21 (diff) | |
download | binutils-98583463abed11c7659028c0d68a9619fed5f559.zip binutils-98583463abed11c7659028c0d68a9619fed5f559.tar.gz binutils-98583463abed11c7659028c0d68a9619fed5f559.tar.bz2 |
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.
-rw-r--r-- | gas/dwarf2dbg.c | 2 | ||||
-rw-r--r-- | gas/ehopt.c | 35 | ||||
-rw-r--r-- | gas/stabs.c | 4 | ||||
-rw-r--r-- | gas/subsegs.h | 32 |
4 files changed, 41 insertions, 32 deletions
diff --git a/gas/dwarf2dbg.c b/gas/dwarf2dbg.c index 84c9661..f1df564 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 5a9d9d6..a8f450b 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 12b1267..49e2740 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 7d960d3..473bd0f 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; |