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.
This commit is contained in:
Alan Modra
2025-09-03 09:54:50 +09:30
parent 180075d14f
commit 98583463ab
4 changed files with 41 additions and 32 deletions

View 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 ())

View 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;

View 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)

View 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;