Skip to content

Commit bd357b0

Browse files
ndrewhgithub-actions[bot]
authored andcommitted
Automerge: [sanitizer-common] [Darwin] Fix overlapping dyld segment addresses (#166005)
This fixes two problems: - dyld itself resides within the shared cache. MemoryMappingLayout incorrectly computes the slide for dyld's segments, causing them to (appear to) overlap with other modules. This can cause symbolication issues. - The MemoryMappingLayout ranges on Darwin are not disjoint due to the fact that the LINKEDIT segments overlap for each module. We now ignore these segments to ensure the mapping is disjoint. This adds a check for disjointness, and a runtime warning if this is ever violated (as that suggests issues in the sanitizer memory mapping). There is now a test to ensure that these problems do not recur. rdar://163149325
2 parents cac13a5 + e330985 commit bd357b0

File tree

2 files changed

+86
-17
lines changed

2 files changed

+86
-17
lines changed

compiler-rt/lib/sanitizer_common/sanitizer_procmaps_mac.cpp

Lines changed: 61 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,6 @@ struct MemoryMappedSegmentData {
4545
const char *current_load_cmd_addr;
4646
u32 lc_type;
4747
uptr base_virt_addr;
48-
uptr addr_mask;
4948
};
5049

5150
template <typename Section>
@@ -54,12 +53,58 @@ static void NextSectionLoad(LoadedModule *module, MemoryMappedSegmentData *data,
5453
const Section *sc = (const Section *)data->current_load_cmd_addr;
5554
data->current_load_cmd_addr += sizeof(Section);
5655

57-
uptr sec_start = (sc->addr & data->addr_mask) + data->base_virt_addr;
56+
uptr sec_start = sc->addr + data->base_virt_addr;
5857
uptr sec_end = sec_start + sc->size;
5958
module->addAddressRange(sec_start, sec_end, /*executable=*/false, isWritable,
6059
sc->sectname);
6160
}
6261

62+
static bool VerifyMemoryMapping(MemoryMappingLayout* mapping) {
63+
InternalMmapVector<LoadedModule> modules;
64+
modules.reserve(128); // matches DumpProcessMap
65+
mapping->DumpListOfModules(&modules);
66+
67+
InternalMmapVector<LoadedModule::AddressRange> segments;
68+
for (uptr i = 0; i < modules.size(); ++i) {
69+
for (auto& range : modules[i].ranges()) {
70+
segments.push_back(range);
71+
}
72+
}
73+
74+
// Verify that none of the segments overlap:
75+
// 1. Sort the segments by the start address
76+
// 2. Check that every segment starts after the previous one ends.
77+
Sort(segments.data(), segments.size(),
78+
[](LoadedModule::AddressRange& a, LoadedModule::AddressRange& b) {
79+
return a.beg < b.beg;
80+
});
81+
82+
// To avoid spam, we only print the report message once-per-process.
83+
static bool invalid_module_map_reported = false;
84+
bool well_formed = true;
85+
86+
for (size_t i = 1; i < segments.size(); i++) {
87+
uptr cur_start = segments[i].beg;
88+
uptr prev_end = segments[i - 1].end;
89+
if (cur_start < prev_end) {
90+
well_formed = false;
91+
VReport(2, "Overlapping mappings: %s start = %p, %s end = %p\n",
92+
segments[i].name, (void*)cur_start, segments[i - 1].name,
93+
(void*)prev_end);
94+
if (!invalid_module_map_reported) {
95+
Report(
96+
"WARN: Invalid dyld module map detected. This is most likely a bug "
97+
"in the sanitizer.\n");
98+
Report("WARN: Backtraces may be unreliable.\n");
99+
invalid_module_map_reported = true;
100+
}
101+
}
102+
}
103+
104+
mapping->Reset();
105+
return well_formed;
106+
}
107+
63108
void MemoryMappedSegment::AddAddressRanges(LoadedModule *module) {
64109
// Don't iterate over sections when the caller hasn't set up the
65110
// data pointer, when there are no sections, or when the segment
@@ -85,6 +130,7 @@ void MemoryMappedSegment::AddAddressRanges(LoadedModule *module) {
85130

86131
MemoryMappingLayout::MemoryMappingLayout(bool cache_enabled) {
87132
Reset();
133+
VerifyMemoryMapping(this);
88134
}
89135

90136
MemoryMappingLayout::~MemoryMappingLayout() {
@@ -190,6 +236,7 @@ typedef struct dyld_shared_cache_dylib_text_info
190236

191237
extern bool _dyld_get_shared_cache_uuid(uuid_t uuid);
192238
extern const void *_dyld_get_shared_cache_range(size_t *length);
239+
extern intptr_t _dyld_get_image_slide(const struct mach_header* mh);
193240
extern int dyld_shared_cache_iterate_text(
194241
const uuid_t cacheUuid,
195242
void (^callback)(const dyld_shared_cache_dylib_text_info *info));
@@ -258,23 +305,21 @@ static bool NextSegmentLoad(MemoryMappedSegment *segment,
258305
layout_data->current_load_cmd_count--;
259306
if (((const load_command *)lc)->cmd == kLCSegment) {
260307
const SegmentCommand* sc = (const SegmentCommand *)lc;
261-
uptr base_virt_addr, addr_mask;
262-
if (layout_data->current_image == kDyldImageIdx) {
263-
base_virt_addr = (uptr)get_dyld_hdr();
264-
// vmaddr is masked with 0xfffff because on macOS versions < 10.12,
265-
// it contains an absolute address rather than an offset for dyld.
266-
// To make matters even more complicated, this absolute address
267-
// isn't actually the absolute segment address, but the offset portion
268-
// of the address is accurate when combined with the dyld base address,
269-
// and the mask will give just this offset.
270-
addr_mask = 0xfffff;
271-
} else {
308+
if (strncmp(sc->segname, "__LINKEDIT", sizeof("__LINKEDIT")) == 0) {
309+
// The LINKEDIT sections are for internal linker use, and may alias
310+
// with the LINKEDIT section for other modules. (If we included them,
311+
// our memory map would contain overlappping sections.)
312+
return false;
313+
}
314+
315+
uptr base_virt_addr;
316+
if (layout_data->current_image == kDyldImageIdx)
317+
base_virt_addr = (uptr)_dyld_get_image_slide(get_dyld_hdr());
318+
else
272319
base_virt_addr =
273320
(uptr)_dyld_get_image_vmaddr_slide(layout_data->current_image);
274-
addr_mask = ~0;
275-
}
276321

277-
segment->start = (sc->vmaddr & addr_mask) + base_virt_addr;
322+
segment->start = sc->vmaddr + base_virt_addr;
278323
segment->end = segment->start + sc->vmsize;
279324
// Most callers don't need section information, so only fill this struct
280325
// when required.
@@ -284,7 +329,6 @@ static bool NextSegmentLoad(MemoryMappedSegment *segment,
284329
(const char *)lc + sizeof(SegmentCommand);
285330
seg_data->lc_type = kLCSegment;
286331
seg_data->base_virt_addr = base_virt_addr;
287-
seg_data->addr_mask = addr_mask;
288332
internal_strncpy(seg_data->name, sc->segname,
289333
ARRAY_SIZE(seg_data->name));
290334
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
// This test simply checks that the "Invalid dyld module map" warning is not printed
2+
// in the output of a backtrace.
3+
4+
// RUN: %clangxx_asan -DSHARED_LIB -g %s -dynamiclib -o %t.dylib
5+
// RUN: %clangxx_asan -O0 -g %s %t.dylib -o %t.executable
6+
// RUN: %env_asan_opts="print_module_map=2" not %run %t.executable 2>&1 | FileCheck %s -DDYLIB=%t.dylib
7+
8+
// CHECK-NOT: WARN: Invalid dyld module map
9+
// CHECK-DAG: 0x{{.*}}-0x{{.*}} [[DYLIB]]
10+
// CHECK-DAG: 0x{{.*}}-0x{{.*}} {{.*}}libsystem
11+
12+
#ifdef SHARED_LIB
13+
extern "C" void foo(int *a) { *a = 5; }
14+
#else
15+
# include <cstdlib>
16+
17+
extern "C" void foo(int *a);
18+
19+
int main() {
20+
int *a = (int *)malloc(sizeof(int));
21+
free(a);
22+
foo(a);
23+
return 0;
24+
}
25+
#endif

0 commit comments

Comments
 (0)