1 /* 2 * PROJECT: ReactOS Kernel 3 * LICENSE: GPL - See COPYING in the top level directory 4 * FILE: ntoskrnl/ob/obref.c 5 * PURPOSE: Manages the referencing and de-referencing of all Objects, 6 * as well as the Object Fast Reference implementation. 7 * PROGRAMMERS: Alex Ionescu (alex.ionescu@reactos.org) 8 * Eric Kohl 9 * Thomas Weidenmueller (w3seek@reactos.org) 10 */ 11 12 /* INCLUDES ******************************************************************/ 13 14 #include <ntoskrnl.h> 15 #define NDEBUG 16 #include <debug.h> 17 18 /* PRIVATE FUNCTIONS *********************************************************/ 19 20 BOOLEAN 21 FASTCALL 22 ObReferenceObjectSafe(IN PVOID Object) 23 { 24 POBJECT_HEADER ObjectHeader; 25 LONG OldValue, NewValue; 26 27 /* Get the object header */ 28 ObjectHeader = OBJECT_TO_OBJECT_HEADER(Object); 29 30 /* Get the current reference count and fail if it's zero */ 31 OldValue = ObjectHeader->PointerCount; 32 if (!OldValue) return FALSE; 33 34 /* Start reference loop */ 35 do 36 { 37 /* Increase the reference count */ 38 NewValue = InterlockedCompareExchange(&ObjectHeader->PointerCount, 39 OldValue + 1, 40 OldValue); 41 if (OldValue == NewValue) return TRUE; 42 43 /* Keep looping */ 44 OldValue = NewValue; 45 } while (OldValue); 46 47 /* If we got here, then the reference count is now 0 */ 48 return FALSE; 49 } 50 51 VOID 52 NTAPI 53 ObpDeferObjectDeletion(IN POBJECT_HEADER Header) 54 { 55 PVOID Entry; 56 57 /* Loop while trying to update the list */ 58 do 59 { 60 /* Get the current entry */ 61 Entry = ObpReaperList; 62 63 /* Link our object to the list */ 64 Header->NextToFree = Entry; 65 66 /* Update the list */ 67 } while (InterlockedCompareExchangePointer(&ObpReaperList, 68 Header, 69 Entry) != Entry); 70 71 /* Queue the work item if needed */ 72 if (!Entry) ExQueueWorkItem(&ObpReaperWorkItem, CriticalWorkQueue); 73 } 74 75 LONG 76 FASTCALL 77 ObReferenceObjectEx(IN PVOID Object, 78 IN LONG Count) 79 { 80 /* Increment the reference count and return the count now */ 81 return InterlockedExchangeAdd(&OBJECT_TO_OBJECT_HEADER(Object)-> 82 PointerCount, 83 Count) + Count; 84 } 85 86 LONG 87 FASTCALL 88 ObDereferenceObjectEx(IN PVOID Object, 89 IN LONG Count) 90 { 91 POBJECT_HEADER Header; 92 LONG NewCount; 93 94 /* Extract the object header */ 95 Header = OBJECT_TO_OBJECT_HEADER(Object); 96 97 /* Check whether the object can now be deleted. */ 98 NewCount = InterlockedExchangeAdd(&Header->PointerCount, -Count) - Count; 99 if (!NewCount) ObpDeferObjectDeletion(Header); 100 101 /* Return the current count */ 102 return NewCount; 103 } 104 105 VOID 106 FASTCALL 107 ObInitializeFastReference(IN PEX_FAST_REF FastRef, 108 IN PVOID Object OPTIONAL) 109 { 110 /* Check if we were given an object and reference it 7 times */ 111 if (Object) ObReferenceObjectEx(Object, MAX_FAST_REFS); 112 113 /* Setup the fast reference */ 114 ExInitializeFastReference(FastRef, Object); 115 } 116 117 PVOID 118 FASTCALL 119 ObFastReferenceObjectLocked(IN PEX_FAST_REF FastRef) 120 { 121 PVOID Object; 122 EX_FAST_REF OldValue = *FastRef; 123 124 /* Get the object and reference it slowly */ 125 Object = ExGetObjectFastReference(OldValue); 126 if (Object) ObReferenceObject(Object); 127 return Object; 128 } 129 130 PVOID 131 FASTCALL 132 ObFastReferenceObject(IN PEX_FAST_REF FastRef) 133 { 134 EX_FAST_REF OldValue; 135 ULONG_PTR Count; 136 PVOID Object; 137 138 /* Reference the object and get it pointer */ 139 OldValue = ExAcquireFastReference(FastRef); 140 Object = ExGetObjectFastReference(OldValue); 141 142 /* Check how many references are left */ 143 Count = ExGetCountFastReference(OldValue); 144 145 /* Check if the reference count is over 1 */ 146 if (Count > 1) return Object; 147 148 /* Check if the reference count has reached 0 */ 149 if (!Count) return NULL; 150 151 /* Otherwise, reference the object 7 times */ 152 ObReferenceObjectEx(Object, MAX_FAST_REFS); 153 154 /* Now update the reference count */ 155 if (!ExInsertFastReference(FastRef, Object)) 156 { 157 /* We failed: completely dereference the object */ 158 ObDereferenceObjectEx(Object, MAX_FAST_REFS); 159 } 160 161 /* Return the Object */ 162 return Object; 163 } 164 165 VOID 166 FASTCALL 167 ObFastDereferenceObject(IN PEX_FAST_REF FastRef, 168 IN PVOID Object) 169 { 170 /* Release a fast reference. If this failed, use the slow path */ 171 if (!ExReleaseFastReference(FastRef, Object)) ObDereferenceObject(Object); 172 } 173 174 PVOID 175 FASTCALL 176 ObFastReplaceObject(IN PEX_FAST_REF FastRef, 177 PVOID Object) 178 { 179 EX_FAST_REF OldValue; 180 PVOID OldObject; 181 ULONG Count; 182 183 /* Check if we were given an object and reference it 7 times */ 184 if (Object) ObReferenceObjectEx(Object, MAX_FAST_REFS); 185 186 /* Do the swap */ 187 OldValue = ExSwapFastReference(FastRef, Object); 188 OldObject = ExGetObjectFastReference(OldValue); 189 190 /* Check if we had an active object and dereference it */ 191 Count = ExGetCountFastReference(OldValue); 192 if ((OldObject) && (Count)) ObDereferenceObjectEx(OldObject, Count); 193 194 /* Return the old object */ 195 return OldObject; 196 } 197 198 /* PUBLIC FUNCTIONS *********************************************************/ 199 200 LONG_PTR 201 FASTCALL 202 ObfReferenceObject(IN PVOID Object) 203 { 204 ASSERT(Object); 205 206 /* Get the header and increment the reference count */ 207 return InterlockedIncrement(&OBJECT_TO_OBJECT_HEADER(Object)->PointerCount); 208 } 209 210 LONG_PTR 211 FASTCALL 212 ObfDereferenceObject(IN PVOID Object) 213 { 214 POBJECT_HEADER Header; 215 LONG_PTR OldCount; 216 217 /* Extract the object header */ 218 Header = OBJECT_TO_OBJECT_HEADER(Object); 219 220 if (Header->PointerCount < Header->HandleCount) 221 { 222 DPRINT1("Misbehaving object: %wZ\n", &Header->Type->Name); 223 return Header->PointerCount; 224 } 225 226 /* Check whether the object can now be deleted. */ 227 OldCount = InterlockedDecrement(&Header->PointerCount); 228 if (!OldCount) 229 { 230 /* Sanity check */ 231 ASSERT(Header->HandleCount == 0); 232 233 /* Check if APCs are still active */ 234 if (!KeAreAllApcsDisabled()) 235 { 236 /* Remove the object */ 237 ObpDeleteObject(Object, FALSE); 238 } 239 else 240 { 241 /* Add us to the deferred deletion list */ 242 ObpDeferObjectDeletion(Header); 243 } 244 } 245 246 /* Return the old count */ 247 return OldCount; 248 } 249 250 VOID 251 NTAPI 252 ObDereferenceObjectDeferDelete(IN PVOID Object) 253 { 254 POBJECT_HEADER Header = OBJECT_TO_OBJECT_HEADER(Object); 255 256 /* Check whether the object can now be deleted. */ 257 if (!InterlockedDecrement(&Header->PointerCount)) 258 { 259 /* Add us to the deferred deletion list */ 260 ObpDeferObjectDeletion(Header); 261 } 262 } 263 264 #undef ObDereferenceObject 265 VOID 266 NTAPI 267 ObDereferenceObject(IN PVOID Object) 268 { 269 /* Call the fastcall function */ 270 ObfDereferenceObject(Object); 271 } 272 273 NTSTATUS 274 NTAPI 275 ObReferenceObjectByPointer(IN PVOID Object, 276 IN ACCESS_MASK DesiredAccess, 277 IN POBJECT_TYPE ObjectType, 278 IN KPROCESSOR_MODE AccessMode) 279 { 280 POBJECT_HEADER Header; 281 282 /* Get the header */ 283 Header = OBJECT_TO_OBJECT_HEADER(Object); 284 285 /* 286 * Validate object type if the call is for UserMode. 287 * NOTE: Unless it's a symbolic link (Caz Yokoyama [MSFT]) 288 */ 289 if ((Header->Type != ObjectType) && ((AccessMode != KernelMode) || 290 (ObjectType == ObSymbolicLinkType))) 291 { 292 /* Invalid type */ 293 return STATUS_OBJECT_TYPE_MISMATCH; 294 } 295 296 /* Increment the reference count and return success */ 297 InterlockedIncrement(&Header->PointerCount); 298 return STATUS_SUCCESS; 299 } 300 301 NTSTATUS 302 NTAPI 303 ObReferenceObjectByName(IN PUNICODE_STRING ObjectPath, 304 IN ULONG Attributes, 305 IN PACCESS_STATE PassedAccessState, 306 IN ACCESS_MASK DesiredAccess, 307 IN POBJECT_TYPE ObjectType, 308 IN KPROCESSOR_MODE AccessMode, 309 IN OUT PVOID ParseContext, 310 OUT PVOID* ObjectPtr) 311 { 312 PVOID Object = NULL; 313 UNICODE_STRING ObjectName; 314 NTSTATUS Status; 315 OBP_LOOKUP_CONTEXT Context; 316 AUX_ACCESS_DATA AuxData; 317 ACCESS_STATE AccessState; 318 PAGED_CODE(); 319 320 /* Fail quickly */ 321 if (!ObjectPath) return STATUS_OBJECT_NAME_INVALID; 322 323 /* Capture the name */ 324 Status = ObpCaptureObjectName(&ObjectName, ObjectPath, AccessMode, TRUE); 325 if (!NT_SUCCESS(Status)) return Status; 326 327 /* We also need a valid name after capture */ 328 if (!ObjectName.Length) return STATUS_OBJECT_NAME_INVALID; 329 330 /* Check if we didn't get an access state */ 331 if (!PassedAccessState) 332 { 333 /* Use our built-in access state */ 334 PassedAccessState = &AccessState; 335 Status = SeCreateAccessState(&AccessState, 336 &AuxData, 337 DesiredAccess, 338 &ObjectType->TypeInfo.GenericMapping); 339 if (!NT_SUCCESS(Status)) goto Quickie; 340 } 341 342 /* Find the object */ 343 *ObjectPtr = NULL; 344 Status = ObpLookupObjectName(NULL, 345 &ObjectName, 346 Attributes, 347 ObjectType, 348 AccessMode, 349 ParseContext, 350 NULL, 351 NULL, 352 PassedAccessState, 353 &Context, 354 &Object); 355 356 /* Cleanup after lookup */ 357 ObpReleaseLookupContext(&Context); 358 359 /* Check if the lookup succeeded */ 360 if (NT_SUCCESS(Status)) 361 { 362 /* Check if access is allowed */ 363 if (ObpCheckObjectReference(Object, 364 PassedAccessState, 365 FALSE, 366 AccessMode, 367 &Status)) 368 { 369 /* Return the object */ 370 *ObjectPtr = Object; 371 } 372 } 373 374 /* Free the access state */ 375 if (PassedAccessState == &AccessState) 376 { 377 SeDeleteAccessState(PassedAccessState); 378 } 379 380 Quickie: 381 /* Free the captured name if we had one, and return status */ 382 ObpFreeObjectNameBuffer(&ObjectName); 383 return Status; 384 } 385 386 NTSTATUS 387 NTAPI 388 ObReferenceObjectByHandle(IN HANDLE Handle, 389 IN ACCESS_MASK DesiredAccess, 390 IN POBJECT_TYPE ObjectType, 391 IN KPROCESSOR_MODE AccessMode, 392 OUT PVOID* Object, 393 OUT POBJECT_HANDLE_INFORMATION HandleInformation OPTIONAL) 394 { 395 PHANDLE_TABLE_ENTRY HandleEntry; 396 POBJECT_HEADER ObjectHeader; 397 ACCESS_MASK GrantedAccess; 398 ULONG Attributes; 399 PEPROCESS CurrentProcess; 400 PVOID HandleTable; 401 PETHREAD CurrentThread; 402 NTSTATUS Status; 403 PAGED_CODE(); 404 405 /* Assume failure */ 406 *Object = NULL; 407 408 /* Check if this is a special handle */ 409 if (HandleToLong(Handle) < 0) 410 { 411 /* Check if this is the current process */ 412 if (Handle == NtCurrentProcess()) 413 { 414 /* Check if this is the right object type */ 415 if ((ObjectType == PsProcessType) || !(ObjectType)) 416 { 417 /* Get the current process and granted access */ 418 CurrentProcess = PsGetCurrentProcess(); 419 GrantedAccess = CurrentProcess->GrantedAccess; 420 421 /* Validate access */ 422 /* ~GrantedAccess = RefusedAccess.*/ 423 /* ~GrantedAccess & DesiredAccess = list of refused bits. */ 424 /* !(~GrantedAccess & DesiredAccess) == TRUE means ALL requested rights are granted */ 425 if ((AccessMode == KernelMode) || 426 !(~GrantedAccess & DesiredAccess)) 427 { 428 /* Check if the caller wanted handle information */ 429 if (HandleInformation) 430 { 431 /* Return it */ 432 HandleInformation->HandleAttributes = 0; 433 HandleInformation->GrantedAccess = GrantedAccess; 434 } 435 436 /* Reference ourselves */ 437 ObjectHeader = OBJECT_TO_OBJECT_HEADER(CurrentProcess); 438 InterlockedExchangeAdd(&ObjectHeader->PointerCount, 1); 439 440 /* Return the pointer */ 441 *Object = CurrentProcess; 442 ASSERT(*Object != NULL); 443 Status = STATUS_SUCCESS; 444 } 445 else 446 { 447 /* Access denied */ 448 Status = STATUS_ACCESS_DENIED; 449 } 450 } 451 else 452 { 453 /* The caller used this special handle value with a non-process type */ 454 Status = STATUS_OBJECT_TYPE_MISMATCH; 455 } 456 457 /* Return the status */ 458 return Status; 459 } 460 else if (Handle == NtCurrentThread()) 461 { 462 /* Check if this is the right object type */ 463 if ((ObjectType == PsThreadType) || !(ObjectType)) 464 { 465 /* Get the current process and granted access */ 466 CurrentThread = PsGetCurrentThread(); 467 GrantedAccess = CurrentThread->GrantedAccess; 468 469 /* Validate access */ 470 /* ~GrantedAccess = RefusedAccess.*/ 471 /* ~GrantedAccess & DesiredAccess = list of refused bits. */ 472 /* !(~GrantedAccess & DesiredAccess) == TRUE means ALL requested rights are granted */ 473 if ((AccessMode == KernelMode) || 474 !(~GrantedAccess & DesiredAccess)) 475 { 476 /* Check if the caller wanted handle information */ 477 if (HandleInformation) 478 { 479 /* Return it */ 480 HandleInformation->HandleAttributes = 0; 481 HandleInformation->GrantedAccess = GrantedAccess; 482 } 483 484 /* Reference ourselves */ 485 ObjectHeader = OBJECT_TO_OBJECT_HEADER(CurrentThread); 486 InterlockedExchangeAdd(&ObjectHeader->PointerCount, 1); 487 488 /* Return the pointer */ 489 *Object = CurrentThread; 490 ASSERT(*Object != NULL); 491 Status = STATUS_SUCCESS; 492 } 493 else 494 { 495 /* Access denied */ 496 Status = STATUS_ACCESS_DENIED; 497 } 498 } 499 else 500 { 501 /* The caller used this special handle value with a non-process type */ 502 Status = STATUS_OBJECT_TYPE_MISMATCH; 503 } 504 505 /* Return the status */ 506 return Status; 507 } 508 else if (AccessMode == KernelMode) 509 { 510 /* Use the kernel handle table and get the actual handle value */ 511 Handle = ObKernelHandleToHandle(Handle); 512 HandleTable = ObpKernelHandleTable; 513 } 514 else 515 { 516 /* Invalid access, fail */ 517 return STATUS_INVALID_HANDLE; 518 } 519 } 520 else 521 { 522 /* Otherwise use this process's handle table */ 523 HandleTable = PsGetCurrentProcess()->ObjectTable; 524 } 525 526 /* Enter a critical region while we touch the handle table */ 527 ASSERT(HandleTable != NULL); 528 KeEnterCriticalRegion(); 529 530 /* Get the handle entry */ 531 HandleEntry = ExMapHandleToPointer(HandleTable, Handle); 532 if (HandleEntry) 533 { 534 /* Get the object header and validate the type*/ 535 ObjectHeader = ObpGetHandleObject(HandleEntry); 536 if (!(ObjectType) || (ObjectType == ObjectHeader->Type)) 537 { 538 /* Get the granted access and validate it */ 539 GrantedAccess = HandleEntry->GrantedAccess; 540 541 /* Validate access */ 542 /* ~GrantedAccess = RefusedAccess.*/ 543 /* ~GrantedAccess & DesiredAccess = list of refused bits. */ 544 /* !(~GrantedAccess & DesiredAccess) == TRUE means ALL requested rights are granted */ 545 if ((AccessMode == KernelMode) || 546 !(~GrantedAccess & DesiredAccess)) 547 { 548 /* Reference the object directly since we have its header */ 549 InterlockedIncrement(&ObjectHeader->PointerCount); 550 551 /* Mask out the internal attributes */ 552 Attributes = HandleEntry->ObAttributes & OBJ_HANDLE_ATTRIBUTES; 553 554 /* Check if the caller wants handle information */ 555 if (HandleInformation) 556 { 557 /* Fill out the information */ 558 HandleInformation->HandleAttributes = Attributes; 559 HandleInformation->GrantedAccess = GrantedAccess; 560 } 561 562 /* Return the pointer */ 563 *Object = &ObjectHeader->Body; 564 565 /* Unlock the handle */ 566 ExUnlockHandleTableEntry(HandleTable, HandleEntry); 567 KeLeaveCriticalRegion(); 568 569 /* Return success */ 570 ASSERT(*Object != NULL); 571 return STATUS_SUCCESS; 572 } 573 else 574 { 575 /* Requested access failed */ 576 DPRINT("Rights not granted: %x\n", ~GrantedAccess & DesiredAccess); 577 Status = STATUS_ACCESS_DENIED; 578 } 579 } 580 else 581 { 582 /* Invalid object type */ 583 Status = STATUS_OBJECT_TYPE_MISMATCH; 584 } 585 586 /* Unlock the entry */ 587 ExUnlockHandleTableEntry(HandleTable, HandleEntry); 588 } 589 else 590 { 591 /* Invalid handle */ 592 Status = STATUS_INVALID_HANDLE; 593 } 594 595 /* Return failure status */ 596 KeLeaveCriticalRegion(); 597 *Object = NULL; 598 return Status; 599 } 600 601 /* EOF */ 602