text: // 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 <sys/types.h>
  #include <unistd.h>
  
  #include <map>
  #include <string>
  #include <vector>
  
  #include "base/files/file_path.h"
  #include "base/macros.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;
  
      // In 10.12, dyld fatal errors as tested by test_type_ = kCrashDyld are via
      // abort_with_payload(). In 10.12.1, the task port delivered in an exception
      // message for this termination type is a corpse, even when the exception is
      // EXC_CRASH and not EXC_CORPSE_NOTIFY. The corpse task port (here, |task|)
      // is distinct from the process’ original task port (ChildTask()). This is
      // filed as https://openradar.appspot.com/29079442.
      //
      // Instead of comparing task ports, compare PIDs.
      pid_t task_pid;
      kern_return_t kr = pid_for_task(task, &task_pid);
      EXPECT_EQ(KERN_SUCCESS, kr) << MachErrorMessage(kr, "pid_for_task");
      EXPECT_EQ(ChildPID(), task_pid);
  
      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:
            // Prior to 10.12, 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. This changed in 10.12 to use
            // abort_with_payload(), which appears as SIGABRT to a waiting parent.
            SetExpectedChildTermination(
                kTerminationSignal,
                MacOSXMinorVersion() < 12 ? SIGTRAP : SIGABRT);
            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
  