// 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 "util/mach/exc_server_variants.h"

#include <Availability.h>
#include <string.h>

#include <algorithm>
#include <vector>

#include "base/stl_util.h"
#include "build/build_config.h"
#include "util/mac/mac_util.h"
#include "util/mach/composite_mach_message_server.h"
#include "util/mach/exc.h"
#include "util/mach/excServer.h"
#include "util/mach/exception_behaviors.h"
#include "util/mach/mach_exc.h"
#include "util/mach/mach_excServer.h"
#include "util/mach/mach_message.h"

namespace crashpad {

namespace {

// Traits for ExcServer<> and SimplifiedExcServer<> adapting them for use with
// the exc subsystem.
struct ExcTraits {
  using ExceptionCode = exception_data_type_t;

  using RequestUnion = __RequestUnion__exc_subsystem;
  using ReplyUnion = __ReplyUnion__exc_subsystem;

  using ExceptionRaiseRequest = __Request__exception_raise_t;
  using ExceptionRaiseStateRequest = __Request__exception_raise_state_t;
  using ExceptionRaiseStateIdentityRequest =
      __Request__exception_raise_state_identity_t;

  using ExceptionRaiseReply = __Reply__exception_raise_t;
  using ExceptionRaiseStateReply = __Reply__exception_raise_state_t;
  using ExceptionRaiseStateIdentityReply =
      __Reply__exception_raise_state_identity_t;

  // The MIG-generated __MIG_check__Request__*() functions are not declared as
  // accepting const data, but they could have been because they in fact do not
  // modify the data.

  static kern_return_t MIGCheckRequestExceptionRaise(
      const ExceptionRaiseRequest* in_request) {
    return __MIG_check__Request__exception_raise_t(
        const_cast<ExceptionRaiseRequest*>(in_request));
  }

  static kern_return_t MIGCheckRequestExceptionRaiseState(
      const ExceptionRaiseStateRequest* in_request,
      const ExceptionRaiseStateRequest** in_request_1) {
    return __MIG_check__Request__exception_raise_state_t(
        const_cast<ExceptionRaiseStateRequest*>(in_request),
        const_cast<ExceptionRaiseStateRequest**>(in_request_1));
  }

  static kern_return_t MIGCheckRequestExceptionRaiseStateIdentity(
      const ExceptionRaiseStateIdentityRequest* in_request,
      const ExceptionRaiseStateIdentityRequest** in_request_1) {
    return __MIG_check__Request__exception_raise_state_identity_t(
        const_cast<ExceptionRaiseStateIdentityRequest*>(in_request),
        const_cast<ExceptionRaiseStateIdentityRequest**>(in_request_1));
  }

  // There are no predefined constants for these.
  static const mach_msg_id_t kMachMessageIDExceptionRaise = 2401;
  static const mach_msg_id_t kMachMessageIDExceptionRaiseState = 2402;
  static const mach_msg_id_t kMachMessageIDExceptionRaiseStateIdentity = 2403;

  static const exception_behavior_t kExceptionBehavior = 0;
};

// Traits for ExcServer<> and SimplifiedExcServer<> adapting them for use with
// the mach_exc subsystem.
struct MachExcTraits {
  using ExceptionCode = mach_exception_data_type_t;

  using RequestUnion = __RequestUnion__mach_exc_subsystem;
  using ReplyUnion = __ReplyUnion__mach_exc_subsystem;

  using ExceptionRaiseRequest = __Request__mach_exception_raise_t;
  using ExceptionRaiseStateRequest = __Request__mach_exception_raise_state_t;
  using ExceptionRaiseStateIdentityRequest =
      __Request__mach_exception_raise_state_identity_t;

  using ExceptionRaiseReply = __Reply__mach_exception_raise_t;
  using ExceptionRaiseStateReply = __Reply__mach_exception_raise_state_t;
  using ExceptionRaiseStateIdentityReply =
      __Reply__mach_exception_raise_state_identity_t;

