Index: libunwind/src/DwarfParser.hpp =================================================================== --- libunwind/src/DwarfParser.hpp +++ libunwind/src/DwarfParser.hpp @@ -360,13 +360,25 @@ PrologInfoStackEntry *rememberStack = NULL; // parse CIE then FDE instructions - return parseInstructions(addressSpace, cieInfo.cieInstructions, - cieInfo.cieStart + cieInfo.cieLength, cieInfo, - (pint_t)(-1), rememberStack, arch, results) && - parseInstructions(addressSpace, fdeInfo.fdeInstructions, - fdeInfo.fdeStart + fdeInfo.fdeLength, cieInfo, - upToPC - fdeInfo.pcStart, rememberStack, arch, - results); + bool returnValue = + parseInstructions(addressSpace, cieInfo.cieInstructions, + cieInfo.cieStart + cieInfo.cieLength, cieInfo, + (pint_t)(-1), rememberStack, arch, results) && + parseInstructions(addressSpace, fdeInfo.fdeInstructions, + fdeInfo.fdeStart + fdeInfo.fdeLength, cieInfo, + upToPC - fdeInfo.pcStart, rememberStack, arch, results); + + // Clean up rememberStack. Even in the case where every DW_CFA_remember_state + // is paired with a DW_CFA_restore_state, parseInstructions can skip restore + // opcodes if it reaches the target PC and stops interpreting, so we have to + // make sure we don't leak memory. + while (rememberStack) { + PrologInfoStackEntry *next = rememberStack->next; + free(rememberStack); + rememberStack = next; + } + + return returnValue; } /// "run" the DWARF instructions Index: libunwind/test/remember_state_leak.pass.sh.s =================================================================== --- /dev/null +++ libunwind/test/remember_state_leak.pass.sh.s @@ -0,0 +1,57 @@ +# REQUIRES: linux +# RUN: %build +# RUN: %run + +# The following main function is a translation of this code: +# +# int main() { +# asm(".cfi_remember_state\n\t"); +# _Unwind_Exception exc; +# _Unwind_ForcedUnwind(&exc, callback, 0); +# asm(".cfi_restore_state\n\t"); +# } +# +# When unwinding, the CFI parser will stop parsing opcodes after the current PC, +# so in this case the DW_CFA_restore_state opcode will never be processed and, +# if the library doesn't clean up properly, the store allocated by +# DW_CFA_remember_state will be leaked. +# +# This test will fail when linked with an asan-enabled libunwind if the +# remembered state is leaked. + +.text + +callback: + xorl %eax, %eax + retq + +.globl main # -- Begin function main +.p2align 4, 0x90 +.type main,@function +main: # @main +.cfi_startproc +# %bb.0: +pushq %rbp +.cfi_def_cfa_offset 16 +.cfi_offset %rbp, -16 +movq %rsp, %rbp +.cfi_def_cfa_register %rbp +subq $48, %rsp +.cfi_remember_state +xorl %eax, %eax +movl %eax, %edx +leaq -32(%rbp), %rdi +movabsq $callback, %rsi +callq _Unwind_ForcedUnwind +.cfi_restore_state +xorl %ecx, %ecx +movl %eax, -36(%rbp) # 4-byte Spill +movl %ecx, %eax +addq $48, %rsp +popq %rbp +.cfi_def_cfa %rsp, 8 +retq +.Lfunc_end1: +.size main, .Lfunc_end1-main +.cfi_endproc + # -- End function