From 305705207e0831e6387706065e59f91296ba0d0a Mon Sep 17 00:00:00 2001 From: Jan Kratochvil Date: Fri, 3 Jan 2014 22:06:12 +0100 Subject: [PATCH] Detect infinite backtraces. libdwfl/ 2014-01-03 Jan Kratochvil 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 * 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 --- libdwfl/dwfl_frame.c | 3 +- libdwfl/frame_unwind.c | 46 +++++++++++------ libdwfl/libdwflP.h | 7 ++- tests/Makefile.am | 8 +-- tests/run-cfaloop.sh | 22 ++++++++ tests/testfilecfaloop.S | 90 +++++++++++++++++++++++++++++++++ tests/testfilecfaloop.bz2 | Bin 0 -> 640 bytes tests/testfilecfaloop.c | 22 ++++++++ tests/testfilecfaloop.core.bz2 | Bin 0 -> 5765 bytes 9 files changed, 177 insertions(+), 21 deletions(-) create mode 100755 tests/run-cfaloop.sh create mode 100644 tests/testfilecfaloop.S create mode 100644 tests/testfilecfaloop.bz2 create mode 100644 tests/testfilecfaloop.c create mode 100644 tests/testfilecfaloop.core.bz2 diff --git a/libdwfl/dwfl_frame.c b/libdwfl/dwfl_frame.c index 28008e903..89fd92059 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 3ce45479b..d434c4687 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, ®val, bias)) + else if (! expr_eval (state, reg_ops, reg_nops, ®val, 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 63615a8a5..5f6bbcddf 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 52eb50aab..ac83d9c49 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 000000000..afad5dba5 --- /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 . + +. $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 000000000..f4108a278 --- /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 . */ + + .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 0000000000000000000000000000000000000000..ab30fcd07f4d76bbdf6a1b3f5ec49a7dacf6d921 GIT binary patch literal 640 zc-jGg0)PENT4*^jL0KkKSphLXJpcjcfB*mg{@hjPeA!>sazOv*-f+MOKwv-s1Yrok zKmYtM14@scN834osKm$gDO*F^=(?v~2$kfvlLqGsD$&)64G-;p!WY7kh8UO|x!9+4ikd=&TmWU9Bo54(GL?j4+)v;VQ za8=L3X44a#{?BL}q)|BEEpld_a(58$$qkiqd7fA=Jo^eMm%|KDbtR5q{fWvl_v! att%KHfp|NFJ. */ + +// 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 0000000000000000000000000000000000000000..923d5e566200df60988e5880dcc2c268adbef4eb GIT binary patch literal 5765 zc-jGl7JBJIT4*^jL0KkKS)fagKmZvvfB*mg|Nj5~|NsC0|NsC0|NsC0*L(lx_x=C> z|9}7g|Nr0=eckf!a$pAaoh|Ne*LLeY-Rpa{27qU`nGbe*vF{f%Kr`3?0A+WBtexJS z>8>G?5SjvFGEIdwJx^$9F;8VrXqr>>Vw)jP)Ov{gsqGqRsL7IkqUx=^+NX+nGzkv~YG}}CXb(s<2dHQ?Gz~P+4GkIqWB|};8V^%I&}aid00006fDcdr z8WSQkMNit8l>C!vN9v5!8eok-QU;9%ng^%=000^R>KO)r00E|e$N=>K00T_`00T^c zpa3*90z^njlT$=AdIa>;^%)bv3FS0nLLaDljUJ*fjEwY%(@#^-4NL%=)DzUr0UBXF zO*T-#fYTEb1Y%~B2*|=|kPIf606+jV01W^D27mwn0000q0000027nC!00000000Jn z00E!?0FgofjQ|ADo|8=`Oif0fO)2Vrl=D+W{WVWdMACYjP&CjzAOO=%10kc-^*vA3 zJxA(*(9mh8q#mY&LFxcKPf!2=XaL9nXfXJ%)>8h3<+$8(yvgk3Rg&3oy}P$znYM$S zPV5k}J@iVom;sE%U{QLCOcjHS>!?detQPXSg0N&q93)0oMk18RpD%1$+NSMp+sNj} z`0%4Jysd_KdECwmEHEtw^(aho8{F(P=v%dJn>*YRDZOV7yJb_ynh`SU)r{rM$v2>| z)e1cyDtWKIVV^v9JMo1Ki|3k%7W54#H0K!&>NhSgUt=I==#dXw+8)YNN}7R|2JfB^ z!V;>?2?lbz_+H)qLs;#MjfX31<2kOLFEYWqxY$tJkevDO8Emj-Gx^!U!IJ{j+uc39 zg=I5YJtr|x*s^Z7?zJTxCX92+>EpRwGeLtY`5ihh_(T@ZK z1G)A2w(KWMh21Z;(?{;KcP);jprDB^zNu_T{obZq1OY3IrIpK2T&WmrA2OYRVp&0{ zPbD8cb#U~`X^IRKevWXaKp6Hrs@R*rlPd zD)jIR>Kfc_H{M#$m9LbfYkvKYH*yDMAuq7P?*P;F5Gg-3$vgu$M%8?d6(r9jF*ZMxyySOsr zJcFmC;3OHSMHCkgE~yC`wkTq3EEvC0p5LaXkl2<}DmKillw&+1AZeyIm4>1kLus?a z%#kR*A_D|1grx?2VA1F8)xZ2-DW-+mztm>79D#V~XSaUkG#?=`(n!TQ!wo`m9@~X$ z(%Yv-NODpU@tUE>*2dhv{ydovjD)NZ;zf%}+rvF+PfYS`GjC#P-HXlmg`vPUb&V!P z-q~kPddgOEmrX1yU%aj0Tr>q>JopI-Wm5+($@tJCV&#EI5LLV08;o3Rshy_j(~8=x zsN{K#Ay&S0c(%veK2VQ~2slPxj56PFV7MG) zU^CN)w$w`-wMgsqkQ(De_k9ebAsl#B9kZob$&wCobpAb2Rl(K-B>htQ z;>0PS59S6FVA3wzv|=QT8#I*3WATwU#d8xUYJ6??jNW;m(?t@cUB_DSV>s}8@sJ&N zb*R+rWAq7Ug&q6QvT(u&kEvIYc=SAEe^waZCV(~+CKE-HX1WH%C5!CIYFnt?#QDlo zg!XH;Cc8;);~QE}OfkF9dLa{71gqOOWUvaJ8|d|~xXzBpn4n(Xo-W)@n?1GM32wzg zVsfl)%^tgxHQRdeZRgyGti=7Ok)fucLbGjfs10`E%k2qNmSyr&$HL5OA{c*Y@4dnL zX1`suJO$tF-2K~kfgr%r4JBIHM%zEM{Y85Yn+I39kMB&NR1g9VLST8oQ2-BR!Xpu3 z?%9(RlbGClTX%yYjT@vThcTeYMRin_l!j67ZNBeUbatmoZZ*Ht4z;uu!SXCUqG?z$PP^w+8i+CkS=5D`sq z36C`pCbNKh&#y66P^hHU7&O`#z{+BR&hgb{auKO@an%JcLX=u1PBdCyx>IrQW!@$( zjd$&g_mKp;sF!h(5sg%lEfDSeSO~6uCaQ&2Au

0ga$Z^2!hj9mmGu>EO>0@1AsK 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 D=5F$~ literal 0 Hc-jL100001 -- 2.47.2