<html>
    <head>
      <base href="https://bugs.llvm.org/">
    </head>
    <body><table border="1" cellspacing="0" cellpadding="8">
        <tr>
          <th>Bug ID</th>
          <td><a class="bz_bug_link 
          bz_status_NEW "
   title="NEW - libunwind: Segfault at the end of _Unwind_Backtrace"
   href="https://bugs.llvm.org/show_bug.cgi?id=36005">36005</a>
          </td>
        </tr>

        <tr>
          <th>Summary</th>
          <td>libunwind: Segfault at the end of _Unwind_Backtrace
          </td>
        </tr>

        <tr>
          <th>Product</th>
          <td>Runtime Libraries
          </td>
        </tr>

        <tr>
          <th>Version</th>
          <td>6.0
          </td>
        </tr>

        <tr>
          <th>Hardware</th>
          <td>PC
          </td>
        </tr>

        <tr>
          <th>OS</th>
          <td>Linux
          </td>
        </tr>

        <tr>
          <th>Status</th>
          <td>NEW
          </td>
        </tr>

        <tr>
          <th>Severity</th>
          <td>release blocker
          </td>
        </tr>

        <tr>
          <th>Priority</th>
          <td>P
          </td>
        </tr>

        <tr>
          <th>Component</th>
          <td>other
          </td>
        </tr>

        <tr>
          <th>Assignee</th>
          <td>unassignedbugs@nondot.org
          </td>
        </tr>

        <tr>
          <th>Reporter</th>
          <td>Andrew.Caldwell@metaswitch.com
          </td>
        </tr>

        <tr>
          <th>CC</th>
          <td>llvm-bugs@lists.llvm.org
          </td>
        </tr></table>
      <p>
        <div>
        <pre># Headline

libunwind miscalculates the length of the .eh_frame section and sometimes reads
off the end of the containing segment, leading to segfaults and sadness.

# Discovery

We discovered this in a rust project at our company, the program would segfault
deep in _Unwind_Backtrace but only in a few specific circumstances:

 * Compiling against the MUSL libc, which implies:
     * Static linking of all libraries
     * Using a static build of libunwind from LLVM (version 3.9)
 * Only one of our codebases
     * Attempts to reproduce in a more controlled environment have all failed
     * The codebase in question is **big** resulting in an 84Mb binary

# Backtrace (courtesy of GDB + line number links reverse engineered... Rustc
builds libunwind without debug symbols)

```
#0  0x0000000000fa733b in libunwind::LocalAddressSpace::get32(unsigned long) ()
   
<a href="https://github.com/llvm-mirror/libunwind/blob/release_39/src/AddressSpace.hpp#L131">https://github.com/llvm-mirror/libunwind/blob/release_39/src/AddressSpace.hpp#L131</a>
#1  0x0000000000faa0a2 in
libunwind::CFI_Parser<libunwind::LocalAddressSpace>::findFDE(libunwind::LocalAddressSpace&,
unsigned long, unsigned long, unsigned int, unsigned long,
libunwind::CFI_Parser<libunwind::LocalAddressSpace>::FDE_Info*,
libunwind::CFI_Parser<libunwind::LocalAddressSpace>::CIE_Info*) ()
   
<a href="https://github.com/llvm-mirror/libunwind/blob/release_39/src/DwarfParser.hpp#L184">https://github.com/llvm-mirror/libunwind/blob/release_39/src/DwarfParser.hpp#L184</a>
#2  0x0000000000fa983d in libunwind::UnwindCursor<libunwind::LocalAddressSpace,
libunwind::Registers_x86_64>::getInfoFromDwarfSection(unsigned long,
libunwind::UnwindInfoSections const&, unsigned int) ()
   
<a href="https://github.com/llvm-mirror/libunwind/blob/release_39/src/UnwindCursor.hpp#L900">https://github.com/llvm-mirror/libunwind/blob/release_39/src/UnwindCursor.hpp#L900</a>
#3  0x0000000000fa923d in libunwind::UnwindCursor<libunwind::LocalAddressSpace,
libunwind::Registers_x86_64>::setInfoBasedOnIPRegister(bool) ()
   
<a href="https://github.com/llvm-mirror/libunwind/blob/release_39/src/UnwindCursor.hpp#L1241">https://github.com/llvm-mirror/libunwind/blob/release_39/src/UnwindCursor.hpp#L1241</a>
#4  0x0000000000fa8fff in libunwind::UnwindCursor<libunwind::LocalAddressSpace,
libunwind::Registers_x86_64>::step() ()
#5  0x0000000000fa820e in unw_step ()
#6  0x0000000000fa6ac0 in _Unwind_Backtrace ()
```

