summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJan Kratochvil <jan.kratochvil@redhat.com>2014-01-03 22:06:12 +0100
committerJan Kratochvil <jan.kratochvil@redhat.com>2014-01-03 22:06:36 +0100
commit305705207e0831e6387706065e59f91296ba0d0a (patch)
treeed834da7297a96dc1faf31eadabb7352df57f2fb
parenta6141d27691a68a4af5eb48ab10d27e74d3c2a6b (diff)
Detect infinite backtraces.upstream/jankratochvil/cfaloop
libdwfl/ 2014-01-03 Jan Kratochvil <jan.kratochvil@redhat.com> Detect infinite backtraces. * dwfl_frame.c (state_alloc): Initialize CFA. * frame_unwind.c (expr_eval): Remove parameter frame, add parameter elfclass. Move elfclass to handle_cfi. Replace recursive call by state->unwound->CFA. (new_unwound): Initialize CFA. (handle_cfi): Move elfclass here. Compute CFA. Update expr_eval caller parameters. * libdwflP.h (DWFL_ERRORS): Add UNWIND_BAD_CFA. (struct Dwfl_Frame): Add field cfa. tests/ 2014-01-03 Jan Kratochvil <jan.kratochvil@redhat.com> * Makefile.am (TESTS): Add run-cfaloop.sh. (EXTRA_DIST): Add run-cfaloop.sh, testfilecfaloop.S, testfilecfaloop.bz2, testfilecfaloop.c and testfilecfaloop.core.bz2. * run-cfaloop.sh: New file. * testfilecfaloop.S: New file. * testfilecfaloop.bz2: New file. * testfilecfaloop.c: New file. * testfilecfaloop.core.bz2: New file. Signed-off-by: Jan Kratochvil <jan.kratochvil@redhat.com>
-rw-r--r--libdwfl/dwfl_frame.c3
-rw-r--r--libdwfl/frame_unwind.c46
-rw-r--r--libdwfl/libdwflP.h7
-rw-r--r--tests/Makefile.am8
-rwxr-xr-xtests/run-cfaloop.sh22
-rw-r--r--tests/testfilecfaloop.S90
-rw-r--r--tests/testfilecfaloop.bz2bin0 -> 640 bytes
-rw-r--r--tests/testfilecfaloop.c22
-rw-r--r--tests/testfilecfaloop.core.bz2bin0 -> 5765 bytes
9 files changed, 177 insertions, 21 deletions
diff --git a/libdwfl/dwfl_frame.c b/libdwfl/dwfl_frame.c
index 28008e90..89fd9205 100644
--- a/libdwfl/dwfl_frame.c
+++ b/libdwfl/dwfl_frame.c
@@ -1,5 +1,5 @@
/* Get Dwarf Frame state for target PID or core file.
- Copyright (C) 2013 Red Hat, Inc.
+ Copyright (C) 2013-2014 Red Hat, Inc.
This file is part of elfutils.
This file is free software; you can redistribute it and/or modify
@@ -98,6 +98,7 @@ state_alloc (Dwfl_Thread *thread)
state->thread = thread;
state->signal_frame = false;
state->initial_frame = true;
+ state->cfa = 0;
state->pc_state = DWFL_FRAME_STATE_ERROR;
memset (state->regs_set, 0, sizeof (state->regs_set));
thread->unwound = state;
diff --git a/libdwfl/frame_unwind.c b/libdwfl/frame_unwind.c
index 3ce45479..d434c468 100644
--- a/libdwfl/frame_unwind.c
+++ b/libdwfl/frame_unwind.c
@@ -1,5 +1,5 @@
/* Get previous frame state for an existing frame state.
- Copyright (C) 2013 Red Hat, Inc.
+ Copyright (C) 2013-2014 Red Hat, Inc.
This file is part of elfutils.
This file is free software; you can redistribute it and/or modify
@@ -105,8 +105,8 @@ bra_compar (const void *key_voidp, const void *elem_voidp)
DW_OP_call_frame_cfa is no longer permitted. */
static bool
-expr_eval (Dwfl_Frame *state, Dwarf_Frame *frame, const Dwarf_Op *ops,
- size_t nops, Dwarf_Addr *result, Dwarf_Addr bias)
+expr_eval (Dwfl_Frame *state, const Dwarf_Op *ops, size_t nops,
+ Dwarf_Addr *result, Dwarf_Addr bias, const int elfclass)
{
Dwfl_Process *process = state->thread->process;
if (nops == 0)
@@ -310,7 +310,6 @@ expr_eval (Dwfl_Frame *state, Dwarf_Frame *frame, const Dwarf_Op *ops,
}
if (op->atom == DW_OP_deref_size)
{
- const int elfclass = frame->cache->e_ident[EI_CLASS];
const unsigned addr_bytes = elfclass == ELFCLASS32 ? 4 : 8;
if (op->number > addr_bytes)
{
@@ -450,16 +449,15 @@ expr_eval (Dwfl_Frame *state, Dwarf_Frame *frame, const Dwarf_Op *ops,
break;
/* DW_OP_* not listed in libgcc/unwind-dw2.c execute_stack_op: */
case DW_OP_call_frame_cfa:;
- // Not used by CFI itself but it is synthetized by elfutils internation.
- Dwarf_Op *cfa_ops;
- size_t cfa_nops;
- Dwarf_Addr cfa;
- if (frame == NULL
- || dwarf_frame_cfa (frame, &cfa_ops, &cfa_nops) != 0
- || ! expr_eval (state, NULL, cfa_ops, cfa_nops, &cfa, bias)
- || ! push (cfa))
- {
- __libdwfl_seterrno (DWFL_E_LIBDW);
+ if (state->unwound->cfa == 0)
+ {
+ /* DW_OP_call_frame_cfa is needed to compute CFA itself. */
+ free (stack);
+ __libdwfl_seterrno (DWFL_E_INVALID_DWARF);
+ return false;
+ }
+ if (! push (state->unwound->cfa))
+ {
free (stack);
return false;
}
@@ -510,6 +508,7 @@ new_unwound (Dwfl_Frame *state)
unwound->unwound = NULL;
unwound->signal_frame = false;
unwound->initial_frame = false;
+ unwound->cfa = 0;
unwound->pc_state = DWFL_FRAME_STATE_ERROR;
memset (unwound->regs_set, 0, sizeof (unwound->regs_set));
}
@@ -536,12 +535,29 @@ handle_cfi (Dwfl_Frame *state, Dwarf_Addr pc, Dwarf_CFI *cfi, Dwarf_Addr bias)
Ebl *ebl = process->ebl;
size_t nregs = ebl_frame_nregs (ebl);
assert (nregs > 0);
+ const int elfclass = frame->cache->e_ident[EI_CLASS];
/* The return register is special for setting the unwound->pc_state. */
unsigned ra = frame->fde->cie->return_address_register;
bool ra_set = false;
ebl_dwarf_to_regno (ebl, &ra);
+ // Set unwound->CFA.
+ Dwarf_Op *cfa_ops;
+ size_t cfa_nops;
+ if (dwarf_frame_cfa (frame, &cfa_ops, &cfa_nops) != 0
+ || ! expr_eval (state, cfa_ops, cfa_nops, &unwound->cfa, bias, elfclass))
+ {
+ __libdwfl_seterrno (DWFL_E_LIBDW);
+ return;
+ }
+ if (unwound->cfa == 0
+ || (! state->initial_frame && unwound->cfa <= state->cfa))
+ {
+ __libdwfl_seterrno (DWFL_E_UNWIND_BAD_CFA);
+ return;
+ }
+
for (unsigned regno = 0; regno < nregs; regno++)
{
Dwarf_Op reg_ops_mem[3], *reg_ops;
@@ -574,7 +590,7 @@ handle_cfi (Dwfl_Frame *state, Dwarf_Addr pc, Dwarf_CFI *cfi, Dwarf_Addr bias)
continue;
}
}
- else if (! expr_eval (state, frame, reg_ops, reg_nops, &regval, bias))
+ else if (! expr_eval (state, reg_ops, reg_nops, &regval, bias, elfclass))
{
/* PPC32 vDSO has various invalid operations, ignore them. The
register will look as unset causing an error later, if used.
diff --git a/libdwfl/libdwflP.h b/libdwfl/libdwflP.h
index 63615a8a..5f6bbcdd 100644
--- a/libdwfl/libdwflP.h
+++ b/libdwfl/libdwflP.h
@@ -1,5 +1,5 @@
/* Internal definitions for libdwfl.
- Copyright (C) 2005-2013 Red Hat, Inc.
+ Copyright (C) 2005-2014 Red Hat, Inc.
This file is part of elfutils.
This file is free software; you can redistribute it and/or modify
@@ -89,7 +89,8 @@ typedef struct Dwfl_Process Dwfl_Process;
DWFL_ERROR (ATTACH_STATE_CONFLICT, N_("Dwfl already has attached state")) \
DWFL_ERROR (NO_ATTACH_STATE, N_("Dwfl has no attached state")) \
DWFL_ERROR (NO_UNWIND, N_("Unwinding not supported for this architecture")) \
- DWFL_ERROR (INVALID_ARGUMENT, N_("Invalid argument"))
+ DWFL_ERROR (INVALID_ARGUMENT, N_("Invalid argument")) \
+ DWFL_ERROR (UNWIND_BAD_CFA, N_("Unwind not monotonous (corrupt stack?)"))
#define DWFL_ERROR(name, text) DWFL_E_##name,
typedef enum { DWFL_ERRORS DWFL_E_NUM } Dwfl_Error;
@@ -241,6 +242,8 @@ struct Dwfl_Frame
Dwfl_Frame *unwound;
bool signal_frame : 1;
bool initial_frame : 1;
+ /* Used to catch infinite unwinding. */
+ Dwarf_Addr cfa;
enum
{
/* This structure is still being initialized or there was an error
diff --git a/tests/Makefile.am b/tests/Makefile.am
index 52eb50aa..ac83d9c4 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -1,6 +1,6 @@
## Process this file with automake to create Makefile.in
##
-## Copyright (C) 1996-2013 Red Hat, Inc.
+## Copyright (C) 1996-2014 Red Hat, Inc.
## This file is part of elfutils.
##
## This file is free software; you can redistribute it and/or modify
@@ -107,7 +107,7 @@ TESTS = run-arextract.sh run-arsymtest.sh newfile test-nlist \
run-backtrace-native-biarch.sh run-backtrace-native-core.sh \
run-backtrace-native-core-biarch.sh run-backtrace-core-x86_64.sh \
run-backtrace-core-i386.sh run-backtrace-core-ppc.sh \
- run-backtrace-core-s390x.sh run-backtrace-core-s390.sh
+ run-backtrace-core-s390x.sh run-backtrace-core-s390.sh run-cfaloop.sh
if !BIARCH
export ELFUTILS_DISABLE_BIARCH = 1
@@ -256,7 +256,9 @@ EXTRA_DIST = run-arextract.sh run-arsymtest.sh \
run-backtrace-core-ppc.sh testfile66.bz2 testfile66.core.bz2 \
backtrace.s390x.core.bz2 backtrace.s390x.exec.bz2 \
backtrace.s390.core.bz2 backtrace.s390.exec.bz2 \
- run-backtrace-core-s390x.sh run-backtrace-core-s390.sh
+ run-backtrace-core-s390x.sh run-backtrace-core-s390.sh \
+ run-cfaloop.sh testfilecfaloop.S testfilecfaloop.bz2 \
+ testfilecfaloop.c testfilecfaloop.core.bz2
if USE_VALGRIND
valgrind_cmd='valgrind -q --error-exitcode=1 --run-libc-freeres=no'
diff --git a/tests/run-cfaloop.sh b/tests/run-cfaloop.sh
new file mode 100755
index 00000000..afad5dba
--- /dev/null
+++ b/tests/run-cfaloop.sh
@@ -0,0 +1,22 @@
+#! /bin/bash
+# Copyright (C) 2014 Red Hat, Inc.
+# This file is part of elfutils.
+#
+# This file is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# elfutils is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+. $srcdir/test-subr.sh
+
+testfiles testfilecfaloop testfilecfaloop.core
+testrun ${abs_top_builddir}/src/stack -e testfilecfaloop --core=testfilecfaloop.core 2>&1 \
+ | grep -q 'Unwind not monotonous'
diff --git a/tests/testfilecfaloop.S b/tests/testfilecfaloop.S
new file mode 100644
index 00000000..f4108a27
--- /dev/null
+++ b/tests/testfilecfaloop.S
@@ -0,0 +1,90 @@
+/* Test program for run-cfaloop.sh.
+ Copyright (C) 2014 Red Hat, Inc.
+ This file is part of elfutils.
+
+ This file is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ elfutils is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+ .file "testfilecfaloop.c"
+ .text
+ .p2align 4,,15
+ .globl _start
+ .type _start, @function
+_start:
+.LFB0:
+# BLOCK 2 freq:10000 seq:0
+# PRED: ENTRY [100.0%] (FALLTHRU)
+ movq $0, -8(%rsp)
+ movq -8(%rsp), %rax
+ movl $0, (%rax)
+# SUCC: EXIT [100.0%]
+ ret
+.LFE0:
+ .size _start, .-_start
+#APP
+ .section .eh_frame,"a",@progbits
+.Lframe1:
+ .long .LECIE1-.LSCIE1 # Length of Common Information Entry
+.LSCIE1:
+ .long 0 # CIE Identifier Tag
+ .byte 0x3 # CIE Version
+ .ascii "zR\0" # CIE Augmentation
+ .uleb128 0x1 # CIE Code Alignment Factor
+ .sleb128 -8 # CIE Data Alignment Factor
+ .uleb128 0x10 # CIE RA Column
+ .uleb128 0x1 # Augmentation size
+ .byte 0x3 # FDE Encoding (udata4)
+ .byte 0xc # DW_CFA_def_cfa
+ .uleb128 0x7
+ .uleb128 0x8
+ .byte 0x90 # DW_CFA_offset, column 0x10
+ .uleb128 0x1
+ .align 8
+.LECIE1:
+.LSFDE1:
+ .long .LEFDE1-.LASFDE1 # FDE Length
+.LASFDE1:
+ .long .LASFDE1-.Lframe1 # FDE CIE offset
+ .long .LFB0 # FDE initial location
+ .long .LFE0-.LFB0 # FDE address range
+ .uleb128 0 # Augmentation size
+// BEGIN inserted data { DW_CFA_same_value + ULEB128 register }
+#define REG(n) \
+ .byte 0x8; \
+ .uleb128 n;
+REG(0)
+REG(1)
+REG(2)
+REG(3)
+REG(4)
+REG(5)
+REG(6)
+REG(7)
+REG(8)
+REG(9)
+REG(10)
+REG(11)
+REG(12)
+REG(13)
+REG(14)
+REG(15)
+REG(16)
+ .byte 0xc # DW_CFA_def_cfa
+ .uleb128 0x7
+ .uleb128 0x0
+// END inserted data
+ .align 8
+.LEFDE1:
+#NO_APP
+ .ident "GCC: (GNU) 4.8.2 20131212 (Red Hat 4.8.2-7)"
+ .section .note.GNU-stack,"",@progbits
diff --git a/tests/testfilecfaloop.bz2 b/tests/testfilecfaloop.bz2
new file mode 100644
index 00000000..ab30fcd0
--- /dev/null
+++ b/tests/testfilecfaloop.bz2
Binary files differ
diff --git a/tests/testfilecfaloop.c b/tests/testfilecfaloop.c
new file mode 100644
index 00000000..14a2fca8
--- /dev/null
+++ b/tests/testfilecfaloop.c
@@ -0,0 +1,22 @@
+/* Test program for run-cfaloop.sh.
+ Copyright (C) 2014 Red Hat, Inc.
+ This file is part of elfutils.
+
+ This file is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ elfutils is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+// gcc -o testfilecfaloop.S testfilecfaloop.c -Wall -O2 -dA -fno-dwarf2-cfi-asm -nostdlib -S
+void _start(void) {
+ volatile int *volatile p = 0;
+ *p = 0;
+}
diff --git a/tests/testfilecfaloop.core.bz2 b/tests/testfilecfaloop.core.bz2
new file mode 100644
index 00000000..923d5e56
--- /dev/null
+++ b/tests/testfilecfaloop.core.bz2
Binary files differ