mirror of
https://github.com/bminor/binutils-gdb.git
synced 2025-11-16 04:24:43 +00:00
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:
@@ -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 ())
|
||||
|
||||
35
gas/ehopt.c
35
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;
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user