blob: 66f396f3aa7d65b0d863c8a3f2afe6d5f52109fa [file] [log] [blame]
// Copyright 2020 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/handle_table.h"
#include <lib/crypto/global_prng.h>
#include <object/job_dispatcher.h>
constexpr uint32_t kHandleMustBeOneMask = ((0x1u << kHandleReservedBits) - 1);
static_assert(kHandleMustBeOneMask == ZX_HANDLE_FIXED_BITS_MASK,
"kHandleMustBeOneMask must match ZX_HANDLE_FIXED_BITS_MASK!");
static zx_handle_t map_handle_to_value(const Handle* handle, uint32_t mixer) {
// Ensure that the last two bits of the result is not zero, and make sure we
// don't lose any base_value bits when shifting.
constexpr uint32_t kBaseValueMustBeZeroMask =
(kHandleMustBeOneMask << ((sizeof(handle->base_value()) * 8) - kHandleReservedBits));
DEBUG_ASSERT((mixer & kHandleMustBeOneMask) == 0);
DEBUG_ASSERT((handle->base_value() & kBaseValueMustBeZeroMask) == 0);
auto handle_id = (handle->base_value() << kHandleReservedBits) | kHandleMustBeOneMask;
return static_cast<zx_handle_t>(mixer ^ handle_id);
}
static Handle* map_value_to_handle(zx_handle_t value, uint32_t mixer) {
// Validate that the "must be one" bits are actually one.
if ((value & kHandleMustBeOneMask) != kHandleMustBeOneMask) {
return nullptr;
}
uint32_t handle_id = (static_cast<uint32_t>(value) ^ mixer) >> kHandleReservedBits;
return Handle::FromU32(handle_id);
}
HandleTable::HandleTable(ProcessDispatcher* process) : process_(process) {
// Generate handle XOR mask with top bit and bottom two bits cleared
uint32_t secret;
auto prng = crypto::GlobalPRNG::GetInstance();
prng->Draw(&secret, sizeof(secret));
// Handle values must always have the low kHandleReservedBits set. Do not
// ever attempt to toggle these bits using the random_value_ xor mask.
random_value_ = secret << kHandleReservedBits;
}
HandleTable::~HandleTable() {
DEBUG_ASSERT(handles_.is_empty());
DEBUG_ASSERT(count_ == 0);
DEBUG_ASSERT(cursors_.is_empty());
}
void HandleTable::Clean() {
HandleList to_clean;
{
Guard<BrwLockPi, BrwLockPi::Writer> guard{&lock_};
for (auto& cursor : cursors_) {
cursor.Invalidate();
}
for (auto& handle : handles_) {
handle.set_process_id(ZX_KOID_INVALID);
}
count_ = 0;
to_clean.swap(handles_);
}
// This needs to be done outside of the critical section above.
while (!to_clean.is_empty()) {
// Delete handle via HandleOwner dtor.
HandleOwner ho(to_clean.pop_front());
}
}
zx_handle_t HandleTable::MapHandleToValue(const Handle* handle) const {
return map_handle_to_value(handle, random_value_);
}
zx_handle_t HandleTable::MapHandleToValue(const HandleOwner& handle) const {
return map_handle_to_value(handle.get(), random_value_);
}
Handle* HandleTable::GetHandleLocked(zx_handle_t handle_value, bool skip_policy) {
auto handle = map_value_to_handle(handle_value, random_value_);
if (handle && handle->process_id() == process_->get_koid())
return handle;
if (likely(!skip_policy)) {
// Handle lookup failed. We potentially generate an exception or kill the process,
// depending on the job policy. Note that we don't use the return value from
// EnforceBasicPolicy() here: ZX_POL_ACTION_ALLOW and ZX_POL_ACTION_DENY are equivalent for
// ZX_POL_BAD_HANDLE.
__UNUSED auto result = process_->EnforceBasicPolicy(ZX_POL_BAD_HANDLE);
}
return nullptr;
}
uint32_t HandleTable::HandleCount() const {
Guard<BrwLockPi, BrwLockPi::Reader> guard{&lock_};
return count_;
}
void HandleTable::AddHandle(HandleOwner handle) {
Guard<BrwLockPi, BrwLockPi::Writer> guard{&lock_};
AddHandleLocked(ktl::move(handle));
}
void HandleTable::AddHandleLocked(HandleOwner handle) {
handle->set_process_id(process_->get_koid());
handles_.push_front(handle.release());
count_++;
}
HandleOwner HandleTable::RemoveHandleLocked(Handle* handle) {
DEBUG_ASSERT(count_ > 0);
handle->set_process_id(ZX_KOID_INVALID);
// Make sure we don't leave any dangling cursors.
for (auto& cursor : cursors_) {
// If it points to |handle|, skip over it.
cursor.AdvanceIf(handle);
}
handles_.erase(*handle);
count_--;
return HandleOwner(handle);
}
HandleOwner HandleTable::RemoveHandle(zx_handle_t handle_value) {
Guard<BrwLockPi, BrwLockPi::Writer> guard{&lock_};
return RemoveHandleLocked(handle_value);
}
HandleOwner HandleTable::RemoveHandleLocked(zx_handle_t handle_value) {
auto handle = GetHandleLocked(handle_value);
if (!handle)
return nullptr;
return RemoveHandleLocked(handle);
}
zx_status_t HandleTable::RemoveHandles(ktl::span<const zx_handle_t> handles) {
zx_status_t status = ZX_OK;
Guard<BrwLockPi, BrwLockPi::Writer> guard{get_lock()};
for (zx_handle_t handle_value : handles) {
if (handle_value == ZX_HANDLE_INVALID)
continue;
auto handle = RemoveHandleLocked(handle_value);
if (!handle)
status = ZX_ERR_BAD_HANDLE;
}
return status;
}
zx_koid_t HandleTable::GetKoidForHandle(zx_handle_t handle_value) {
Guard<BrwLockPi, BrwLockPi::Reader> guard{&lock_};
Handle* handle = GetHandleLocked(handle_value);
if (!handle)
return ZX_KOID_INVALID;
return handle->dispatcher()->get_koid();
}
zx_status_t HandleTable::GetDispatcherInternal(zx_handle_t handle_value,
fbl::RefPtr<Dispatcher>* dispatcher,
zx_rights_t* rights) {
Guard<BrwLockPi, BrwLockPi::Reader> guard{&lock_};
Handle* handle = GetHandleLocked(handle_value);
if (!handle)
return ZX_ERR_BAD_HANDLE;
*dispatcher = handle->dispatcher();
if (rights)
*rights = handle->rights();
return ZX_OK;
}
zx_status_t HandleTable::GetHandleInfo(fbl::Array<zx_info_handle_extended_t>* handles) const {
for (;;) {
size_t count = HandleCount();
// TODO: Bug 45685. This memory allocation should come from a different pool since it
// can be larger than one page.
fbl::AllocChecker ac;
handles->reset(new (&ac) zx_info_handle_extended_t[count], count);
if (!ac.check()) {
return ZX_ERR_NO_MEMORY;
}
{
Guard<BrwLockPi, BrwLockPi::Reader> guard{&lock_};
if (count != count_) {
continue;
}
size_t index = 0;
ForEachHandleLocked([&](zx_handle_t handle, zx_rights_t rights, const Dispatcher* disp) {
auto& entry = (*handles)[index++];
entry = {disp->get_type(), handle, rights, 0u, disp->get_koid(),
disp->get_related_koid(), 0u};
return ZX_OK;
});
}
return ZX_OK;
}
}
bool HandleTable::IsHandleValid(zx_handle_t handle_value) {
Guard<BrwLockPi, BrwLockPi::Reader> guard{&lock_};
return (GetHandleLocked(handle_value) != nullptr);
}
HandleTable::HandleCursor::HandleCursor(HandleTable* handle_table) : handle_table_(handle_table) {
Guard<BrwLockPi, BrwLockPi::Writer> guard{&handle_table_->lock_};
if (!handle_table_->handles_.is_empty()) {
iter_ = handle_table_->handles_.begin();
} else {
iter_ = handle_table_->handles_.end();
}
// Register so this cursor can be invalidated or advanced if the handle it points to is removed.
handle_table_->cursors_.push_front(this);
}
HandleTable::HandleCursor::~HandleCursor() {
Guard<BrwLockPi, BrwLockPi::Writer> guard{&handle_table_->lock_};
handle_table_->cursors_.erase(*this);
}
void HandleTable::HandleCursor::Invalidate() { iter_ = handle_table_->handles_.end(); }
Handle* HandleTable::HandleCursor::Next() {
if (iter_ == handle_table_->handles_.end()) {
return nullptr;
}
Handle* result = &*iter_;
iter_++;
return result;
}
void HandleTable::HandleCursor::AdvanceIf(const Handle* h) {
if (iter_ != handle_table_->handles_.end()) {
if (&*iter_ == h) {
iter_++;
}
}
}