aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlan Modra <amodra@gmail.com>2025-09-03 09:54:50 +0930
committerAlan Modra <amodra@gmail.com>2025-09-03 11:22:06 +0930
commit98583463abed11c7659028c0d68a9619fed5f559 (patch)
treee5cad908c8c99fae1527de2381f49214577bf12f
parent180075d14fab9f75eaf679589f9d175fb5448d21 (diff)
downloadbinutils-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.c2
-rw-r--r--gas/ehopt.c35
-rw-r--r--gas/stabs.c4
-rw-r--r--gas/subsegs.h32
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 (&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 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;