1 //
2 // invalid_parameter.cpp
3 //
4 //      Copyright (c) Microsoft Corporation. All rights reserved.
5 //
6 // The invalid parameter handlers and related functionality
7 //
8 #include <corecrt_internal.h>
9 #include <corecrt_internal_ptd_propagation.h>
10 
11 
12 static __crt_state_management::dual_state_global<_invalid_parameter_handler> __acrt_invalid_parameter_handler;
13 
14 
15 
16 #if defined _M_X64 && !defined _UCRT_ENCLAVE_BUILD
17 
capture_current_context(CONTEXT * const context_record)18     static void __cdecl capture_current_context(CONTEXT* const context_record) throw()
19     {
20         ULONG64 ControlPc;
21         ULONG64 EstablisherFrame;
22         ULONG64 ImageBase;
23         PRUNTIME_FUNCTION FunctionEntry;
24         PVOID HandlerData;
25 
26         RtlCaptureContext(context_record);
27 
28         ControlPc = context_record->Rip;
29         FunctionEntry = RtlLookupFunctionEntry(ControlPc, &ImageBase, nullptr);
30 
31         if (FunctionEntry != nullptr)
32         {
33             RtlVirtualUnwind(
34                 UNW_FLAG_NHANDLER,
35                 ImageBase,
36                 ControlPc,
37                 FunctionEntry,
38                 context_record,
39                 &HandlerData,
40                 &EstablisherFrame,
41                 nullptr);
42         }
43     }
44 
45 #endif // _M_X64 && !_UCRT_ENCLAVE_BUILD
46 
47 #if defined _CRT_GLOBAL_STATE_ISOLATION
48 
49     // The legacy Windows CRT (msvcrt) does not terminate the process when an
50     // invalid parameter is passed to a library function.  Turning on this
51     // validation for Windows components would be a big app compat problem.
52     //
53     // For OS components the default behavior will be to ignore the invalid parameters
54     // and that is accomplished by providing an empty IPH.
invalid_parameter_handler_continue(wchar_t const * const,wchar_t const * const,wchar_t const * const,unsigned int,uintptr_t)55     static void __cdecl invalid_parameter_handler_continue(
56         wchar_t const * const,    // pszExpression
57         wchar_t const * const,    // pszFunction
58         wchar_t const * const,    // pszFile
59         unsigned int,              // nLine
60         uintptr_t                   // pReserved
61     ) throw()
62     {
63     }
64 
65 #endif
66 
__acrt_initialize_invalid_parameter_handler(void * const encoded_null)67 extern "C" void __cdecl __acrt_initialize_invalid_parameter_handler(void* const encoded_null)
68 {
69 #if defined _CRT_GLOBAL_STATE_ISOLATION
70     const _invalid_parameter_handler encoded_os_iph = __crt_fast_encode_pointer(invalid_parameter_handler_continue);
71 #endif
72     const _invalid_parameter_handler iph[] =
73     {
74         reinterpret_cast<_invalid_parameter_handler>(encoded_null)
75 #if defined _CRT_GLOBAL_STATE_ISOLATION
76         ,encoded_os_iph
77 #endif
78     };
79 
80     __acrt_invalid_parameter_handler.initialize_from_array(iph);
81 }
82 
83 
84 
85 //-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
86 //
87 // _invalid_parameter
88 //
89 //-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
_invalid_parameter_internal(wchar_t const * const expression,wchar_t const * const function_name,wchar_t const * const file_name,unsigned int const line_number,uintptr_t const reserved,__crt_cached_ptd_host & ptd)90 extern "C" void __cdecl _invalid_parameter_internal(
91     wchar_t const*     const expression,
92     wchar_t const*     const function_name,
93     wchar_t const*     const file_name,
94     unsigned int       const line_number,
95     uintptr_t          const reserved,
96     __crt_cached_ptd_host&   ptd
97     )
98 {
99     __acrt_ptd * const raw_ptd = ptd.get_raw_ptd_noexit();
100     if (raw_ptd && raw_ptd->_thread_local_iph)
101     {
102         raw_ptd->_thread_local_iph(expression, function_name, file_name, line_number, reserved);
103         return;
104     }
105 
106     _invalid_parameter_handler const global_handler = __crt_fast_decode_pointer(__acrt_invalid_parameter_handler.value(ptd));
107     if (global_handler)
108     {
109         global_handler(expression, function_name, file_name, line_number, reserved);
110         return;
111     }
112 
113     _invoke_watson(expression, function_name, file_name, line_number, reserved);
114 }
115 
_invalid_parameter(wchar_t const * const expression,wchar_t const * const function_name,wchar_t const * const file_name,unsigned int const line_number,uintptr_t const reserved)116 extern "C" void __cdecl _invalid_parameter(
117     wchar_t const* const expression,
118     wchar_t const* const function_name,
119     wchar_t const* const file_name,
120     unsigned int   const line_number,
121     uintptr_t      const reserved
122     )
123 {
124     __crt_cached_ptd_host ptd;
125     return _invalid_parameter_internal(expression, function_name, file_name, line_number, reserved, ptd);
126 }
127 
_invalid_parameter_noinfo()128 extern "C" void __cdecl _invalid_parameter_noinfo()
129 {
130     _invalid_parameter(nullptr, nullptr, nullptr, 0, 0);
131 }
132 
133 // This is used by inline code in the C++ Standard Library and the SafeInt
134 // library.  Because it is __declspec(noreturn), the compiler can better
135 // optimize use of the invalid parameter handler for inline code.
_invalid_parameter_noinfo_noreturn()136 extern "C" __declspec(noreturn) void __cdecl _invalid_parameter_noinfo_noreturn()
137 {
138     _invalid_parameter(nullptr, nullptr, nullptr, 0, 0);
139     _invoke_watson    (nullptr, nullptr, nullptr, 0, 0);
140 }
141 
142 
143 
144 //-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
145 //
146 // _invoke_watson
147 //
148 //-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
149 #if (defined _M_IX86 || defined _M_X64) && !defined _UCRT_ENCLAVE_BUILD
150 
__acrt_call_reportfault(int const debugger_hook_code,DWORD const _exception_code,DWORD const _exception_flags)151     extern "C" void __cdecl __acrt_call_reportfault(
152         int   const debugger_hook_code,
153         DWORD const _exception_code,
154         DWORD const _exception_flags
155         )
156     {
157         // Notify the debugger if attached.
158         if (debugger_hook_code != _CRT_DEBUGGER_IGNORE)
159         {
160             _CRT_DEBUGGER_HOOK(debugger_hook_code);
161         }
162 
163         // Fake an exception to call report fault:
164         EXCEPTION_RECORD   ExceptionRecord{};
165         CONTEXT            ContextRecord{};
166         EXCEPTION_POINTERS ExceptionPointers = {&ExceptionRecord, &ContextRecord};
167 
168         #ifdef _M_IX86
169         #if defined(__GNUC__) || defined(__clang__)
170         __asm__ __volatile__(
171             "movl %%eax, %[CxEax]\n\t"
172             "movl %%ecx, %[CxEcx]\n\t"
173             "movl %%edx, %[CxEdx]\n\t"
174             "movl %%ebx, %[CxEbx]\n\t"
175             "movl %%esi, %[CxEsi]\n\t"
176             "movl %%edi, %[CxEdi]\n\t"
177             : [CxEax] "=m" (ContextRecord.Eax),
178               [CxEcx] "=m" (ContextRecord.Ecx),
179               [CxEdx] "=m" (ContextRecord.Edx),
180               [CxEbx] "=m" (ContextRecord.Ebx),
181               [CxEsi] "=m" (ContextRecord.Esi),
182               [CxEdi] "=m" (ContextRecord.Edi));
183         __asm__ __volatile__(
184             "movw %%ss, %[CxSegSs]\n\t"
185             "movw %%cs, %[CxSegCs]\n\t"
186             "movw %%ds, %[CxSegDs]\n\t"
187             "movw %%es, %[CxSegEs]\n\t"
188             "movw %%fs, %[CxSegFs]\n\t"
189             "movw %%gs, %[CxSegGs]\n\t"
190             : [CxSegSs] "=m" (ContextRecord.SegSs),
191               [CxSegCs] "=m" (ContextRecord.SegCs),
192               [CxSegDs] "=m" (ContextRecord.SegDs),
193               [CxSegEs] "=m" (ContextRecord.SegEs),
194               [CxSegFs] "=m" (ContextRecord.SegFs),
195               [CxSegGs] "=m" (ContextRecord.SegGs));
196         __asm__ __volatile__(
197             "pushfl\n\t"
198             "popl %[CxEFlags]\n\t"
199             : [CxEFlags] "=m" (ContextRecord.EFlags));
200         #else // ^^^ __GNUC__ ^^^ // vvv !__GNUC__ vvv //
201         __asm
202         {
203             mov dword ptr [ContextRecord.Eax  ], eax
204             mov dword ptr [ContextRecord.Ecx  ], ecx
205             mov dword ptr [ContextRecord.Edx  ], edx
206             mov dword ptr [ContextRecord.Ebx  ], ebx
207             mov dword ptr [ContextRecord.Esi  ], esi
208             mov dword ptr [ContextRecord.Edi  ], edi
209             mov word ptr  [ContextRecord.SegSs], ss
210             mov word ptr  [ContextRecord.SegCs], cs
211             mov word ptr  [ContextRecord.SegDs], ds
212             mov word ptr  [ContextRecord.SegEs], es
213             mov word ptr  [ContextRecord.SegFs], fs
214             mov word ptr  [ContextRecord.SegGs], gs
215             pushfd
216             pop [ContextRecord.EFlags]
217         }
218         #endif // !__GNUC__
219 
220         ContextRecord.ContextFlags = CONTEXT_CONTROL;
221 
222         ContextRecord.Eip = (ULONG)_ReturnAddress();
223         ContextRecord.Esp = (ULONG)_AddressOfReturnAddress();
224         ContextRecord.Ebp = *((ULONG *)_AddressOfReturnAddress()-1);
225 
226         #else // ^^^ _M_IX86 ^^^ // vvv _M_X64 vvv //
227 
228         capture_current_context(&ContextRecord);
229         ContextRecord.Rip = (ULONGLONG)_ReturnAddress();
230         ContextRecord.Rsp = (ULONGLONG)_AddressOfReturnAddress()+8;
231 
232         #endif // _M_X64
233 
234         ExceptionRecord.ExceptionCode    = _exception_code;
235         ExceptionRecord.ExceptionFlags   = _exception_flags;
236         ExceptionRecord.ExceptionAddress = _ReturnAddress();
237 
238         BOOL const was_debugger_present = IsDebuggerPresent();
239 
240         // Raises an exception that bypasses all exception handlers:
241 #pragma warning(suppress:__WARNING_SETUNHANDLEDEXCEPTIONFILTER_USE) // 28725 Use Watson instead of this SetUnhandledExceptionFilter.
242         SetUnhandledExceptionFilter(nullptr);
243         DWORD const handler_result = UnhandledExceptionFilter(&ExceptionPointers);
244 
245         // If no handler was found and no debugger was previously attached, then
246         // execution must stop into the debugger hook:
247         if (handler_result == EXCEPTION_CONTINUE_SEARCH && !was_debugger_present && debugger_hook_code != _CRT_DEBUGGER_IGNORE)
248         {
249             _CRT_DEBUGGER_HOOK(debugger_hook_code);
250         }
251     }
252 
_invoke_watson(wchar_t const * const expression,wchar_t const * const function_name,wchar_t const * const file_name,unsigned int const line_number,uintptr_t const reserved)253     extern "C" __declspec(noreturn) void __cdecl _invoke_watson(
254         wchar_t const* const expression,
255         wchar_t const* const function_name,
256         wchar_t const* const file_name,
257         unsigned int   const line_number,
258         uintptr_t      const reserved
259         )
260     {
261         UNREFERENCED_PARAMETER(expression   );
262         UNREFERENCED_PARAMETER(function_name);
263         UNREFERENCED_PARAMETER(file_name    );
264         UNREFERENCED_PARAMETER(line_number  );
265         UNREFERENCED_PARAMETER(reserved     );
266 
267         if (IsProcessorFeaturePresent(PF_FASTFAIL_AVAILABLE))
268         {
269             __fastfail(FAST_FAIL_INVALID_ARG);
270         }
271 
272         // Otherwise, raise a fast-fail exception and termintae the process:
273         __acrt_call_reportfault(
274             _CRT_DEBUGGER_INVALIDPARAMETER,
275             STATUS_INVALID_CRUNTIME_PARAMETER,
276             EXCEPTION_NONCONTINUABLE);
277 
278         TerminateProcess(GetCurrentProcess(), STATUS_INVALID_CRUNTIME_PARAMETER);
279         UNREACHABLE;
280     }
281 
282 #else // ^^^ (_M_IX86 || _M_X64) && !_UCRT_ENCLAVE_BUILD ^^^ // vvv Newer Architectures vvv //
283 
_invoke_watson(wchar_t const * const expression,wchar_t const * const function_name,wchar_t const * const file_name,unsigned int const line_number,uintptr_t const reserved)284     extern "C" __declspec(noreturn) void __cdecl _invoke_watson(
285         wchar_t const* const expression,
286         wchar_t const* const function_name,
287         wchar_t const* const file_name,
288         unsigned int   const line_number,
289         uintptr_t      const reserved
290         )
291     {
292         UNREFERENCED_PARAMETER(expression   );
293         UNREFERENCED_PARAMETER(function_name);
294         UNREFERENCED_PARAMETER(file_name    );
295         UNREFERENCED_PARAMETER(line_number  );
296         UNREFERENCED_PARAMETER(reserved     );
297 
298         __fastfail(FAST_FAIL_INVALID_ARG);
299     }
300 
301 #endif
302 
303 
304 
305 //-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
306 //
307 // Handler Accessors
308 //
309 //-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
_set_invalid_parameter_handler(_invalid_parameter_handler const new_handler)310 extern "C" _invalid_parameter_handler __cdecl _set_invalid_parameter_handler(_invalid_parameter_handler const new_handler)
311 {
312     _invalid_parameter_handler const old_handler = __crt_fast_decode_pointer(__acrt_invalid_parameter_handler.value());
313     __acrt_invalid_parameter_handler.value() = __crt_fast_encode_pointer(new_handler);
314     return old_handler;
315 }
316 
_get_invalid_parameter_handler()317 extern "C" _invalid_parameter_handler __cdecl _get_invalid_parameter_handler()
318 {
319     return __crt_fast_decode_pointer(__acrt_invalid_parameter_handler.value());
320 }
321 
322 
323 
_set_thread_local_invalid_parameter_handler(_invalid_parameter_handler const new_handler)324 extern "C" _invalid_parameter_handler __cdecl _set_thread_local_invalid_parameter_handler(_invalid_parameter_handler const new_handler)
325 {
326     __acrt_ptd* const ptd = __acrt_getptd();
327 
328     _invalid_parameter_handler const old_handler = ptd->_thread_local_iph;
329     ptd->_thread_local_iph = new_handler;
330     return old_handler;
331 }
332 
_get_thread_local_invalid_parameter_handler()333 extern "C" _invalid_parameter_handler __cdecl _get_thread_local_invalid_parameter_handler()
334 {
335     __acrt_ptd* const ptd = __acrt_getptd_noexit();
336     if (!ptd)
337     {
338         return nullptr;
339     }
340 
341     return ptd->_thread_local_iph;
342 }
343