  // The MIG-generated __MIG_check__Request__*() functions are not declared as
  // accepting const data, but they could have been because they in fact do not
  // modify the data.

  static kern_return_t MIGCheckRequestExceptionRaise(
      const ExceptionRaiseRequest* in_request) {
    return __MIG_check__Request__mach_exception_raise_t(
        const_cast<ExceptionRaiseRequest*>(in_request));
  }

  static kern_return_t MIGCheckRequestExceptionRaiseState(
      const ExceptionRaiseStateRequest* in_request,
      const ExceptionRaiseStateRequest** in_request_1) {
    return __MIG_check__Request__mach_exception_raise_state_t(
        const_cast<ExceptionRaiseStateRequest*>(in_request),
        const_cast<ExceptionRaiseStateRequest**>(in_request_1));
  }

  static kern_return_t MIGCheckRequestExceptionRaiseStateIdentity(
      const ExceptionRaiseStateIdentityRequest* in_request,
      const ExceptionRaiseStateIdentityRequest** in_request_1) {
    return __MIG_check__Request__mach_exception_raise_state_identity_t(
        const_cast<ExceptionRaiseStateIdentityRequest*>(in_request),
        const_cast<ExceptionRaiseStateIdentityRequest**>(in_request_1));
  }

  // There are no predefined constants for these.
  static const mach_msg_id_t kMachMessageIDExceptionRaise = 2405;
  static const mach_msg_id_t kMachMessageIDExceptionRaiseState = 2406;
  static const mach_msg_id_t kMachMessageIDExceptionRaiseStateIdentity = 2407;

  static const exception_behavior_t kExceptionBehavior = MACH_EXCEPTION_CODES;
};

//! \brief A server interface for the `exc` or `mach_exc` Mach subsystems.
template <typename Traits>
class ExcServer : public MachMessageServer::Interface {
 public:
  //! \brief An interface that the different request messages that are a part of
  //!     the `exc` or `mach_exc` Mach subsystems can be dispatched to.
  class Interface {
   public:
    //! \brief Handles exceptions raised by `exception_raise()` or
    //!     `mach_exception_raise()`.
    //!
    //! This behaves equivalently to a `catch_exception_raise()` function used
    //! with `exc_server()`, or a `catch_mach_exception_raise()` function used
    //! with `mach_exc_server()`.
    //!
    //! \param[in] trailer The trailer received with the request message.
    //! \param[out] destroy_request `true` if the request message is to be
    //!     destroyed even when this method returns success. See
    //!     MachMessageServer::Interface.
    virtual kern_return_t CatchExceptionRaise(
        exception_handler_t exception_port,
        thread_t thread,
        task_t task,
        exception_type_t exception,
        const typename Traits::ExceptionCode* code,
        mach_msg_type_number_t code_count,
        const mach_msg_trailer_t* trailer,
        bool* destroy_request) = 0;

    //! \brief Handles exceptions raised by `exception_raise_state()` or
    //!     `mach_exception_raise_state()`.
    //!
    //! This behaves equivalently to a `catch_exception_raise_state()` function
    //! used with `exc_server()`, or a `catch_mach_exception_raise_state()`
    //! function used with `mach_exc_server()`.
    //!
    //! There is no \a destroy_request parameter because, unlike
    //! CatchExceptionRaise() and CatchExceptionRaiseStateIdentity(), the
    //! request message is not complex (it does not carry the \a thread or \a
    //! task port rights) and thus there is nothing to destroy.
    //!
    //! \param[in] trailer The trailer received with the request message.
    virtual kern_return_t CatchExceptionRaiseState(
        exception_handler_t exception_port,
        exception_type_t exception,
        const typename Traits::ExceptionCode* 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) = 0;

