xref: /reactos/ntoskrnl/ob/obref.c (revision 94a413ae)
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