| 1 | // Licensed to the .NET Foundation under one or more agreements. |
| 2 | // The .NET Foundation licenses this file to you under the MIT license. |
| 3 | // See the LICENSE file in the project root for more information. |
| 4 | |
| 5 | /*++ |
| 6 | |
| 7 | |
| 8 | |
| 9 | Module Name: |
| 10 | |
| 11 | handlemgr.cpp |
| 12 | |
| 13 | Abstract: |
| 14 | |
| 15 | Implementation of a basic handle table |
| 16 | |
| 17 | |
| 18 | |
| 19 | --*/ |
| 20 | |
| 21 | #include "pal/thread.hpp" |
| 22 | #include "pal/handlemgr.hpp" |
| 23 | #include "pal/cs.hpp" |
| 24 | #include "pal/malloc.hpp" |
| 25 | #include "pal/dbgmsg.h" |
| 26 | |
| 27 | using namespace CorUnix; |
| 28 | |
| 29 | SET_DEFAULT_DEBUG_CHANNEL(HANDLE); |
| 30 | |
| 31 | /* Constants */ |
| 32 | /* Special handles */ |
| 33 | /* Pseudo handles constant for current thread and process */ |
| 34 | const HANDLE hPseudoCurrentProcess = (HANDLE) 0xFFFFFF01; |
| 35 | const HANDLE hPseudoCurrentThread = (HANDLE) 0xFFFFFF03; |
| 36 | /* Pseudo handle constant for the global IO Completion port */ |
| 37 | const HANDLE hPseudoGlobalIOCP = (HANDLE) 0xFFFFFF05; |
| 38 | |
| 39 | PAL_ERROR |
| 40 | CSimpleHandleManager::Initialize( |
| 41 | void |
| 42 | ) |
| 43 | { |
| 44 | PAL_ERROR palError = NO_ERROR; |
| 45 | |
| 46 | InternalInitializeCriticalSection(&m_csLock); |
| 47 | m_fLockInitialized = TRUE; |
| 48 | |
| 49 | m_dwTableGrowthRate = c_BasicGrowthRate; |
| 50 | |
| 51 | /* initialize the handle table - the free list is stored in the 'object' |
| 52 | field, with the head in the global 'm_hiFreeListStart'. */ |
| 53 | m_dwTableSize = m_dwTableGrowthRate; |
| 54 | |
| 55 | m_rghteHandleTable = reinterpret_cast<HANDLE_TABLE_ENTRY*>(InternalMalloc((m_dwTableSize * sizeof(HANDLE_TABLE_ENTRY)))); |
| 56 | if(NULL == m_rghteHandleTable) |
| 57 | { |
| 58 | ERROR("Unable to create initial handle table array" ); |
| 59 | palError = ERROR_OUTOFMEMORY; |
| 60 | goto InitializeExit; |
| 61 | } |
| 62 | |
| 63 | for (DWORD i = 0; i < m_dwTableSize; i++) |
| 64 | { |
| 65 | m_rghteHandleTable[i].u.hiNextIndex = i + 1; |
| 66 | m_rghteHandleTable[i].fEntryAllocated = FALSE; |
| 67 | } |
| 68 | |
| 69 | m_rghteHandleTable[m_dwTableSize - 1].u.hiNextIndex = (HANDLE_INDEX)-1; |
| 70 | |
| 71 | m_hiFreeListStart = 0; |
| 72 | m_hiFreeListEnd = m_dwTableSize - 1; |
| 73 | |
| 74 | TRACE("Handle Manager initialization complete.\n" ); |
| 75 | |
| 76 | InitializeExit: |
| 77 | |
| 78 | return palError; |
| 79 | } |
| 80 | |
| 81 | PAL_ERROR |
| 82 | CSimpleHandleManager::AllocateHandle( |
| 83 | CPalThread *pThread, |
| 84 | IPalObject *pObject, |
| 85 | DWORD dwAccessRights, |
| 86 | bool fInheritable, |
| 87 | HANDLE *ph |
| 88 | ) |
| 89 | { |
| 90 | PAL_ERROR palError = NO_ERROR; |
| 91 | DWORD dwIndex; |
| 92 | |
| 93 | Lock(pThread); |
| 94 | |
| 95 | /* if no free handles are available, we need to grow the handle table and |
| 96 | add new handles to the pool */ |
| 97 | if (m_hiFreeListStart == c_hiInvalid) |
| 98 | { |
| 99 | HANDLE_TABLE_ENTRY* rghteTempTable; |
| 100 | |
| 101 | TRACE("Handle pool empty (%d handles allocated), growing handle table " |
| 102 | "by %d entries.\n" , m_dwTableSize, m_dwTableGrowthRate ); |
| 103 | |
| 104 | /* make sure handle values don't overflow */ |
| 105 | if (m_dwTableSize + m_dwTableGrowthRate >= c_MaxIndex) |
| 106 | { |
| 107 | WARN("Unable to allocate handle : maximum (%d) reached!\n" , |
| 108 | m_dwTableSize); |
| 109 | palError = ERROR_OUTOFMEMORY; |
| 110 | goto AllocateHandleExit; |
| 111 | } |
| 112 | |
| 113 | /* grow handle table */ |
| 114 | rghteTempTable = reinterpret_cast<HANDLE_TABLE_ENTRY*>(InternalRealloc( |
| 115 | m_rghteHandleTable, |
| 116 | (m_dwTableSize + m_dwTableGrowthRate) * sizeof(HANDLE_TABLE_ENTRY))); |
| 117 | |
| 118 | if (NULL == rghteTempTable) |
| 119 | { |
| 120 | WARN("not enough memory to grow handle table!\n" ); |
| 121 | palError = ERROR_OUTOFMEMORY; |
| 122 | goto AllocateHandleExit; |
| 123 | } |
| 124 | m_rghteHandleTable = rghteTempTable; |
| 125 | |
| 126 | /* update handle table and handle pool */ |
| 127 | for (DWORD dw = m_dwTableSize; dw < m_dwTableSize + m_dwTableGrowthRate; dw += 1) |
| 128 | { |
| 129 | /* new handles are initially invalid */ |
| 130 | /* the last "old" handle was m_dwTableSize-1, so the new |
| 131 | handles range from m_dwTableSize to |
| 132 | m_dwTableSize+m_dwTableGrowthRate-1 */ |
| 133 | m_rghteHandleTable[dw].u.hiNextIndex = dw + 1; |
| 134 | m_rghteHandleTable[dw].fEntryAllocated = FALSE; |
| 135 | } |
| 136 | |
| 137 | m_hiFreeListStart = m_dwTableSize; |
| 138 | m_dwTableSize += m_dwTableGrowthRate; |
| 139 | m_rghteHandleTable[m_dwTableSize - 1].u.hiNextIndex = (HANDLE_INDEX)-1; |
| 140 | m_hiFreeListEnd = m_dwTableSize - 1; |
| 141 | |
| 142 | } |
| 143 | |
| 144 | /* take the next free handle */ |
| 145 | dwIndex = m_hiFreeListStart; |
| 146 | |
| 147 | /* remove the handle from the pool */ |
| 148 | m_hiFreeListStart = m_rghteHandleTable[dwIndex].u.hiNextIndex; |
| 149 | |
| 150 | /* clear the tail record if this is the last handle slot available */ |
| 151 | if(m_hiFreeListStart == c_hiInvalid) |
| 152 | { |
| 153 | m_hiFreeListEnd = c_hiInvalid; |
| 154 | } |
| 155 | |
| 156 | /* save the data associated with the new handle */ |
| 157 | *ph = HandleIndexToHandle(dwIndex); |
| 158 | |
| 159 | pObject->AddReference(); |
| 160 | m_rghteHandleTable[dwIndex].u.pObject = pObject; |
| 161 | m_rghteHandleTable[dwIndex].dwAccessRights = dwAccessRights; |
| 162 | m_rghteHandleTable[dwIndex].fInheritable = fInheritable; |
| 163 | m_rghteHandleTable[dwIndex].fEntryAllocated = TRUE; |
| 164 | |
| 165 | AllocateHandleExit: |
| 166 | |
| 167 | Unlock(pThread); |
| 168 | |
| 169 | return palError; |
| 170 | } |
| 171 | |
| 172 | PAL_ERROR |
| 173 | CSimpleHandleManager::GetObjectFromHandle( |
| 174 | CPalThread *pThread, |
| 175 | HANDLE h, |
| 176 | DWORD *pdwRightsGranted, |
| 177 | IPalObject **ppObject |
| 178 | ) |
| 179 | { |
| 180 | PAL_ERROR palError = NO_ERROR; |
| 181 | HANDLE_INDEX hi; |
| 182 | |
| 183 | Lock(pThread); |
| 184 | |
| 185 | if (!ValidateHandle(h)) |
| 186 | { |
| 187 | ERROR("Tried to dereference an invalid handle %p\n" , h); |
| 188 | palError = ERROR_INVALID_HANDLE; |
| 189 | goto GetObjectFromHandleExit; |
| 190 | } |
| 191 | |
| 192 | hi = HandleToHandleIndex(h); |
| 193 | |
| 194 | *pdwRightsGranted = m_rghteHandleTable[hi].dwAccessRights; |
| 195 | *ppObject = m_rghteHandleTable[hi].u.pObject; |
| 196 | (*ppObject)->AddReference(); |
| 197 | |
| 198 | GetObjectFromHandleExit: |
| 199 | |
| 200 | Unlock(pThread); |
| 201 | |
| 202 | return palError; |
| 203 | } |
| 204 | |
| 205 | PAL_ERROR |
| 206 | CSimpleHandleManager::FreeHandle( |
| 207 | CPalThread *pThread, |
| 208 | HANDLE h |
| 209 | ) |
| 210 | { |
| 211 | PAL_ERROR palError = NO_ERROR; |
| 212 | IPalObject *pobj = NULL; |
| 213 | HANDLE_INDEX hi = HandleToHandleIndex(h); |
| 214 | |
| 215 | Lock(pThread); |
| 216 | |
| 217 | if (!ValidateHandle(h)) |
| 218 | { |
| 219 | ERROR("Trying to free invalid handle %p.\n" , h); |
| 220 | palError = ERROR_INVALID_HANDLE; |
| 221 | goto FreeHandleExit; |
| 222 | } |
| 223 | |
| 224 | if (HandleIsSpecial(h)) |
| 225 | { |
| 226 | ASSERT("Trying to free Special Handle %p.\n" , h); |
| 227 | palError = ERROR_INVALID_HANDLE; |
| 228 | goto FreeHandleExit; |
| 229 | } |
| 230 | |
| 231 | pobj = m_rghteHandleTable[hi].u.pObject; |
| 232 | m_rghteHandleTable[hi].fEntryAllocated = FALSE; |
| 233 | |
| 234 | /* add handle to the free pool */ |
| 235 | if(m_hiFreeListEnd != c_hiInvalid) |
| 236 | { |
| 237 | m_rghteHandleTable[m_hiFreeListEnd].u.hiNextIndex = hi; |
| 238 | } |
| 239 | else |
| 240 | { |
| 241 | m_hiFreeListStart = hi; |
| 242 | } |
| 243 | |
| 244 | m_rghteHandleTable[hi].u.hiNextIndex = c_hiInvalid; |
| 245 | m_hiFreeListEnd = hi; |
| 246 | |
| 247 | FreeHandleExit: |
| 248 | |
| 249 | Unlock(pThread); |
| 250 | |
| 251 | if (NULL != pobj) |
| 252 | { |
| 253 | pobj->ReleaseReference(pThread); |
| 254 | } |
| 255 | |
| 256 | return palError; |
| 257 | } |
| 258 | |
| 259 | /*++ |
| 260 | Function : |
| 261 | ValidateHandle |
| 262 | |
| 263 | Check if a handle was allocated by this handle manager |
| 264 | |
| 265 | Parameters : |
| 266 | HANDLE handle : handle to check. |
| 267 | |
| 268 | Return Value : |
| 269 | TRUE if valid, FALSE if invalid. |
| 270 | --*/ |
| 271 | bool CSimpleHandleManager::ValidateHandle(HANDLE handle) |
| 272 | { |
| 273 | DWORD dwIndex; |
| 274 | |
| 275 | if (NULL == m_rghteHandleTable) |
| 276 | { |
| 277 | ASSERT("Handle Manager is not initialized!\n" ); |
| 278 | return FALSE; |
| 279 | } |
| 280 | |
| 281 | if (handle == INVALID_HANDLE_VALUE || handle == 0) |
| 282 | { |
| 283 | TRACE( "INVALID_HANDLE_VALUE or NULL value is not a valid handle.\n" ); |
| 284 | return FALSE; |
| 285 | } |
| 286 | |
| 287 | if (HandleIsSpecial(handle)) |
| 288 | { |
| 289 | // |
| 290 | // Special handles are valid in the general sense. They are not valid |
| 291 | // in this context, though, as they were not allocated by the handle |
| 292 | // manager. Hitting this case indicates a logic error within the PAL |
| 293 | // (since clients of the handle manager should have already dealt with |
| 294 | // the specialness of the handle) so we assert here. |
| 295 | // |
| 296 | |
| 297 | ASSERT ("Handle %p is a special handle, returning FALSE.\n" , handle); |
| 298 | return FALSE; |
| 299 | } |
| 300 | |
| 301 | dwIndex = HandleToHandleIndex(handle); |
| 302 | |
| 303 | if (dwIndex >= m_dwTableSize) |
| 304 | { |
| 305 | WARN( "The handle value(%p) is out of the bounds for the handle table.\n" , handle ); |
| 306 | return FALSE; |
| 307 | } |
| 308 | |
| 309 | if (!m_rghteHandleTable[dwIndex].fEntryAllocated) |
| 310 | { |
| 311 | WARN("The handle value (%p) has not been allocated\n" , handle); |
| 312 | return FALSE; |
| 313 | } |
| 314 | |
| 315 | return TRUE; |
| 316 | } |
| 317 | |
| 318 | bool |
| 319 | CorUnix::HandleIsSpecial( |
| 320 | HANDLE h |
| 321 | ) |
| 322 | { |
| 323 | return (hPseudoCurrentProcess == h || |
| 324 | hPseudoCurrentThread == h || |
| 325 | hPseudoGlobalIOCP == h); |
| 326 | } |
| 327 | |
| 328 | |