xref: /reactos/ntoskrnl/include/internal/ob_x.h (revision 4c63ed5a)
1 /*
2 * PROJECT:         ReactOS Kernel
3 * LICENSE:         GPL - See COPYING in the top level directory
4 * FILE:            ntoskrnl/include/internal/ob_x.h
5 * PURPOSE:         Internal Inlined Functions for the Object Manager
6 * PROGRAMMERS:     Alex Ionescu (alex.ionescu@reactos.org)
7 */
8 
9 #include "ex.h"
10 
11 #define OBP_LOCK_STATE_PRE_ACQUISITION_EXCLUSIVE    0xAAAA1234
12 #define OBP_LOCK_STATE_PRE_ACQUISITION_SHARED       0xBBBB1234
13 #define OBP_LOCK_STATE_POST_ACQUISITION_EXCLUSIVE   0xCCCC1234
14 #define OBP_LOCK_STATE_POST_ACQUISITION_SHARED      0xDDDD1234
15 #define OBP_LOCK_STATE_RELEASED                     0xEEEE1234
16 #define OBP_LOCK_STATE_INITIALIZED                  0xFFFF1234
17 
18 #define OBP_NAME_LOOKASIDE_MAX_SIZE 248
19 
20 FORCEINLINE
21 ULONG
ObpValidateAttributes(IN ULONG Attributes,IN KPROCESSOR_MODE PreviousMode)22 ObpValidateAttributes(IN ULONG Attributes,
23                       IN KPROCESSOR_MODE PreviousMode)
24 {
25     if (PreviousMode == KernelMode)
26     {
27         /* For kernel, allow any valid attributes */
28         return Attributes & OBJ_VALID_KERNEL_ATTRIBUTES;
29     }
30     else
31     {
32         /* For user, mask out kernel-only attributes */
33         return (Attributes & OBJ_VALID_ATTRIBUTES) &
34                ~(OBJ_KERNEL_HANDLE);
35     }
36 }
37 
38 FORCEINLINE
39 ULONG
ObpSelectObjectLockSlot(IN POBJECT_HEADER ObjectHeader)40 ObpSelectObjectLockSlot(IN POBJECT_HEADER ObjectHeader)
41 {
42     /* We have 4 locks total, this will return a 0-index slot */
43     return (((ULONG_PTR)ObjectHeader) >> 8) & 3;
44 }
45 
46 FORCEINLINE
47 VOID
ObpAcquireObjectLock(IN POBJECT_HEADER ObjectHeader)48 ObpAcquireObjectLock(IN POBJECT_HEADER ObjectHeader)
49 {
50     ULONG Slot;
51     POBJECT_TYPE ObjectType = ObjectHeader->Type;
52 
53     /* Sanity check */
54     ASSERT(KeGetCurrentIrql() <= APC_LEVEL);
55 
56     /* Pick a slot */
57     Slot = ObpSelectObjectLockSlot(ObjectHeader);
58 
59     /* Enter a critical region and acquire the resource */
60     KeEnterCriticalRegion();
61     ExAcquireResourceExclusiveLite(&ObjectType->ObjectLocks[Slot], TRUE);
62 }
63 
64 FORCEINLINE
65 VOID
ObpAcquireObjectLockShared(IN POBJECT_HEADER ObjectHeader)66 ObpAcquireObjectLockShared(IN POBJECT_HEADER ObjectHeader)
67 {
68     ULONG Slot;
69     POBJECT_TYPE ObjectType = ObjectHeader->Type;
70 
71     /* Sanity check */
72     ASSERT(KeGetCurrentIrql() <= APC_LEVEL);
73 
74     /* Pick a slot */
75     Slot = ObpSelectObjectLockSlot(ObjectHeader);
76 
77     /* Enter a critical region and acquire the resource */
78     KeEnterCriticalRegion();
79     ExAcquireResourceSharedLite(&ObjectType->ObjectLocks[Slot], TRUE);
80 }
81 
82 FORCEINLINE
83 VOID
ObpReleaseObjectLock(IN POBJECT_HEADER ObjectHeader)84 ObpReleaseObjectLock(IN POBJECT_HEADER ObjectHeader)
85 {
86     ULONG Slot;
87     POBJECT_TYPE ObjectType = ObjectHeader->Type;
88 
89     /* Pick a slot */
90     Slot = ObpSelectObjectLockSlot(ObjectHeader);
91 
92     /* Release the resource and leave a critical region */
93     ExReleaseResourceLite(&ObjectType->ObjectLocks[Slot]);
94     KeLeaveCriticalRegion();
95 
96     /* Sanity check */
97     ASSERT(KeGetCurrentIrql() <= APC_LEVEL);
98 }
99 
100 FORCEINLINE
101 POBJECT_HEADER_NAME_INFO
ObpReferenceNameInfo(IN POBJECT_HEADER ObjectHeader)102 ObpReferenceNameInfo(IN POBJECT_HEADER ObjectHeader)
103 {
104     POBJECT_HEADER_NAME_INFO ObjectNameInfo;
105     ULONG NewValue, References;
106 
107     /* Make sure we have name information at all */
108     ObjectNameInfo = OBJECT_HEADER_TO_NAME_INFO(ObjectHeader);
109     if (!ObjectNameInfo) return NULL;
110 
111     /* Get the number of references */
112     References = ObjectNameInfo->QueryReferences;
113     for (;;)
114     {
115         /* Check if the count is 0 and fail if so */
116         if (!References) return NULL;
117 
118         /* Increment the number of references */
119         NewValue = InterlockedCompareExchange((PLONG)&ObjectNameInfo->
120                                               QueryReferences,
121                                               References + 1,
122                                               References);
123         if (NewValue == References) break;
124 
125         /* We failed, try again */
126         References = NewValue;
127     }
128 
129     /* Check for magic flag */
130     if (ObjectNameInfo->QueryReferences & 0x80000000)
131     {
132         /* FIXME: Unhandled*/
133         DbgPrint("OB: Unhandled path\n");
134         ASSERT(FALSE);
135     }
136 
137     /* Return the name information */
138     return ObjectNameInfo;
139 }
140 
141 FORCEINLINE
142 VOID
ObpDereferenceNameInfo(IN POBJECT_HEADER_NAME_INFO HeaderNameInfo)143 ObpDereferenceNameInfo(IN POBJECT_HEADER_NAME_INFO HeaderNameInfo)
144 {
145     POBJECT_DIRECTORY Directory;
146 
147     /* Bail out if there's no info at all */
148     if (!HeaderNameInfo) return;
149 
150     /* Remove a query reference and check if it was the last one */
151     if (!InterlockedDecrement((PLONG)&HeaderNameInfo->QueryReferences))
152     {
153         /* Check if we have a name */
154         if (HeaderNameInfo->Name.Buffer)
155         {
156             /* We can get rid of the object name now */
157             ExFreePoolWithTag(HeaderNameInfo->Name.Buffer, OB_NAME_TAG);
158             RtlInitEmptyUnicodeString(&HeaderNameInfo->Name, NULL, 0);
159         }
160 
161         /* Check if the object has a directory associated to it */
162         Directory = HeaderNameInfo->Directory;
163         if (Directory)
164         {
165             /* Delete the directory */
166             HeaderNameInfo->Directory = NULL;
167             ObDereferenceObjectDeferDelete(Directory);
168         }
169     }
170 }
171 
172 /**
173  * @brief
174  * Locks a directory for shared access.
175  * Used for reading members of the directory object.
176  *
177  * @param[in] Directory
178  * The directory to lock.
179  *
180  * @param[in] Context
181  * The lookup lock context.
182  */
183 FORCEINLINE
184 VOID
ObpAcquireDirectoryLockShared(IN POBJECT_DIRECTORY Directory,IN POBP_LOOKUP_CONTEXT Context)185 ObpAcquireDirectoryLockShared(IN POBJECT_DIRECTORY Directory,
186                               IN POBP_LOOKUP_CONTEXT Context)
187 {
188     /* Update lock flag */
189     Context->LockStateSignature = OBP_LOCK_STATE_PRE_ACQUISITION_SHARED;
190 
191     /* Acquire an shared directory lock */
192     KeEnterCriticalRegion();
193     ExAcquirePushLockShared(&Directory->Lock);
194 
195     /* Update lock flag */
196     Context->LockStateSignature = OBP_LOCK_STATE_POST_ACQUISITION_SHARED;
197 }
198 
199 /**
200  * @brief
201  * Locks a directory for exclusive access.
202  * Used for writing/reading members of the directory object.
203  *
204  * @param[in] Directory
205  * The directory to lock.
206  *
207  * @param[in] Context
208  * The lookup lock context.
209  */
210 FORCEINLINE
211 VOID
ObpAcquireDirectoryLockExclusive(IN POBJECT_DIRECTORY Directory,IN POBP_LOOKUP_CONTEXT Context)212 ObpAcquireDirectoryLockExclusive(IN POBJECT_DIRECTORY Directory,
213                                  IN POBP_LOOKUP_CONTEXT Context)
214 {
215     /* Update lock flag */
216     Context->LockStateSignature = OBP_LOCK_STATE_PRE_ACQUISITION_EXCLUSIVE;
217 
218     /* Acquire an exclusive directory lock */
219     KeEnterCriticalRegion();
220     ExAcquirePushLockExclusive(&Directory->Lock);
221 
222     /* Update lock flag */
223     Context->LockStateSignature = OBP_LOCK_STATE_POST_ACQUISITION_EXCLUSIVE;
224 }
225 
226 /**
227  * @brief
228  * Unlocks a previously shared or exclusively locked directory.
229  *
230  * @param[in] Directory
231  * The directory to unlock.
232  *
233  * @param[in] Context
234  * The lookup lock context.
235  */
236 FORCEINLINE
237 VOID
ObpReleaseDirectoryLock(IN POBJECT_DIRECTORY Directory,IN POBP_LOOKUP_CONTEXT Context)238 ObpReleaseDirectoryLock(IN POBJECT_DIRECTORY Directory,
239                         IN POBP_LOOKUP_CONTEXT Context)
240 {
241     /* Release the lock */
242     ExReleasePushLock(&Directory->Lock);
243     Context->LockStateSignature = OBP_LOCK_STATE_RELEASED;
244     KeLeaveCriticalRegion();
245 }
246 
247 /**
248  * @brief
249  * Initializes a new object directory lookup context.
250  * Used for lookup operations (insertions/deletions) in a directory.
251  * Employed in conjunction with the directory locking functions.
252  *
253  * @param[in] Context
254  * The new lookup context to initialize.
255  */
256 FORCEINLINE
257 VOID
ObpInitializeLookupContext(IN POBP_LOOKUP_CONTEXT Context)258 ObpInitializeLookupContext(IN POBP_LOOKUP_CONTEXT Context)
259 {
260     /* Initialize a null context */
261     Context->Object = NULL;
262     Context->Directory = NULL;
263     Context->DirectoryLocked = FALSE;
264     Context->LockStateSignature = OBP_LOCK_STATE_INITIALIZED;
265 }
266 
267 /**
268  * @brief
269  * Locks an object directory lookup context for performing
270  * lookup operations (insertions/deletions) in a directory.
271  * The directory is locked for exclusive access.
272  *
273  * @param[in] Context
274  * The lookup context to lock.
275  *
276  * @param[in] Directory
277  * The directory on which the lookup context applies.
278  */
279 FORCEINLINE
280 VOID
ObpAcquireLookupContextLock(IN POBP_LOOKUP_CONTEXT Context,IN POBJECT_DIRECTORY Directory)281 ObpAcquireLookupContextLock(IN POBP_LOOKUP_CONTEXT Context,
282                             IN POBJECT_DIRECTORY Directory)
283 {
284     /* Acquire an exclusive directory lock and save its lock state */
285     ObpAcquireDirectoryLockExclusive(Directory, Context);
286     Context->Directory = Directory;
287     Context->DirectoryLocked = TRUE;
288 }
289 
290 FORCEINLINE
291 VOID
ObpReleaseLookupContextObject(IN POBP_LOOKUP_CONTEXT Context)292 ObpReleaseLookupContextObject(IN POBP_LOOKUP_CONTEXT Context)
293 {
294     POBJECT_HEADER ObjectHeader;
295     POBJECT_HEADER_NAME_INFO HeaderNameInfo;
296 
297     /* Check if we had an object */
298     if (Context->Object)
299     {
300         /* Get the object name information */
301         ObjectHeader = OBJECT_TO_OBJECT_HEADER(Context->Object);
302         HeaderNameInfo = OBJECT_HEADER_TO_NAME_INFO(ObjectHeader);
303 
304         /* Release the name information */
305         ObpDereferenceNameInfo(HeaderNameInfo);
306 
307         /* Dereference the object */
308         ObDereferenceObject(Context->Object);
309         Context->Object = NULL;
310     }
311 }
312 
313 /**
314  * @brief
315  * Releases an initialized object directory lookup context.
316  * Unlocks it if necessary, and dereferences the underlying object.
317  *
318  * @param[in] Context
319  * The lookup context to release.
320  */
321 FORCEINLINE
322 VOID
ObpReleaseLookupContext(IN POBP_LOOKUP_CONTEXT Context)323 ObpReleaseLookupContext(IN POBP_LOOKUP_CONTEXT Context)
324 {
325     /* Check if we came back with the directory locked */
326     if (Context->DirectoryLocked)
327     {
328         /* Release the directory lock */
329         ObpReleaseDirectoryLock(Context->Directory, Context);
330         Context->Directory = NULL;
331         Context->DirectoryLocked = FALSE;
332     }
333 
334     /* Clear the context */
335     ObpReleaseLookupContextObject(Context);
336 }
337 
338 FORCEINLINE
339 VOID
ObpEnterObjectTypeMutex(IN POBJECT_TYPE ObjectType)340 ObpEnterObjectTypeMutex(IN POBJECT_TYPE ObjectType)
341 {
342     /* Sanity check */
343     ASSERT(KeGetCurrentIrql() <= APC_LEVEL);
344 
345     /* Enter a critical region and acquire the resource */
346     KeEnterCriticalRegion();
347     ExAcquireResourceExclusiveLite(&ObjectType->Mutex, TRUE);
348 }
349 
350 FORCEINLINE
351 VOID
ObpLeaveObjectTypeMutex(IN POBJECT_TYPE ObjectType)352 ObpLeaveObjectTypeMutex(IN POBJECT_TYPE ObjectType)
353 {
354     /* Enter a critical region and acquire the resource */
355     ExReleaseResourceLite(&ObjectType->Mutex);
356     KeLeaveCriticalRegion();
357 
358     /* Sanity check */
359     ASSERT(KeGetCurrentIrql() <= APC_LEVEL);
360 }
361 
362 FORCEINLINE
363 VOID
ObpReleaseObjectCreateInformation(IN POBJECT_CREATE_INFORMATION ObjectCreateInfo)364 ObpReleaseObjectCreateInformation(IN POBJECT_CREATE_INFORMATION ObjectCreateInfo)
365 {
366     /* Check if we have a security descriptor */
367     if (ObjectCreateInfo->SecurityDescriptor)
368     {
369         /* Release it */
370         SeReleaseSecurityDescriptor(ObjectCreateInfo->SecurityDescriptor,
371                                     ObjectCreateInfo->ProbeMode,
372                                     TRUE);
373         ObjectCreateInfo->SecurityDescriptor = NULL;
374     }
375 }
376 
377 FORCEINLINE
378 PVOID
ObpAllocateObjectCreateInfoBuffer(IN PP_NPAGED_LOOKASIDE_NUMBER Type)379 ObpAllocateObjectCreateInfoBuffer(IN PP_NPAGED_LOOKASIDE_NUMBER Type)
380 {
381     PVOID Buffer;
382     PNPAGED_LOOKASIDE_LIST List;
383     PKPRCB Prcb = KeGetCurrentPrcb();
384 
385     /* Get the P list first */
386     List = (PNPAGED_LOOKASIDE_LIST)Prcb->PPLookasideList[Type].P;
387 
388     /* Attempt allocation */
389     List->L.TotalAllocates++;
390     Buffer = (PVOID)InterlockedPopEntrySList(&List->L.ListHead);
391     if (!Buffer)
392     {
393         /* Let the balancer know that the P list failed */
394         List->L.AllocateMisses++;
395 
396         /* Try the L List */
397         List = (PNPAGED_LOOKASIDE_LIST)Prcb->PPLookasideList[Type].L;
398         List->L.TotalAllocates++;
399         Buffer = (PVOID)InterlockedPopEntrySList(&List->L.ListHead);
400         if (!Buffer)
401         {
402             /* Let the balancer know the L list failed too */
403             List->L.AllocateMisses++;
404 
405             /* Allocate it */
406             Buffer = List->L.Allocate(List->L.Type, List->L.Size, List->L.Tag);
407         }
408     }
409 
410     /* Return buffer */
411     return Buffer;
412 }
413 
414 FORCEINLINE
415 VOID
ObpFreeCapturedAttributes(IN PVOID Buffer,IN PP_NPAGED_LOOKASIDE_NUMBER Type)416 ObpFreeCapturedAttributes(IN PVOID Buffer,
417                           IN PP_NPAGED_LOOKASIDE_NUMBER Type)
418 {
419     PNPAGED_LOOKASIDE_LIST List;
420     PKPRCB Prcb = KeGetCurrentPrcb();
421 
422     /* Use the P List */
423     List = (PNPAGED_LOOKASIDE_LIST)Prcb->PPLookasideList[Type].P;
424     List->L.TotalFrees++;
425 
426     /* Check if the Free was within the Depth or not */
427     if (ExQueryDepthSList(&List->L.ListHead) >= List->L.Depth)
428     {
429         /* Let the balancer know */
430         List->L.FreeMisses++;
431 
432         /* Use the L List */
433         List = (PNPAGED_LOOKASIDE_LIST)Prcb->PPLookasideList[Type].L;
434         List->L.TotalFrees++;
435 
436         /* Check if the Free was within the Depth or not */
437         if (ExQueryDepthSList(&List->L.ListHead) >= List->L.Depth)
438         {
439             /* All lists failed, use the pool */
440             List->L.FreeMisses++;
441             List->L.Free(Buffer);
442         }
443         else
444         {
445             /* The free was within the Depth */
446             InterlockedPushEntrySList(&List->L.ListHead,
447                                       (PSLIST_ENTRY)Buffer);
448         }
449     }
450     else
451     {
452         /* The free was within the Depth */
453         InterlockedPushEntrySList(&List->L.ListHead,
454                                   (PSLIST_ENTRY)Buffer);
455     }
456 }
457 
458 FORCEINLINE
459 VOID
ObpFreeObjectCreateInformation(IN POBJECT_CREATE_INFORMATION ObjectCreateInfo)460 ObpFreeObjectCreateInformation(IN POBJECT_CREATE_INFORMATION ObjectCreateInfo)
461 {
462     /* First release the attributes, then free them from the lookaside list */
463     ObpReleaseObjectCreateInformation(ObjectCreateInfo);
464     ObpFreeCapturedAttributes(ObjectCreateInfo, LookasideCreateInfoList);
465 }
466 
467 #if DBG
468 FORCEINLINE
469 VOID
ObpCalloutStart(IN PKIRQL CalloutIrql)470 ObpCalloutStart(IN PKIRQL CalloutIrql)
471 {
472     /* Save the callout IRQL */
473     *CalloutIrql = KeGetCurrentIrql();
474 }
475 
476 FORCEINLINE
477 VOID
ObpCalloutEnd(IN KIRQL CalloutIrql,IN PCHAR Procedure,IN POBJECT_TYPE ObjectType,IN PVOID Object)478 ObpCalloutEnd(IN KIRQL CalloutIrql,
479               IN PCHAR Procedure,
480               IN POBJECT_TYPE ObjectType,
481               IN PVOID Object)
482 {
483     /* Detect IRQL change */
484     if (CalloutIrql != KeGetCurrentIrql())
485     {
486         /* Print error */
487         DbgPrint("OB: ObjectType: %wZ  Procedure: %s  Object: %p\n",
488                  &ObjectType->Name, Procedure, Object);
489         DbgPrint("    Returned at %x IRQL, but was called at %x IRQL\n",
490                  KeGetCurrentIrql(), CalloutIrql);
491         DbgBreakPoint();
492     }
493 }
494 #else
495 FORCEINLINE
496 VOID
ObpCalloutStart(IN PKIRQL CalloutIrql)497 ObpCalloutStart(IN PKIRQL CalloutIrql)
498 {
499     /* No-op */
500     UNREFERENCED_PARAMETER(CalloutIrql);
501 }
502 
503 FORCEINLINE
504 VOID
ObpCalloutEnd(IN KIRQL CalloutIrql,IN PCHAR Procedure,IN POBJECT_TYPE ObjectType,IN PVOID Object)505 ObpCalloutEnd(IN KIRQL CalloutIrql,
506               IN PCHAR Procedure,
507               IN POBJECT_TYPE ObjectType,
508               IN PVOID Object)
509 {
510     UNREFERENCED_PARAMETER(CalloutIrql);
511 }
512 #endif
513