My Project
Loading...
Searching...
No Matches
handle.c
Go to the documentation of this file.
1/*++
2
3Module Name:
4
5 handle.c
6
7Purpose:
8
9 This module contains the implementation of MatanelOS's Handle Table.
10
11Author:
12
13 slep (Matanel) 2025.
14
15Revision History:
16
17--*/
18
19#include "../../includes/ht.h"
20#include "../../includes/ob.h"
21#include "../../assert.h"
22
23// ->>>>> The handle table handles are accessed in pageable memory, we cannot be at DISPATCH_LEVEL or above.
24// FIXME FIXME Implement Push locks to use in PASSIVE_LEVEL in order to use the handle table at Pageable memory, because using spinlocks raises to DISPATCH.
25
28
29static
31HtpLookupEntry(
32 IN PHANDLE_TABLE Table,
33 IN HANDLE Handle
34)
35
36/*++
37
38 Routine description:
39
40 Lookups the entry in the given handle table from supplied handle.
41
42 Arguments:
43
44 [IN] PHANDLE_TABLE Table - Pointer to the handle table.
45 [IN] HANDLE Handle - The handle.
46
47 Return Values:
48
49 The entry on success, or NULL on invalid table/handle.
50
51--*/
52
53{
54 if (!Table || !Handle || ((uint64_t)Handle & 3)) return NULL;
55
56 uint64_t TableCode = Table->TableCode;
57 uint64_t Level = TableCode & TABLE_LEVEL_MASK;
58 void* TableBase = (void*)(TableCode & ~TABLE_LEVEL_MASK);
59
60 // Handles are multiples of 4, so we divide by 4 to get actual index
61 uint64_t Index = (uint64_t)(Handle) >> 2;
62
63 if (Level == 0) {
64 // Direct Array
66 return &Entries[Index];
67 }
68 else if (Level == 1) {
69 // TableBase points to an array of pointers to pages
70 // We need to find WHICH page, and then WHICH entry in that page.
71 uint64_t MaxEntriesPerLevel = LOW_LEVEL_ENTRIES;
72 uint64_t PageIndex = Index / MaxEntriesPerLevel;
73 uint64_t EntryIndex = Index % MaxEntriesPerLevel;
74
75 PHANDLE_TABLE_ENTRY* PageTable = (PHANDLE_TABLE_ENTRY*)TableBase;
76 PHANDLE_TABLE_ENTRY ActualPage = PageTable[PageIndex];
77 if (ActualPage) {
78 return &ActualPage[EntryIndex];
79 }
80 }
81
82 // We dont really support millions of handles, so. (level 2 support needed.)
83 return NULL;
84}
85
88 IN PEPROCESS Process
89)
90
91/*++
92
93 Routine description:
94
95 Create a handle table for the given process.
96
97 Arguments:
98
99 [IN] PEPROCESS Process - Pointer to process.
100
101 Return Values:
102
103 Pointer to table on success, or NULL on failure.
104
105--*/
106
107{
108 PHANDLE_TABLE Table = MmAllocatePoolWithTag(NonPagedPool, sizeof(HANDLE_TABLE), 'bTtH'); // HtTb - Handle Table.
109
110 // Allocate the first page of the entries (level 0) (switched to paged pool now)
112
113 // Initialize the free list in the new page.
114 for (uint64_t i = 1; i < LOW_LEVEL_ENTRIES - 1; i++) {
115 Level0[i].NextFreeTableEntry = (i + 1) * 4; // Store as a handle value.
116 }
117 Level0[LOW_LEVEL_ENTRIES - 1].NextFreeTableEntry = 0; // End of the list.
118 // The first index is NULL, always.
119 Level0[0].Object = NULL;
120
121 Table->TableCode = (uint64_t)Level0; // Level is 0, so bottom bits are 0
122 Table->FirstFreeHandle = 4;
123 Table->QuotaProcess = Process;
124
125 return Table;
126}
127
128static
130HtpAllocateAndInitHandlePage(
131 IN PHANDLE_TABLE Table,
132 IN uint32_t BaseHandleIndex
133)
134
135/*++
136
137 Routine description:
138
139 Creates a HANDLE_TABLE_ENTRY for the given Table (does not insert).
140
141 Arguments:
142
143 [IN] PHANDLE_TABLE Table - The table to create the entry for.
144 [IN] uint32_t BaseHandleIndex - The base index to create the starting handle table entry for. (e.g if last index was 16, then supply it, :))
145
146 Return Values:
147
148 Pointer to allocated table entry on success, or NULL on failure.
149
150--*/
151
152{
154 if (!NewPage) return NULL;
155
156 // Link all entries in this new page together
157 uint32_t i;
158 for (i = 0; i < LOW_LEVEL_ENTRIES - 1; i++) {
159 // Calculate the actual handle value for the *next* entry
160 NewPage[i].NextFreeTableEntry = (BaseHandleIndex + i + 1) * 4;
161 }
162
163 // The last entry in this new page points to the CURRENT FirstFreeHandle.
164 NewPage[LOW_LEVEL_ENTRIES - 1].NextFreeTableEntry = Table->FirstFreeHandle;
165 NewPage[0].Object = NULL;
166
167 return NewPage;
168}
169
170static
171void
172HtpExpandTable(
173 PHANDLE_TABLE Table
174)
175
176/*++
177
178 Routine description:
179
180 Expands a HANDLE_TABLE to its next intended level.
181
182 Arguments:
183
184 [IN] PHANDLE_TABLE Table - The table to expand.
185
186 Return Values:
187
188 None.
189
190 Notes:
191
192 Any level above 1 is not supported.
193
194--*/
195
196{
197 uint64_t TableCode = Table->TableCode;
198 uint64_t CurrentLevel = TableCode & TABLE_LEVEL_MASK;
199 void* TableBase = (void*)(TableCode & ~TABLE_LEVEL_MASK);
200
201 PHANDLE_TABLE_ENTRY NewFreePage = NULL;
202 uint32_t NewBaseIndex = 0;
203
204 //
205 // Case 1: Promoting from Level 0 to Level 1
206 //
207 if (CurrentLevel == 0) {
208 // Allocate the "Directory" page (holds pointers, not entries)
210 if (!Directory) return; // OOM
211
212 // The existing Level 0 page becomes the first entry in the directory
213 Directory[0] = (PHANDLE_TABLE_ENTRY)TableBase;
214
215 // Allocate a NEW Level 0 page for the second slot
216 NewBaseIndex = LOW_LEVEL_ENTRIES; // The index starts where the first page left off
217 NewFreePage = HtpAllocateAndInitHandlePage(Table, NewBaseIndex);
218
219 if (!NewFreePage) {
220 MmFreePool(Directory);
221 return;
222 }
223
224 Directory[1] = NewFreePage;
225
226 // Update TableCode: Pointer to Directory | Level 1
227 Table->TableCode = ((uint64_t)Directory) | 1;
228
229 // Update the free list to point to the start of our new page
230 // (NewFreePage[0] corresponds to NewBaseIndex)
231 Table->FirstFreeHandle = NewBaseIndex * 4;
232 }
233 //
234 // Case 2: Already Level 1, need to add a new page
235 //
236 else if (CurrentLevel == 1) {
237 PHANDLE_TABLE_ENTRY* Directory = (PHANDLE_TABLE_ENTRY*)TableBase;
238
239 // Find the first empty slot in the directory
240 uint32_t DirectoryIndex = 0;
241 for (DirectoryIndex = 0; DirectoryIndex < LOW_LEVEL_ENTRIES; DirectoryIndex++) {
242 if (Directory[DirectoryIndex] == NULL) break;
243 }
244
245 if (DirectoryIndex >= LOW_LEVEL_ENTRIES) {
246 // Level 1 is full, no level 2 yet.
247 goto Level2Setup;
248 }
249
250 // Calculate the Handle Index base for this new page
251 NewBaseIndex = DirectoryIndex * LOW_LEVEL_ENTRIES;
252
253 // Allocate the new page
254 NewFreePage = HtpAllocateAndInitHandlePage(Table, NewBaseIndex);
255 if (!NewFreePage) return;
256
257 // Link it into the directory
258 Directory[DirectoryIndex] = NewFreePage;
259
260 // Update Free List
261 Table->FirstFreeHandle = NewBaseIndex * 4;
262 }
263
264Level2Setup:
265 return;
266}
267
268HANDLE
270 PHANDLE_TABLE Table,
271 void* Object,
272 uint32_t Access
273)
274
275/*++
276
277 Routine description:
278
279 Creates a HANDLE for specified Object. (Ob)
280
281 Arguments:
282
283 [IN] PHANDLE_TABLE Table - The table to insert the handle in.
284 [IN] void* Object - The Object to create the handle for.
285 [IN] uint32_t Access - The maximum access the handle should have.
286
287 Return Values:
288
289 The HANDLE number, or MT_INVALID_HANDLE on new handle allocation failure.
290
291--*/
292
293{
294 IRQL oldIrql;
295 MsAcquireSpinlock(&Table->TableLock, &oldIrql);
296
297 // Is there a free handle in the list?
298 if (Table->FirstFreeHandle == 0)
299 {
300
301 // Expand table, no free handles.
302 HtpExpandTable(Table);
303
304 // Check again. If it is STILL 0, expansion failed (OOM).
305 if (Table->FirstFreeHandle == 0) {
306 MsReleaseSpinlock(&Table->TableLock, oldIrql);
307 return MT_INVALID_HANDLE; // Return NULL/0
308 }
309 }
310
311 // Pop from Free List
312 uint32_t FreeIndex = Table->FirstFreeHandle;
313 PHANDLE_TABLE_ENTRY Entry = HtpLookupEntry(Table, (HANDLE)FreeIndex);
314
315 // Sanity check (Should never happen if FirstFreeHandle != 0)
316 if (!Entry) {
317 MsReleaseSpinlock(&Table->TableLock, oldIrql);
318 return MT_INVALID_HANDLE;
319 }
320
321 // Update head of free list
322 Table->FirstFreeHandle = Entry->NextFreeTableEntry;
323
324 // Setup the Entry
325 Entry->Object = Object;
326 Entry->GrantedAccess = Access;
327 MsReleaseSpinlock(&Table->TableLock, oldIrql);
328
329 return (HANDLE)FreeIndex;
330}
331
332void
334 PHANDLE_TABLE Table,
335 HANDLE Handle
336)
337
338/*++
339
340 Routine description:
341
342 Delets a HANDLE from the table.
343
344 Arguments:
345
346 [IN] PHANDLE_TABLE Table - The table to delete the handle from.
347 [IN] HANDLE Handle - The handle to delete.
348
349 Return Values:
350
351 None.
352
353--*/
354
355{
356 IRQL oldIrql;
357 MsAcquireSpinlock(&Table->TableLock, &oldIrql);
358
359 // Validate Handle
360 // Ensure it's not 0 (if 0 is invalid) and is a multiple of 4
361 if (!Handle || ((uint64_t)Handle & 3)) {
362 MsReleaseSpinlock(&Table->TableLock, oldIrql);
363 return;
364 }
365
366 // Lookup the entry
367 PHANDLE_TABLE_ENTRY Entry = HtpLookupEntry(Table, Handle);
368
369 // Check if entry is actually in use
370 if (!Entry || !Entry->Object) {
371 // Handle is already free or invalid
372 MsReleaseSpinlock(&Table->TableLock, oldIrql);
373 return;
374 }
375
376 // 4. Invalidate the Entry
377 Entry->Object = NULL;
378 Entry->GrantedAccess = 0;
379
380 // Push onto Free List (LIFO - Stack)
381 // The current head of the list becomes the next for this entry.
382 Entry->NextFreeTableEntry = Table->FirstFreeHandle;
383 // This entry becomes the new head.
384 Table->FirstFreeHandle = (uint32_t)Handle;
385 MsReleaseSpinlock(&Table->TableLock, oldIrql);
386}
387
388void*
390 PHANDLE_TABLE Table,
391 HANDLE Handle,
392 PHANDLE_TABLE_ENTRY* OutEntry
393)
394
395/*++
396
397 Routine description:
398
399 Retrieves the object for the specified Handle.
400
401 Arguments:
402
403 [IN] PHANDLE_TABLE Table - The table to enumerate the handle in.
404 [IN] HANDLE Handle - The Object's handle.
405 [OUT OPTIONAL] PHANDLE_TABLE_ENTRY* OutEntry - The table entry for the handle.
406
407 Return Values:
408
409 The Object found for the handle.
410
411--*/
412
413{
414 IRQL oldIrql;
415 void* Object = NULL;
416
417 MsAcquireSpinlock(&Table->TableLock, &oldIrql);
418
419 PHANDLE_TABLE_ENTRY Entry = HtpLookupEntry(Table, Handle);
420
421 // Check if valid and allocated
422 if (Entry && Entry->Object) {
423 Object = Entry->Object;
424 }
425
426 MsReleaseSpinlock(&Table->TableLock, oldIrql);
427 if (Entry) {
428 if (OutEntry) *OutEntry = Entry;
429 }
430 return Object;
431}
432
433void
435 IN PHANDLE_TABLE Table
436)
437
438/*++
439
440 Routine description:
441
442 Deletes the handle table allocated. (Dereferences any objects that are still alive, frees directory and other)
443
444 Arguments:
445
446 [IN] PHANDLE_TABLE Table - The table to delete.
447
448 Return Values:
449
450 None.
451
452--*/
453
454{
455 // We just free all of the levels and the table itself.
456 if (!Table) return;
457
458 // Grab the table lock.
459 IRQL oldIrql;
460 MsAcquireSpinlock(&Table->TableLock, &oldIrql);
461
462 uint64_t TableCode = Table->TableCode;
463 uint64_t Level = TableCode & TABLE_LEVEL_MASK;
464 void* TableBase = (void*)(TableCode & ~TABLE_LEVEL_MASK);
465
466 if (Level == 0) {
467 // Single contigious page of entries
469 if (Entries) {
470 // Walk and dereference any live objects that are alive.
471 for (uint64_t i = 0; i < LOW_LEVEL_ENTRIES; i++) {
472 void* Object = Entries[i].Object;
473 if (Object) {
474 Entries[i].Object = NULL;
475 ObDereferenceObject(Object);
476 }
477 }
478 }
479
480 // No more live handles — release lock and free the page
481 MsReleaseSpinlock(&Table->TableLock, oldIrql);
483 }
484
485 else if (Level == 1) {
486 // Directory of page pointers.
487 PHANDLE_TABLE_ENTRY* Directory = (PHANDLE_TABLE_ENTRY*)TableBase;
488 if (Directory) {
489 // Walk every allocated page.
490 for (uint64_t dir = 0; dir < LOW_LEVEL_ENTRIES; dir++) {
491 PHANDLE_TABLE_ENTRY Page = Directory[dir];
492 if (!Page) continue;
493
494 for (uint64_t i = 0; i < LOW_LEVEL_ENTRIES; i++) {
495 void* Object = Page[i].Object;
496 if (Object) {
497 Page[i].Object = NULL;
498 ObDereferenceObject(Object);
499 }
500 }
501
502 // Free this page of handles.
503 MmFreePool(Page);
504 }
505 }
506
507 // Release spinlock and free the directory itself.
508 MsReleaseSpinlock(&Table->TableLock, oldIrql);
509 if (Directory) MmFreePool(Directory);
510 }
511
512 else {
513 // Unsupported level, release lock and get out.
514 assert(false, "Unsupported level encountered on handle table free.");
515 MsReleaseSpinlock(&Table->TableLock, oldIrql);
516 }
517
518 // Finally, free our table itself.
519 MmFreePool(Table);
520}
#define IN
Definition annotations.h:7
#define assert(...)
Definition assert.h:57
enum _IRQL IRQL
EPROCESS * PEPROCESS
Definition core.h:50
struct _DOUBLY_LINKED_LIST DOUBLY_LINKED_LIST
void * HtGetObject(PHANDLE_TABLE Table, HANDLE Handle, PHANDLE_TABLE_ENTRY *OutEntry)
Definition handle.c:389
PHANDLE_TABLE HtCreateHandleTable(IN PEPROCESS Process)
Definition handle.c:87
HANDLE HtCreateHandle(PHANDLE_TABLE Table, void *Object, uint32_t Access)
Definition handle.c:269
SPINLOCK HandleTableLock
Definition handle.c:27
DOUBLY_LINKED_LIST HandleTableList
Definition handle.c:26
void HtDeleteHandle(PHANDLE_TABLE Table, HANDLE Handle)
Definition handle.c:333
void HtDeleteHandleTable(IN PHANDLE_TABLE Table)
Definition handle.c:434
struct _HANDLE_TABLE HANDLE_TABLE
struct _HANDLE_TABLE_ENTRY * PHANDLE_TABLE_ENTRY
struct _HANDLE_TABLE * PHANDLE_TABLE
int32_t HANDLE
Definition ht.h:59
#define LOW_LEVEL_ENTRIES
Definition ht.h:38
#define TABLE_LEVEL_MASK
Definition ht.h:39
uint64_t Entries[]
Definition mh.h:1
@ NonPagedPool
Definition mm.h:316
#define VirtualPageSize
Definition mm.h:53
struct _SPINLOCK SPINLOCK
#define MT_INVALID_HANDLE
Definition mtstatus.h:36
void ObDereferenceObject(IN void *Object)
Definition ob.c:446
void MmFreePool(IN void *buf)
Definition pool.c:586
void * MmAllocatePoolWithTag(IN enum _POOL_TYPE PoolType, IN size_t NumberOfBytes, IN uint32_t Tag)
Definition pool.c:427
void MsAcquireSpinlock(IN PSPINLOCK lock, IN PIRQL OldIrql)
Definition spinlock.c:13
void MsReleaseSpinlock(IN PSPINLOCK lock, IN IRQL OldIrql)
Definition spinlock.c:45
uint32_t NextFreeTableEntry
Definition ht.h:34
void * Object
Definition ht.h:29
uint32_t GrantedAccess
Definition ht.h:33
SPINLOCK TableLock
Definition ht.h:44
uint64_t TableCode
Definition ht.h:47
PEPROCESS QuotaProcess
Definition ht.h:48
uint32_t FirstFreeHandle
Definition ht.h:52