xref: /reactos/ntoskrnl/include/internal/probe.h (revision 1734f297)
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
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
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