1// Copyright 2016 The Fuchsia Authors 2// 3// Use of this source code is governed by a MIT-style 4// license that can be found in the LICENSE file or at 5// https://opensource.org/licenses/MIT 6 7#include <object/handle.h> 8 9#include <object/dispatcher.h> 10#include <fbl/arena.h> 11#include <fbl/mutex.h> 12#include <lib/counters.h> 13#include <pow2.h> 14 15namespace { 16 17// The number of possible handles in the arena. 18constexpr size_t kMaxHandleCount = 256 * 1024u; 19 20// Warning level: high_handle_count() is called when 21// there are this many outstanding handles. 22constexpr size_t kHighHandleCount = (kMaxHandleCount * 7) / 8; 23 24KCOUNTER(handle_count_new, "kernel.handles.new"); 25KCOUNTER(handle_count_duped, "kernel.handles.duped"); 26KCOUNTER(handle_count_freed, "kernel.handles.freed"); 27 28// Masks for building a Handle's base_value, which ProcessDispatcher 29// uses to create zx_handle_t values. 30// 31// base_value bit fields: 32// [31..30]: Must be zero 33// [29..kHandleGenerationShift]: Generation number 34// Masked by kHandleGenerationMask 35// [kHandleGenerationShift-1..0]: Index into handle_arena 36// Masked by kHandleIndexMask 37constexpr uint32_t kHandleIndexMask = kMaxHandleCount - 1; 38static_assert((kHandleIndexMask & kMaxHandleCount) == 0, 39 "kMaxHandleCount must be a power of 2"); 40constexpr uint32_t kHandleGenerationMask = ~kHandleIndexMask & ~(3 << 30); 41constexpr uint32_t kHandleGenerationShift = log2_uint_floor(kMaxHandleCount); 42static_assert(((3 << (kHandleGenerationShift - 1)) & kHandleGenerationMask) == 43 1 << kHandleGenerationShift, 44 "Shift is wrong"); 45static_assert((kHandleGenerationMask >> kHandleGenerationShift) >= 255, 46 "Not enough room for a useful generation count"); 47static_assert(((3 << 30) ^ kHandleGenerationMask ^ kHandleIndexMask) == 48 0xffffffffu, 49 "Masks do not agree"); 50 51} // namespace 52 53fbl::Arena Handle::arena_; 54 55void Handle::Init() TA_NO_THREAD_SAFETY_ANALYSIS { 56 arena_.Init("handles", sizeof(Handle), kMaxHandleCount); 57} 58 59// Returns a new |base_value| based on the value stored in the free 60// arena slot pointed to by |addr|. The new value will be different 61// from the last |base_value| used by this slot. 62uint32_t Handle::GetNewBaseValue(void* addr) TA_REQ(ArenaLock::Get()) { 63 // Get the index of this slot within the arena. 64 uint32_t handle_index = HandleToIndex(reinterpret_cast<Handle*>(addr)); 65 DEBUG_ASSERT((handle_index & ~kHandleIndexMask) == 0); 66 67 // Check the free memory for a stashed base_value. 68 uint32_t v = *reinterpret_cast<uint32_t*>(addr); 69 uint32_t old_gen = 0; 70 if (v != 0) { 71 // This slot has been used before. 72 DEBUG_ASSERT((v & kHandleIndexMask) == handle_index); 73 old_gen = (v & kHandleGenerationMask) >> kHandleGenerationShift; 74 } 75 uint32_t new_gen = 76 (((old_gen + 1) << kHandleGenerationShift) & kHandleGenerationMask); 77 return (handle_index | new_gen); 78} 79 80// Allocate space for a Handle from the arena, but don't instantiate the 81// object. |base_value| gets the value for Handle::base_value_. |what| 82// says whether this is allocation or duplication, for the error message. 83void* Handle::Alloc(const fbl::RefPtr<Dispatcher>& dispatcher, 84 const char* what, uint32_t* base_value) { 85 size_t outstanding_handles; 86 { 87 Guard<fbl::Mutex> guard{ArenaLock::Get()}; 88 void* addr = arena_.Alloc(); 89 outstanding_handles = arena_.DiagnosticCount(); 90 if (likely(addr)) { 91 if (outstanding_handles > kHighHandleCount) { 92 // TODO: Avoid calling this for every handle after 93 // kHighHandleCount; printfs are slow and we're 94 // holding the mutex. 95 printf("WARNING: High handle count: %zu handles\n", 96 outstanding_handles); 97 } 98 dispatcher->increment_handle_count(); 99 *base_value = GetNewBaseValue(addr); 100 return addr; 101 } 102 } 103 104 printf("WARNING: Could not allocate %s handle (%zu outstanding)\n", 105 what, outstanding_handles); 106 return nullptr; 107} 108 109HandleOwner Handle::Make(fbl::RefPtr<Dispatcher> dispatcher, 110 zx_rights_t rights) { 111 uint32_t base_value; 112 void* addr = Alloc(dispatcher, "new", &base_value); 113 if (unlikely(!addr)) 114 return nullptr; 115 kcounter_add(handle_count_new, 1); 116 return HandleOwner(new (addr) Handle(fbl::move(dispatcher), 117 rights, base_value)); 118} 119 120// Called only by Make. 121Handle::Handle(fbl::RefPtr<Dispatcher> dispatcher, zx_rights_t rights, 122 uint32_t base_value) 123 : process_id_(0u), 124 dispatcher_(fbl::move(dispatcher)), 125 rights_(rights), 126 base_value_(base_value) { 127} 128 129HandleOwner Handle::Dup(Handle* source, zx_rights_t rights) { 130 uint32_t base_value; 131 void* addr = Alloc(source->dispatcher(), "duplicate", &base_value); 132 if (unlikely(!addr)) 133 return nullptr; 134 kcounter_add(handle_count_duped, 1); 135 return HandleOwner(new (addr) Handle(source, rights, base_value)); 136} 137 138// Called only by Dup. 139Handle::Handle(Handle* rhs, zx_rights_t rights, uint32_t base_value) 140 : process_id_(rhs->process_id()), 141 dispatcher_(rhs->dispatcher_), 142 rights_(rights), 143 base_value_(base_value) { 144} 145 146// Destroys, but does not free, the Handle, and fixes up its memory to protect 147// against stale pointers to it. Also stashes the Handle's base_value for reuse 148// the next time this slot is allocated. 149void Handle::TearDown() TA_EXCL(ArenaLock::Get()) { 150 uint32_t old_base_value = base_value(); 151 152 // Calling the handle dtor can cause many things to happen, so it is 153 // important to call it outside the lock. 154 this->~Handle(); 155 156 // There may be stale pointers to this slot. Zero out most of its fields 157 // to ensure that the Handle does not appear to belong to any process 158 // or point to any Dispatcher. 159 memset(this, 0, sizeof(*this)); 160 161 // Hold onto the base_value for the next user of this slot, stashing 162 // it at the beginning of the free slot. 163 *reinterpret_cast<uint32_t*>(this) = old_base_value; 164 165 // Double-check that the process_id field is zero, ensuring that 166 // no process can refer to this slot while it's free. This isn't 167 // completely legal since |handle| points to unconstructed memory, 168 // but it should be safe enough for an assertion. 169 DEBUG_ASSERT(process_id() == 0); 170} 171 172void Handle::Delete() { 173 fbl::RefPtr<Dispatcher> disp = dispatcher(); 174 175 if (disp->has_state_tracker()) 176 disp->Cancel(this); 177 178 TearDown(); 179 180 bool zero_handles = false; 181 { 182 Guard<fbl::Mutex> guard{ArenaLock::Get()}; 183 zero_handles = disp->decrement_handle_count(); 184 arena_.Free(this); 185 } 186 187 if (zero_handles) 188 disp->on_zero_handles(); 189 190 // If |disp| is the last reference then the dispatcher object 191 // gets destroyed here. 192 kcounter_add(handle_count_freed, 1); 193} 194 195Handle* Handle::FromU32(uint32_t value) TA_NO_THREAD_SAFETY_ANALYSIS { 196 uintptr_t handle_addr = IndexToHandle(value & kHandleIndexMask); 197 { 198 Guard<fbl::Mutex> guard{ArenaLock::Get()}; 199 if (unlikely(!arena_.in_range(handle_addr))) 200 return nullptr; 201 } 202 auto handle = reinterpret_cast<Handle*>(handle_addr); 203 return likely(handle->base_value() == value) ? handle : nullptr; 204} 205 206uint32_t Handle::Count(const fbl::RefPtr<const Dispatcher>& dispatcher) { 207 // Handle::ArenaLock also guards Dispatcher::handle_count_. 208 Guard<fbl::Mutex> guard{ArenaLock::Get()}; 209 return dispatcher->current_handle_count(); 210} 211 212size_t Handle::diagnostics::OutstandingHandles() { 213 Guard<fbl::Mutex> guard{ArenaLock::Get()}; 214 return arena_.DiagnosticCount(); 215} 216 217void Handle::diagnostics::DumpTableInfo() { 218 Guard<fbl::Mutex> guard{ArenaLock::Get()}; 219 arena_.Dump(); 220} 221