| Issue |
185025
|
| Summary |
[PAC][libunwind] Signing oracles in `findUnwindSectionsByPhdr`
|
| Labels |
libunwind
|
| Assignees |
atrosinenko
|
| Reporter |
atrosinenko
|
Two signing oracles are reported by PAuth gadget scanner of `llvm-bolt-binary-analysis` (see https://github.com/llvm/llvm-project/issues/165244#issuecomment-4007695426 for the details) for `findUnwindSectionsByPhdr` function from [`AddressSpace.hpp`](https://github.com/llvm/llvm-project/blob/24ac5987b482edb33b73f0ebff509e0a520eca1c/libunwind/src/AddressSpace.hpp#L456). The reports actually belong to two other `static` functions called by `findUnwindSectionsByPhdr`, here are the sources (lines that are always removed by the preprocessor when `_LIBUNWIND_SUPPORT_DWARF_INDEX` is defined are omitted for clarity):
```cpp
static bool checkAddrInSegment(const Elf_Phdr *phdr, size_t image_base,
dl_iterate_cb_data *cbdata) {
if (phdr->p_type == PT_LOAD) {
uintptr_t begin = image_base + phdr->p_vaddr;
uintptr_t end = begin + phdr->p_memsz;
if (cbdata->targetAddr >= begin && cbdata->targetAddr < end) {
cbdata->sects->dso_base = begin; // <-- signing oracle
cbdata->sects->text_segment_length = phdr->p_memsz;
return true;
}
}
return false;
}
static bool checkForUnwindInfoSegment(const Elf_Phdr *phdr, size_t image_base,
dl_iterate_cb_data *cbdata) {
if (phdr->p_type == PT_GNU_EH_FRAME) {
EHHeaderParser<LocalAddressSpace>::EHHeaderInfo hdrInfo;
uintptr_t eh_frame_hdr_start = image_base + phdr->p_vaddr;
cbdata->sects->dwarf_index_section = eh_frame_hdr_start;
cbdata->sects->dwarf_index_section_length = phdr->p_memsz;
if (EHHeaderParser<LocalAddressSpace>::decodeEHHdr(
*cbdata->addressSpace, eh_frame_hdr_start,
eh_frame_hdr_start + phdr->p_memsz, hdrInfo)) {
// .eh_frame_hdr records the start of .eh_frame, but not its size.
// Rely on a zero terminator to find the end of the section.
cbdata->sects->dwarf_section = hdrInfo.eh_frame_ptr; // <-- signing oracle
cbdata->sects->dwarf_section_length = SIZE_MAX;
return true;
}
}
return false;
}
static int findUnwindSectionsByPhdr(struct dl_phdr_info *pinfo,
size_t pinfo_size, void *data) {
auto cbdata = static_cast<dl_iterate_cb_data *>(data);
if (pinfo->dlpi_phnum == 0 || cbdata->targetAddr < pinfo->dlpi_addr)
return 0;
#if defined(_LIBUNWIND_USE_FRAME_HEADER_CACHE)
if (TheFrameHeaderCache.find(pinfo, pinfo_size, data))
return 1;
#else
// Avoid warning about unused variable.
(void)pinfo_size;
#endif
Elf_Addr image_base = pinfo->dlpi_addr;
// Most shared objects seen in this callback function likely don't contain the
// target address, so optimize for that. Scan for a matching PT_LOAD segment
// first and bail when it isn't found.
bool found_text = false;
for (Elf_Half i = 0; i < pinfo->dlpi_phnum; ++i) {
if (checkAddrInSegment(&pinfo->dlpi_phdr[i], image_base, cbdata)) {
found_text = true;
break;
}
}
if (!found_text)
return 0;
// PT_GNU_EH_FRAME and PT_ARM_EXIDX are usually near the end. Iterate
// backward.
bool found_unwind = false;
for (Elf_Half i = pinfo->dlpi_phnum; i > 0; i--) {
const Elf_Phdr *phdr = &pinfo->dlpi_phdr[i - 1];
if (checkForUnwindInfoSegment(phdr, image_base, cbdata)) {
found_unwind = true;
break;
}
}
if (!found_unwind)
return 0;
#if defined(_LIBUNWIND_USE_FRAME_HEADER_CACHE)
TheFrameHeaderCache.add(cbdata->sects);
#endif
return 1;
}
```
[`dso_base`](https://github.com/llvm/llvm-project/blob/24ac5987b482edb33b73f0ebff509e0a520eca1c/libunwind/src/AddressSpace.hpp#L132) and [`dwarf_section`](https://github.com/llvm/llvm-project/blob/24ac5987b482edb33b73f0ebff509e0a520eca1c/libunwind/src/AddressSpace.hpp#L140) are the fields of `struct UnwindInfoSections` defined in the same file.
The first oracle corresponds to assigning an initially untrusted value (sum of `dl_phdr_info::dlpi_addr` and `Elf64_Phdr::p_vaddr` fields of structures returned by the dynamic linker).
The second oracle corresponds to copying from an unprotected `EHHeaderInfo::eh_frame_ptr` to `__ptrauth`-qualified `dwarf_section` field. Here, it should be possible to strictly improve the protection by providing a strong signing schema for `EHHeaderInfo::eh_frame_ptr` which is only assigned once, in [`EHHeaderParser::decodeEHHdr`](https://github.com/llvm/llvm-project/blob/24ac5987b482edb33b73f0ebff509e0a520eca1c/libunwind/src/EHHeaderParser.hpp#L83). There still would be a signing oracle in the latter function, thus such change merely extends the time span when this variable is protected.
_______________________________________________
llvm-bugs mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-bugs