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

Unified Diff: third_party/crashpad/crashpad/snapshot/mac/mach_o_image_annotations_reader_test.cc

Issue 1505213004: Copy Crashpad into the Chrome tree instead of importing it via DEPS (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Address review comments, update README.chromium Created 5 years 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 side-by-side diff with in-line comments
Download patch
Index: third_party/crashpad/crashpad/snapshot/mac/mach_o_image_annotations_reader_test.cc
diff --git a/third_party/crashpad/crashpad/snapshot/mac/mach_o_image_annotations_reader_test.cc b/third_party/crashpad/crashpad/snapshot/mac/mach_o_image_annotations_reader_test.cc
new file mode 100644
index 0000000000000000000000000000000000000000..32f3b5c052987a62f86b5373e9662d10b75a65e5
--- /dev/null
+++ b/third_party/crashpad/crashpad/snapshot/mac/mach_o_image_annotations_reader_test.cc
@@ -0,0 +1,411 @@
+// Copyright 2014 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "snapshot/mac/mach_o_image_annotations_reader.h"
+
+#include <dlfcn.h>
+#include <mach/mach.h>
+#include <signal.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <map>
+#include <string>
+#include <vector>
+
+#include "base/basictypes.h"
+#include "base/files/file_path.h"
+#include "client/crashpad_info.h"
+#include "client/simple_string_dictionary.h"
+#include "gtest/gtest.h"
+#include "snapshot/mac/process_reader.h"
+#include "test/errors.h"
+#include "test/mac/mach_errors.h"
+#include "test/mac/mach_multiprocess.h"
+#include "test/paths.h"
+#include "util/file/file_io.h"
+#include "util/mac/mac_util.h"
+#include "util/mach/exc_server_variants.h"
+#include "util/mach/exception_ports.h"
+#include "util/mach/mach_extensions.h"
+#include "util/mach/mach_message.h"
+#include "util/mach/mach_message_server.h"
+
+namespace crashpad {
+namespace test {
+namespace {
+
+// \return The path to crashpad_snapshot_test_module_crashy_initializer.so
+std::string ModuleWithCrashyInitializer() {
+ return Paths::Executable().value() + "_module_crashy_initializer.so";
+}
+
+//! \return The path to the crashpad_snapshot_test_no_op executable.
+base::FilePath NoOpExecutable() {
+ return base::FilePath(Paths::Executable().value() + "_no_op");
+}
+
+class TestMachOImageAnnotationsReader final
+ : public MachMultiprocess,
+ public UniversalMachExcServer::Interface {
+ public:
+ enum TestType {
+ // Don’t crash, just test the CrashpadInfo interface.
+ kDontCrash = 0,
+
+ // The child process should crash by calling abort(). The parent verifies
+ // that the system libraries set the expected annotations.
+ //
+ // This test verifies that the message field in crashreporter_annotations_t
+ // can be recovered. Either 10.10.2 Libc-1044.1.2/stdlib/FreeBSD/abort.c
+ // abort() or 10.10.2 Libc-1044.10.1/sys/_libc_fork_child.c
+ // _libc_fork_child() calls CRSetCrashLogMessage() to set the message field.
+ kCrashAbort,
+
+ // The child process should crash at module initialization time, when dyld
+ // will have set an annotation matching the path of the module being
+ // initialized.
+ //
+ // This test exists to verify that the message2 field in
+ // crashreporter_annotations_t can be recovered. 10.10.2
+ // dyld-353.2.1/src/ImageLoaderMachO.cpp
+ // ImageLoaderMachO::doInitialization() calls CRSetCrashLogMessage2() to set
+ // the message2 field.
+ kCrashModuleInitialization,
+
+ // The child process should crash by setting DYLD_INSERT_LIBRARIES to
+ // contain a nonexistent library. The parent verifies that dyld sets the
+ // expected annotations.
+ kCrashDyld,
+ };
+
+ explicit TestMachOImageAnnotationsReader(TestType test_type)
+ : MachMultiprocess(),
+ UniversalMachExcServer::Interface(),
+ test_type_(test_type) {
+ }
+
+ ~TestMachOImageAnnotationsReader() {}
+
+ // UniversalMachExcServer::Interface:
+ kern_return_t CatchMachException(exception_behavior_t behavior,
+ exception_handler_t exception_port,
+ thread_t thread,
+ task_t task,
+ exception_type_t exception,
+ const mach_exception_data_type_t* code,
+ mach_msg_type_number_t code_count,
+ thread_state_flavor_t* flavor,
+ ConstThreadState old_state,
+ mach_msg_type_number_t old_state_count,
+ thread_state_t new_state,
+ mach_msg_type_number_t* new_state_count,
+ const mach_msg_trailer_t* trailer,
+ bool* destroy_complex_request) override {
+ *destroy_complex_request = true;
+
+ EXPECT_EQ(ChildTask(), task);
+
+ ProcessReader process_reader;
+ bool rv = process_reader.Initialize(task);
+ if (!rv) {
+ ADD_FAILURE();
+ } else {
+ const std::vector<ProcessReader::Module>& modules =
+ process_reader.Modules();
+ std::vector<std::string> all_annotations_vector;
+ for (const ProcessReader::Module& module : modules) {
+ if (module.reader) {
+ MachOImageAnnotationsReader module_annotations_reader(
+ &process_reader, module.reader, module.name);
+ std::vector<std::string> module_annotations_vector =
+ module_annotations_reader.Vector();
+ all_annotations_vector.insert(all_annotations_vector.end(),
+ module_annotations_vector.begin(),
+ module_annotations_vector.end());
+ } else {
+ EXPECT_TRUE(module.reader);
+ }
+ }
+
+ // Mac OS X 10.6 doesn’t have support for CrashReporter annotations
+ // (CrashReporterClient.h), so don’t look for any special annotations in
+ // that version.
+ int mac_os_x_minor_version = MacOSXMinorVersion();
+ if (mac_os_x_minor_version > 7) {
+ EXPECT_GE(all_annotations_vector.size(), 1u);
+
+ std::string expected_annotation;
+ switch (test_type_) {
+ case kCrashAbort:
+ // The child process calls abort(), so the expected annotation
+ // reflects this, with a string set by 10.7.5
+ // Libc-763.13/stdlib/abort-fbsd.c abort(). This string is still
+ // present in 10.9.5 Libc-997.90.3/stdlib/FreeBSD/abort.c abort(),
+ // but because abort() tests to see if a message is already set and
+ // something else in Libc will have set a message, this string is
+ // not the expectation on 10.9 or higher. Instead, after fork(), the
+ // child process has a message indicating that a fork() without
+ // exec() occurred. See 10.9.5 Libc-997.90.3/sys/_libc_fork_child.c
+ // _libc_fork_child().
+ expected_annotation =
+ mac_os_x_minor_version <= 8
+ ? "abort() called"
+ : "crashed on child side of fork pre-exec";
+ break;
+
+ case kCrashModuleInitialization:
+ // This message is set by dyld-353.2.1/src/ImageLoaderMachO.cpp
+ // ImageLoaderMachO::doInitialization().
+ expected_annotation = ModuleWithCrashyInitializer();
+ break;
+
+ case kCrashDyld:
+ // This is independent of dyld’s error_string, which is tested
+ // below.
+ expected_annotation = "dyld: launch, loading dependent libraries";
+ break;
+
+ default:
+ ADD_FAILURE();
+ break;
+ }
+
+ bool found = false;
+ for (const std::string& annotation : all_annotations_vector) {
+ // Look for the expectation as a leading susbtring, because the actual
+ // string that dyld uses will have the contents of the
+ // DYLD_INSERT_LIBRARIES environment variable appended to it on Mac
+ // OS X 10.10.
+ if (annotation.substr(0, expected_annotation.length()) ==
+ expected_annotation) {
+ found = true;
+ break;
+ }
+ }
+ EXPECT_TRUE(found) << expected_annotation;
+ }
+
+ // dyld exposes its error_string at least as far back as Mac OS X 10.4.
+ if (test_type_ == kCrashDyld) {
+ const char kExpectedAnnotation[] = "could not load inserted library";
+ size_t expected_annotation_length = strlen(kExpectedAnnotation);
+ bool found = false;
+ for (const std::string& annotation : all_annotations_vector) {
+ // Look for the expectation as a leading substring, because the actual
+ // string will contain the library’s pathname and, on Mac OS X 10.9
+ // and later, a reason.
+ if (annotation.substr(0, expected_annotation_length) ==
+ kExpectedAnnotation) {
+ found = true;
+ break;
+ }
+ }
+
+ EXPECT_TRUE(found) << kExpectedAnnotation;
+ }
+ }
+
+ ExcServerCopyState(
+ behavior, old_state, old_state_count, new_state, new_state_count);
+ return ExcServerSuccessfulReturnValue(exception, behavior, false);
+ }
+
+ private:
+ // MachMultiprocess:
+
+ void MachMultiprocessParent() override {
+ ProcessReader process_reader;
+ ASSERT_TRUE(process_reader.Initialize(ChildTask()));
+
+ // Wait for the child process to indicate that it’s done setting up its
+ // annotations via the CrashpadInfo interface.
+ char c;
+ CheckedReadFile(ReadPipeHandle(), &c, sizeof(c));
+
+ // Verify the “simple map” annotations set via the CrashpadInfo interface.
+ const std::vector<ProcessReader::Module>& modules =
+ process_reader.Modules();
+ std::map<std::string, std::string> all_annotations_simple_map;
+ for (const ProcessReader::Module& module : modules) {
+ MachOImageAnnotationsReader module_annotations_reader(
+ &process_reader, module.reader, module.name);
+ std::map<std::string, std::string> module_annotations_simple_map =
+ module_annotations_reader.SimpleMap();
+ all_annotations_simple_map.insert(module_annotations_simple_map.begin(),
+ module_annotations_simple_map.end());
+ }
+
+ EXPECT_GE(all_annotations_simple_map.size(), 5u);
+ EXPECT_EQ("crash", all_annotations_simple_map["#TEST# pad"]);
+ EXPECT_EQ("value", all_annotations_simple_map["#TEST# key"]);
+ EXPECT_EQ("y", all_annotations_simple_map["#TEST# x"]);
+ EXPECT_EQ("shorter", all_annotations_simple_map["#TEST# longer"]);
+ EXPECT_EQ("", all_annotations_simple_map["#TEST# empty_value"]);
+
+ // Tell the child process that it’s permitted to crash.
+ CheckedWriteFile(WritePipeHandle(), &c, sizeof(c));
+
+ if (test_type_ != kDontCrash) {
+ // Handle the child’s crash. Further validation will be done in
+ // CatchMachException().
+ UniversalMachExcServer universal_mach_exc_server(this);
+
+ mach_msg_return_t mr =
+ MachMessageServer::Run(&universal_mach_exc_server,
+ LocalPort(),
+ MACH_MSG_OPTION_NONE,
+ MachMessageServer::kOneShot,
+ MachMessageServer::kReceiveLargeError,
+ kMachMessageTimeoutWaitIndefinitely);
+ EXPECT_EQ(MACH_MSG_SUCCESS, mr)
+ << MachErrorMessage(mr, "MachMessageServer::Run");
+
+ switch (test_type_) {
+ case kCrashAbort:
+ SetExpectedChildTermination(kTerminationSignal, SIGABRT);
+ break;
+
+ case kCrashModuleInitialization:
+ // This crash is triggered by __builtin_trap(), which shows up as
+ // SIGILL.
+ SetExpectedChildTermination(kTerminationSignal, SIGILL);
+ break;
+
+ case kCrashDyld:
+ // dyld fatal errors result in the execution of an int3 instruction on
+ // x86 and a trap instruction on ARM, both of which raise SIGTRAP.
+ // 10.9.5 dyld-239.4/src/dyldStartup.s _dyld_fatal_error.
+ SetExpectedChildTermination(kTerminationSignal, SIGTRAP);
+ break;
+
+ default:
+ FAIL();
+ break;
+ }
+ }
+ }
+
+ void MachMultiprocessChild() override {
+ CrashpadInfo* crashpad_info = CrashpadInfo::GetCrashpadInfo();
+
+ // This is “leaked” to crashpad_info.
+ SimpleStringDictionary* simple_annotations = new SimpleStringDictionary();
+ simple_annotations->SetKeyValue("#TEST# pad", "break");
+ simple_annotations->SetKeyValue("#TEST# key", "value");
+ simple_annotations->SetKeyValue("#TEST# pad", "crash");
+ simple_annotations->SetKeyValue("#TEST# x", "y");
+ simple_annotations->SetKeyValue("#TEST# longer", "shorter");
+ simple_annotations->SetKeyValue("#TEST# empty_value", "");
+
+ crashpad_info->set_simple_annotations(simple_annotations);
+
+ // Tell the parent that the environment has been set up.
+ char c = '\0';
+ CheckedWriteFile(WritePipeHandle(), &c, sizeof(c));
+
+ // Wait for the parent to indicate that it’s safe to crash.
+ CheckedReadFile(ReadPipeHandle(), &c, sizeof(c));
+
+ // Direct an exception message to the exception server running in the
+ // parent.
+ ExceptionPorts exception_ports(ExceptionPorts::kTargetTypeTask,
+ mach_task_self());
+ ASSERT_TRUE(exception_ports.SetExceptionPort(
+ EXC_MASK_CRASH, RemotePort(), EXCEPTION_DEFAULT, THREAD_STATE_NONE));
+
+ switch (test_type_) {
+ case kDontCrash: {
+ break;
+ }
+
+ case kCrashAbort: {
+ abort();
+ break;
+ }
+
+ case kCrashModuleInitialization: {
+ // Load a module that crashes while executing a module initializer.
+ void* dl_handle = dlopen(ModuleWithCrashyInitializer().c_str(),
+ RTLD_LAZY | RTLD_LOCAL);
+
+ // This should have crashed in the dlopen(). If dlopen() failed, the
+ // ASSERT_NE() will show the message. If it succeeded without crashing,
+ // the FAIL() will fail the test.
+ ASSERT_NE(nullptr, dl_handle) << dlerror();
+ FAIL();
+ break;
+ }
+
+ case kCrashDyld: {
+ // Set DYLD_INSERT_LIBRARIES to contain a library that does not exist.
+ // Unable to load it, dyld will abort with a fatal error.
+ ASSERT_EQ(
+ 0,
+ setenv(
+ "DYLD_INSERT_LIBRARIES", "/var/empty/NoDirectory/NoLibrary", 1))
+ << ErrnoMessage("setenv");
+
+ // The actual executable doesn’t matter very much, because dyld won’t
+ // ever launch it. It just needs to be an executable that uses dyld as
+ // its LC_LOAD_DYLINKER (all normal executables do). A custom no-op
+ // executable is provided because DYLD_INSERT_LIBRARIES does not work
+ // with system executables on OS X 10.11 due to System Integrity
+ // Protection.
+ base::FilePath no_op_executable = NoOpExecutable();
+ ASSERT_EQ(0, execl(no_op_executable.value().c_str(),
+ no_op_executable.BaseName().value().c_str(),
+ nullptr))
+ << ErrnoMessage("execl");
+ break;
+ }
+
+ default:
+ break;
+ }
+ }
+
+ TestType test_type_;
+
+ DISALLOW_COPY_AND_ASSIGN(TestMachOImageAnnotationsReader);
+};
+
+TEST(MachOImageAnnotationsReader, DontCrash) {
+ TestMachOImageAnnotationsReader test_mach_o_image_annotations_reader(
+ TestMachOImageAnnotationsReader::kDontCrash);
+ test_mach_o_image_annotations_reader.Run();
+}
+
+TEST(MachOImageAnnotationsReader, CrashAbort) {
+ TestMachOImageAnnotationsReader test_mach_o_image_annotations_reader(
+ TestMachOImageAnnotationsReader::kCrashAbort);
+ test_mach_o_image_annotations_reader.Run();
+}
+
+TEST(MachOImageAnnotationsReader, CrashModuleInitialization) {
+ TestMachOImageAnnotationsReader test_mach_o_image_annotations_reader(
+ TestMachOImageAnnotationsReader::kCrashModuleInitialization);
+ test_mach_o_image_annotations_reader.Run();
+}
+
+TEST(MachOImageAnnotationsReader, CrashDyld) {
+ TestMachOImageAnnotationsReader test_mach_o_image_annotations_reader(
+ TestMachOImageAnnotationsReader::kCrashDyld);
+ test_mach_o_image_annotations_reader.Run();
+}
+
+} // namespace
+} // namespace test
+} // namespace crashpad

Powered by Google App Engine
This is Rietveld 408576698