1//===-- sanitizer_procmaps_mac_test.cpp ---------------------------------------===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8//
9// This file is a part of ThreadSanitizer/AddressSanitizer runtime.
10//
11//===----------------------------------------------------------------------===//
12
13# include "sanitizer_common/sanitizer_platform.h"
14
15# if SANITIZER_APPLE
16
17# include <stdlib.h>
18# include <string.h>
19# include <stdint.h>
20# include <stdio.h>
21
22# include <vector>
23# include <mach-o/dyld.h>
24# include <mach-o/loader.h>
25
26# include "gtest/gtest.h"
27
28# include "sanitizer_common/sanitizer_procmaps.h"
29
30namespace __sanitizer {
31
32class MemoryMappingLayoutMock final : public MemoryMappingLayout {
33private:
34 static constexpr uuid_command mock_uuid_command = {
35 .cmd = LC_UUID,
36 .cmdsize = sizeof(uuid_command),
37 .uuid = {}
38 };
39
40 static constexpr char libclang_rt_dylib_name[] =
41 "libclang_rt.\0\0\0"; // 8 bytes aligned, padded with zeros per loader.h
42 static constexpr char uninstrumented_dylib_name[] =
43 "uninst___rt.\0\0\0"; // 8 bytes aligned, padded with zeros per loader.h
44
45 static constexpr dylib_command mock_dylib_command = {
46 .cmd = LC_LOAD_DYLIB,
47 .cmdsize = sizeof(dylib_command) + sizeof(libclang_rt_dylib_name),
48 .dylib = {.name = {.offset = sizeof(dylib_command)}}};
49
50 static constexpr uuid_command mock_trap_command = {
51 .cmd = LC_UUID,
52 .cmdsize = 0x10000,
53 .uuid = {}
54 };
55
56 const char *start_load_cmd_addr;
57 size_t sizeofcmds;
58 std::vector<unsigned char> mock_header;
59
60public:
61 MemoryMappingLayoutMock(bool instrumented) : MemoryMappingLayout(false) {
62 EXPECT_EQ(mock_uuid_command.cmdsize % 8, 0u);
63 EXPECT_EQ(mock_dylib_command.cmdsize % 8, 0u);
64
65 Reset();
66
67# ifdef MH_MAGIC_64
68 const struct mach_header_64 *header =
69 (mach_header_64 *)_dyld_get_image_header(0); // Any header will do
70 const size_t header_size = sizeof(mach_header_64);
71# else
72 const struct mach_header *header = _dyld_get_image_header(0);
73 const size_t header_size = sizeof(mach_header);
74# endif
75 const size_t mock_header_size_with_extras =
76 header_size + header->sizeofcmds + mock_uuid_command.cmdsize +
77 mock_dylib_command.cmdsize + sizeof(uuid_command);
78
79 mock_header.reserve(mock_header_size_with_extras);
80 // Copy the original header
81 copy((unsigned char *)header,
82 (unsigned char *)header + header_size + header->sizeofcmds,
83 back_inserter(mock_header));
84 // The following commands are not supposed to be processed
85 // by the (correct) ::Next method at all, since they're not
86 // accounted for in header->ncmds .
87 copy((unsigned char *)&mock_uuid_command,
88 ((unsigned char *)&mock_uuid_command) + mock_uuid_command.cmdsize,
89 back_inserter(mock_header));
90 copy((unsigned char *)&mock_dylib_command,
91 ((unsigned char *)&mock_dylib_command) +
92 sizeof(dylib_command), // as mock_dylib_command.cmdsize contains
93 // the following string
94 back_inserter(mock_header));
95 const char(&dylib_name)[16] =
96 instrumented ? libclang_rt_dylib_name : uninstrumented_dylib_name;
97 copy((unsigned char *)dylib_name,
98 ((unsigned char *)dylib_name) + sizeof(dylib_name),
99 back_inserter(mock_header));
100
101 // Append a command w. huge size to have the test detect the read overrun
102 copy((unsigned char *)&mock_trap_command,
103 ((unsigned char *)&mock_trap_command) + sizeof(uuid_command),
104 back_inserter(mock_header));
105
106 start_load_cmd_addr = (const char *)(mock_header.data() + header_size);
107 sizeofcmds = header->sizeofcmds;
108
109 const char *last_byte_load_cmd_addr = (start_load_cmd_addr + sizeofcmds - 1);
110 data_.current_image = -1; // So the loop in ::Next runs just once
111 }
112
113 size_t SizeOfLoadCommands() {
114 return sizeofcmds;
115 }
116
117 size_t CurrentLoadCommandOffset() {
118 size_t offset = data_.current_load_cmd_addr - start_load_cmd_addr;
119 return offset;
120 }
121
122protected:
123 virtual ImageHeader *CurrentImageHeader() override {
124 return (ImageHeader *)mock_header.data();
125 }
126};
127
128TEST(MemoryMappingLayout, NextInstrumented) {
129 __sanitizer::MemoryMappingLayoutMock memory_mapping(true);
130 __sanitizer::MemoryMappedSegment segment;
131 size_t size = memory_mapping.SizeOfLoadCommands();
132 while (memory_mapping.Next(&segment)) {
133 size_t offset = memory_mapping.CurrentLoadCommandOffset();
134 EXPECT_LE(offset, size);
135 }
136 size_t final_offset = memory_mapping.CurrentLoadCommandOffset();
137 EXPECT_EQ(final_offset, size); // All commands processed, no more, no less
138}
139
140TEST(MemoryMappingLayout, NextUnInstrumented) {
141 __sanitizer::MemoryMappingLayoutMock memory_mapping(false);
142 __sanitizer::MemoryMappedSegment segment;
143 size_t size = memory_mapping.SizeOfLoadCommands();
144 while (memory_mapping.Next(&segment)) {
145 size_t offset = memory_mapping.CurrentLoadCommandOffset();
146 EXPECT_LE(offset, size);
147 }
148 size_t final_offset = memory_mapping.CurrentLoadCommandOffset();
149 EXPECT_EQ(final_offset, size); // All commands processed, no more, no less
150}
151
152} // namespace __sanitizer
153
154# endif // SANITIZER_APPLE
155

source code of compiler-rt/lib/sanitizer_common/tests/sanitizer_procmaps_mac_test.cpp