    //! \brief Handles exceptions raised by `exception_raise_state_identity()`
    //!     or `mach_exception_raise_state_identity()`.
    //!
    //! This behaves equivalently to a `catch_exception_raise_state_identity()`
    //! function used with `exc_server()`, or a
    //! `catch_mach_exception_raise_state_identity()` function used with
    //! `mach_exc_server()`.
    //!
    //! \param[in] trailer The trailer received with the request message.
    //! \param[out] destroy_request `true` if the request message is to be
    //!     destroyed even when this method returns success. See
    //!     MachMessageServer::Interface.
    virtual kern_return_t CatchExceptionRaiseStateIdentity(
        exception_handler_t exception_port,
        thread_t thread,
        task_t task,
        exception_type_t exception,
        const typename Traits::ExceptionCode* 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_request) = 0;

   protected:
    ~Interface() {}
  };

  //! \brief Constructs an object of this class.
  //!
  //! \param[in] interface The interface to dispatch requests to. Weak.
  explicit ExcServer(Interface* interface)
      : MachMessageServer::Interface(), interface_(interface) {}

  // MachMessageServer::Interface:

  bool MachMessageServerFunction(const mach_msg_header_t* in_header,
                                 mach_msg_header_t* out_header,
                                 bool* destroy_complex_request) override;

  std::set<mach_msg_id_t> MachMessageServerRequestIDs() override {
    constexpr mach_msg_id_t request_ids[] = {
        Traits::kMachMessageIDExceptionRaise,
        Traits::kMachMessageIDExceptionRaiseState,
        Traits::kMachMessageIDExceptionRaiseStateIdentity,
    };
    return std::set<mach_msg_id_t>(&request_ids[0],
                                   &request_ids[base::size(request_ids)]);
  }

  mach_msg_size_t MachMessageServerRequestSize() override {
    return sizeof(typename Traits::RequestUnion);
  }

  mach_msg_size_t MachMessageServerReplySize() override {
    return sizeof(typename Traits::ReplyUnion);
  }

 private:
  Interface* interface_;  // weak

