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