OLD | NEW |
(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 "snapshot/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 "snapshot/mac/process_reader.h" |
| 33 #include "util/file/fd_io.h" |
| 34 #include "util/mac/mac_util.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 // UniversalMachExcServer: |
| 72 kern_return_t CatchMachException(exception_behavior_t behavior, |
| 73 exception_handler_t exception_port, |
| 74 thread_t thread, |
| 75 task_t task, |
| 76 exception_type_t exception, |
| 77 const mach_exception_data_type_t* code, |
| 78 mach_msg_type_number_t code_count, |
| 79 thread_state_flavor_t* flavor, |
| 80 const natural_t* old_state, |
| 81 mach_msg_type_number_t old_state_count, |
| 82 thread_state_t new_state, |
| 83 mach_msg_type_number_t* new_state_count, |
| 84 bool* destroy_complex_request) override { |
| 85 *destroy_complex_request = true; |
| 86 |
| 87 EXPECT_EQ(ChildTask(), task); |
| 88 |
| 89 ProcessReader process_reader; |
| 90 bool rv = process_reader.Initialize(task); |
| 91 if (!rv) { |
| 92 ADD_FAILURE(); |
| 93 } else { |
| 94 const std::vector<ProcessReader::Module>& modules = |
| 95 process_reader.Modules(); |
| 96 std::vector<std::string> all_annotations_vector; |
| 97 for (const ProcessReader::Module& module : modules) { |
| 98 MachOImageAnnotationsReader module_annotations_reader( |
| 99 &process_reader, module.reader, module.name); |
| 100 std::vector<std::string> module_annotations_vector = |
| 101 module_annotations_reader.Vector(); |
| 102 all_annotations_vector.insert(all_annotations_vector.end(), |
| 103 module_annotations_vector.begin(), |
| 104 module_annotations_vector.end()); |
| 105 } |
| 106 |
| 107 // Mac OS X 10.6 doesn’t have support for CrashReporter annotations |
| 108 // (CrashReporterClient.h), so don’t look for any special annotations in |
| 109 // that version. |
| 110 int mac_os_x_minor_version = MacOSXMinorVersion(); |
| 111 if (mac_os_x_minor_version > 7) { |
| 112 EXPECT_GE(all_annotations_vector.size(), 1u); |
| 113 |
| 114 const char* expected_annotation = nullptr; |
| 115 switch (test_type_) { |
| 116 case kCrashAbort: |
| 117 // The child process calls abort(), so the expected annotation |
| 118 // reflects this, with a string set by 10.7.5 |
| 119 // Libc-763.13/stdlib/abort-fbsd.c abort(). This string is still |
| 120 // present in 10.9.5 Libc-997.90.3/stdlib/FreeBSD/abort.c abort(), |
| 121 // but because abort() tests to see if a message is already set and |
| 122 // something else in Libc will have set a message, this string is |
| 123 // not the expectation on 10.9 or higher. Instead, after fork(), the |
| 124 // child process has a message indicating that a fork() without |
| 125 // exec() occurred. See 10.9.5 Libc-997.90.3/sys/_libc_fork_child.c |
| 126 // _libc_fork_child(). |
| 127 expected_annotation = |
| 128 mac_os_x_minor_version <= 8 |
| 129 ? "abort() called" |
| 130 : "crashed on child side of fork pre-exec"; |
| 131 break; |
| 132 |
| 133 case kCrashDyld: |
| 134 // This is independent of dyld’s error_string, which is tested |
| 135 // below. |
| 136 expected_annotation = "dyld: launch, loading dependent libraries"; |
| 137 break; |
| 138 |
| 139 default: |
| 140 ADD_FAILURE(); |
| 141 break; |
| 142 } |
| 143 |
| 144 size_t expected_annotation_length = strlen(expected_annotation); |
| 145 bool found = false; |
| 146 for (const std::string& annotation : all_annotations_vector) { |
| 147 // Look for the expectation as a leading susbtring, because the actual |
| 148 // string that dyld uses will have the contents of the |
| 149 // DYLD_INSERT_LIBRARIES environment variable appended to it on Mac |
| 150 // OS X 10.10. |
| 151 if (annotation.substr(0, expected_annotation_length) == |
| 152 expected_annotation) { |
| 153 found = true; |
| 154 break; |
| 155 } |
| 156 } |
| 157 EXPECT_TRUE(found); |
| 158 } |
| 159 |
| 160 // dyld exposes its error_string at least as far back as Mac OS X 10.4. |
| 161 if (test_type_ == kCrashDyld) { |
| 162 const char kExpectedAnnotation[] = "could not load inserted library"; |
| 163 size_t expected_annotation_length = strlen(kExpectedAnnotation); |
| 164 bool found = false; |
| 165 for (const std::string& annotation : all_annotations_vector) { |
| 166 // Look for the expectation as a leading substring, because the actual |
| 167 // string will contain the library’s pathname and, on Mac OS X 10.9 |
| 168 // and later, a reason. |
| 169 if (annotation.substr(0, expected_annotation_length) == |
| 170 kExpectedAnnotation) { |
| 171 found = true; |
| 172 break; |
| 173 } |
| 174 } |
| 175 |
| 176 EXPECT_TRUE(found); |
| 177 } |
| 178 } |
| 179 |
| 180 return ExcServerSuccessfulReturnValue(behavior, false); |
| 181 } |
| 182 |
| 183 private: |
| 184 // MachMultiprocess: |
| 185 |
| 186 void MachMultiprocessParent() override { |
| 187 ProcessReader process_reader; |
| 188 ASSERT_TRUE(process_reader.Initialize(ChildTask())); |
| 189 |
| 190 // Wait for the child process to indicate that it’s done setting up its |
| 191 // annotations via the CrashpadInfo interface. |
| 192 char c; |
| 193 CheckedReadFD(ReadPipeFD(), &c, sizeof(c)); |
| 194 |
| 195 // Verify the “simple map” annotations set via the CrashpadInfo interface. |
| 196 const std::vector<ProcessReader::Module>& modules = |
| 197 process_reader.Modules(); |
| 198 std::map<std::string, std::string> all_annotations_simple_map; |
| 199 for (const ProcessReader::Module& module : modules) { |
| 200 MachOImageAnnotationsReader module_annotations_reader( |
| 201 &process_reader, module.reader, module.name); |
| 202 std::map<std::string, std::string> module_annotations_simple_map = |
| 203 module_annotations_reader.SimpleMap(); |
| 204 all_annotations_simple_map.insert(module_annotations_simple_map.begin(), |
| 205 module_annotations_simple_map.end()); |
| 206 } |
| 207 |
| 208 EXPECT_GE(all_annotations_simple_map.size(), 5u); |
| 209 EXPECT_EQ("crash", all_annotations_simple_map["#TEST# pad"]); |
| 210 EXPECT_EQ("value", all_annotations_simple_map["#TEST# key"]); |
| 211 EXPECT_EQ("y", all_annotations_simple_map["#TEST# x"]); |
| 212 EXPECT_EQ("shorter", all_annotations_simple_map["#TEST# longer"]); |
| 213 EXPECT_EQ("", all_annotations_simple_map["#TEST# empty_value"]); |
| 214 |
| 215 // Tell the child process that it’s permitted to crash. |
| 216 CheckedWriteFD(WritePipeFD(), &c, sizeof(c)); |
| 217 |
| 218 if (test_type_ != kDontCrash) { |
| 219 // Handle the child’s crash. Further validation will be done in |
| 220 // CatchMachException(). |
| 221 mach_msg_return_t mr = |
| 222 MachMessageServer::Run(this, |
| 223 LocalPort(), |
| 224 MACH_MSG_OPTION_NONE, |
| 225 MachMessageServer::kOneShot, |
| 226 MachMessageServer::kBlocking, |
| 227 MACH_MSG_TIMEOUT_NONE); |
| 228 EXPECT_EQ(MACH_MSG_SUCCESS, mr) |
| 229 << MachErrorMessage(mr, "MachMessageServer::Run"); |
| 230 |
| 231 switch (test_type_) { |
| 232 case kCrashAbort: |
| 233 SetExpectedChildTermination(kTerminationSignal, SIGABRT); |
| 234 break; |
| 235 |
| 236 case kCrashDyld: |
| 237 // dyld fatal errors result in the execution of an int3 instruction on |
| 238 // x86 and a trap instruction on ARM, both of which raise SIGTRAP. |
| 239 // 10.9.5 dyld-239.4/src/dyldStartup.s _dyld_fatal_error. |
| 240 SetExpectedChildTermination(kTerminationSignal, SIGTRAP); |
| 241 break; |
| 242 |
| 243 default: |
| 244 FAIL(); |
| 245 break; |
| 246 } |
| 247 } |
| 248 } |
| 249 |
| 250 void MachMultiprocessChild() override { |
| 251 CrashpadInfo* crashpad_info = CrashpadInfo::GetCrashpadInfo(); |
| 252 |
| 253 // This is “leaked” to crashpad_info. |
| 254 SimpleStringDictionary* simple_annotations = new SimpleStringDictionary(); |
| 255 simple_annotations->SetKeyValue("#TEST# pad", "break"); |
| 256 simple_annotations->SetKeyValue("#TEST# key", "value"); |
| 257 simple_annotations->SetKeyValue("#TEST# pad", "crash"); |
| 258 simple_annotations->SetKeyValue("#TEST# x", "y"); |
| 259 simple_annotations->SetKeyValue("#TEST# longer", "shorter"); |
| 260 simple_annotations->SetKeyValue("#TEST# empty_value", ""); |
| 261 |
| 262 crashpad_info->set_simple_annotations(simple_annotations); |
| 263 |
| 264 // Tell the parent that the environment has been set up. |
| 265 char c = '\0'; |
| 266 CheckedWriteFD(WritePipeFD(), &c, sizeof(c)); |
| 267 |
| 268 // Wait for the parent to indicate that it’s safe to crash. |
| 269 CheckedReadFD(ReadPipeFD(), &c, sizeof(c)); |
| 270 |
| 271 // Direct an exception message to the exception server running in the |
| 272 // parent. |
| 273 ExceptionPorts exception_ports(ExceptionPorts::kTargetTypeTask, |
| 274 mach_task_self()); |
| 275 ASSERT_TRUE(exception_ports.SetExceptionPort( |
| 276 EXC_MASK_CRASH, RemotePort(), EXCEPTION_DEFAULT, THREAD_STATE_NONE)); |
| 277 |
| 278 switch (test_type_) { |
| 279 case kDontCrash: |
| 280 break; |
| 281 |
| 282 case kCrashAbort: |
| 283 abort(); |
| 284 break; |
| 285 |
| 286 case kCrashDyld: { |
| 287 // Set DYLD_INSERT_LIBRARIES to contain a library that does not exist. |
| 288 // Unable to load it, dyld will abort with a fatal error. |
| 289 ASSERT_EQ( |
| 290 0, |
| 291 setenv( |
| 292 "DYLD_INSERT_LIBRARIES", "/var/empty/NoDirectory/NoLibrary", 1)) |
| 293 << ErrnoMessage("setenv"); |
| 294 |
| 295 // The actual executable doesn’t matter very much, because dyld won’t |
| 296 // ever launch it. It just needs to be an executable that uses dyld as |
| 297 // its LC_LOAD_DYLINKER (all normal executables do). /usr/bin/true is on |
| 298 // every system, so use it. |
| 299 ASSERT_EQ(0, execl("/usr/bin/true", "true", nullptr)) |
| 300 << ErrnoMessage("execl"); |
| 301 break; |
| 302 } |
| 303 |
| 304 default: |
| 305 break; |
| 306 } |
| 307 } |
| 308 |
| 309 TestType test_type_; |
| 310 |
| 311 DISALLOW_COPY_AND_ASSIGN(TestMachOImageAnnotationsReader); |
| 312 }; |
| 313 |
| 314 TEST(MachOImageAnnotationsReader, DontCrash) { |
| 315 TestMachOImageAnnotationsReader test_mach_o_image_annotations_reader( |
| 316 TestMachOImageAnnotationsReader::kDontCrash); |
| 317 test_mach_o_image_annotations_reader.Run(); |
| 318 } |
| 319 |
| 320 TEST(MachOImageAnnotationsReader, CrashAbort) { |
| 321 TestMachOImageAnnotationsReader test_mach_o_image_annotations_reader( |
| 322 TestMachOImageAnnotationsReader::kCrashAbort); |
| 323 test_mach_o_image_annotations_reader.Run(); |
| 324 } |
| 325 |
| 326 TEST(MachOImageAnnotationsReader, CrashDyld) { |
| 327 TestMachOImageAnnotationsReader test_mach_o_image_annotations_reader( |
| 328 TestMachOImageAnnotationsReader::kCrashDyld); |
| 329 test_mach_o_image_annotations_reader.Run(); |
| 330 } |
| 331 |
| 332 } // namespace |
| 333 } // namespace test |
| 334 } // namespace crashpad |
OLD | NEW |