  DISALLOW_COPY_AND_ASSIGN(ExcServer);
};

template <typename Traits>
bool ExcServer<Traits>::MachMessageServerFunction(
    const mach_msg_header_t* in_header,
    mach_msg_header_t* out_header,
    bool* destroy_complex_request) {
  PrepareMIGReplyFromRequest(in_header, out_header);

  const mach_msg_trailer_t* in_trailer =
      MachMessageTrailerFromHeader(in_header);

  switch (in_header->msgh_id) {
    case Traits::kMachMessageIDExceptionRaise: {
      // exception_raise(), catch_exception_raise(), mach_exception_raise(),
      // catch_mach_exception_raise().
      using Request = typename Traits::ExceptionRaiseRequest;
      const Request* in_request = reinterpret_cast<const Request*>(in_header);
      kern_return_t kr = Traits::MIGCheckRequestExceptionRaise(in_request);
      if (kr != MACH_MSG_SUCCESS) {
        SetMIGReplyError(out_header, kr);
        return true;
      }

      using Reply = typename Traits::ExceptionRaiseReply;
      Reply* out_reply = reinterpret_cast<Reply*>(out_header);
      out_reply->RetCode =
          interface_->CatchExceptionRaise(in_header->msgh_local_port,
                                          in_request->thread.name,
                                          in_request->task.name,
                                          in_request->exception,
                                          in_request->code,
                                          in_request->codeCnt,
                                          in_trailer,
                                          destroy_complex_request);
      if (out_reply->RetCode != KERN_SUCCESS) {
        return true;
      }

      out_header->msgh_size = sizeof(*out_reply);
      return true;
    }

    case Traits::kMachMessageIDExceptionRaiseState: {
      // exception_raise_state(), catch_exception_raise_state(),
      // mach_exception_raise_state(), catch_mach_exception_raise_state().
      using Request = typename Traits::ExceptionRaiseStateRequest;
      const Request* in_request = reinterpret_cast<const Request*>(in_header);

      // in_request_1 is used for the portion of the request after the codes,
      // which in theory can be variable-length. The check function will set it.
      const Request* in_request_1;
      kern_return_t kr =
          Traits::MIGCheckRequestExceptionRaiseState(in_request, &in_request_1);
      if (kr != MACH_MSG_SUCCESS) {
        SetMIGReplyError(out_header, kr);
        return true;
      }

      using Reply = typename Traits::ExceptionRaiseStateReply;
      Reply* out_reply = reinterpret_cast<Reply*>(out_header);
      out_reply->flavor = in_request_1->flavor;
      out_reply->new_stateCnt = base::size(out_reply->new_state);
      out_reply->RetCode =
          interface_->CatchExceptionRaiseState(in_header->msgh_local_port,
                                               in_request->exception,
                                               in_request->code,
                                               in_request->codeCnt,
                                               &out_reply->flavor,
                                               in_request_1->old_state,
                                               in_request_1->old_stateCnt,
                                               out_reply->new_state,
                                               &out_reply->new_stateCnt,
                                               in_trailer);
      if (out_reply->RetCode != KERN_SUCCESS) {
        return true;
      }

      out_header->msgh_size =
          sizeof(*out_reply) - sizeof(out_reply->new_state) +
          sizeof(out_reply->new_state[0]) * out_reply->new_stateCnt;
      return true;
    }

    case Traits::kMachMessageIDExceptionRaiseStateIdentity: {
      // exception_raise_state_identity(),
      // catch_exception_raise_state_identity(),
      // mach_exception_raise_state_identity(),
      // catch_mach_exception_raise_state_identity().
      using Request = typename Traits::ExceptionRaiseStateIdentityRequest;
      const Request* in_request = reinterpret_cast<const Request*>(in_header);

      // in_request_1 is used for the portion of the request after the codes,
      // which in theory can be variable-length. The check function will set it.
      const Request* in_request_1;
      kern_return_t kr = Traits::MIGCheckRequestExceptionRaiseStateIdentity(
          in_request, &in_request_1);
      if (kr != MACH_MSG_SUCCESS) {
        SetMIGReplyError(out_header, kr);
        return true;
      }

      using Reply = typename Traits::ExceptionRaiseStateIdentityReply;
      Reply* out_reply = reinterpret_cast<Reply*>(out_header);
      out_reply->flavor = in_request_1->flavor;
      out_reply->new_stateCnt = base::size(out_reply->new_state);
      out_reply->RetCode = interface_->CatchExceptionRaiseStateIdentity(
          in_header->msgh_local_port,
          in_request->thread.name,
          in_request->task.name,
          in_request->exception,
          in_request->code,
          in_request->codeCnt,
          &out_reply->flavor,
          in_request_1->old_state,
          in_request_1->old_stateCnt,
          out_reply->new_state,
          &out_reply->new_stateCnt,
          in_trailer,
          destroy_complex_request);
      if (out_reply->RetCode != KERN_SUCCESS) {
        return true;
      }

      out_header->msgh_size =
          sizeof(*out_reply) - sizeof(out_reply->new_state) +
          sizeof(out_reply->new_state[0]) * out_reply->new_stateCnt;
      return true;
    }

    default: {
      SetMIGReplyError(out_header, MIG_BAD_ID);
      return false;
    }
  }
}

//! \brief A server interface for the `exc` or `mach_exc` Mach subsystems,
//!     simplified to have only a single interface method needing
//!     implementation.
template <typename Traits>
class SimplifiedExcServer final : public ExcServer<Traits>,
                                  public ExcServer<Traits>::Interface {
 public:
  //! \brief An interface that the different request messages that are a part of
  //!     the `exc` or `mach_exc` Mach subsystems can be dispatched to.
  class Interface {
   public:
    //! \brief Handles exceptions raised by `exception_raise()`,
    //!     `exception_raise_state()`, and `exception_raise_state_identity()`;
    //!     or `mach_exception_raise()`, `mach_exception_raise_state()`, and
    //!     `mach_exception_raise_state_identity()`.
    //!
    //! For convenience in implementation, these different “behaviors” of
    //! exception messages are all mapped to a single interface method. The
    //! exception’s original “behavior” is specified in the \a behavior
    //! parameter. Only parameters that were supplied in the request message
    //! are populated, other parameters are set to reasonable default values.
    //!
    //! The meanings of most parameters are identical to that of
    //! ExcServer<>::Interface::CatchExceptionRaiseStateIdentity().
    //!
    //! \param[in] behavior `EXCEPTION_DEFAULT`, `EXCEPTION_STATE`, or
    //!     `EXCEPTION_STATE_IDENTITY`, identifying which exception request
    //!     message was processed and thus which other parameters are valid.
    //!     When used with the `mach_exc` subsystem, `MACH_EXCEPTION_CODES` will
    //!     be ORed in to this parameter.
    virtual kern_return_t CatchException(
        exception_behavior_t behavior,
        exception_handler_t exception_port,
        thread_t thread,
        task_t task,
        exception_type_t exception,
        const typename Traits::ExceptionCode* 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) = 0;

   protected:
    ~Interface() {}
  };

  //! \brief Constructs an object of this class.
  //!
  //! \param[in] interface The interface to dispatch requests to. Weak.
  explicit SimplifiedExcServer(Interface* interface)
      : ExcServer<Traits>(this),
        ExcServer<Traits>::Interface(),
        interface_(interface) {}

  // ExcServer::Interface:

  kern_return_t CatchExceptionRaise(exception_handler_t exception_port,
                                    thread_t thread,
                                    task_t task,
                                    exception_type_t exception,
                                    const typename Traits::ExceptionCode* code,
                                    mach_msg_type_number_t code_count,
                                    const mach_msg_trailer_t* trailer,
                                    bool* destroy_request) override {
    thread_state_flavor_t flavor = THREAD_STATE_NONE;
    mach_msg_type_number_t new_state_count = 0;
    return interface_->CatchException(
        Traits::kExceptionBehavior | EXCEPTION_DEFAULT,
        exception_port,
        thread,
        task,
        exception,
        code_count ? code : nullptr,
        code_count,
        &flavor,
        nullptr,
        0,
        nullptr,
        &new_state_count,
        trailer,
        destroy_request);
  }

  kern_return_t CatchExceptionRaiseState(
      exception_handler_t exception_port,
      exception_type_t exception,
      const typename Traits::ExceptionCode* 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) override {
    bool destroy_complex_request = false;
    return interface_->CatchException(
        Traits::kExceptionBehavior | EXCEPTION_STATE,
        exception_port,
        THREAD_NULL,
        TASK_NULL,
        exception,
        code_count ? code : nullptr,
        code_count,
        flavor,
        old_state_count ? old_state : nullptr,
        old_state_count,
        new_state_count ? new_state : nullptr,
        new_state_count,
        trailer,
        &destroy_complex_request);
  }

  kern_return_t CatchExceptionRaiseStateIdentity(
      exception_handler_t exception_port,
      thread_t thread,
      task_t task,
      exception_type_t exception,
      const typename Traits::ExceptionCode* 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_request) override {
    return interface_->CatchException(
        Traits::kExceptionBehavior | EXCEPTION_STATE_IDENTITY,
        exception_port,
        thread,
        task,
        exception,
        code_count ? code : nullptr,
        code_count,
        flavor,
        old_state_count ? old_state : nullptr,
        old_state_count,
        new_state_count ? new_state : nullptr,
        new_state_count,
        trailer,
        destroy_request);
  }

 private:
  Interface* interface_;  // weak

  DISALLOW_COPY_AND_ASSIGN(SimplifiedExcServer);
};

}  // namespace

namespace internal {

class UniversalMachExcServerImpl final
    : public CompositeMachMessageServer,
      public SimplifiedExcServer<ExcTraits>::Interface,
      public SimplifiedExcServer<MachExcTraits>::Interface {
 public:
  explicit UniversalMachExcServerImpl(
      UniversalMachExcServer::Interface* interface)
      : CompositeMachMessageServer(),
        SimplifiedExcServer<ExcTraits>::Interface(),
        SimplifiedExcServer<MachExcTraits>::Interface(),
        exc_server_(this),
        mach_exc_server_(this),
        interface_(interface) {
    AddHandler(&exc_server_);
    AddHandler(&mach_exc_server_);
  }

  ~UniversalMachExcServerImpl() {}

  // SimplifiedExcServer<ExcTraits>::Interface:
  kern_return_t CatchException(exception_behavior_t behavior,
                               exception_handler_t exception_port,
                               thread_t thread,
                               task_t task,
                               exception_type_t exception,
                               const 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) {
    std::vector<mach_exception_data_type_t> mach_codes;
    mach_codes.reserve(code_count);
    for (size_t index = 0; index < code_count; ++index) {
      mach_codes.push_back(code[index]);
    }

    return interface_->CatchMachException(behavior,
                                          exception_port,
                                          thread,
                                          task,
                                          exception,
                                          code_count ? &mach_codes[0] : nullptr,
                                          code_count,
                                          flavor,
                                          old_state_count ? old_state : nullptr,
                                          old_state_count,
                                          new_state_count ? new_state : nullptr,
                                          new_state_count,
                                          trailer,
                                          destroy_complex_request);
  }

  // SimplifiedExcServer<MachExcTraits>::Interface:
  kern_return_t CatchException(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) {
    return interface_->CatchMachException(behavior,
                                          exception_port,
                                          thread,
                                          task,
                                          exception,
                                          code_count ? code : nullptr,
                                          code_count,
                                          flavor,
                                          old_state_count ? old_state : nullptr,
                                          old_state_count,
                                          new_state_count ? new_state : nullptr,
                                          new_state_count,
                                          trailer,
                                          destroy_complex_request);
  }

 private:
  SimplifiedExcServer<ExcTraits> exc_server_;
  SimplifiedExcServer<MachExcTraits> mach_exc_server_;
  UniversalMachExcServer::Interface* interface_;  // weak

  DISALLOW_COPY_AND_ASSIGN(UniversalMachExcServerImpl);
};

}  // namespace internal

UniversalMachExcServer::UniversalMachExcServer(
    UniversalMachExcServer::Interface* interface)
    : MachMessageServer::Interface(),
      impl_(new internal::UniversalMachExcServerImpl(interface)) {
}

UniversalMachExcServer::~UniversalMachExcServer() {
}

bool UniversalMachExcServer::MachMessageServerFunction(
    const mach_msg_header_t* in_header,
    mach_msg_header_t* out_header,
    bool* destroy_complex_request) {
  return impl_->MachMessageServerFunction(
      in_header, out_header, destroy_complex_request);
}

std::set<mach_msg_id_t> UniversalMachExcServer::MachMessageServerRequestIDs() {
  return impl_->MachMessageServerRequestIDs();
}

mach_msg_size_t UniversalMachExcServer::MachMessageServerRequestSize() {
  return impl_->MachMessageServerRequestSize();
}

mach_msg_size_t UniversalMachExcServer::MachMessageServerReplySize() {
  return impl_->MachMessageServerReplySize();
}

kern_return_t ExcServerSuccessfulReturnValue(exception_type_t exception,
                                             exception_behavior_t behavior,
                                             bool set_thread_state) {
  if (exception == EXC_CRASH
#if defined(OS_MAC) && __MAC_OS_X_VERSION_MIN_REQUIRED < __MAC_10_11
      && MacOSVersionNumber() >= 10'11'00
#endif
     ) {
    return KERN_SUCCESS;
  }

  if (!set_thread_state && ExceptionBehaviorHasState(behavior)) {
    return MACH_RCV_PORT_DIED;
  }

  return KERN_SUCCESS;
}

void ExcServerCopyState(exception_behavior_t behavior,
                        ConstThreadState old_state,
                        mach_msg_type_number_t old_state_count,
                        thread_state_t new_state,
                        mach_msg_type_number_t* new_state_count) {
  if (ExceptionBehaviorHasState(behavior)) {
    *new_state_count = std::min(old_state_count, *new_state_count);
    memcpy(new_state, old_state, *new_state_count * sizeof(old_state[0]));
  }
}

}  // namespace crashpad
