| // Copyright 2016 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.h" |
| |
| #include <lib/counters.h> |
| #include <pow2.h> |
| |
| #include <fbl/conditional_select_nospec.h> |
| #include <object/dispatcher.h> |
| |
| namespace { |
| |
| // The number of outstanding (live) handles in the arena. |
| constexpr size_t kMaxHandleCount = 256 * 1024u; |
| |
| // Warning level: When the number of handles exceeds this value, we start to emit |
| // warnings to the kernel's debug log. |
| constexpr size_t kHighHandleCount = (kMaxHandleCount * 7) / 8; |
| |
| KCOUNTER(handle_count_made, "handles.made") |
| KCOUNTER(handle_count_duped, "handles.duped") |
| KCOUNTER(handle_count_live, "handles.live") |
| KCOUNTER(handle_count_alloc_failed, "handles.alloc.failed") |
| |
| // Masks for building a Handle's base_value, which ProcessDispatcher |
| // uses to create zx_handle_t values. |
| // |
| // base_value bit fields: |
| // [31..(32 - kHandleReservedBits)] : Must be zero |
| // [(31 - kHandleReservedBits)..kHandleGenerationShift] : Generation number |
| // Masked by kHandleGenerationMask |
| // [kHandleGenerationShift-1..0] : Index into handle_arena |
| // Masked by kHandleIndexMask |
| constexpr uint32_t kHandleIndexMask = kMaxHandleCount - 1; |
| static_assert((kHandleIndexMask & kMaxHandleCount) == 0, "kMaxHandleCount must be a power of 2"); |
| |
| constexpr uint32_t kHandleReservedBitsMask = ((1 << kHandleReservedBits) - 1) |
| << (32 - kHandleReservedBits); |
| constexpr uint32_t kHandleGenerationMask = ~kHandleIndexMask & ~kHandleReservedBitsMask; |
| constexpr uint32_t kHandleGenerationShift = log2_uint_floor(kMaxHandleCount); |
| static_assert(((3 << (kHandleGenerationShift - 1)) & kHandleGenerationMask) == |
| 1 << kHandleGenerationShift, |
| "Shift is wrong"); |
| static_assert((kHandleGenerationMask >> kHandleGenerationShift) >= 255, |
| "Not enough room for a useful generation count"); |
| |
| static_assert((kHandleReservedBitsMask & kHandleGenerationMask) == 0, "Handle Mask Overlap!"); |
| static_assert((kHandleReservedBitsMask & kHandleIndexMask) == 0, "Handle Mask Overlap!"); |
| static_assert((kHandleGenerationMask & kHandleIndexMask) == 0, "Handle Mask Overlap!"); |
| static_assert((kHandleReservedBitsMask | kHandleGenerationMask | kHandleIndexMask) == 0xffffffffu, |
| "Handle masks do not cover all bits!"); |
| |
| // |index| is the literal index into the table. |old_value| is the |
| // |index mixed with the per-handle-lifetime state. |
| uint32_t NewHandleValue(uint32_t index, uint32_t old_value) { |
| DEBUG_ASSERT((index & ~kHandleIndexMask) == 0); |
| |
| uint32_t old_gen = 0; |
| if (old_value != 0) { |
| // This slot has been used before. |
| DEBUG_ASSERT((old_value & kHandleIndexMask) == index); |
| old_gen = (old_value & kHandleGenerationMask) >> kHandleGenerationShift; |
| } |
| uint32_t new_gen = (((old_gen + 1) << kHandleGenerationShift) & kHandleGenerationMask); |
| return (index | new_gen); |
| } |
| |
| uint32_t HandleValueToIndex(uint32_t value) { return value & kHandleIndexMask; } |
| |
| } // namespace |
| |
| HandleTableArena gHandleTableArena; |
| |
| void Handle::Init() { gHandleTableArena.arena_.Init("handles", kMaxHandleCount); } |
| |
| void Handle::set_handle_table_id(zx_koid_t pid) { |
| handle_table_id_.store(pid, ktl::memory_order_relaxed); |
| dispatcher_->set_owner(pid); |
| } |
| |
| // Returns a new |base_value| based on the value stored in the free |
| // arena slot pointed to by |addr|. The new value will be different |
| // from the last |base_value| used by this slot. |
| uint32_t HandleTableArena::GetNewBaseValue(void* addr) { |
| // Get the index of this slot within the arena. |
| uint32_t handle_index = HandleToIndex(reinterpret_cast<Handle*>(addr)); |
| |
| // Check the free memory for a stashed base_value. |
| uint32_t v = reinterpret_cast<Handle*>(addr)->base_value_; |
| |
| return NewHandleValue(handle_index, v); |
| } |
| |
| // Allocate space for a Handle from the arena, but don't instantiate the |
| // object. |base_value| gets the value for Handle::base_value_. |what| |
| // says whether this is allocation or duplication, for the error message. |
| void* HandleTableArena::Alloc(const fbl::RefPtr<Dispatcher>& dispatcher, const char* what, |
| uint32_t* base_value) { |
| // Attempt to allocate a handle. |
| void* addr = arena_.Alloc(); |
| size_t outstanding_handles = arena_.DiagnosticCount(); |
| if (unlikely(addr == nullptr)) { |
| kcounter_add(handle_count_alloc_failed, 1); |
| printf("WARNING: Could not allocate %s handle (%zu outstanding)\n", what, outstanding_handles); |
| return nullptr; |
| } |
| |
| // Emit a warning if too many handles have been created and we haven't recently logged |
| if (unlikely(outstanding_handles > kHighHandleCount) && handle_count_high_log_.Ready()) { |
| printf("WARNING: High handle count: %zu / %zu handles\n", outstanding_handles, |
| kHighHandleCount); |
| } |
| |
| dispatcher->increment_handle_count(); |
| // checking the handle_table_id_ and dispatcher_ is really about trying to catch cases where this |
| // Handle might somehow already be in use. |
| DEBUG_ASSERT(reinterpret_cast<Handle*>(addr)->handle_table_id_ == ZX_KOID_INVALID); |
| DEBUG_ASSERT(reinterpret_cast<Handle*>(addr)->dispatcher_ == nullptr); |
| *base_value = GetNewBaseValue(addr); |
| return addr; |
| } |
| |
| HandleOwner Handle::Make(fbl::RefPtr<Dispatcher> dispatcher, zx_rights_t rights) { |
| uint32_t base_value; |
| void* addr = gHandleTableArena.Alloc(dispatcher, "new", &base_value); |
| if (unlikely(!addr)) |
| return nullptr; |
| kcounter_add(handle_count_made, 1); |
| kcounter_add(handle_count_live, 1); |
| return HandleOwner(new (addr) Handle(ktl::move(dispatcher), rights, base_value)); |
| } |
| |
| HandleOwner Handle::Make(KernelHandle<Dispatcher> kernel_handle, zx_rights_t rights) { |
| uint32_t base_value; |
| void* addr = gHandleTableArena.Alloc(kernel_handle.dispatcher(), "new", &base_value); |
| if (unlikely(!addr)) |
| return nullptr; |
| kcounter_add(handle_count_made, 1); |
| kcounter_add(handle_count_live, 1); |
| return HandleOwner(new (addr) Handle(kernel_handle.release(), rights, base_value)); |
| } |
| |
| // Called only by Make. |
| Handle::Handle(fbl::RefPtr<Dispatcher> dispatcher, zx_rights_t rights, uint32_t base_value) |
| : handle_table_id_(ZX_KOID_INVALID), |
| dispatcher_(ktl::move(dispatcher)), |
| rights_(rights), |
| base_value_(base_value) {} |
| |
| HandleOwner Handle::Dup(Handle* source, zx_rights_t rights) { |
| uint32_t base_value; |
| void* addr = gHandleTableArena.Alloc(source->dispatcher(), "duplicate", &base_value); |
| if (unlikely(!addr)) |
| return nullptr; |
| kcounter_add(handle_count_duped, 1); |
| kcounter_add(handle_count_live, 1); |
| return HandleOwner(new (addr) Handle(source, rights, base_value)); |
| } |
| |
| // Called only by Dup. |
| Handle::Handle(Handle* rhs, zx_rights_t rights, uint32_t base_value) |
| // Although this handle is intended to become owned by rhs->handle_table_id_ at the point of |
| // creation it is stack owned and may be destroyed without actually being assigned to the |
| // handle table. If this happens the assert in TearDown would get triggered. |
| : handle_table_id_(ZX_KOID_INVALID), |
| dispatcher_(rhs->dispatcher_), |
| rights_(rights), |
| base_value_(base_value) {} |
| |
| void HandleTableArena::Delete(Handle* handle) { |
| fbl::RefPtr<Dispatcher> dispatcher(ktl::move(handle->dispatcher_)); |
| uint32_t __UNUSED old_base_value = handle->base_value_; |
| const uint32_t* __UNUSED base_value = &handle->base_value_; |
| // There may be stale pointers to this slot and they will look at handle_table_id. We expect |
| // handle_table_id to already have been cleared by the process dispatcher before the handle got to |
| // this point. |
| DEBUG_ASSERT(handle->handle_table_id() == ZX_KOID_INVALID); |
| |
| if (dispatcher->is_waitable()) { |
| dispatcher->Cancel(handle); |
| } |
| // The destructor should not do anything interesting but call it for completeness. |
| handle->~Handle(); |
| // Make sure the base value was not altered by the destructor. |
| DEBUG_ASSERT(*base_value == old_base_value); |
| |
| bool zero_handles = dispatcher->decrement_handle_count(); |
| arena_.Free(handle); |
| |
| if (zero_handles) { |
| dispatcher->on_zero_handles(); |
| } |
| |
| // If |disp| is the last reference (which is likely) then the dispatcher object |
| // gets destroyed at the exit of this function. |
| kcounter_add(handle_count_live, -1); |
| } |
| |
| Handle* Handle::FromU32(uint32_t value) { |
| uint32_t index = HandleValueToIndex(value); |
| uintptr_t handle_addr = IndexToHandle(index); |
| if (unlikely(!gHandleTableArena.arena_.Committed(reinterpret_cast<void*>(handle_addr)))) |
| return nullptr; |
| handle_addr = gHandleTableArena.arena_.Confine(handle_addr); |
| auto handle = reinterpret_cast<Handle*>(handle_addr); |
| return reinterpret_cast<Handle*>( |
| fbl::conditional_select_nospec_eq(handle->base_value(), value, handle_addr, 0)); |
| } |
| |
| uint32_t Handle::Count(const fbl::RefPtr<const Dispatcher>& dispatcher) { |
| return dispatcher->current_handle_count(); |
| } |
| |
| uint32_t Handle::Count(const Dispatcher& dispatcher) { return dispatcher.current_handle_count(); } |
| |
| size_t Handle::diagnostics::OutstandingHandles() { |
| return gHandleTableArena.arena_.DiagnosticCount(); |
| } |
| |
| void Handle::diagnostics::DumpTableInfo() { gHandleTableArena.arena_.Dump(); } |
| |
| uintptr_t Handle::IndexToHandle(uint32_t index) { |
| return reinterpret_cast<uintptr_t>(gHandleTableArena.arena_.Base()) + index * sizeof(Handle); |
| } |
| |
| uint32_t HandleTableArena::HandleToIndex(Handle* handle) { |
| return static_cast<uint32_t>(handle - reinterpret_cast<Handle*>(arena_.Base())); |
| } |
| |
| int64_t HandleTableArena::get_alloc_failed_count() { |
| return handle_count_alloc_failed.SumAcrossAllCpus(); |
| } |