/* $Id: exceptionsR3.cpp $ */ /** @file * exceptionsR3 - Tests various ring-3 CPU exceptions. */ /* * Copyright (C) 2009-2019 Oracle Corporation * * This file is part of VirtualBox Open Source Edition (OSE), as * available from http://www.virtualbox.org. This file is free software; * you can redistribute it and/or modify it under the terms of the GNU * General Public License (GPL) as published by the Free Software * Foundation, in version 2 as it comes in the "COPYING" file of the * VirtualBox OSE distribution. VirtualBox OSE is distributed in the * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. * * The contents of this file may alternatively be used under the terms * of the Common Development and Distribution License Version 1.0 * (CDDL) only, as it comes in the "COPYING.CDDL" file of the * VirtualBox OSE distribution, in which case the provisions of the * CDDL are applicable instead of those of the GPL. * * You may elect to license modified versions of this file under the * terms and conditions of either the GPL or the CDDL or both. */ /********************************************************************************************************************************* * Header Files * *********************************************************************************************************************************/ #include #include #include #include #include #include #include #include #ifndef RT_OS_WINDOWS # define USE_SIGNALS # include # include #endif /********************************************************************************************************************************* * Defined Constants And Macros * *********************************************************************************************************************************/ /** Executes a simple test. */ #define TST_XCPT(Trapper, iTrap, uErr) \ do \ { \ RTTestISub(#Trapper); \ tstXcptReset(); \ if (!setjmp(g_JmpBuf)) \ { \ tstXcptAsm##Trapper(); \ RTTestIFailed("%s didn't trap (line no %u)", #Trapper, __LINE__); \ } \ else if ( (iTrap) != tstXcptCurTrap() \ || (uErr) != tstXcptCurErr() ) \ RTTestIFailed("%s trapped with %#x/%#x, expected %#x/%#x (line no %u)", \ #Trapper, tstXcptCurTrap(), tstXcptCurErr(), (iTrap), (uErr), __LINE__); \ else \ RTTestISubDone(); \ } while (0) /********************************************************************************************************************************* * Global Variables * *********************************************************************************************************************************/ /** Where to longjmp to when getting a signal/exception. */ jmp_buf g_JmpBuf; #ifdef USE_SIGNALS /** Pending signal. * -1 if no signal is pending. */ int32_t volatile g_iSignal; /** Pending signal info. */ siginfo_t volatile g_SigInfo; #endif /********************************************************************************************************************************* * Internal Functions * *********************************************************************************************************************************/ DECLASM(void) tstXcptAsmNullPtrRead(void); DECLASM(void) tstXcptAsmNullPtrWrite(void); DECLASM(void) tstXcptAsmSysEnter(void); DECLASM(void) tstXcptAsmSysCall(void); #ifdef USE_SIGNALS /** * Generic signal handler. */ static void tstXcptSigHandler(int iSignal, siginfo_t *pSigInfo, void *pvCtx) { #if 1 RTStrmPrintf(g_pStdErr, "signal %d pSigInfo=%p pvCtx=%p", iSignal, pSigInfo, pvCtx); if (pSigInfo) RTStrmPrintf(g_pStdErr, " si_addr=%p si_code=%#x sival_ptr=%p sival_int=%d", pSigInfo->si_addr, pSigInfo->si_code, pSigInfo->si_value.sival_ptr, pSigInfo->si_value.sival_int); RTStrmPrintf(g_pStdErr, "\n"); #endif if (g_iSignal == -1) { g_iSignal = iSignal; if (pSigInfo) memcpy((void *)&g_SigInfo, pSigInfo, sizeof(g_SigInfo)); longjmp(g_JmpBuf, 1); } else { /* we're up the infamous creek... */ _Exit(2); } } #elif defined(RT_OS_WINDOWS) /** @todo */ //# error "PORTME" #else # error "PORTME" #endif /** Reset the current exception state and get ready for a new trap. */ static void tstXcptReset(void) { #ifdef USE_SIGNALS g_iSignal = -1; memset((void *)&g_SigInfo, 0, sizeof(g_SigInfo)); #endif } /** Get the current intel trap number. Returns -1 if none. */ static int tstXcptCurTrap(void) { #ifdef USE_SIGNALS /** @todo this is just a quick sketch. */ switch (g_iSignal) { case SIGBUS: # ifdef RT_OS_DARWIN if (g_SigInfo.si_code == 2 /*KERN_PROTECTION_FAILURE*/) return X86_XCPT_PF; # endif return X86_XCPT_GP; case SIGSEGV: return X86_XCPT_GP; } #endif return -1; } /** Get the exception error code if applicable. */ static uint32_t tstXcptCurErr(void) { #ifdef USE_SIGNALS /** @todo this is just a quick sketch. */ switch (g_iSignal) { case SIGBUS: # ifdef RT_OS_DARWIN if (g_SigInfo.si_code == 2 /*KERN_PROTECTION_FAILURE*/) return 0; # endif break; case SIGSEGV: break; } #endif return UINT32_MAX; } int main(int argc, char **argv) { /* * Prolog. */ RTTEST hTest; int rc = RTTestInitAndCreate("exceptionsR3", &hTest); if (rc) return rc; /* * Parse options. */ bool volatile fRawMode = false; static const RTGETOPTDEF s_aOptions[] = { { "--raw-mode", 'r', RTGETOPT_REQ_NOTHING }, }; RTGETOPTUNION ValUnion; RTGETOPTSTATE GetState; RTGetOptInit(&GetState, argc, argv, s_aOptions, RT_ELEMENTS(s_aOptions), 1, 0); while ((rc = RTGetOpt(&GetState, &ValUnion))) { switch (rc) { case 'r': fRawMode = true; break; default: return RTGetOptPrintError(rc, &ValUnion); } } /* * Test setup. */ #ifdef USE_SIGNALS struct sigaction Act; RT_ZERO(Act); Act.sa_sigaction = tstXcptSigHandler; Act.sa_flags = SA_SIGINFO; sigfillset(&Act.sa_mask); sigaction(SIGILL, &Act, NULL); sigaction(SIGTRAP, &Act, NULL); # ifdef SIGEMT sigaction(SIGEMT, &Act, NULL); # endif sigaction(SIGFPE, &Act, NULL); sigaction(SIGBUS, &Act, NULL); sigaction(SIGSEGV, &Act, NULL); #else /** @todo Implement this using structured exception handling on Windows and * OS/2. */ #endif /* * The tests. */ RTTestBanner(hTest); TST_XCPT(NullPtrRead, X86_XCPT_PF, 0); TST_XCPT(NullPtrWrite, X86_XCPT_PF, 0); if (fRawMode) { TST_XCPT(SysEnter, X86_XCPT_GP, 0); TST_XCPT(SysCall, X86_XCPT_UD, 0); } /* * Epilog. */ return RTTestSummaryAndDestroy(hTest); }