/*
 * Copyright (C) 2013 Apple Inc. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#include "config.h"

#include "StructuredExceptionHandlerSuppressor.h"

#if defined(_M_IX86)
extern "C" int __stdcall exceptionHandlerThunk(); // Defined in makesafeseh.asm
#endif

static bool exceptionShouldTerminateProgram(DWORD code)
{
    switch (code) {
#ifndef NDEBUG
    case EXCEPTION_DATATYPE_MISALIGNMENT:
    case EXCEPTION_FLT_DENORMAL_OPERAND:
    case EXCEPTION_FLT_DIVIDE_BY_ZERO:
    case EXCEPTION_FLT_INEXACT_RESULT:
    case EXCEPTION_FLT_INVALID_OPERATION:
    case EXCEPTION_FLT_OVERFLOW:
    case EXCEPTION_FLT_STACK_CHECK:
    case EXCEPTION_FLT_UNDERFLOW:
#endif
    case EXCEPTION_ACCESS_VIOLATION:
    case EXCEPTION_ARRAY_BOUNDS_EXCEEDED:
    case EXCEPTION_INT_DIVIDE_BY_ZERO:
    case EXCEPTION_INT_OVERFLOW:
    case EXCEPTION_PRIV_INSTRUCTION:
    case EXCEPTION_IN_PAGE_ERROR:
    case EXCEPTION_ILLEGAL_INSTRUCTION:
    case EXCEPTION_NONCONTINUABLE_EXCEPTION:
    case EXCEPTION_STACK_OVERFLOW:
    case EXCEPTION_INVALID_DISPOSITION:
    case EXCEPTION_GUARD_PAGE:
    case EXCEPTION_INVALID_HANDLE:
        return true;
    };

    return false;
}

extern "C" EXCEPTION_DISPOSITION __stdcall exceptionHandler(struct _EXCEPTION_RECORD* exceptionRecord, void* /*establisherFrame*/, struct _CONTEXT* /*contextRecord*/, void* /*dispatcherContext*/)
{
    if (exceptionShouldTerminateProgram(exceptionRecord->ExceptionCode))
        abort();

    return ExceptionContinueSearch;
}

namespace WebCore {

#pragma warning(push)
#pragma warning(disable: 4733) // Disable "not registered as safe handler" warning

StructuredExceptionHandlerSuppressor::StructuredExceptionHandlerSuppressor(ExceptionRegistration& exceptionRegistration)
{
#if defined(_M_IX86)
    // Note: Windows requires that the EXCEPTION_REGISTRATION block (modeled here as our
    // ExceptionRegistration struct) be stack allocated. Therefore we instantiated it prior
    // to building this object so that Windows can still find it in stack memory when it
    // attempts to use the handler.

    // Windows puts an __try/__except block around some calls, such as hooks.
    // The exception handler then ignores system exceptions like invalid addresses
    // and null pointers. This class can be used to remove this block and prevent
    // it from catching the exception. Typically this will cause the exception to crash
    // which is often desirable to allow crashlogs to be recorded for debugging purposed.
    // While this class is in scope we replace the Windows exception handler with a custom
    // handler that indicates exceptions that should not be handled.
    //
    // See http://www.microsoft.com/msj/0197/Exception/Exception.aspx,
    //     http://www.microsoft.com/msj/archive/S2CE.aspx
    //     http://www.hexblog.com/wp-content/uploads/2012/06/Recon-2012-Skochinsky-Compiler-Internals.pdf
    //     http://www.codeproject.com/Articles/2126/How-a-C-compiler-implements-exception-handling

    // Windows doesn't like assigning to member variables, so we need to get the value into
    // a local variable and store it afterwards.
    void* registration;

    // Note: The FS register on Windows always holds the Thread Information Block.
    // FS:[0] points to the structured exception handling chain (a chain of
    // EXCEPTION_REGISTRATION structs).
    //
    // struct EXCEPTION_REGISTRATION
    // {
    //     DWORD next;
    //     DWORD handler;
    // };
    //
    // The first four bytes of FS:[0] point to the 'Next' member in the chain. Grab it so we can restore it later.
    __asm mov eax, FS:[0]
    __asm mov [registration], eax

    exceptionRegistration.prev = (ExceptionRegistration*)registration;
    exceptionRegistration.handler = (void*)exceptionHandlerThunk;

    void* erStructMem = &exceptionRegistration;

    __asm mov eax, erStructMem
    __asm mov FS:[0], eax

    m_savedExceptionRegistration = registration;
#else
    // 64-bit x64 no longer needs dynamic modification of the exception handlers.
#endif
}

StructuredExceptionHandlerSuppressor::~StructuredExceptionHandlerSuppressor()
{
#if defined(_M_IX86)
    // Restore the exception handler
    __asm mov eax, [m_savedExceptionRegistration]
    __asm mov FS:[0], eax
#else
#endif
}

#pragma warning(pop)

}
