//
// invalid_parameter.cpp
//
//      Copyright (c) Microsoft Corporation. All rights reserved.
//
// The invalid parameter handlers and related functionality
//
#include <corecrt_internal.h>
#include <corecrt_internal_ptd_propagation.h>


static __crt_state_management::dual_state_global<_invalid_parameter_handler> __acrt_invalid_parameter_handler;



#if defined _M_X64 && !defined _UCRT_ENCLAVE_BUILD

    static void __cdecl capture_current_context(CONTEXT* const context_record) throw()
    {
        ULONG64 ControlPc;
        ULONG64 EstablisherFrame;
        ULONG64 ImageBase;
        PRUNTIME_FUNCTION FunctionEntry;
        PVOID HandlerData;

        RtlCaptureContext(context_record);

        ControlPc = context_record->Rip;
        FunctionEntry = RtlLookupFunctionEntry(ControlPc, &ImageBase, nullptr);

        if (FunctionEntry != nullptr)
        {
            RtlVirtualUnwind(
                UNW_FLAG_NHANDLER,
                ImageBase,
                ControlPc,
                FunctionEntry,
                context_record,
                &HandlerData,
                &EstablisherFrame,
                nullptr);
        }
    }

#endif // _M_X64 && !_UCRT_ENCLAVE_BUILD

#if defined _CRT_GLOBAL_STATE_ISOLATION

    // The legacy Windows CRT (msvcrt) does not terminate the process when an
    // invalid parameter is passed to a library function.  Turning on this
    // validation for Windows components would be a big app compat problem.
    //
    // For OS components the default behavior will be to ignore the invalid parameters
    // and that is accomplished by providing an empty IPH.
    static void __cdecl invalid_parameter_handler_continue(
        wchar_t const * const,    // pszExpression
        wchar_t const * const,    // pszFunction
        wchar_t const * const,    // pszFile
        unsigned int,              // nLine
        uintptr_t                   // pReserved
    ) throw()
    {
    }

#endif

extern "C" void __cdecl __acrt_initialize_invalid_parameter_handler(void* const encoded_null)
{
#if defined _CRT_GLOBAL_STATE_ISOLATION
    const _invalid_parameter_handler encoded_os_iph = __crt_fast_encode_pointer(invalid_parameter_handler_continue);
#endif
    const _invalid_parameter_handler iph[] =
    {
        reinterpret_cast<_invalid_parameter_handler>(encoded_null)
#if defined _CRT_GLOBAL_STATE_ISOLATION
        ,encoded_os_iph
#endif
    };

    __acrt_invalid_parameter_handler.initialize_from_array(iph);
}



//-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
//
// _invalid_parameter
//
//-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
extern "C" void __cdecl _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
    )
{
    __acrt_ptd * const raw_ptd = ptd.get_raw_ptd_noexit();
    if (raw_ptd && raw_ptd->_thread_local_iph)
    {
        raw_ptd->_thread_local_iph(expression, function_name, file_name, line_number, reserved);
        return;
    }

    _invalid_parameter_handler const global_handler = __crt_fast_decode_pointer(__acrt_invalid_parameter_handler.value(ptd));
    if (global_handler)
    {
        global_handler(expression, function_name, file_name, line_number, reserved);
        return;
    }

    _invoke_watson(expression, function_name, file_name, line_number, reserved);
}

extern "C" void __cdecl _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
    )
{
    __crt_cached_ptd_host ptd;
    return _invalid_parameter_internal(expression, function_name, file_name, line_number, reserved, ptd);
}

extern "C" void __cdecl _invalid_parameter_noinfo()
{
    _invalid_parameter(nullptr, nullptr, nullptr, 0, 0);
}

// This is used by inline code in the C++ Standard Library and the SafeInt
// library.  Because it is __declspec(noreturn), the compiler can better
// optimize use of the invalid parameter handler for inline code.
extern "C" __declspec(noreturn) void __cdecl _invalid_parameter_noinfo_noreturn()
{
    _invalid_parameter(nullptr, nullptr, nullptr, 0, 0);
    _invoke_watson    (nullptr, nullptr, nullptr, 0, 0);
}



