Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
78 changes: 61 additions & 17 deletions compiler-rt/lib/sanitizer_common/sanitizer_procmaps_mac.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,6 @@ struct MemoryMappedSegmentData {
const char *current_load_cmd_addr;
u32 lc_type;
uptr base_virt_addr;
uptr addr_mask;
};

template <typename Section>
Expand All @@ -51,12 +50,58 @@ static void NextSectionLoad(LoadedModule *module, MemoryMappedSegmentData *data,
const Section *sc = (const Section *)data->current_load_cmd_addr;
data->current_load_cmd_addr += sizeof(Section);

uptr sec_start = (sc->addr & data->addr_mask) + data->base_virt_addr;
uptr sec_start = sc->addr + data->base_virt_addr;
uptr sec_end = sec_start + sc->size;
module->addAddressRange(sec_start, sec_end, /*executable=*/false, isWritable,
sc->sectname);
}

static bool VerifyMemoryMapping(MemoryMappingLayout* mapping) {
InternalMmapVector<LoadedModule> modules;
modules.reserve(128); // matches DumpProcessMap
mapping->DumpListOfModules(&modules);

InternalMmapVector<LoadedModule::AddressRange> segments;
for (uptr i = 0; i < modules.size(); ++i) {
for (auto& range : modules[i].ranges()) {
segments.push_back(range);
}
}

// Verify that none of the segments overlap:
// 1. Sort the segments by the start address
// 2. Check that every segment starts after the previous one ends.
Sort(segments.data(), segments.size(),
[](LoadedModule::AddressRange& a, LoadedModule::AddressRange& b) {
return a.beg < b.beg;
});

// To avoid spam, we only print the report message once-per-process.
static bool invalid_module_map_reported = false;
bool well_formed = true;

for (size_t i = 1; i < segments.size(); i++) {
uptr cur_start = segments[i].beg;
uptr prev_end = segments[i - 1].end;
if (cur_start < prev_end) {
well_formed = false;
VReport(2, "Overlapping mappings: %s start = %p, %s end = %p\n",
segments[i].name, (void*)cur_start, segments[i - 1].name,
(void*)prev_end);
if (!invalid_module_map_reported) {
Report(
"WARN: Invalid dyld module map detected. This is most likely a bug "
"in the sanitizer.\n");
Report("WARN: Backtraces may be unreliable.\n");
invalid_module_map_reported = true;
}
}
}

mapping->Reset();
return well_formed;
}

void MemoryMappedSegment::AddAddressRanges(LoadedModule *module) {
// Don't iterate over sections when the caller hasn't set up the
// data pointer, when there are no sections, or when the segment
Expand All @@ -82,6 +127,7 @@ void MemoryMappedSegment::AddAddressRanges(LoadedModule *module) {

MemoryMappingLayout::MemoryMappingLayout(bool cache_enabled) {
Reset();
VerifyMemoryMapping(this);
}

MemoryMappingLayout::~MemoryMappingLayout() {
Expand Down Expand Up @@ -187,6 +233,7 @@ typedef struct dyld_shared_cache_dylib_text_info

extern bool _dyld_get_shared_cache_uuid(uuid_t uuid);
extern const void *_dyld_get_shared_cache_range(size_t *length);
extern intptr_t _dyld_get_image_slide(const struct mach_header* mh);
extern int dyld_shared_cache_iterate_text(
const uuid_t cacheUuid,
void (^callback)(const dyld_shared_cache_dylib_text_info *info));
Expand Down Expand Up @@ -255,23 +302,21 @@ static bool NextSegmentLoad(MemoryMappedSegment *segment,
layout_data->current_load_cmd_count--;
if (((const load_command *)lc)->cmd == kLCSegment) {
const SegmentCommand* sc = (const SegmentCommand *)lc;
uptr base_virt_addr, addr_mask;
if (layout_data->current_image == kDyldImageIdx) {
base_virt_addr = (uptr)get_dyld_hdr();
// vmaddr is masked with 0xfffff because on macOS versions < 10.12,
// it contains an absolute address rather than an offset for dyld.
// To make matters even more complicated, this absolute address
// isn't actually the absolute segment address, but the offset portion
// of the address is accurate when combined with the dyld base address,
// and the mask will give just this offset.
addr_mask = 0xfffff;
} else {
if (strncmp(sc->segname, "__LINKEDIT", sizeof("__LINKEDIT")) == 0) {
// The LINKEDIT sections are for internal linker use, and may alias
// with the LINKEDIT section for other modules. (If we included them,
// our memory map would contain overlappping sections.)
return false;
}

uptr base_virt_addr;
if (layout_data->current_image == kDyldImageIdx)
base_virt_addr = (uptr)_dyld_get_image_slide(get_dyld_hdr());
else
base_virt_addr =
(uptr)_dyld_get_image_vmaddr_slide(layout_data->current_image);
addr_mask = ~0;
}

segment->start = (sc->vmaddr & addr_mask) + base_virt_addr;
segment->start = sc->vmaddr + base_virt_addr;
segment->end = segment->start + sc->vmsize;
// Most callers don't need section information, so only fill this struct
// when required.
Expand All @@ -281,7 +326,6 @@ static bool NextSegmentLoad(MemoryMappedSegment *segment,
(const char *)lc + sizeof(SegmentCommand);
seg_data->lc_type = kLCSegment;
seg_data->base_virt_addr = base_virt_addr;
seg_data->addr_mask = addr_mask;
internal_strncpy(seg_data->name, sc->segname,
ARRAY_SIZE(seg_data->name));
}
Expand Down
25 changes: 25 additions & 0 deletions compiler-rt/test/asan/TestCases/Darwin/asan-verify-module-map.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
// This test simply checks that the "Invalid dyld module map" warning is not printed
// in the output of a backtrace.

// RUN: %clangxx_asan -DSHARED_LIB -g %s -dynamiclib -o %t.dylib
// RUN: %clangxx_asan -O0 -g %s %t.dylib -o %t.executable
// RUN: %env_asan_opts="print_module_map=2" not %run %t.executable 2>&1 | FileCheck %s -DDYLIB=%t.dylib

// CHECK-NOT: WARN: Invalid dyld module map
// CHECK-DAG: 0x{{.*}}-0x{{.*}} [[DYLIB]]
// CHECK-DAG: 0x{{.*}}-0x{{.*}} {{.*}}libsystem

#ifdef SHARED_LIB
extern "C" void foo(int *a) { *a = 5; }
#else
# include <cstdlib>

extern "C" void foo(int *a);

int main() {
int *a = (int *)malloc(sizeof(int));
free(a);
foo(a);
return 0;
}
#endif