<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 - CFI_Parser::findFDE's end-of-section calculation looks broken"
   href="https://bugs.llvm.org/show_bug.cgi?id=46829">46829</a>
          </td>
        </tr>

        <tr>
          <th>Summary</th>
          <td>CFI_Parser::findFDE's end-of-section calculation looks broken
          </td>
        </tr>

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

        <tr>
          <th>Version</th>
          <td>trunk
          </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>enhancement
          </td>
        </tr>

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

        <tr>
          <th>Component</th>
          <td>libunwind
          </td>
        </tr>

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

        <tr>
          <th>Reporter</th>
          <td>rprichard@google.com
          </td>
        </tr>

        <tr>
          <th>CC</th>
          <td>llvm-bugs@lists.llvm.org
          </td>
        </tr></table>
      <p>
        <div>
        <pre>CFI_Parser<A>::findFDE scans an .eh_frame section looking for an FDE matching a
PC. The start of .eh_frame is passed as an argument, ehSectionStart, and the
function calculates the end of .eh_frame (ehSectionEnd):

    /// Scan an eh_frame section to find an FDE for a pc
    template <typename A>
    bool CFI_Parser<A>::findFDE(A &addressSpace, pint_t pc, pint_t
ehSectionStart,
                                uint32_t sectionLength, pint_t fdeHint,
                                FDE_Info *fdeInfo, CIE_Info *cieInfo) {
      //fprintf(stderr, "findFDE(0x%llX)\n", (long long)pc);
      pint_t p = (fdeHint != 0) ? fdeHint : ehSectionStart;
      const pint_t ehSectionEnd = p + sectionLength;
      while (p < ehSectionEnd) {

There are two problems with the calculation:

 * AFAICT, when a hint is provided, ehSectionEnd should still use
ehSectionStart instead of p.

 * sectionLength comes from UnwindInfoSections::dwarf_section_length, which on
targets using dl_iterate_phdr, is the length of a PT_LOAD segment, not the
length of .eh_frame.

To expand on the second bullet point, UnwindInfoSections has (among others)
these five fields:

 - dso_base
 - dwarf_section
 - dwarf_section_length
 - dwarf_index_section
 - dwarf_index_section_length

dwarf_index_section and dwarf_index_section_length are the start and size of
the .eh_frame_hdr section, which indexes into .eh_frame and allows binary
searching.

I'm not sure what dso_base is, generally, but for targets that use
dl_iterate_phdr, it seems to be the start of a PT_LOAD segment associated with
the unwind info.

dwarf_section is always the start of .eh_frame.

For most configurations, dwarf_section_length is the size of .eh_frame, and
UnwindCursor::getInfoFromDwarfSection assumes this is the case when it calls
CFI_Parser::findFDE. For targets using dl_iterate_phdr, however,
dwarf_section_length is instead the size of the PT_LOAD segment indicated by
dso_base.

The recently-added FrameHeaderCache uses (dso_base, dwarf_section_length) to
indicate each cache entry's range of PC values. That happens to match how we're
initializing those fields. (I think initializing dwarf_section_length to the
PT_LOAD size predated the new cache.)

I'm unsure of the practical consequences. I suppose if the unwinder tries to
look up a function with no FDE, then it will enter this .eh_frame linear scan
fallback, and it could read past the end of .eh_frame, unless there's an end
marker (cfiLength == 0, "return false; // end marker").

---

Aside: On targets that use .eh_frame_hdr, it seems like a waste of code to scan
.eh_frame (and a waste of memory in FrameHeaderCache to track .eh_frame
bounds). I think these targets have to locate .eh_frame using .eh_frame_hdr, so
it shouldn't ever find a match. Some Apple targets are different, because the
compact unwind table can defer to using the .eh_frame scan
(compactSaysUseDwarf).

Aside 2: If the .eh_frame scan does find a match, it would add the FDE to
DwarfFDECache, whose entries are not invalidated on module unload (except with
Apple OS's). On Android, the lack of automatic per-module invalidation should
be OK as long as DwarfFDECache is only used for manually-registered FDEs
(__register_frame / __unw_add_dynamic_fde).

Aside 3: The DwarfFDECache offset in getInfoFromDwarfSection is not quite a
"hint". It's used as an offset into .eh_frame, so if it's wrong, we'll try to
decode arbitrary data and may crash. If the hinted-scan ends without a match,
then we try again with a complete .eh_frame scan. If *that* scan turns up an
FDE, then we add it to the end of DwarfFDECache. Adding an entry doesn't remove
the earlier entry, though, so AFAICT, each unwind lookup would find the bad
hint and add another good hint to the end.</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>