//-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
//
// _invoke_watson
//
//-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
#if (defined _M_IX86 || defined _M_X64) && !defined _UCRT_ENCLAVE_BUILD

    extern "C" void __cdecl __acrt_call_reportfault(
        int   const debugger_hook_code,
        DWORD const _exception_code,
        DWORD const _exception_flags
        )
    {
        // Notify the debugger if attached.
        if (debugger_hook_code != _CRT_DEBUGGER_IGNORE)
        {
            _CRT_DEBUGGER_HOOK(debugger_hook_code);
        }

        // Fake an exception to call report fault:
        EXCEPTION_RECORD   ExceptionRecord{};
        CONTEXT            ContextRecord{};
        EXCEPTION_POINTERS ExceptionPointers = {&ExceptionRecord, &ContextRecord};

        #ifdef _M_IX86

        __asm
        {
            mov dword ptr [ContextRecord.Eax  ], eax
            mov dword ptr [ContextRecord.Ecx  ], ecx
            mov dword ptr [ContextRecord.Edx  ], edx
            mov dword ptr [ContextRecord.Ebx  ], ebx
            mov dword ptr [ContextRecord.Esi  ], esi
            mov dword ptr [ContextRecord.Edi  ], edi
            mov word ptr  [ContextRecord.SegSs], ss
            mov word ptr  [ContextRecord.SegCs], cs
            mov word ptr  [ContextRecord.SegDs], ds
            mov word ptr  [ContextRecord.SegEs], es
            mov word ptr  [ContextRecord.SegFs], fs
            mov word ptr  [ContextRecord.SegGs], gs
            pushfd
            pop [ContextRecord.EFlags]
        }

        ContextRecord.ContextFlags = CONTEXT_CONTROL;

        ContextRecord.Eip = (ULONG)_ReturnAddress();
        ContextRecord.Esp = (ULONG)_AddressOfReturnAddress();
        ContextRecord.Ebp = *((ULONG *)_AddressOfReturnAddress()-1);

        #else // ^^^ _M_IX86 ^^^ // vvv _M_X64 vvv //

        capture_current_context(&ContextRecord);
        ContextRecord.Rip = (ULONGLONG)_ReturnAddress();
        ContextRecord.Rsp = (ULONGLONG)_AddressOfReturnAddress()+8;

        #endif // _M_X64

        ExceptionRecord.ExceptionCode    = _exception_code;
        ExceptionRecord.ExceptionFlags   = _exception_flags;
        ExceptionRecord.ExceptionAddress = _ReturnAddress();

        BOOL const was_debugger_present = IsDebuggerPresent();

        // Raises an exception that bypasses all exception handlers:
#pragma warning(suppress:__WARNING_SETUNHANDLEDEXCEPTIONFILTER_USE) // 28725 Use Watson instead of this SetUnhandledExceptionFilter.
        SetUnhandledExceptionFilter(nullptr);
        DWORD const handler_result = UnhandledExceptionFilter(&ExceptionPointers);

        // If no handler was found and no debugger was previously attached, then
        // execution must stop into the debugger hook:
        if (handler_result == EXCEPTION_CONTINUE_SEARCH && !was_debugger_present && debugger_hook_code != _CRT_DEBUGGER_IGNORE)
        {
            _CRT_DEBUGGER_HOOK(debugger_hook_code);
        }
    }

    extern "C" __declspec(noreturn) void __cdecl _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
        )
    {
        UNREFERENCED_PARAMETER(expression   );
        UNREFERENCED_PARAMETER(function_name);
        UNREFERENCED_PARAMETER(file_name    );
        UNREFERENCED_PARAMETER(line_number  );
        UNREFERENCED_PARAMETER(reserved     );

        if (IsProcessorFeaturePresent(PF_FASTFAIL_AVAILABLE))
        {
            __fastfail(FAST_FAIL_INVALID_ARG);
        }

        // Otherwise, raise a fast-fail exception and termintae the process:
        __acrt_call_reportfault(
            _CRT_DEBUGGER_INVALIDPARAMETER,
            STATUS_INVALID_CRUNTIME_PARAMETER,
            EXCEPTION_NONCONTINUABLE);

        TerminateProcess(GetCurrentProcess(), STATUS_INVALID_CRUNTIME_PARAMETER);
    }

#else // ^^^ (_M_IX86 || _M_X64) && !_UCRT_ENCLAVE_BUILD ^^^ // vvv Newer Architectures vvv //

    extern "C" __declspec(noreturn) void __cdecl _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
        )
    {
        UNREFERENCED_PARAMETER(expression   );
        UNREFERENCED_PARAMETER(function_name);
        UNREFERENCED_PARAMETER(file_name    );
        UNREFERENCED_PARAMETER(line_number  );
        UNREFERENCED_PARAMETER(reserved     );

        __fastfail(FAST_FAIL_INVALID_ARG);
    }

#endif



//-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
//
// Handler Accessors
//
//-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
extern "C" _invalid_parameter_handler __cdecl _set_invalid_parameter_handler(_invalid_parameter_handler const new_handler)
{
    _invalid_parameter_handler const old_handler = __crt_fast_decode_pointer(__acrt_invalid_parameter_handler.value());
    __acrt_invalid_parameter_handler.value() = __crt_fast_encode_pointer(new_handler);
    return old_handler;
}

extern "C" _invalid_parameter_handler __cdecl _get_invalid_parameter_handler()
{
    return __crt_fast_decode_pointer(__acrt_invalid_parameter_handler.value());
}



extern "C" _invalid_parameter_handler __cdecl _set_thread_local_invalid_parameter_handler(_invalid_parameter_handler const new_handler)
{
    __acrt_ptd* const ptd = __acrt_getptd();

    _invalid_parameter_handler const old_handler = ptd->_thread_local_iph;
    ptd->_thread_local_iph = new_handler;
    return old_handler;
}

extern "C" _invalid_parameter_handler __cdecl _get_thread_local_invalid_parameter_handler()
{
    __acrt_ptd* const ptd = __acrt_getptd_noexit();
    if (!ptd)
    {
        return nullptr;
    }

    return ptd->_thread_local_iph;
}
