Skip to content

Commit 5dbba89

Browse files
pccrocallahan
authored andcommitted
Fix resolve_address to account for multiple mappings of the same file offset
Some linkers such as lld will create program headers with multiple mappings of the same file offset. This can lead to problems when a symbol of interest to rr, such as __aarch64_ldadd4_relax, is covered by more than one mapping, as that will lead to us finding the function in multiple mappings. For that symbol in particular, we can end up misinterpreting the instructions in the wrong mapping and incorrectly computing an address to write to, which can lead to an assertion failure, or worse, silent memory corruption. Fix it by changing resolve_address to check whether the mapping is the correct one (fully covers the appropriate program header and has the same memory permissions) before returning the address.
1 parent 028e59b commit 5dbba89

File tree

5 files changed

+82
-59
lines changed

5 files changed

+82
-59
lines changed

src/ElfReader.cc

Lines changed: 14 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ class ElfReaderImplBase {
2828
virtual Debugaltlink read_debugaltlink() = 0;
2929
virtual string read_buildid() = 0;
3030
virtual string read_interp() = 0;
31-
virtual bool addr_to_offset(uintptr_t addr, uintptr_t& offset) = 0;
31+
virtual bool addr_to_phdr(uintptr_t addr, PhdrInfo& phdr) = 0;
3232
virtual SectionOffsets find_section_file_offsets(const char* name) = 0;
3333
virtual const vector<uint8_t>* decompress_section(SectionOffsets offsets) = 0;
3434
bool ok() { return ok_; }
@@ -49,7 +49,7 @@ template <typename Arch> class ElfReaderImpl : public ElfReaderImplBase {
4949
virtual Debugaltlink read_debugaltlink() override;
5050
virtual string read_buildid() override;
5151
virtual string read_interp() override;
52-
virtual bool addr_to_offset(uintptr_t addr, uintptr_t& offset) override;
52+
virtual bool addr_to_phdr(uintptr_t addr, PhdrInfo& phdr) override;
5353
virtual SectionOffsets find_section_file_offsets(const char* name) override;
5454
virtual const vector<uint8_t>* decompress_section(SectionOffsets offsets) override;
5555

@@ -518,20 +518,19 @@ string ElfReaderImpl<Arch>::read_interp() {
518518
}
519519

520520
template <typename Arch>
521-
bool ElfReaderImpl<Arch>::addr_to_offset(uintptr_t addr, uintptr_t& offset) {
522-
for (size_t i = 0; i < sections_size; ++i) {
523-
const auto& section = sections[i];
524-
// Skip the section if it either "occupies no space in the file" or
525-
// doesn't have a valid address because it does not "occupy memory
526-
// during process execution".
527-
if (section.sh_type == SHT_NOBITS || !(section.sh_flags & SHF_ALLOC)) {
528-
continue;
529-
}
530-
if (addr >= section.sh_addr && addr - section.sh_addr < section.sh_size) {
531-
offset = addr - section.sh_addr + section.sh_offset;
521+
bool ElfReaderImpl<Arch>::addr_to_phdr(uintptr_t addr, PhdrInfo& phdr) {
522+
for (size_t i = 0; i < programheader_size; ++i) {
523+
auto& p = programheader[i];
524+
if (p.p_type == PT_LOAD && addr >= p.p_vaddr &&
525+
addr - p.p_vaddr < p.p_memsz) {
526+
phdr.vaddr = p.p_vaddr;
527+
phdr.offset = p.p_offset;
528+
phdr.filesz = p.p_filesz;
529+
phdr.flags = p.p_flags;
532530
return true;
533531
}
534532
}
533+
535534
return false;
536535
}
537536

@@ -573,8 +572,8 @@ DwarfSpan ElfReader::dwarf_section(const char* name, bool known_to_be_compressed
573572
string ElfReader::read_buildid() { return impl().read_buildid(); }
574573
string ElfReader::read_interp() { return impl().read_interp(); }
575574

576-
bool ElfReader::addr_to_offset(uintptr_t addr, uintptr_t& offset) {
577-
return impl().addr_to_offset(addr, offset);
575+
bool ElfReader::addr_to_phdr(uintptr_t addr, PhdrInfo& phdr) {
576+
return impl().addr_to_phdr(addr, phdr);
578577
}
579578

580579
bool ElfReader::ok() { return impl().ok(); }

src/ElfReader.h

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,13 @@ struct SectionOffsets {
7474
bool compressed;
7575
};
7676

77+
struct PhdrInfo {
78+
uint64_t vaddr;
79+
uint64_t offset;
80+
uint64_t filesz;
81+
uint64_t flags;
82+
};
83+
7784
class ElfReader {
7885
public:
7986
ElfReader(SupportedArch arch);
@@ -102,11 +109,11 @@ class ElfReader {
102109
Debugaltlink read_debugaltlink();
103110
std::string read_buildid();
104111
std::string read_interp();
105-
// Returns true and sets file |offset| if ELF address |addr| is mapped from
106-
// a section in the ELF file. Returns false if no section maps to
107-
// |addr|. |addr| is an address indicated by the ELF file, not its
108-
// relocated address in memory.
109-
bool addr_to_offset(uintptr_t addr, uintptr_t& offset);
112+
// Returns true if ELF address |addr| is mapped from a program header in the
113+
// ELF file, and sets the fields of |phdr| to the program header fields.
114+
// Returns false if no program header maps to |addr|. |addr| is an address
115+
// indicated by the ELF file, not its relocated address in memory.
116+
bool addr_to_phdr(uintptr_t addr, PhdrInfo& phdr);
110117
SectionOffsets find_section_file_offsets(const char* name);
111118
DwarfSpan dwarf_section(const char* name, bool known_to_be_compressed = false);
112119
SupportedArch arch() const { return arch_; }

src/Monkeypatcher.cc

Lines changed: 48 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
#include "Monkeypatcher.h"
44

5+
#include <elf.h>
56
#include <limits.h>
67
#include <linux/auxvec.h>
78

@@ -1423,9 +1424,8 @@ void patch_after_exec_arch<X64Arch>(RecordTask* t, Monkeypatcher& patcher) {
14231424

14241425
for (const auto& m : t->vm()->maps()) {
14251426
auto& km = m.map;
1426-
patcher.patch_after_mmap(t, km.start(), km.size(),
1427-
km.file_offset_bytes(), -1,
1428-
Monkeypatcher::MMAP_EXEC);
1427+
patcher.patch_after_mmap(t, km.start(), km.size(), km.file_offset_bytes(),
1428+
km.prot(), -1, Monkeypatcher::MMAP_EXEC);
14291429
}
14301430

14311431
if (!t->vm()->has_vdso()) {
@@ -1443,9 +1443,8 @@ void patch_after_exec_arch<ARM64Arch>(RecordTask* t, Monkeypatcher& patcher) {
14431443

14441444
for (const auto& m : t->vm()->maps()) {
14451445
auto& km = m.map;
1446-
patcher.patch_after_mmap(t, km.start(), km.size(),
1447-
km.file_offset_bytes(), -1,
1448-
Monkeypatcher::MMAP_EXEC);
1446+
patcher.patch_after_mmap(t, km.start(), km.size(), km.file_offset_bytes(),
1447+
km.prot(), -1, Monkeypatcher::MMAP_EXEC);
14491448
}
14501449

14511450
if (!t->vm()->has_vdso()) {
@@ -1498,27 +1497,46 @@ void Monkeypatcher::patch_at_preload_init(RecordTask* t) {
14981497

14991498
static remote_ptr<void> resolve_address(ElfReader& reader, uintptr_t elf_addr,
15001499
remote_ptr<void> map_start,
1501-
size_t map_size,
1502-
uintptr_t map_offset) {
1503-
uintptr_t file_offset;
1504-
if (!reader.addr_to_offset(elf_addr, file_offset)) {
1500+
size_t map_size, uintptr_t map_offset,
1501+
uint64_t prot) {
1502+
PhdrInfo phdr;
1503+
if (!reader.addr_to_phdr(elf_addr, phdr)) {
15051504
LOG(warn) << "ELF address " << HEX(elf_addr) << " not in file";
15061505
}
1507-
if (file_offset < map_offset || file_offset + 32 > map_offset + map_size) {
1508-
// The value(s) to be set are outside the mapped range. This happens
1509-
// because code and data can be mapped in separate, partial mmaps in which
1510-
// case some symbols will be outside the mapped range.
1506+
if (phdr.offset < map_offset ||
1507+
phdr.offset + phdr.filesz > map_offset + map_size ||
1508+
bool(phdr.flags & PF_R) != bool(prot & PROT_READ) ||
1509+
bool(phdr.flags & PF_W) != bool(prot & PROT_WRITE) ||
1510+
bool(phdr.flags & PF_X) != bool(prot & PROT_EXEC)) {
1511+
// The value(s) to be set are outside the mapped range or belong to another
1512+
// mapping as indicated by the flags. This happens because code and data can
1513+
// be mapped in separate, partial mmaps in which case some symbols will be
1514+
// outside the mapped range.
15111515
return nullptr;
15121516
}
1513-
return map_start + uintptr_t(file_offset - map_offset);
1517+
1518+
// This is the distance from the file offset of the mapping to p_offset, which
1519+
// is the same as the distance from map_start to p_vaddr + load bias.
1520+
auto page_start_offset = phdr.offset - map_offset;
1521+
1522+
// Adjust map_start so that it points to p_vaddr + load bias.
1523+
map_start += page_start_offset;
1524+
1525+
// Now subtract p_vaddr to make it point to virtual address 0 (the load bias).
1526+
map_start -= phdr.vaddr;
1527+
1528+
// Finally, add the address from the symbol table to adjust it by the load
1529+
// bias.
1530+
return map_start + elf_addr;
15141531
}
15151532

15161533
static void set_and_record_bytes(RecordTask* t, ElfReader& reader,
15171534
uintptr_t elf_addr, const void* bytes,
15181535
size_t size, remote_ptr<void> map_start,
1519-
size_t map_size, size_t map_offset) {
1536+
size_t map_size, size_t map_offset,
1537+
uint64_t prot) {
15201538
remote_ptr<void> addr =
1521-
resolve_address(reader, elf_addr, map_start, map_size, map_offset);
1539+
resolve_address(reader, elf_addr, map_start, map_size, map_offset, prot);
15221540
if (!addr) {
15231541
return;
15241542
}
@@ -1539,13 +1557,13 @@ static void set_and_record_bytes(RecordTask* t, ElfReader& reader,
15391557
void Monkeypatcher::patch_dl_runtime_resolve(RecordTask* t, ElfReader& reader,
15401558
uintptr_t elf_addr,
15411559
remote_ptr<void> map_start,
1542-
size_t map_size,
1543-
size_t map_offset) {
1560+
size_t map_size, size_t map_offset,
1561+
uint64_t prot) {
15441562
if (t->arch() != x86_64) {
15451563
return;
15461564
}
15471565
remote_ptr<void> addr =
1548-
resolve_address(reader, elf_addr, map_start, map_size, map_offset);
1566+
resolve_address(reader, elf_addr, map_start, map_size, map_offset, prot);
15491567
if (!addr) {
15501568
return;
15511569
}
@@ -1641,14 +1659,13 @@ static bool is_aarch64_ldrb(uint32_t instruction, uint32_t* offset) {
16411659
* Patch the __aarch64_have_lse_atomics variable to ensure that LSE atomics are
16421660
* always used even if init_lse_atomics
16431661
*/
1644-
void Monkeypatcher::patch_aarch64_have_lse_atomics(RecordTask* t, ElfReader& reader,
1645-
uintptr_t ldadd4_addr,
1646-
remote_ptr<void> map_start,
1647-
size_t map_size,
1648-
size_t map_offset) {
1662+
void Monkeypatcher::patch_aarch64_have_lse_atomics(
1663+
RecordTask* t, ElfReader& reader, uintptr_t ldadd4_addr,
1664+
remote_ptr<void> map_start, size_t map_size, size_t map_offset,
1665+
uint64_t prot) {
16491666
ASSERT(t, t->arch() == aarch64);
16501667
remote_ptr<void> addr =
1651-
resolve_address(reader, ldadd4_addr, map_start, map_size, map_offset);
1668+
resolve_address(reader, ldadd4_addr, map_start, map_size, map_offset, prot);
16521669
if (!addr) {
16531670
return;
16541671
}
@@ -1694,7 +1711,7 @@ static bool file_may_need_instrumentation(const AddressSpace::Mapping& map) {
16941711
}
16951712

16961713
void Monkeypatcher::patch_after_mmap(RecordTask* t, remote_ptr<void> start,
1697-
size_t size, size_t offset_bytes,
1714+
size_t size, size_t offset_bytes, uint64_t prot,
16981715
int child_fd, MmapMode mode) {
16991716
const auto& map = t->vm()->mapping_of(start);
17001717
if (!file_may_need_instrumentation(map)) {
@@ -1747,7 +1764,7 @@ void Monkeypatcher::patch_after_mmap(RecordTask* t, remote_ptr<void> start,
17471764
// pthread rwlocks don't try to use elision at all. See ELIDE_LOCK
17481765
// in glibc's elide.h.
17491766
set_and_record_bytes(t, reader, syms.addr(i) + 8, &zero, sizeof(zero),
1750-
start, size, offset_bytes);
1767+
start, size, offset_bytes, prot);
17511768
}
17521769
if (syms.is_name(i, "elision_init")) {
17531770
// Make elision_init return without doing anything. This means
@@ -1756,7 +1773,7 @@ void Monkeypatcher::patch_after_mmap(RecordTask* t, remote_ptr<void> start,
17561773
// elision-conf.c.
17571774
static const uint8_t ret = 0xC3;
17581775
set_and_record_bytes(t, reader, syms.addr(i), &ret, sizeof(ret), start,
1759-
size, offset_bytes);
1776+
size, offset_bytes, prot);
17601777
}
17611778
// The following operations can only be applied once because after the
17621779
// patch is applied the code no longer matches the expected template.
@@ -1768,15 +1785,15 @@ void Monkeypatcher::patch_after_mmap(RecordTask* t, remote_ptr<void> start,
17681785
syms.is_name(i, "_dl_runtime_resolve_xsave") ||
17691786
syms.is_name(i, "_dl_runtime_resolve_xsavec"))) {
17701787
patch_dl_runtime_resolve(t, reader, syms.addr(i), start, size,
1771-
offset_bytes);
1788+
offset_bytes, prot);
17721789
}
17731790
}
17741791
break;
17751792
case aarch64:
17761793
for (size_t i = 0; i < syms.size(); ++i) {
17771794
if (syms.is_name(i, "__aarch64_ldadd4_relax")) {
17781795
patch_aarch64_have_lse_atomics(t, reader, syms.addr(i), start, size,
1779-
offset_bytes);
1796+
offset_bytes, prot);
17801797
}
17811798
}
17821799
break;

src/Monkeypatcher.h

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -118,7 +118,8 @@ class Monkeypatcher {
118118
* patch libpthread.so.
119119
*/
120120
void patch_after_mmap(RecordTask* t, remote_ptr<void> start, size_t size,
121-
size_t offset_bytes, int child_fd, MmapMode mode);
121+
size_t offset_bytes, uint64_t prot, int child_fd,
122+
MmapMode mode);
122123

123124
/**
124125
* The list of pages we've allocated to hold our extended jumps.
@@ -153,15 +154,14 @@ class Monkeypatcher {
153154

154155
private:
155156
void patch_dl_runtime_resolve(RecordTask* t, ElfReader& reader,
156-
uintptr_t elf_addr,
157-
remote_ptr<void> map_start,
158-
size_t map_size,
159-
size_t map_offset);
157+
uintptr_t elf_addr, remote_ptr<void> map_start,
158+
size_t map_size, size_t map_offset,
159+
uint64_t prot);
160160
void patch_aarch64_have_lse_atomics(RecordTask* t, ElfReader& reader,
161161
uintptr_t elf_addr,
162162
remote_ptr<void> map_start,
163-
size_t map_size,
164-
size_t map_offset);
163+
size_t map_size, size_t map_offset,
164+
uint64_t prot);
165165

166166
/**
167167
* `ip` is the address of the instruction that triggered the syscall or trap

src/record_syscall.cc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6394,7 +6394,7 @@ static void process_mmap(RecordTask* t, size_t length, int prot, int flags,
63946394
// at an assertion, in the worst case, we'd end up modifying the underlying
63956395
// file.
63966396
if (!(flags & MAP_SHARED)) {
6397-
t->vm()->monkeypatcher().patch_after_mmap(t, addr, size, offset, fd,
6397+
t->vm()->monkeypatcher().patch_after_mmap(t, addr, size, offset, prot, fd,
63986398
Monkeypatcher::MMAP_SYSCALL);
63996399
}
64006400

0 commit comments

Comments
 (0)