# Analysis

* Looking at frame #1 it appears that, although `p < ehSectionEnd`, it points
into invalid memory, playing around suggests that `ehSectionEnd` is massively
beyond the range of readable memory.
* Tracking back, we see that `ehSectionEnd` is `sects.dwarf_section_length`
which is populated by the code at
<a href="https://github.com/llvm-mirror/libunwind/blob/release_39/src/AddressSpace.hpp#L390-L441">https://github.com/llvm-mirror/libunwind/blob/release_39/src/AddressSpace.hpp#L390-L441</a>
(called from
<a href="https://github.com/llvm-mirror/libunwind/blob/release_39/src/UnwindCursor.hpp#L1214">https://github.com/llvm-mirror/libunwind/blob/release_39/src/UnwindCursor.hpp#L1214</a>).
* This code is a little weird, and I'm not 100% it's doing anything sensible,
but I've assumed it's doing the following:
    * Searching for a `LOAD` segment that contains the address we're interested
in and remembering this segment's base address and length
    * Searching for a `GNU_EH_FRAME` segment, parsing it (assuming it contains
exactly one `.eh_frame_ptr` section, which I guess is the spec for a segment of
this type) to find the location of the `.eh_frame` section
    * Assuming that the `.eh_frame` points into the LOAD segment we found (not
sure this is guaranteed, but maybe?)
    * Storing off the `.eh_frame` section's length by assuming it's the `LOAD`
segment's `memsz`
* This last bullet is where I think the bug lies, the `.eh_frame` is not always
(never?) the first section in the `LOAD` segment, so the length of that section
is strictly less than the size of the `LOAD` segment

# Proposed fix

Change
<a href="https://github.com/llvm-mirror/libunwind/blob/release_39/src/AddressSpace.hpp#L434">https://github.com/llvm-mirror/libunwind/blob/release_39/src/AddressSpace.hpp#L434</a>
to:

    cbdata->sects->dwarf_section_length = object_length -
(cbdata->sects->dwarf_section - cbdata->sects->dso_base);

This is better than the existing code (since the span from
`cbdata->sects->dwarf_section` to `cbdata->sects->dwarf_section +
cbdata->sects->dwarf_section_length` is all contained in the `LOAD` segment and
is thus valid memory, but it's still not perfectly correct (as that span
contains any sections after the `.eh_frame` section in the `LOAD` segment). 
I'm not sure there is a way to determine the end of a `.eh_frame` section,
otherwise I'd recommend using that here.  OTOH this change prevented the
segfault I was seeing, without affecting backtrace generation.

# Affected versions

I found this on version 3.9, but the issue is still present in version 6.0 and
seems to have been around since at least version 3.7 (and all versions in
between).

# Platform

Linux Mint 18.1 Serena (based on Ubuntu 16.04 Xenial Xerus) on x86_64.  Also
seen on Centos 7 on x86_64.  Segfaulting code compiled with Rustc version
1.22.0, 1.22.1, 1.23.0, 1.24.0-beta or 1.25.0-nightly.

# Open Questions

* Why doesn't this always crash?
* Why do we only see this in `musl`-targeting Rust builds?
     * Is it simply that musl binaries are statically linked and thus bigger
and this means the section layout is different?
* Is there a way to spot the end of the `.eh_frame` section?
     * Is it always followed by a `.eh_frame_ptr` section?  Can we distinguish
the two?
     * Is there a "this section is XXX long" or "this section contains XXX
records header?</pre>
        </div>
      </p>


      <hr>
      <span>You are receiving this mail because:</span>

      <ul>
          <li>You are on the CC list for the bug.</li>
      </ul>
    </body>
</html>