Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(125)

Side by Side Diff: util/mac/mach_o_image_annotations_reader_test.cc

Issue 651283003: Add crashpad_info, MachOImageAnnotationsReader, and its test (Closed) Base URL: https://chromium.googlesource.com/crashpad/crashpad@master
Patch Set: Refactor to reduce deep nesting Created 6 years, 2 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 // Copyright 2014 The Crashpad Authors. All rights reserved.
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14
15 #include "util/mac/mach_o_image_annotations_reader.h"
16
17 #include <dlfcn.h>
18 #include <mach/mach.h>
19 #include <signal.h>
20 #include <stdlib.h>
21 #include <string.h>
22 #include <unistd.h>
23
24 #include <map>
25 #include <string>
26 #include <vector>
27
28 #include "base/basictypes.h"
29 #include "client/crashpad_info.h"
30 #include "client/simple_string_dictionary.h"
31 #include "gtest/gtest.h"
32 #include "util/file/fd_io.h"
33 #include "util/mac/mac_util.h"
34 #include "util/mac/process_reader.h"
35 #include "util/mach/exc_server_variants.h"
36 #include "util/mach/exception_ports.h"
37 #include "util/mach/mach_message_server.h"
38 #include "util/test/errors.h"
39 #include "util/test/mac/mach_errors.h"
40 #include "util/test/mac/mach_multiprocess.h"
41
42 namespace crashpad {
43 namespace test {
44 namespace {
45
46 class TestMachOImageAnnotationsReader final : public MachMultiprocess,
47 public UniversalMachExcServer {
48 public:
49 enum TestType {
50 // Don’t crash, just test the CrashpadInfo interface.
51 kDontCrash = 0,
52
53 // The child process should crash by calling abort(). The parent verifies
54 // that the system libraries set the expected annotations.
55 kCrashAbort,
56
57 // The child process should crash by setting DYLD_INSERT_LIBRARIES to
58 // contain a nonexistent library. The parent verifies that dyld sets the
59 // expected annotations.
60 kCrashDyld,
61 };
62
63 explicit TestMachOImageAnnotationsReader(TestType test_type)
64 : MachMultiprocess(),
65 UniversalMachExcServer(),
66 test_type_(test_type) {
67 }
68
69 ~TestMachOImageAnnotationsReader() {}
70
71 kern_return_t CatchMachException(exception_behavior_t behavior,
72 exception_handler_t exception_port,
73 thread_t thread,
74 task_t task,
75 exception_type_t exception,
76 const mach_exception_data_type_t* code,
77 mach_msg_type_number_t code_count,
78 thread_state_flavor_t* flavor,
79 const natural_t* old_state,
80 mach_msg_type_number_t old_state_count,
81 thread_state_t new_state,
82 mach_msg_type_number_t* new_state_count,
83 bool* destroy_complex_request) override {
84 *destroy_complex_request = true;
85
86 EXPECT_EQ(ChildTask(), task);
87
88 ProcessReader process_reader;
89 bool rv = process_reader.Initialize(task);
90 if (!rv) {
91 ADD_FAILURE();
92 } else {
93 const std::vector<ProcessReader::Module>& modules =
94 process_reader.Modules();
95 std::vector<std::string> all_annotations_vector;
96 for (const ProcessReader::Module& module : modules) {
97 MachOImageAnnotationsReader module_annotations_reader(
98 &process_reader, module.reader, module.name);
99 std::vector<std::string> module_annotations_vector =
100 module_annotations_reader.Vector();
101 all_annotations_vector.insert(all_annotations_vector.end(),
102 module_annotations_vector.begin(),
103 module_annotations_vector.end());
104 }
105
106 // Mac OS X 10.6 doesn’t have support for CrashReporter annotations
107 // (CrashReporterClient.h), so don’t look for any special annotations in
108 // that version.
109 int mac_os_x_minor_version = MacOSXMinorVersion();
110 if (mac_os_x_minor_version > 7) {
111 EXPECT_GE(all_annotations_vector.size(), 1u);
112
113 const char* expected_annotation = nullptr;
114 switch (test_type_) {
115 case kCrashAbort:
116 // The child process calls abort(), so the expected annotation
117 // reflects this, with a string set by 10.7.5
118 // Libc-763.13/stdlib/abort-fbsd.c abort(). This string is still
119 // present in 10.9.5 Libc-997.90.3/stdlib/FreeBSD/abort.c abort(),
120 // but because abort() tests to see if a message is already set and
121 // something else in Libc will have set a message, this string is
122 // not the expectation on 10.9 or higher. Instead, after fork(), the
123 // child process has a message indicating that a fork() without
124 // exec() occurred. See 10.9.5 Libc-997.90.3/sys/_libc_fork_child.c
125 // _libc_fork_child().
126 expected_annotation =
127 mac_os_x_minor_version <= 8
128 ? "abort() called"
129 : "crashed on child side of fork pre-exec";
130 break;
131
132 case kCrashDyld:
133 // This is independent of dyld’s error_string, which is tested
134 // below.
135 expected_annotation = "dyld: launch, loading dependent libraries";
136 break;
137
138 default:
139 ADD_FAILURE();
140 break;
141 }
142
143 size_t expected_annotation_length = strlen(expected_annotation);
144 bool found = false;
145 for (const std::string& annotation : all_annotations_vector) {
146 // Look for the expectation as a leading susbtring, because the actual
147 // string that dyld uses will have the contents of the
148 // DYLD_INSERT_LIBRARIES environment variable appended to it on Mac
149 // OS X 10.10.
150 if (annotation.substr(0, expected_annotation_length) ==
151 expected_annotation) {
Robert Sesek 2014/10/15 20:25:21 nit: indent +4
152 found = true;
153 break;
154 }
155 }
156 EXPECT_TRUE(found);
157 }
158
159 // dyld exposes its error_string at least as far back as Mac OS X 10.4.
160 if (test_type_ == kCrashDyld) {
161 const char kExpectedAnnotation[] = "could not load inserted library";
162 size_t expected_annotation_length = strlen(kExpectedAnnotation);
163 bool found = false;
164 for (const std::string& annotation : all_annotations_vector) {
165 // Look for the expectation as a leading substring, because the actual
166 // string will contain the library’s pathname and, on Mac OS X 10.9
167 // and later, a reason.
168 if (annotation.substr(0, expected_annotation_length) ==
169 kExpectedAnnotation) {
Robert Sesek 2014/10/15 20:25:20 +4
170 found = true;
171 break;
172 }
173 }
174
175 EXPECT_TRUE(found);
176 }
177 }
178
179 return ExcServerSuccessfulReturnValue(behavior, false);
180 }
181
182 private:
183 // MachMultiprocess:
184
185 void MachMultiprocessParent() override {
186 ProcessReader process_reader;
187 ASSERT_TRUE(process_reader.Initialize(ChildTask()));
188
189 // Wait for the child process to indicate that it’s done setting up its
190 // annotations via the CrashpadInfo interface.
191 char c;
192 CheckedReadFD(ReadPipeFD(), &c, sizeof(c));
193
194 // Verify the “simple map” annotations set via the CrashpadInfo interface.
195 const std::vector<ProcessReader::Module>& modules =
196 process_reader.Modules();
197 std::map<std::string, std::string> all_annotations_simple_map;
198 for (const ProcessReader::Module& module : modules) {
199 MachOImageAnnotationsReader module_annotations_reader(
200 &process_reader, module.reader, module.name);
201 std::map<std::string, std::string> module_annotations_simple_map =
202 module_annotations_reader.SimpleMap();
203 all_annotations_simple_map.insert(module_annotations_simple_map.begin(),
204 module_annotations_simple_map.end());
205 }
206
207 EXPECT_GE(all_annotations_simple_map.size(), 2u);
208 EXPECT_EQ("crash", all_annotations_simple_map["#TEST# pad"]);
209 EXPECT_EQ("value", all_annotations_simple_map["#TEST# key"]);
210
211 // Tell the child process that it’s permitted to crash.
212 CheckedWriteFD(WritePipeFD(), &c, sizeof(c));
213
214 if (test_type_ != kDontCrash) {
215 // Handle the child’s crash. Further validation will be done in
216 // CatchMachException().
217 mach_msg_return_t mr =
218 MachMessageServer::Run(this,
219 LocalPort(),
220 MACH_MSG_OPTION_NONE,
221 MachMessageServer::kOneShot,
222 MachMessageServer::kBlocking,
223 MACH_MSG_TIMEOUT_NONE);
224 EXPECT_EQ(MACH_MSG_SUCCESS, mr)
225 << MachErrorMessage(mr, "MachMessageServer::Run");
226
227 switch (test_type_) {
228 case kCrashAbort:
229 SetExpectedChildTermination(kTerminationSignal, SIGABRT);
230 break;
231
232 case kCrashDyld:
233 // dyld fatal errors result in the execution of an int3 instruction on
234 // x86 and a trap instruction on ARM, both of which raise SIGTRAP.
235 // 10.9.5 dyld-239.4/src/dyldStartup.s _dyld_fatal_error.
236 SetExpectedChildTermination(kTerminationSignal, SIGTRAP);
237 break;
238
239 default:
240 FAIL();
241 break;
242 }
243 }
244 }
245
246 void MachMultiprocessChild() override {
247 CrashpadInfo* crashpad_info = CrashpadInfo::GetCrashpadInfo();
248
249 // This is “leaked” to crashpad_info.
250 SimpleStringDictionary* simple_annotations = new SimpleStringDictionary();
251 simple_annotations->SetKeyValue("#TEST# pad", "break");
252 simple_annotations->SetKeyValue("#TEST# key", "value");
253 simple_annotations->SetKeyValue("#TEST# pad", "crash");
Robert Sesek 2014/10/15 20:25:20 Maybe also add a key/value pair that doesn't have
254
255 crashpad_info->set_simple_annotations(simple_annotations);
256
257 // Tell the parent that the environment has been set up.
258 char c = '\0';
259 CheckedWriteFD(WritePipeFD(), &c, sizeof(c));
260
261 // Wait for the parent to indicate that it’s safe to crash.
262 CheckedReadFD(ReadPipeFD(), &c, sizeof(c));
263
264 // Direct an exception message to the exception server running in the
265 // parent.
266 ExceptionPorts exception_ports(ExceptionPorts::kTargetTypeTask,
267 mach_task_self());
268 ASSERT_TRUE(exception_ports.SetExceptionPort(
269 EXC_MASK_CRASH, RemotePort(), EXCEPTION_DEFAULT, THREAD_STATE_NONE));
270
271 switch (test_type_) {
272 case kDontCrash:
273 break;
274
275 case kCrashAbort:
276 abort();
277 break;
278
279 case kCrashDyld: {
280 // Set DYLD_INSERT_LIBRARIES to contain a library that does not exist.
281 // Unable to load it, dyld will abort with a fatal error.
282 ASSERT_EQ(
283 0,
284 setenv(
285 "DYLD_INSERT_LIBRARIES", "/var/empty/NoDirectory/NoLibrary", 1))
286 << ErrnoMessage("setenv");
287
288 // The actual executable doesn’t matter very much, because dyld won’t
289 // ever launch it. It just needs to be an executable that uses dyld as
290 // its LC_LOAD_DYLINKER (all normal executables do). /usr/bin/true is on
291 // every system, so use it.
292 ASSERT_EQ(0, execl("/usr/bin/true", "true", nullptr))
293 << ErrnoMessage("execl");
294 break;
295 }
296
297 default:
298 break;
299 }
300 }
301
302 TestType test_type_;
303
304 DISALLOW_COPY_AND_ASSIGN(TestMachOImageAnnotationsReader);
305 };
306
307 TEST(MachOImageAnnotationsReader, DontCrash) {
308 TestMachOImageAnnotationsReader test_mach_o_image_annotations_reader(
309 TestMachOImageAnnotationsReader::kDontCrash);
310 test_mach_o_image_annotations_reader.Run();
311 }
312
313 TEST(MachOImageAnnotationsReader, CrashAbort) {
314 TestMachOImageAnnotationsReader test_mach_o_image_annotations_reader(
315 TestMachOImageAnnotationsReader::kCrashAbort);
316 test_mach_o_image_annotations_reader.Run();
317 }
318
319 TEST(MachOImageAnnotationsReader, CrashDyld) {
320 TestMachOImageAnnotationsReader test_mach_o_image_annotations_reader(
321 TestMachOImageAnnotationsReader::kCrashDyld);
322 test_mach_o_image_annotations_reader.Run();
323 }
324
325 } // namespace
326 } // namespace test
327 } // namespace crashpad
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698