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