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
8 #pragma once
9
10 #include <reactos/probe.h>
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 */
67 static
68 __inline
69 NTSTATUS
DefaultSetInfoBufferCheck(_In_ ULONG Class,_In_ const INFORMATION_CLASS_INFO * ClassList,_In_ ULONG ClassListEntries,_In_ PVOID Buffer,_In_ ULONG BufferLength,_In_ 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)
77 {
78 NTSTATUS Status = STATUS_SUCCESS;
79
80 if (Class < ClassListEntries)
81 {
82 if (!(ClassList[Class].Flags & ICIF_SET))
83 {
84 Status = STATUS_INVALID_INFO_CLASS;
85 }
86 else if (ClassList[Class].RequiredSizeSET > 0 &&
87 BufferLength != ClassList[Class].RequiredSizeSET)
88 {
89 if (!(ClassList[Class].Flags & ICIF_SET_SIZE_VARIABLE))
90 {
91 Status = STATUS_INFO_LENGTH_MISMATCH;
92 }
93 }
94
95 if (NT_SUCCESS(Status))
96 {
97 if (PreviousMode != KernelMode)
98 {
99 _SEH2_TRY
100 {
101 ProbeForRead(Buffer,
102 BufferLength,
103 ClassList[Class].AlignmentSET);
104 }
105 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
106 {
107 Status = _SEH2_GetExceptionCode();
108 }
109 _SEH2_END;
110 }
111 }
112 }
113 else
114 Status = STATUS_INVALID_INFO_CLASS;
115
116 return Status;
117 }
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 */
216 static
217 __inline
218 NTSTATUS
DefaultQueryInfoBufferCheck(_In_ ULONG Class,_In_ const INFORMATION_CLASS_INFO * ClassList,_In_ ULONG ClassListEntries,_In_ ULONG Flags,_In_opt_ PVOID Buffer,_In_ ULONG BufferLength,_In_opt_ PULONG ReturnLength,_In_opt_ PULONG_PTR ReturnLengthPtr,_In_ KPROCESSOR_MODE PreviousMode)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)
229 {
230 NTSTATUS Status = STATUS_SUCCESS;
231
232 if (Class < ClassListEntries)
233 {
234 if (!(ClassList[Class].Flags & ICIF_QUERY))
235 {
236 Status = STATUS_INVALID_INFO_CLASS;
237 }
238 else if (ClassList[Class].RequiredSizeQUERY > 0 &&
239 BufferLength != ClassList[Class].RequiredSizeQUERY)
240 {
241 if (!(ClassList[Class].Flags & ICIF_QUERY_SIZE_VARIABLE))
242 {
243 Status = STATUS_INFO_LENGTH_MISMATCH;
244 }
245 }
246
247 if (NT_SUCCESS(Status))
248 {
249 if (PreviousMode != KernelMode)
250 {
251 _SEH2_TRY
252 {
253 if (Buffer != NULL)
254 {
255 if (Flags & ICIF_PROBE_READ)
256 {
257 ProbeForRead(Buffer,
258 BufferLength,
259 ClassList[Class].AlignmentQUERY);
260 }
261 else
262 {
263 ProbeForWrite(Buffer,
264 BufferLength,
265 ClassList[Class].AlignmentQUERY);
266 }
267 }
268
269 if ((Flags & ICIF_FORCE_RETURN_LENGTH_PROBE) || (ReturnLength != NULL))
270 {
271 ProbeForWriteUlong(ReturnLength);
272 }
273
274 if (ReturnLengthPtr != NULL)
275 {
276 ProbeForWrite(ReturnLengthPtr, sizeof(ULONG_PTR), sizeof(ULONG_PTR));
277 }
278 }
279 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
280 {
281 Status = _SEH2_GetExceptionCode();
282 }
283 _SEH2_END;
284 }
285 }
286 }
287 else
288 Status = STATUS_INVALID_INFO_CLASS;
289
290 return Status;
291 }
292