| // Copyright 2019 The Fuchsia Authors |
| // |
| // Use of this source code is governed by a MIT-style |
| // license that can be found in the LICENSE file or at |
| // https://opensource.org/licenses/MIT |
| |
| #include "object/exceptionate.h" |
| |
| #include <zircon/errors.h> |
| #include <zircon/syscalls/exception.h> |
| |
| #include <object/exception_dispatcher.h> |
| #include <object/handle.h> |
| #include <object/message_packet.h> |
| #include <object/process_dispatcher.h> |
| #include <object/thread_dispatcher.h> |
| |
| Exceptionate::Exceptionate(uint32_t type) : type_(type) {} |
| |
| Exceptionate::~Exceptionate() { Shutdown(); } |
| |
| zx_status_t Exceptionate::SetChannel(KernelHandle<ChannelDispatcher> channel_handle, |
| zx_rights_t thread_rights, zx_rights_t process_rights) { |
| if (!channel_handle.dispatcher()) { |
| return ZX_ERR_INVALID_ARGS; |
| } |
| |
| Guard<Mutex> guard{&lock_}; |
| |
| if (is_shutdown_) { |
| return ZX_ERR_BAD_STATE; |
| } |
| |
| if (HasValidChannelLocked()) { |
| return ZX_ERR_ALREADY_BOUND; |
| } |
| |
| // At this point we're certain that either there is no channel or it's a |
| // dead channel with no peer (since channel endpoints can't re-open) so we |
| // can overwrite it. |
| channel_handle_ = ktl::move(channel_handle); |
| thread_rights_ = thread_rights; |
| process_rights_ = process_rights; |
| |
| return ZX_OK; |
| } |
| |
| void Exceptionate::Shutdown() { |
| Guard<Mutex> guard{&lock_}; |
| channel_handle_.reset(); |
| is_shutdown_ = true; |
| } |
| |
| bool Exceptionate::HasValidChannel() const { |
| Guard<Mutex> guard{&lock_}; |
| return HasValidChannelLocked(); |
| } |
| |
| bool Exceptionate::HasValidChannelLocked() const { |
| return channel_handle_.dispatcher() && !channel_handle_.dispatcher()->PeerHasClosed(); |
| } |
| |
| zx_status_t Exceptionate::SendException(const fbl::RefPtr<ExceptionDispatcher>& exception) { |
| DEBUG_ASSERT(exception); |
| |
| Guard<Mutex> guard{&lock_}; |
| |
| if (!channel_handle_.dispatcher()) { |
| return ZX_ERR_NEXT; |
| } |
| |
| zx_exception_info_t info{}; |
| |
| // Since info will be copied to a usermode process make sure it's safe to to be copied (no |
| // internal padding, trivially copyable, etc.). |
| static_assert(internal::is_copy_allowed<decltype(info)>::value); |
| |
| info.tid = exception->thread()->get_koid(); |
| info.pid = exception->thread()->process()->get_koid(); |
| info.type = exception->exception_type(); |
| |
| MessagePacketPtr message; |
| zx_status_t status = |
| MessagePacket::Create(reinterpret_cast<const char*>(&info), sizeof(info), 1, &message); |
| if (status != ZX_OK) { |
| return status; |
| } |
| |
| // It's OK if the function fails after this point, all exception sending |
| // funnels through here so the task rights will get overwritten next time |
| // we try to send it. |
| // |
| // This is safe to do because we know that an ExceptionDispatcher only goes |
| // to one handler at a time, so we'll never change the task rights while |
| // the exception is out in userspace. |
| exception->SetTaskRights(thread_rights_, process_rights_); |
| |
| HandleOwner exception_handle(Handle::Make(exception, ExceptionDispatcher::default_rights())); |
| if (!exception_handle) { |
| return ZX_ERR_NO_MEMORY; |
| } |
| message->mutable_handles()[0] = exception_handle.release(); |
| message->set_owns_handles(true); |
| |
| status = channel_handle_.dispatcher()->Write(ZX_KOID_INVALID, ktl::move(message)); |
| |
| // If sending failed for any reason, the exception handle never made it to |
| // userspace and has now gone out of scope, triggering on_zero_handles(), |
| // so we need to reset the exception. |
| if (status != ZX_OK) { |
| exception->DiscardHandleClose(); |
| } |
| |
| // ZX_ERR_PEER_CLOSED just indicates that there's no longer an endpoint |
| // to receive exceptions, simplify things for callers by collapsing this |
| // into the ZX_ERR_NEXT case since it means the same thing. |
| if (status == ZX_ERR_PEER_CLOSED) { |
| channel_handle_.reset(); |
| return ZX_ERR_NEXT; |
| } |
| |
| return status; |
| } |