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_PTR 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 = InterlockedCompareExchangeSizeT(&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 InterlockedExchangeAddSizeT(&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_PTR 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 = InterlockedExchangeAddSizeT(&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 NTSTATUS 199 NTAPI 200 ObReferenceFileObjectForWrite(IN HANDLE Handle, 201 IN KPROCESSOR_MODE AccessMode, 202 OUT PFILE_OBJECT *FileObject, 203 OUT POBJECT_HANDLE_INFORMATION HandleInformation) 204 { 205 NTSTATUS Status; 206 PHANDLE_TABLE HandleTable; 207 POBJECT_HEADER ObjectHeader; 208 PHANDLE_TABLE_ENTRY HandleEntry; 209 ACCESS_MASK GrantedAccess, DesiredAccess; 210 211 /* Assume failure */ 212 *FileObject = NULL; 213 214 /* Check if this is a special handle */ 215 if (HandleToLong(Handle) < 0) 216 { 217 /* Make sure we have a valid kernel handle */ 218 if (AccessMode != KernelMode || Handle == NtCurrentProcess() || Handle == NtCurrentThread()) 219 { 220 return STATUS_INVALID_HANDLE; 221 } 222 223 /* Use the kernel handle table and get the actual handle value */ 224 Handle = ObKernelHandleToHandle(Handle); 225 HandleTable = ObpKernelHandleTable; 226 } 227 else 228 { 229 /* Otherwise use this process's handle table */ 230 HandleTable = PsGetCurrentProcess()->ObjectTable; 231 } 232 233 ASSERT(HandleTable != NULL); 234 KeEnterCriticalRegion(); 235 236 /* Get the handle entry */ 237 HandleEntry = ExMapHandleToPointer(HandleTable, Handle); 238 if (HandleEntry) 239 { 240 /* Get the object header and validate the type*/ 241 ObjectHeader = ObpGetHandleObject(HandleEntry); 242 243 /* Get the desired access from the file object */ 244 if (!NT_SUCCESS(IoComputeDesiredAccessFileObject((PFILE_OBJECT)&ObjectHeader->Body, 245 &DesiredAccess))) 246 { 247 Status = STATUS_OBJECT_TYPE_MISMATCH; 248 } 249 else 250 { 251 /* Extract the granted access from the handle entry */ 252 if (BooleanFlagOn(NtGlobalFlag, FLG_KERNEL_STACK_TRACE_DB)) 253 { 254 /* FIXME: Translate granted access */ 255 GrantedAccess = HandleEntry->GrantedAccess; 256 } 257 else 258 { 259 GrantedAccess = HandleEntry->GrantedAccess & ~ObpAccessProtectCloseBit; 260 } 261 262 /* FIXME: Get handle information for audit */ 263 264 HandleInformation->GrantedAccess = GrantedAccess; 265 266 /* FIXME: Get handle attributes */ 267 HandleInformation->HandleAttributes = 0; 268 269 /* Do granted and desired access match? */ 270 if (GrantedAccess & DesiredAccess) 271 { 272 /* FIXME: Audit access if required */ 273 274 /* Reference the object directly since we have its header */ 275 InterlockedIncrementSizeT(&ObjectHeader->PointerCount); 276 277 /* Unlock the handle */ 278 ExUnlockHandleTableEntry(HandleTable, HandleEntry); 279 KeLeaveCriticalRegion(); 280 281 *FileObject = (PFILE_OBJECT)&ObjectHeader->Body; 282 283 /* Return success */ 284 ASSERT(*FileObject != NULL); 285 return STATUS_SUCCESS; 286 } 287 288 /* No match, deny write access */ 289 Status = STATUS_ACCESS_DENIED; 290 291 ExUnlockHandleTableEntry(HandleTable, HandleEntry); 292 } 293 } 294 else 295 { 296 Status = STATUS_INVALID_HANDLE; 297 } 298 299 /* Return failure status */ 300 KeLeaveCriticalRegion(); 301 return Status; 302 } 303 304 /* PUBLIC FUNCTIONS *********************************************************/ 305 306 LONG_PTR 307 FASTCALL 308 ObfReferenceObject(IN PVOID Object) 309 { 310 ASSERT(Object); 311 312 /* Get the header and increment the reference count */ 313 return InterlockedIncrementSizeT(&OBJECT_TO_OBJECT_HEADER(Object)->PointerCount); 314 } 315 316 LONG_PTR 317 FASTCALL 318 ObfDereferenceObject(IN PVOID Object) 319 { 320 POBJECT_HEADER Header; 321 LONG_PTR NewCount; 322 323 /* Extract the object header */ 324 Header = OBJECT_TO_OBJECT_HEADER(Object); 325 326 if (Header->PointerCount < Header->HandleCount) 327 { 328 DPRINT1("Misbehaving object: %wZ\n", &Header->Type->Name); 329 return Header->PointerCount; 330 } 331 332 /* Check whether the object can now be deleted. */ 333 NewCount = InterlockedDecrementSizeT(&Header->PointerCount); 334 if (!NewCount) 335 { 336 /* Sanity check */ 337 ASSERT(Header->HandleCount == 0); 338 339 /* Check if APCs are still active */ 340 if (!KeAreAllApcsDisabled()) 341 { 342 /* Remove the object */ 343 ObpDeleteObject(Object, FALSE); 344 } 345 else 346 { 347 /* Add us to the deferred deletion list */ 348 ObpDeferObjectDeletion(Header); 349 } 350 } 351 352 /* Return the new count */ 353 return NewCount; 354 } 355 356 VOID 357 NTAPI 358 ObDereferenceObjectDeferDelete(IN PVOID Object) 359 { 360 POBJECT_HEADER Header = OBJECT_TO_OBJECT_HEADER(Object); 361 362 /* Check whether the object can now be deleted. */ 363 if (!InterlockedDecrementSizeT(&Header->PointerCount)) 364 { 365 /* Add us to the deferred deletion list */ 366 ObpDeferObjectDeletion(Header); 367 } 368 } 369 370 #undef ObDereferenceObject 371 VOID 372 NTAPI 373 ObDereferenceObject(IN PVOID Object) 374 { 375 /* Call the fastcall function */ 376 ObfDereferenceObject(Object); 377 } 378 379 NTSTATUS 380 NTAPI 381 ObReferenceObjectByPointer(IN PVOID Object, 382 IN ACCESS_MASK DesiredAccess, 383 IN POBJECT_TYPE ObjectType, 384 IN KPROCESSOR_MODE AccessMode) 385 { 386 POBJECT_HEADER Header; 387 388 /* Get the header */ 389 Header = OBJECT_TO_OBJECT_HEADER(Object); 390 391 /* 392 * Validate object type if the call is for UserMode. 393 * NOTE: Unless it's a symbolic link (Caz Yokoyama [MSFT]) 394 */ 395 if ((Header->Type != ObjectType) && ((AccessMode != KernelMode) || 396 (ObjectType == ObpSymbolicLinkObjectType))) 397 { 398 /* Invalid type */ 399 return STATUS_OBJECT_TYPE_MISMATCH; 400 } 401 402 /* Increment the reference count and return success */ 403 InterlockedIncrementSizeT(&Header->PointerCount); 404 return STATUS_SUCCESS; 405 } 406 407 NTSTATUS 408 NTAPI 409 ObReferenceObjectByName(IN PUNICODE_STRING ObjectPath, 410 IN ULONG Attributes, 411 IN PACCESS_STATE PassedAccessState, 412 IN ACCESS_MASK DesiredAccess, 413 IN POBJECT_TYPE ObjectType, 414 IN KPROCESSOR_MODE AccessMode, 415 IN OUT PVOID ParseContext, 416 OUT PVOID* ObjectPtr) 417 { 418 PVOID Object = NULL; 419 UNICODE_STRING ObjectName; 420 NTSTATUS Status; 421 OBP_LOOKUP_CONTEXT Context; 422 AUX_ACCESS_DATA AuxData; 423 ACCESS_STATE AccessState; 424 PAGED_CODE(); 425 426 /* Fail quickly */ 427 if (!ObjectPath) return STATUS_OBJECT_NAME_INVALID; 428 429 /* Capture the name */ 430 Status = ObpCaptureObjectName(&ObjectName, ObjectPath, AccessMode, TRUE); 431 if (!NT_SUCCESS(Status)) return Status; 432 433 /* We also need a valid name after capture */ 434 if (!ObjectName.Length) return STATUS_OBJECT_NAME_INVALID; 435 436 /* Check if we didn't get an access state */ 437 if (!PassedAccessState) 438 { 439 /* Use our built-in access state */ 440 PassedAccessState = &AccessState; 441 Status = SeCreateAccessState(&AccessState, 442 &AuxData, 443 DesiredAccess, 444 &ObjectType->TypeInfo.GenericMapping); 445 if (!NT_SUCCESS(Status)) goto Quickie; 446 } 447 448 /* Find the object */ 449 *ObjectPtr = NULL; 450 Status = ObpLookupObjectName(NULL, 451 &ObjectName, 452 Attributes, 453 ObjectType, 454 AccessMode, 455 ParseContext, 456 NULL, 457 NULL, 458 PassedAccessState, 459 &Context, 460 &Object); 461 462 /* Cleanup after lookup */ 463 ObpReleaseLookupContext(&Context); 464 465 /* Check if the lookup succeeded */ 466 if (NT_SUCCESS(Status)) 467 { 468 /* Check if access is allowed */ 469 if (ObpCheckObjectReference(Object, 470 PassedAccessState, 471 FALSE, 472 AccessMode, 473 &Status)) 474 { 475 /* Return the object */ 476 *ObjectPtr = Object; 477 } 478 } 479 480 /* Free the access state */ 481 if (PassedAccessState == &AccessState) 482 { 483 SeDeleteAccessState(PassedAccessState); 484 } 485 486 Quickie: 487 /* Free the captured name if we had one, and return status */ 488 ObpFreeObjectNameBuffer(&ObjectName); 489 return Status; 490 } 491 492 NTSTATUS 493 NTAPI 494 ObReferenceObjectByHandle(IN HANDLE Handle, 495 IN ACCESS_MASK DesiredAccess, 496 IN POBJECT_TYPE ObjectType, 497 IN KPROCESSOR_MODE AccessMode, 498 OUT PVOID* Object, 499 OUT POBJECT_HANDLE_INFORMATION HandleInformation OPTIONAL) 500 { 501 PHANDLE_TABLE_ENTRY HandleEntry; 502 POBJECT_HEADER ObjectHeader; 503 ACCESS_MASK GrantedAccess; 504 ULONG Attributes; 505 PEPROCESS CurrentProcess; 506 PVOID HandleTable; 507 PETHREAD CurrentThread; 508 NTSTATUS Status; 509 PAGED_CODE(); 510 511 /* Assume failure */ 512 *Object = NULL; 513 514 /* Check if this is a special handle */ 515 if (HandleToLong(Handle) < 0) 516 { 517 /* Check if this is the current process */ 518 if (Handle == NtCurrentProcess()) 519 { 520 /* Check if this is the right object type */ 521 if ((ObjectType == PsProcessType) || !(ObjectType)) 522 { 523 /* Get the current process and granted access */ 524 CurrentProcess = PsGetCurrentProcess(); 525 GrantedAccess = CurrentProcess->GrantedAccess; 526 527 /* Validate access */ 528 /* ~GrantedAccess = RefusedAccess.*/ 529 /* ~GrantedAccess & DesiredAccess = list of refused bits. */ 530 /* !(~GrantedAccess & DesiredAccess) == TRUE means ALL requested rights are granted */ 531 if ((AccessMode == KernelMode) || 532 !(~GrantedAccess & DesiredAccess)) 533 { 534 /* Check if the caller wanted handle information */ 535 if (HandleInformation) 536 { 537 /* Return it */ 538 HandleInformation->HandleAttributes = 0; 539 HandleInformation->GrantedAccess = GrantedAccess; 540 } 541 542 /* Reference ourselves */ 543 ObjectHeader = OBJECT_TO_OBJECT_HEADER(CurrentProcess); 544 InterlockedExchangeAddSizeT(&ObjectHeader->PointerCount, 1); 545 546 /* Return the pointer */ 547 *Object = CurrentProcess; 548 ASSERT(*Object != NULL); 549 Status = STATUS_SUCCESS; 550 } 551 else 552 { 553 /* Access denied */ 554 Status = STATUS_ACCESS_DENIED; 555 } 556 } 557 else 558 { 559 /* The caller used this special handle value with a non-process type */ 560 Status = STATUS_OBJECT_TYPE_MISMATCH; 561 } 562 563 /* Return the status */ 564 return Status; 565 } 566 else if (Handle == NtCurrentThread()) 567 { 568 /* Check if this is the right object type */ 569 if ((ObjectType == PsThreadType) || !(ObjectType)) 570 { 571 /* Get the current process and granted access */ 572 CurrentThread = PsGetCurrentThread(); 573 GrantedAccess = CurrentThread->GrantedAccess; 574 575 /* Validate access */ 576 /* ~GrantedAccess = RefusedAccess.*/ 577 /* ~GrantedAccess & DesiredAccess = list of refused bits. */ 578 /* !(~GrantedAccess & DesiredAccess) == TRUE means ALL requested rights are granted */ 579 if ((AccessMode == KernelMode) || 580 !(~GrantedAccess & DesiredAccess)) 581 { 582 /* Check if the caller wanted handle information */ 583 if (HandleInformation) 584 { 585 /* Return it */ 586 HandleInformation->HandleAttributes = 0; 587 HandleInformation->GrantedAccess = GrantedAccess; 588 } 589 590 /* Reference ourselves */ 591 ObjectHeader = OBJECT_TO_OBJECT_HEADER(CurrentThread); 592 InterlockedExchangeAddSizeT(&ObjectHeader->PointerCount, 1); 593 594 /* Return the pointer */ 595 *Object = CurrentThread; 596 ASSERT(*Object != NULL); 597 Status = STATUS_SUCCESS; 598 } 599 else 600 { 601 /* Access denied */ 602 Status = STATUS_ACCESS_DENIED; 603 } 604 } 605 else 606 { 607 /* The caller used this special handle value with a non-process type */ 608 Status = STATUS_OBJECT_TYPE_MISMATCH; 609 } 610 611 /* Return the status */ 612 return Status; 613 } 614 else if (AccessMode == KernelMode) 615 { 616 /* Use the kernel handle table and get the actual handle value */ 617 Handle = ObKernelHandleToHandle(Handle); 618 HandleTable = ObpKernelHandleTable; 619 } 620 else 621 { 622 /* Invalid access, fail */ 623 return STATUS_INVALID_HANDLE; 624 } 625 } 626 else 627 { 628 /* Otherwise use this process's handle table */ 629 HandleTable = PsGetCurrentProcess()->ObjectTable; 630 } 631 632 /* Enter a critical region while we touch the handle table */ 633 ASSERT(HandleTable != NULL); 634 KeEnterCriticalRegion(); 635 636 /* Get the handle entry */ 637 HandleEntry = ExMapHandleToPointer(HandleTable, Handle); 638 if (HandleEntry) 639 { 640 /* Get the object header and validate the type*/ 641 ObjectHeader = ObpGetHandleObject(HandleEntry); 642 if (!(ObjectType) || (ObjectType == ObjectHeader->Type)) 643 { 644 /* Get the granted access and validate it */ 645 GrantedAccess = HandleEntry->GrantedAccess; 646 647 /* Validate access */ 648 /* ~GrantedAccess = RefusedAccess.*/ 649 /* ~GrantedAccess & DesiredAccess = list of refused bits. */ 650 /* !(~GrantedAccess & DesiredAccess) == TRUE means ALL requested rights are granted */ 651 if ((AccessMode == KernelMode) || 652 !(~GrantedAccess & DesiredAccess)) 653 { 654 /* Reference the object directly since we have its header */ 655 InterlockedIncrementSizeT(&ObjectHeader->PointerCount); 656 657 /* Mask out the internal attributes */ 658 Attributes = HandleEntry->ObAttributes & OBJ_HANDLE_ATTRIBUTES; 659 660 /* Check if the caller wants handle information */ 661 if (HandleInformation) 662 { 663 /* Fill out the information */ 664 HandleInformation->HandleAttributes = Attributes; 665 HandleInformation->GrantedAccess = GrantedAccess; 666 } 667 668 /* Return the pointer */ 669 *Object = &ObjectHeader->Body; 670 671 /* Unlock the handle */ 672 ExUnlockHandleTableEntry(HandleTable, HandleEntry); 673 KeLeaveCriticalRegion(); 674 675 /* Return success */ 676 ASSERT(*Object != NULL); 677 return STATUS_SUCCESS; 678 } 679 else 680 { 681 /* Requested access failed */ 682 DPRINT("Rights not granted: %x\n", ~GrantedAccess & DesiredAccess); 683 Status = STATUS_ACCESS_DENIED; 684 } 685 } 686 else 687 { 688 /* Invalid object type */ 689 Status = STATUS_OBJECT_TYPE_MISMATCH; 690 } 691 692 /* Unlock the entry */ 693 ExUnlockHandleTableEntry(HandleTable, HandleEntry); 694 } 695 else 696 { 697 /* Invalid handle */ 698 Status = STATUS_INVALID_HANDLE; 699 } 700 701 /* Return failure status */ 702 KeLeaveCriticalRegion(); 703 *Object = NULL; 704 return Status; 705 } 706 707 /* EOF */ 708