Reactos

[NTOSKRNL] Force a probe against ReturnLength on query & Misc ICIF stuff

NtQueryInformationToken is by far the only system call in NT where ReturnLength simply cannot be optional. On Windows this parameter is always probed and an argument to NULL directly leads to an access violation exception.
This is due to the fact of how tokens work, as its information contents (token user, owner, primary group, et al) are dynamic and can vary throughout over time in memory.

What happens on current ReactOS master however is that ReturnLength is only probed if the parameter is not NULL. On a NULL case scenario the probing checks succeed and NtQueryInformationToken fails later. For this, just get rid of CompleteProbing
parameter and opt in for a bit mask flag based approach, with ICIF_FORCE_RETURN_LENGTH_PROBE being set on DefaultQueryInfoBufferCheck which NtQueryInformationToken calls it to do sanity checks.

In addition to that...

- Document the ICIF probe helpers
- Annotate the ICIF prope helpers with SAL
- With the riddance of CompleteProbing and adoption of flags based approach, add ICIF_PROBE_READ_WRITE and ICIF_PROBE_READ flags alongside with ICIF_FORCE_RETURN_LENGTH_PROBE

+200 -35
+2 -2
ntoskrnl/ex/event.c
··· 329 329 ExEventInfoClass, 330 330 sizeof(ExEventInfoClass) / 331 331 sizeof(ExEventInfoClass[0]), 332 + ICIF_PROBE_READ_WRITE, 332 333 EventInformation, 333 334 EventInformationLength, 334 335 ReturnLength, 335 336 NULL, 336 - PreviousMode, 337 - TRUE); 337 + PreviousMode); 338 338 if(!NT_SUCCESS(Status)) 339 339 { 340 340 /* Invalid buffers */
+2 -2
ntoskrnl/ex/mutant.c
··· 239 239 ExMutantInfoClass, 240 240 sizeof(ExMutantInfoClass) / 241 241 sizeof(ExMutantInfoClass[0]), 242 + ICIF_PROBE_READ_WRITE, 242 243 MutantInformation, 243 244 MutantInformationLength, 244 245 ResultLength, 245 246 NULL, 246 - PreviousMode, 247 - TRUE); 247 + PreviousMode); 248 248 if(!NT_SUCCESS(Status)) 249 249 { 250 250 DPRINT("NtQueryMutant() failed, Status: 0x%x\n", Status);
+2 -2
ntoskrnl/ex/sem.c
··· 235 235 ExSemaphoreInfoClass, 236 236 sizeof(ExSemaphoreInfoClass) / 237 237 sizeof(ExSemaphoreInfoClass[0]), 238 + ICIF_PROBE_READ_WRITE, 238 239 SemaphoreInformation, 239 240 SemaphoreInformationLength, 240 241 ReturnLength, 241 242 NULL, 242 - PreviousMode, 243 - TRUE); 243 + PreviousMode); 244 244 if (!NT_SUCCESS(Status)) 245 245 { 246 246 /* Invalid buffers */
+2 -2
ntoskrnl/ex/timer.c
··· 532 532 ExTimerInfoClass, 533 533 sizeof(ExTimerInfoClass) / 534 534 sizeof(ExTimerInfoClass[0]), 535 + ICIF_PROBE_READ_WRITE, 535 536 TimerInformation, 536 537 TimerInformationLength, 537 538 ReturnLength, 538 539 NULL, 539 - PreviousMode, 540 - TRUE); 540 + PreviousMode); 541 541 if (!NT_SUCCESS(Status)) return Status; 542 542 543 543 /* Get the Timer Object */
+5 -2
ntoskrnl/include/internal/icif.h
··· 2 2 * PROJECT: ReactOS Kernel 3 3 * LICENSE: GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later) 4 4 * PURPOSE: Internal header for information classes info interface 5 - * COPYRIGHT: Copyright ??? 6 - * Copyright 2020-2021 George Bișoc <george.bisoc@reactos.org> 5 + * COPYRIGHT: Copyright 2020-2022 George Bișoc <george.bisoc@reactos.org> 7 6 */ 8 7 9 8 #pragma once ··· 21 20 #define ICIF_QUERY_SIZE_VARIABLE 0x4 22 21 #define ICIF_SET_SIZE_VARIABLE 0x8 23 22 #define ICIF_SIZE_VARIABLE (ICIF_QUERY_SIZE_VARIABLE | ICIF_SET_SIZE_VARIABLE) 23 + 24 + #define ICIF_PROBE_READ_WRITE 0x0 25 + #define ICIF_PROBE_READ 0x1 26 + #define ICIF_FORCE_RETURN_LENGTH_PROBE 0x2 24 27 25 28 typedef struct _INFORMATION_CLASS_INFO 26 29 {
+179 -17
ntoskrnl/include/internal/probe.h
··· 1 + /* 2 + * PROJECT: ReactOS Kernel 3 + * LICENSE: GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later) 4 + * PURPOSE: Internal header containing information class probing helpers 5 + * COPYRIGHT: Copyright 2022 George Bișoc <george.bisoc@reactos.org> 6 + */ 7 + 1 8 #pragma once 2 9 3 10 #include <reactos/probe.h> 4 11 12 + /** 13 + * @brief 14 + * Probe helper that validates the provided parameters whenever 15 + * a NtSet*** system call is invoked from user or kernel mode. 16 + * 17 + * @param[in] Class 18 + * The specific class information that the caller explicitly 19 + * requested information to be set into an object. 20 + * 21 + * @param[in] ClassList 22 + * An internal INFORMATION_CLASS_INFO consisting of a list array 23 + * of information classes checked against the requested information 24 + * classes given in Class parameter. 25 + * 26 + * @param[in] ClassListEntries 27 + * Specifies the number of class entries in an array, provided by 28 + * the ClassList parameter. 29 + * 30 + * @param[in] Buffer 31 + * A pointer to an arbitrary data content in memory to be validated. 32 + * Such pointer points to the actual arbitrary information class buffer 33 + * to be set into the object. This buffer is validated only if the 34 + * calling processor mode is UM (aka user mode). 35 + * 36 + * @param[in] BufferLength 37 + * The length of the buffer pointed by the Buffer parameter, whose size 38 + * is in bytes. 39 + * 40 + * @param[in] PreviousMode 41 + * The processor calling level mode. This level mode determines the procedure 42 + * of probing validation in action. If the level calling mode is KM (aka kernel mode) 43 + * this function will only validate the properties of the information class itself 44 + * such as the required information length size. If the calling mode is UM, the 45 + * pointer buffer provided by Buffer parameter is also validated. 46 + * 47 + * @return 48 + * The outcome of the probe validation is based upon the returned NTSTATUS code. 49 + * STATUS_SUCCESS is returned if the validation succeeded. Otherwise, one of the 50 + * following failure status codes is returned: 51 + * 52 + * STATUS_INVALID_INFO_CLASS -- Indicates the given information class is not a valid 53 + * valid SET class (ICIF_SET flag is not set to the corresponding information class) 54 + * or the actual class is not present in the class list array. 55 + * 56 + * STATUS_INFO_LENGTH_MISMATCH -- Indicates the information length doesn't match with 57 + * the one that the information class itself expects. This is the case with classes 58 + * ICIF_SET_SIZE_VARIABLE is not set, which means that the class requires a fixed 59 + * length size. 60 + * 61 + * STATUS_ACCESS_VIOLATION -- Indicates the buffer is not within the user mode probe 62 + * address range. The function will raise an exception. 63 + * 64 + * STATUS_DATATYPE_MISALIGNMENT -- Indicates the address of the buffer in memory is 65 + * not aligned to the required alignment set. 66 + */ 5 67 static 6 68 __inline 7 69 NTSTATUS 8 - DefaultSetInfoBufferCheck(ULONG Class, 9 - const INFORMATION_CLASS_INFO *ClassList, 10 - ULONG ClassListEntries, 11 - PVOID Buffer, 12 - ULONG BufferLength, 13 - KPROCESSOR_MODE PreviousMode) 70 + DefaultSetInfoBufferCheck( 71 + _In_ ULONG Class, 72 + _In_ const INFORMATION_CLASS_INFO *ClassList, 73 + _In_ ULONG ClassListEntries, 74 + _In_ PVOID Buffer, 75 + _In_ ULONG BufferLength, 76 + _In_ KPROCESSOR_MODE PreviousMode) 14 77 { 15 78 NTSTATUS Status = STATUS_SUCCESS; 16 79 ··· 53 116 return Status; 54 117 } 55 118 119 + /** 120 + * @brief 121 + * Probe helper that validates the provided parameters whenever 122 + * a NtQuery*** system call is invoked from user or kernel mode. 123 + * 124 + * @param[in] Class 125 + * The specific class information that the caller explicitly 126 + * requested information to be queried from an object. 127 + * 128 + * @param[in] ClassList 129 + * An internal INFORMATION_CLASS_INFO consisting of a list array 130 + * of information classes checked against the requested information 131 + * classes given in Class parameter. 132 + * 133 + * @param[in] ClassListEntries 134 + * Specifies the number of class entries in an array, provided by 135 + * the ClassList parameter. 136 + * 137 + * @param[in] Flags 138 + * Specifies a bit mask flag that influences how the query probe 139 + * validation must be performed against Buffer and ReturnLength 140 + * parameters. For further information in regard of this parameter, 141 + * see remarks. 142 + * 143 + * @param[in] Buffer 144 + * A pointer to an arbitrary data content in memory to be validated. 145 + * Such parameter must be an initialized variable where the queried 146 + * information is going to be received into this pointer. If the calling 147 + * processor mode is UM (aka user mode) this parameter is validated. 148 + * This parameter can be NULL (see remarks for more details). 149 + * 150 + * @param[in] BufferLength 151 + * The length of the buffer pointed by the Buffer parameter, whose size 152 + * is in bytes. If the Buffer parameter is NULL, this parameter can be 0. 153 + * 154 + * @param[in] ReturnLength 155 + * The returned length of the buffer whose size is in bytes. If Buffer is 156 + * NULL as well as BufferLength is 0, this parameter receives the actual 157 + * return length needed to store the buffer in memory space. If the 158 + * processor level calling mode is UM, this parameter is validated. 159 + * If ICIF_FORCE_RETURN_LENGTH_PROBE is specified in Flags parameter, 160 + * ReturnLength mustn't be NULL (see remarks). Otherwise it can be NULL. 161 + * 162 + * @param[in] ReturnLengthPtr 163 + * This parameter is of the same nature as the ReturnLength one, with the 164 + * difference being that the type parameter can be a ULONG on x86 systems 165 + * or a ULONGLONG on AMD64 systems. If the processor level calling mode is 166 + * UM, this parameter is validated. This parameter is currently unused. 167 + * 168 + * @param[in] PreviousMode 169 + * The processor calling level mode. This level mode determines the procedure 170 + * of probing validation in action. If the level calling mode is KM (aka kernel mode) 171 + * this function will only validate the properties of the information class itself 172 + * such as the required information length size. If the calling mode is UM, the 173 + * pointer buffer provided by Buffer parameter is also validated as well as 174 + * the return length parameter. 175 + * 176 + * @return 177 + * The outcome of the probe validation is based upon the returned NTSTATUS code. 178 + * STATUS_SUCCESS is returned if the validation succeeded. Otherwise, one of the 179 + * following failure status codes is returned: 180 + * 181 + * STATUS_INVALID_INFO_CLASS -- Indicates the given information class is not a valid 182 + * QUERY class (ICIF_QUERY flag is not set to the corresponding information class) 183 + * or the actual class is not present in the class list array. 184 + * 185 + * STATUS_INFO_LENGTH_MISMATCH -- Indicates the information length doesn't match with the 186 + * one that the information class itself expects. This is the case with classes where 187 + * ICIF_QUERY_SIZE_VARIABLE is not set, which means that the class requires a fixed length size. 188 + * 189 + * STATUS_ACCESS_VIOLATION -- Indicates the buffer is not within the user mode probe address range 190 + * or the buffer variable is not writable (see remarks). The function will raise an exception. 191 + * 192 + * STATUS_DATATYPE_MISALIGNMENT -- Indicates the address of the buffer in memory is not 193 + * aligned to the required alignment set. 194 + * 195 + * @remarks 196 + * The probing of Buffer and ReturnLength are influenced based on the probe flags 197 + * pointed by Flags parameter. The following flags are: 198 + * 199 + * ICIF_PROBE_READ_WRITE -- This flag explicitly tells the function to do a read and 200 + * write probe against Buffer parameter. ProbeForWrite is invoked in this case. 201 + * This is the default mechanism. 202 + * 203 + * ICIF_PROBE_READ -- This flag explicitly tells the function to do a read probe against 204 + * Buffer parameter only, that is, the function does not probe if the parameter is actually 205 + * writable. ProbeForRead is invoked in this case. 206 + * 207 + * ICIF_FORCE_RETURN_LENGTH_PROBE -- If this flag is set, the function will force probe 208 + * the ReturnLength parameter. In this scenario if ReturnLength is NULL a STATUS_ACCESS_VIOLATION 209 + * exception is raised. NtQueryInformationToken is the only NT system call where ReturnLength 210 + * has to be properly initialized and not NULL. 211 + * 212 + * Buffer parameter can be NULL if the caller does not want to actually query a certain information 213 + * from an object. This is typically with query NT syscalls where a caller has to query the actual 214 + * buffer length needed to store the queried information before doing a "real" query in the first place. 215 + */ 56 216 static 57 217 __inline 58 218 NTSTATUS 59 - DefaultQueryInfoBufferCheck(ULONG Class, 60 - const INFORMATION_CLASS_INFO *ClassList, 61 - ULONG ClassListEntries, 62 - PVOID Buffer, 63 - ULONG BufferLength, 64 - PULONG ReturnLength, 65 - PULONG_PTR ReturnLengthPtr, 66 - KPROCESSOR_MODE PreviousMode, 67 - BOOLEAN CompleteProbing) 219 + DefaultQueryInfoBufferCheck( 220 + _In_ ULONG Class, 221 + _In_ const INFORMATION_CLASS_INFO *ClassList, 222 + _In_ ULONG ClassListEntries, 223 + _In_ ULONG Flags, 224 + _In_opt_ PVOID Buffer, 225 + _In_ ULONG BufferLength, 226 + _In_opt_ PULONG ReturnLength, 227 + _In_opt_ PULONG_PTR ReturnLengthPtr, 228 + _In_ KPROCESSOR_MODE PreviousMode) 68 229 { 69 230 NTSTATUS Status = STATUS_SUCCESS; 70 231 ··· 91 252 { 92 253 if (Buffer != NULL) 93 254 { 94 - if (!CompleteProbing) 255 + if (Flags & ICIF_PROBE_READ) 95 256 { 96 257 ProbeForRead(Buffer, 97 258 BufferLength, ··· 105 266 } 106 267 } 107 268 108 - if (ReturnLength != NULL) 269 + if ((Flags & ICIF_FORCE_RETURN_LENGTH_PROBE) || (ReturnLength != NULL)) 109 270 { 110 271 ProbeForWriteUlong(ReturnLength); 111 272 } 273 + 112 274 if (ReturnLengthPtr != NULL) 113 275 { 114 276 ProbeForWrite(ReturnLengthPtr, sizeof(ULONG_PTR), sizeof(ULONG_PTR));
+2 -2
ntoskrnl/io/iomgr/iocomp.c
··· 395 395 IoCompletionInfoClass, 396 396 sizeof(IoCompletionInfoClass) / 397 397 sizeof(IoCompletionInfoClass[0]), 398 + ICIF_PROBE_READ_WRITE, 398 399 IoCompletionInformation, 399 400 IoCompletionInformationLength, 400 401 ResultLength, 401 402 NULL, 402 - PreviousMode, 403 - TRUE); 403 + PreviousMode); 404 404 if (!NT_SUCCESS(Status)) return Status; 405 405 406 406 /* Get the Object */
+4 -4
ntoskrnl/ps/query.c
··· 90 90 Status = DefaultQueryInfoBufferCheck(ProcessInformationClass, 91 91 PsProcessInfoClass, 92 92 RTL_NUMBER_OF(PsProcessInfoClass), 93 + ICIF_PROBE_READ, 93 94 ProcessInformation, 94 95 ProcessInformationLength, 95 96 ReturnLength, 96 97 NULL, 97 - PreviousMode, 98 - FALSE); 98 + PreviousMode); 99 99 if (!NT_SUCCESS(Status)) 100 100 { 101 101 DPRINT1("NtQueryInformationProcess(): Information verification class failed! (Status -> 0x%lx, ProcessInformationClass -> %lx)\n", Status, ProcessInformationClass); ··· 2643 2643 Status = DefaultQueryInfoBufferCheck(ThreadInformationClass, 2644 2644 PsThreadInfoClass, 2645 2645 RTL_NUMBER_OF(PsThreadInfoClass), 2646 + ICIF_PROBE_READ, 2646 2647 ThreadInformation, 2647 2648 ThreadInformationLength, 2648 2649 ReturnLength, 2649 2650 NULL, 2650 - PreviousMode, 2651 - FALSE); 2651 + PreviousMode); 2652 2652 if (!NT_SUCCESS(Status)) 2653 2653 { 2654 2654 DPRINT1("NtQueryInformationThread(): Information verification class failed! (Status -> 0x%lx , ThreadInformationClass -> %lx)\n", Status, ThreadInformationClass);
+2 -2
ntoskrnl/se/tokencls.c
··· 492 492 Status = DefaultQueryInfoBufferCheck(TokenInformationClass, 493 493 SeTokenInformationClass, 494 494 RTL_NUMBER_OF(SeTokenInformationClass), 495 + ICIF_PROBE_READ_WRITE | ICIF_FORCE_RETURN_LENGTH_PROBE, 495 496 TokenInformation, 496 497 TokenInformationLength, 497 498 ReturnLength, 498 499 NULL, 499 - PreviousMode, 500 - TRUE); 500 + PreviousMode); 501 501 if (!NT_SUCCESS(Status)) 502 502 { 503 503 DPRINT("NtQueryInformationToken() failed, Status: 0x%x\n", Status);