CSAM.cpp revision 99cd1ce586a12bf6b8c6084cbcdebe8fe3553cc2
3194da424708abdd288b28d96892b3a5f3f7df0bvboxsync * CSAM - Guest OS Code Scanning and Analysis Manager
3194da424708abdd288b28d96892b3a5f3f7df0bvboxsync * Copyright (C) 2006-2007 Oracle Corporation
3194da424708abdd288b28d96892b3a5f3f7df0bvboxsync * This file is part of VirtualBox Open Source Edition (OSE), as
3194da424708abdd288b28d96892b3a5f3f7df0bvboxsync * available from http://www.virtualbox.org. This file is free software;
3194da424708abdd288b28d96892b3a5f3f7df0bvboxsync * you can redistribute it and/or modify it under the terms of the GNU
3194da424708abdd288b28d96892b3a5f3f7df0bvboxsync * General Public License (GPL) as published by the Free Software
3194da424708abdd288b28d96892b3a5f3f7df0bvboxsync * Foundation, in version 2 as it comes in the "COPYING" file of the
3194da424708abdd288b28d96892b3a5f3f7df0bvboxsync * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
3194da424708abdd288b28d96892b3a5f3f7df0bvboxsync * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
3194da424708abdd288b28d96892b3a5f3f7df0bvboxsync/*******************************************************************************
3194da424708abdd288b28d96892b3a5f3f7df0bvboxsync* Header Files *
3194da424708abdd288b28d96892b3a5f3f7df0bvboxsync*******************************************************************************/
3194da424708abdd288b28d96892b3a5f3f7df0bvboxsync/* Enabled by default */
3194da424708abdd288b28d96892b3a5f3f7df0bvboxsync/* Enable to monitor code pages for self-modifying code. */
3194da424708abdd288b28d96892b3a5f3f7df0bvboxsync/* Enable to monitor all scanned pages
3194da424708abdd288b28d96892b3a5f3f7df0bvboxsync#define CSAM_MONITOR_CSAM_CODE_PAGES */
3194da424708abdd288b28d96892b3a5f3f7df0bvboxsync/* Enable to scan beyond ret instructions.
3194da424708abdd288b28d96892b3a5f3f7df0bvboxsync#define CSAM_ANALYSE_BEYOND_RET */
3194da424708abdd288b28d96892b3a5f3f7df0bvboxsync/*******************************************************************************
3194da424708abdd288b28d96892b3a5f3f7df0bvboxsync* Internal Functions *
3194da424708abdd288b28d96892b3a5f3f7df0bvboxsync*******************************************************************************/
3194da424708abdd288b28d96892b3a5f3f7df0bvboxsyncstatic DECLCALLBACK(int) csamr3Save(PVM pVM, PSSMHANDLE pSSM);
3194da424708abdd288b28d96892b3a5f3f7df0bvboxsyncstatic DECLCALLBACK(int) csamr3Load(PVM pVM, PSSMHANDLE pSSM, uint32_t uVersion, uint32_t uPass);
3194da424708abdd288b28d96892b3a5f3f7df0bvboxsyncstatic DECLCALLBACK(int) CSAMCodePageWriteHandler(PVM pVM, RTGCPTR GCPtr, void *pvPtr, void *pvBuf, size_t cbBuf, PGMACCESSTYPE enmAccessType, void *pvUser);
3194da424708abdd288b28d96892b3a5f3f7df0bvboxsyncstatic DECLCALLBACK(int) CSAMCodePageInvalidate(PVM pVM, RTGCPTR GCPtr);
3194da424708abdd288b28d96892b3a5f3f7df0bvboxsyncbool csamIsCodeScanned(PVM pVM, RTRCPTR pInstr, PCSAMPAGE *pPage);
3194da424708abdd288b28d96892b3a5f3f7df0bvboxsyncstatic PCSAMPAGE csamCreatePageRecord(PVM pVM, RTRCPTR GCPtr, CSAMTAG enmTag, bool fCode32, bool fMonitorInvalidation = false);
3194da424708abdd288b28d96892b3a5f3f7df0bvboxsyncstatic int csamRemovePageRecord(PVM pVM, RTRCPTR GCPtr);
3194da424708abdd288b28d96892b3a5f3f7df0bvboxsyncstatic void csamMarkCode(PVM pVM, PCSAMPAGE pPage, RTRCPTR pInstr, uint32_t opsize, bool fScanned);
3194da424708abdd288b28d96892b3a5f3f7df0bvboxsyncstatic int csamAnalyseCodeStream(PVM pVM, RCPTRTYPE(uint8_t *) pInstrGC, RCPTRTYPE(uint8_t *) pCurInstrGC, bool fCode32,
3194da424708abdd288b28d96892b3a5f3f7df0bvboxsync PFN_CSAMR3ANALYSE pfnCSAMR3Analyse, void *pUserData, PCSAMP2GLOOKUPREC pCacheRec);
3194da424708abdd288b28d96892b3a5f3f7df0bvboxsync/** @todo Temporary for debugging. */
3194da424708abdd288b28d96892b3a5f3f7df0bvboxsyncstatic bool fInCSAMCodePageInvalidate = false;
3194da424708abdd288b28d96892b3a5f3f7df0bvboxsync/*******************************************************************************
3194da424708abdd288b28d96892b3a5f3f7df0bvboxsync* Global Variables *
3194da424708abdd288b28d96892b3a5f3f7df0bvboxsync*******************************************************************************/
3194da424708abdd288b28d96892b3a5f3f7df0bvboxsyncstatic DECLCALLBACK(int) csamr3CmdOn(PCDBGCCMD pCmd, PDBGCCMDHLP pCmdHlp, PVM pVM, PCDBGCVAR paArgs, unsigned cArgs);
3194da424708abdd288b28d96892b3a5f3f7df0bvboxsyncstatic DECLCALLBACK(int) csamr3CmdOff(PCDBGCCMD pCmd, PDBGCCMDHLP pCmdHlp, PVM pVM, PCDBGCVAR paArgs, unsigned cArgs);
3194da424708abdd288b28d96892b3a5f3f7df0bvboxsync/** Command descriptors. */
3194da424708abdd288b28d96892b3a5f3f7df0bvboxsync /* pszCmd, cArgsMin, cArgsMax, paArgDesc, cArgDescs, fFlags, pfnHandler pszSyntax, ....pszDescription */
3194da424708abdd288b28d96892b3a5f3f7df0bvboxsync { "csamon", 0, 0, NULL, 0, 0, csamr3CmdOn, "", "Enable CSAM code scanning." },
3194da424708abdd288b28d96892b3a5f3f7df0bvboxsync { "csamoff", 0, 0, NULL, 0, 0, csamr3CmdOff, "", "Disable CSAM code scanning." },
3194da424708abdd288b28d96892b3a5f3f7df0bvboxsync * SSM descriptor table for the CSAM structure.
3194da424708abdd288b28d96892b3a5f3f7df0bvboxsync /** @todo there are more fields that can be ignored here. */
3194da424708abdd288b28d96892b3a5f3f7df0bvboxsync SSMFIELD_ENTRY_PAD_HC64( CSAM, Alignment0, sizeof(uint32_t)),
3194da424708abdd288b28d96892b3a5f3f7df0bvboxsync SSMFIELD_ENTRY_RCPTR( CSAM, pPDBitmapGC), /// @todo ignore this?
3194da424708abdd288b28d96892b3a5f3f7df0bvboxsync SSMFIELD_ENTRY_RCPTR( CSAM, pPDHCBitmapGC), /// @todo ignore this?
3194da424708abdd288b28d96892b3a5f3f7df0bvboxsync SSMFIELD_ENTRY( CSAM, savedstate.cPatchPageRecords),
3194da424708abdd288b28d96892b3a5f3f7df0bvboxsync SSMFIELD_ENTRY_RCPTR_ARRAY( CSAM, pvDirtyBasePage),
3194da424708abdd288b28d96892b3a5f3f7df0bvboxsync SSMFIELD_ENTRY_RCPTR_ARRAY( CSAM, pvDirtyFaultPage),
int rc;
rc = MMR3HyperAllocOnceNoRel(pVM, CSAM_PGDIRBMP_CHUNKS*sizeof(RTHCPTR), 0, MM_TAG_CSAM, (void **)&pVM->csam.s.pPDBitmapHC);
rc = MMR3HyperAllocOnceNoRel(pVM, CSAM_PGDIRBMP_CHUNKS*sizeof(RTRCPTR), 0, MM_TAG_CSAM, (void **)&pVM->csam.s.pPDGCBitmapHC);
STAM_REG(pVM, &pVM->csam.s.StatNrTraps, STAMTYPE_COUNTER, "/CSAM/PageTraps", STAMUNIT_OCCURENCES, "The number of CSAM page traps.");
STAM_REG(pVM, &pVM->csam.s.StatDangerousWrite, STAMTYPE_COUNTER, "/CSAM/DangerousWrites", STAMUNIT_OCCURENCES, "The number of dangerous writes that cause a context switch.");
STAM_REG(pVM, &pVM->csam.s.StatNrPageNPHC, STAMTYPE_COUNTER, "/CSAM/HC/PageNotPresent", STAMUNIT_OCCURENCES, "The number of CSAM pages marked not present.");
STAM_REG(pVM, &pVM->csam.s.StatNrPageNPGC, STAMTYPE_COUNTER, "/CSAM/GC/PageNotPresent", STAMUNIT_OCCURENCES, "The number of CSAM pages marked not present.");
STAM_REG(pVM, &pVM->csam.s.StatNrPages, STAMTYPE_COUNTER, "/CSAM/PageRec/AddedRW", STAMUNIT_OCCURENCES, "The number of CSAM page records (RW monitoring).");
STAM_REG(pVM, &pVM->csam.s.StatNrPagesInv, STAMTYPE_COUNTER, "/CSAM/PageRec/AddedRWI", STAMUNIT_OCCURENCES, "The number of CSAM page records (RW & invalidation monitoring).");
STAM_REG(pVM, &pVM->csam.s.StatNrRemovedPages, STAMTYPE_COUNTER, "/CSAM/PageRec/Removed", STAMUNIT_OCCURENCES, "The number of removed CSAM page records.");
STAM_REG(pVM, &pVM->csam.s.StatPageRemoveREMFlush,STAMTYPE_COUNTER, "/CSAM/PageRec/Removed/REMFlush", STAMUNIT_OCCURENCES, "The number of removed CSAM page records that caused a REM flush.");
STAM_REG(pVM, &pVM->csam.s.StatNrPatchPages, STAMTYPE_COUNTER, "/CSAM/PageRec/Patch", STAMUNIT_OCCURENCES, "The number of CSAM patch page records.");
STAM_REG(pVM, &pVM->csam.s.StatNrUserPages, STAMTYPE_COUNTER, "/CSAM/PageRec/Ignore/User", STAMUNIT_OCCURENCES, "The number of CSAM user page records (ignored).");
STAM_REG(pVM, &pVM->csam.s.StatPagePATM, STAMTYPE_COUNTER, "/CSAM/PageRec/Type/PATM", STAMUNIT_OCCURENCES, "The number of PATM page records.");
STAM_REG(pVM, &pVM->csam.s.StatPageCSAM, STAMTYPE_COUNTER, "/CSAM/PageRec/Type/CSAM", STAMUNIT_OCCURENCES, "The number of CSAM page records.");
STAM_REG(pVM, &pVM->csam.s.StatPageREM, STAMTYPE_COUNTER, "/CSAM/PageRec/Type/REM", STAMUNIT_OCCURENCES, "The number of REM page records.");
STAM_REG(pVM, &pVM->csam.s.StatPageMonitor, STAMTYPE_COUNTER, "/CSAM/PageRec/Monitored", STAMUNIT_OCCURENCES, "The number of monitored pages.");
STAM_REG(pVM, &pVM->csam.s.StatCodePageModified, STAMTYPE_COUNTER, "/CSAM/Monitor/DirtyPage", STAMUNIT_OCCURENCES, "The number of code page modifications.");
STAM_REG(pVM, &pVM->csam.s.StatNrFlushes, STAMTYPE_COUNTER, "/CSAM/PageFlushes", STAMUNIT_OCCURENCES, "The number of CSAM page flushes.");
STAM_REG(pVM, &pVM->csam.s.StatNrFlushesSkipped, STAMTYPE_COUNTER, "/CSAM/PageFlushesSkipped", STAMUNIT_OCCURENCES, "The number of CSAM page flushes that were skipped.");
STAM_REG(pVM, &pVM->csam.s.StatNrKnownPagesHC, STAMTYPE_COUNTER, "/CSAM/HC/KnownPageRecords", STAMUNIT_OCCURENCES, "The number of known CSAM page records.");
STAM_REG(pVM, &pVM->csam.s.StatNrKnownPagesGC, STAMTYPE_COUNTER, "/CSAM/GC/KnownPageRecords", STAMUNIT_OCCURENCES, "The number of known CSAM page records.");
STAM_REG(pVM, &pVM->csam.s.StatNrInstr, STAMTYPE_COUNTER, "/CSAM/ScannedInstr", STAMUNIT_OCCURENCES, "The number of scanned instructions.");
STAM_REG(pVM, &pVM->csam.s.StatNrBytesRead, STAMTYPE_COUNTER, "/CSAM/BytesRead", STAMUNIT_OCCURENCES, "The number of bytes read for scanning.");
STAM_REG(pVM, &pVM->csam.s.StatNrOpcodeRead, STAMTYPE_COUNTER, "/CSAM/OpcodeBytesRead", STAMUNIT_OCCURENCES, "The number of opcode bytes read by the recompiler.");
STAM_REG(pVM, &pVM->csam.s.StatBitmapAlloc, STAMTYPE_COUNTER, "/CSAM/Alloc/PageBitmap", STAMUNIT_OCCURENCES, "The number of page bitmap allocations.");
STAM_REG(pVM, &pVM->csam.s.StatInstrCacheHit, STAMTYPE_COUNTER, "/CSAM/Cache/Hit", STAMUNIT_OCCURENCES, "The number of dangerous instruction cache hits.");
STAM_REG(pVM, &pVM->csam.s.StatInstrCacheMiss, STAMTYPE_COUNTER, "/CSAM/Cache/Miss", STAMUNIT_OCCURENCES, "The number of dangerous instruction cache misses.");
STAM_REG(pVM, &pVM->csam.s.StatScanNextFunction, STAMTYPE_COUNTER, "/CSAM/Function/Scan/Success", STAMUNIT_OCCURENCES, "The number of found functions beyond the ret border.");
STAM_REG(pVM, &pVM->csam.s.StatScanNextFunctionFailed, STAMTYPE_COUNTER, "/CSAM/Function/Scan/Failed", STAMUNIT_OCCURENCES, "The number of refused functions beyond the ret border.");
STAM_REG(pVM, &pVM->csam.s.StatTime, STAMTYPE_PROFILE, "/PROF/CSAM/Scan", STAMUNIT_TICKS_PER_CALL, "Scanning overhead.");
STAM_REG(pVM, &pVM->csam.s.StatTimeCheckAddr, STAMTYPE_PROFILE, "/PROF/CSAM/CheckAddr", STAMUNIT_TICKS_PER_CALL, "Address check overhead.");
STAM_REG(pVM, &pVM->csam.s.StatTimeAddrConv, STAMTYPE_PROFILE, "/PROF/CSAM/AddrConv", STAMUNIT_TICKS_PER_CALL, "Address conversion overhead.");
STAM_REG(pVM, &pVM->csam.s.StatTimeFlushPage, STAMTYPE_PROFILE, "/PROF/CSAM/FlushPage", STAMUNIT_TICKS_PER_CALL, "Page flushing overhead.");
STAM_REG(pVM, &pVM->csam.s.StatTimeDisasm, STAMTYPE_PROFILE, "/PROF/CSAM/Disasm", STAMUNIT_TICKS_PER_CALL, "Disassembly overhead.");
STAM_REG(pVM, &pVM->csam.s.StatFlushDirtyPages, STAMTYPE_PROFILE, "/PROF/CSAM/FlushDirtyPage", STAMUNIT_TICKS_PER_CALL, "Dirty page flushing overhead.");
STAM_REG(pVM, &pVM->csam.s.StatCheckGates, STAMTYPE_PROFILE, "/PROF/CSAM/CheckGates", STAMUNIT_TICKS_PER_CALL, "CSAMR3CheckGates overhead.");
bool fEnabled;
#ifdef CSAM_ENABLE
fEnabled = true;
fEnabled = false;
if (fEnabled)
#ifdef VBOX_WITH_DEBUGGER
static bool fRegisteredCmds = false;
if (!fRegisteredCmds)
fRegisteredCmds = true;
return VINF_SUCCESS;
return VINF_SUCCESS;
if (offDelta)
for(int i=0;i<CSAM_PGDIRBMP_CHUNKS;i++)
int rc;
for(int i=0;i<CSAM_PAGEBMP_CHUNKS;i++)
return VINF_SUCCESS;
for(int i=0;i<CSAM_PGDIRBMP_CHUNKS;i++)
if (pPageRec)
return VINF_SUCCESS;
return VINF_SUCCESS;
int rc;
return VINF_SUCCESS;
int rc;
for (unsigned i=0;i<CSAM_PGDIRBMP_CHUNKS;i++)
return VINF_SUCCESS;
int rc;
rc = SSMR3GetStructEx(pSSM, &csamInfo, sizeof(csamInfo), SSMSTRUCT_FLAGS_MEM_BAND_AID, &g_aCsamFields[0], NULL);
memcpy(pVM->csam.s.pvDirtyFaultPage, csamInfo.pvDirtyFaultPage, sizeof(pVM->csam.s.pvDirtyFaultPage));
memcpy(pVM->csam.s.pvPossibleCodePage, csamInfo.pvPossibleCodePage, sizeof(pVM->csam.s.pvPossibleCodePage));
for (unsigned i=0;i<CSAM_PGDIRBMP_CHUNKS;i++)
rc = MMHyperAlloc(pVM, CSAM_PAGE_BITMAP_SIZE, 0, MM_TAG_CSAM, (void **)&pVM->csam.s.pPDBitmapHC[i]);
return rc;
rc = SSMR3GetStructEx(pSSM, &page, sizeof(page), SSMSTRUCT_FLAGS_MEM_BAND_AID, &g_aCsamPageRecFields[0], NULL);
pPage = csamCreatePageRecord(pVM, page.page.pPageGC, page.page.enmTag, page.page.fCode32, page.page.fMonitorInvalidation);
return VINF_SUCCESS;
static R3PTRTYPE(void *) CSAMGCVirtToHCVirt(PVM pVM, PCSAMP2GLOOKUPREC pCacheRec, RCPTRTYPE(uint8_t *) pGCPtr)
int rc;
if (pHCPtr)
return pHCPtr;
return NULL;
return pHCPtr;
static DECLCALLBACK(int) CSAMR3ReadBytes(PDISCPUSTATE pDisState, uint8_t *pbDst, RTUINTPTR uSrcAddr, uint32_t cbToRead)
for (int i = 0; i < orgsize; i++)
uSrcAddr++;
pbDst++;
cbToRead--;
if (cbToRead == 0)
return VINF_SUCCESS;
if (PAGE_ADDRESS(pInstrGC) != PAGE_ADDRESS(uSrcAddr + cbToRead - 1) && !PATMIsPatchGCAddr(pVM, uSrcAddr))
return VINF_SUCCESS;
(pCpu)->apvUserData[2] = (void *)(uintptr_t)InstrGC; Assert(sizeof(InstrGC) <= sizeof(pCpu->apvUserData[0]));
#ifdef DEBUG
return DISInstrEx(InstrGC, 0, enmCpuMode, CSAMR3ReadBytes, pVM, ~(OPTYPE_INVALID | OPTYPE_HARMLESS | OPTYPE_RRM_MASK),
static int CSAMR3AnalyseCallback(PVM pVM, DISCPUSTATE *pCpu, RCPTRTYPE(uint8_t *) pInstrGC, RCPTRTYPE(uint8_t *) pCurInstrGC,
int rc;
case OP_INT:
return VINF_SUCCESS;
case OP_ILLUD2:
case OP_RETN:
case OP_INT3:
case OP_INVALID:
case OP_IRET:
return VINF_SUCCESS;
case OP_PUSHF:
case OP_POPF:
case OP_CLI:
/* We've scanned the next instruction(s) already. This means we've followed a branch that ended up there before -> dangerous!! */
case OP_PUSH:
case OP_STR:
case OP_LSL:
case OP_LAR:
case OP_SGDT:
case OP_SLDT:
case OP_SIDT:
case OP_SMSW:
case OP_VERW:
case OP_VERR:
case OP_CPUID:
case OP_IRET:
#ifdef DEBUG
case OP_STR:
case OP_LSL:
case OP_LAR:
case OP_SGDT:
case OP_SLDT:
case OP_SIDT:
case OP_SMSW:
case OP_VERW:
case OP_VERR:
case OP_CPUID:
case OP_PUSH:
case OP_IRET:
return VWRN_CONTINUE_ANALYSIS;
return VWRN_CONTINUE_ANALYSIS;
case OP_JMP:
case OP_CALL:
#ifdef DEBUG
case OP_JMP:
case OP_CALL:
return VWRN_CONTINUE_ANALYSIS;
return VWRN_CONTINUE_ANALYSIS;
return VWRN_CONTINUE_ANALYSIS;
#ifdef CSAM_ANALYSE_BEYOND_RET
static int csamAnalyseCallCodeStream(PVM pVM, RCPTRTYPE(uint8_t *) pInstrGC, RCPTRTYPE(uint8_t *) pCurInstrGC, bool fCode32,
int rc;
uint32_t i;
rc = csamAnalyseCodeStream(pVM, pInstrGC, pCurInstrGC, fCode32, pfnCSAMR3Analyse, pUserData, pCacheRec);
int rc2;
#ifdef DEBUG
goto done;
goto done;
#ifdef DEBUG
Log(("Disassembly failed at %RRv with %Rrc (probably page not present) -> return to caller\n", pCurInstrGC, rc2));
goto done;
goto done;
case OP_NOP:
case OP_INT3:
case OP_LEA:
goto next_function;
case OP_PUSH:
goto next_function;
csamAnalyseCallCodeStream(pVM, pInstrGC, pCurInstrGC, fCode32, pfnCSAMR3Analyse, pUserData, pCacheRec);
goto next_function;
case OP_SUB:
goto next_function;
csamAnalyseCallCodeStream(pVM, pInstrGC, pCurInstrGC, fCode32, pfnCSAMR3Analyse, pUserData, pCacheRec);
goto next_function;
goto next_function;
done:
return rc;
* Disassembles the code stream until the callback function detects a failure or decides everything is acceptable
static int csamAnalyseCodeStream(PVM pVM, RCPTRTYPE(uint8_t *) pInstrGC, RCPTRTYPE(uint8_t *) pCurInstrGC, bool fCode32,
int rc2;
#ifdef DEBUG
goto done;
goto done;
goto done;
#ifdef DEBUG
rc2 = CSAMR3DISInstr(pVM, &cpu, pCurInstrGC, pCurInstrHC, (fCode32) ? CPUMODE_32BIT : CPUMODE_16BIT,
Log(("Disassembly failed at %RRv with %Rrc (probably page not present) -> return to caller\n", pCurInstrGC, rc2));
goto done;
goto next_please;
AssertMsg(pfnCSAMR3Analyse(pVM, &cpu, pInstrGC, pCurInstrGC, pCacheRec, (void *)pPage) == VWRN_CONTINUE_ANALYSIS, ("Instruction incorrectly marked harmless?!?!?\n"));
goto next_please;
#ifdef CSAM_ANALYSE_BEYOND_RET
/* Remember the address of the instruction following the ret in case the parent instruction was a call. */
pCacheRec->pCallExitRec->pInstrAfterRetGC[pCacheRec->pCallExitRec->cInstrAfterRet] = pCurInstrGC + opsize;
goto done;
// For our first attempt, we'll handle only simple relative jumps and calls (immediate offset coded in instruction)
if ( ((cpu.pCurInstr->optype & OPTYPE_CONTROLFLOW) && (OP_PARM_VTYPE(cpu.pCurInstr->param1) == OP_PARM_J))
|| (cpu.pCurInstr->opcode == OP_CALL && cpu.param1.flags == USE_DISPLACEMENT32)) /* simple indirect call (call dword ptr [address]) */
/* We need to parse 'call dword ptr [address]' type of calls to catch cpuid instructions in some recent Linux distributions (e.g. OpenSuse 10.3) */
addr = 0;
if (addr == 0)
goto next_please;
goto done;
rc = csamAnalyseCallCodeStream(pVM, pInstrGC, addr, fCode32, pfnCSAMR3Analyse, (void *)pJmpPage, pCacheRec);
rc = csamAnalyseCodeStream(pVM, pInstrGC, addr, fCode32, pfnCSAMR3Analyse, (void *)pJmpPage, pCacheRec);
goto done;
goto done;
} //if ((cpu.pCurInstr->optype & OPTYPE_CONTROLFLOW) && (OP_PARM_VTYPE(cpu.pCurInstr->param1) == OP_PARM_J))
#ifdef CSAM_SCAN_JUMP_TABLE
&& (cpu.param1.flags & (USE_DISPLACEMENT32|USE_INDEX|USE_SCALE)) == (USE_DISPLACEMENT32|USE_INDEX|USE_SCALE)
int rc2;
goto done;
rc = csamAnalyseCodeStream(pVM, pInstrGC, addr, fCode32, pfnCSAMR3Analyse, (void *)pJmpPage, pCacheRec);
goto done;
goto done;
done:
return rc;
int rc;
AssertMsg(RT_SUCCESS(rc) || rc == VERR_PAGE_NOT_PRESENT || rc == VERR_PAGE_TABLE_NOT_PRESENT, ("rc = %Rrc\n", rc));
return ~0ULL;
AssertMsg(RT_SUCCESS(rc) || rc == VERR_PAGE_NOT_PRESENT || rc == VERR_PAGE_TABLE_NOT_PRESENT, ("rc = %Rrc\n", rc));
return ~0ULL;
AssertMsg(RT_SUCCESS(rc) || rc == VERR_PAGE_NOT_PRESENT || rc == VERR_PAGE_TABLE_NOT_PRESENT, ("rc = %Rrc\n", rc));
return ~0ULL;
AssertMsg(RT_SUCCESS(rc) || rc == VERR_PAGE_NOT_PRESENT || rc == VERR_PAGE_TABLE_NOT_PRESENT, ("rc = %Rrc\n", rc));
return ~0ULL;
AssertMsg(RT_SUCCESS(rc) || rc == VERR_PAGE_NOT_PRESENT || rc == VERR_PAGE_TABLE_NOT_PRESENT, ("rc = %Rrc\n", rc));
return ~0ULL;
int rc;
return VINF_SUCCESS;
* Note: searching for the page in our tree first is more expensive (skipped flushes are two orders of magnitude more common)
return VWRN_CSAM_PAGE_NOT_FOUND;
return rc;
return VINF_SUCCESS;
if (pPageRec)
return VINF_SUCCESS;
Log(("CSAMR3FlushPage: page %RRv has changed -> FLUSH (rc=%Rrc) (Phys: %RGp vs %RGp)\n", addr, rc, GCPhys, pPageRec->page.GCPhys));
if (fRemovePage)
return VERR_NO_MEMORY;
return VINF_SUCCESS;
return VWRN_CSAM_PAGE_NOT_FOUND;
int rc;
if (pPageRec)
return VINF_SUCCESS;
return VWRN_CSAM_PAGE_NOT_FOUND;
if (pPageRec)
return VWRN_CSAM_PAGE_NOT_FOUND;
return VINF_SUCCESS;
static PCSAMPAGE csamCreatePageRecord(PVM pVM, RTRCPTR GCPtr, CSAMTAG enmTag, bool fCode32, bool fMonitorInvalidation)
int rc;
bool ret;
return NULL;
AssertMsg(RT_SUCCESS(rc) || rc == VERR_PAGE_NOT_PRESENT || rc == VERR_PAGE_TABLE_NOT_PRESENT, ("rc = %Rrc\n", rc));
#ifdef CSAM_MONITOR_CODE_PAGES
switch (enmTag)
case CSAM_TAG_PATM:
case CSAM_TAG_REM:
#ifdef CSAM_MONITOR_CSAM_CODE_PAGES
case CSAM_TAG_CSAM:
rc = PGMR3HandlerVirtualRegister(pVM, PGMVIRTHANDLERTYPE_WRITE, GCPtr, GCPtr + (PAGE_SIZE - 1) /* inclusive! */,
(fMonitorInvalidation) ? CSAMCodePageInvalidate : 0, CSAMCodePageWriteHandler, "CSAMGCCodePageWriteHandler", 0,
AssertMsg(RT_SUCCESS(rc) || rc == VERR_PGM_HANDLER_VIRTUAL_CONFLICT, ("PGMR3HandlerVirtualRegisterEx %RRv failed with %Rrc\n", GCPtr, rc));
#ifdef VBOX_WITH_STATISTICS
switch (enmTag)
case CSAM_TAG_CSAM:
case CSAM_TAG_PATM:
case CSAM_TAG_REM:
if (fMonitorInvalidation)
int rc;
bool fMonitorInvalidation;
AssertMsg(RT_SUCCESS(rc) || rc == VERR_PAGE_NOT_PRESENT || rc == VERR_PAGE_TABLE_NOT_PRESENT, ("rc = %Rrc\n", rc));
return VINF_SUCCESS;
#ifdef CSAM_MONITOR_CSAM_CODE_PAGES
#ifdef CSAM_MONITOR_CODE_PAGES
rc = PGMR3HandlerVirtualRegister(pVM, PGMVIRTHANDLERTYPE_WRITE, pPageAddrGC, pPageAddrGC + (PAGE_SIZE - 1) /* inclusive! */,
(fMonitorInvalidation) ? CSAMCodePageInvalidate : 0, CSAMCodePageWriteHandler, "CSAMGCCodePageWriteHandler", 0,
AssertMsg(RT_SUCCESS(rc) || rc == VERR_PGM_HANDLER_VIRTUAL_CONFLICT, ("PGMR3HandlerVirtualRegisterEx %RRv failed with %Rrc\n", pPageAddrGC, rc));
// || (pPageRec->page.GCPhys == 0), ("Shadow page flags for %RRv (%RHp) aren't readonly (%RX64)!!\n", pPageAddrGC, GCPhys, fPageShw));
/* The page was changed behind our back. It won't be made read-only until the next SyncCR3, so force it here. */
return VINF_SUCCESS;
#ifdef VBOX_STRICT
if (pPageRec)
#ifdef CSAM_MONITOR_CODE_PAGES
#ifdef VBOX_WITH_STATISTICS
case CSAM_TAG_CSAM:
case CSAM_TAG_PATM:
case CSAM_TAG_REM:
AssertFailed();
return VINF_SUCCESS;
static DECLCALLBACK(int) CSAMCodePageWriteHandler(PVM pVM, RTGCPTR GCPtr, void *pvPtr, void *pvBuf, size_t cbBuf, PGMACCESSTYPE enmAccessType, void *pvUser)
int rc;
return VINF_PGM_HANDLER_DO_DEFAULT;
rc = VMR3ReqCallVoidNoWait(pVM, VMCPUID_ANY, (PFNRT)CSAMDelayedWriteHandler, 3, pVM, (RTRCPTR)GCPtr, cbBuf);
return VINF_PGM_HANDLER_DO_DEFAULT;
fInCSAMCodePageInvalidate = true;
/** @todo We can't remove the page (which unregisters the virtual handler) as we are called from a DoWithAll on the virtual handler tree. Argh. */
fInCSAMCodePageInvalidate = false;
return VINF_SUCCESS;
if (pPageRec)
if (fScanned)
return VINF_SUCCESS;
return VINF_SUCCESS;
return VINF_SUCCESS;
Assert(SELMGetCpuModeFromSelector(VMMGetCpu0(pVM), pCtxCore->eflags, pCtxCore->cs, &pCtxCore->csHid) == CPUMODE_32BIT);
return VINF_SUCCESS;
int rc;
return VINF_SUCCESS;
rc = csamAnalyseCallCodeStream(pVM, pInstrGC, pInstrGC, true /* 32 bits code */, CSAMR3AnalyseCallback, pPage, &cacheRec);
return rc;
return VINF_SUCCESS;
int rc;
#ifdef VBOX_WITH_REM
Log(("CSAMR3FlushDirtyPages: flush %RRv (modifypage rc=%Rrc)\n", pVM->csam.s.pvDirtyBasePage[i], rc));
AssertMsg(RT_SUCCESS(rc) || rc == VERR_PAGE_NOT_PRESENT || rc == VERR_PAGE_TABLE_NOT_PRESENT, ("rc = %Rrc\n", rc));
return VINF_SUCCESS;
return VINF_SUCCESS;
return VINF_SUCCESS;
int rc;
return VINF_SUCCESS;
return VERR_INVALID_PARAMETER;
if (pHandler)
return VERR_INVALID_PARAMETER;
return rc;
&& (pGuestIdte->Gen.u5Type2 == VBOX_IDTE_TYPE2_TRAP_32 || pGuestIdte->Gen.u5Type2 == VBOX_IDTE_TYPE2_INT_32)
Log(("CSAMCheckGates: check gate %d failed due to rc %Rrc GCPtrBase=%RRv limit=%x\n", iGate, rc, selInfo.GCPtrBase, selInfo.cbLimit));
Log(("CSAMCheckGates: check trap gate %d at %04X:%08X (flat %RRv)\n", iGate, pGuestIdte->Gen.u16SegSel, VBOXIDTE_OFFSET(*pGuestIdte), pHandler));
Log(("CSAMCheckGates: check interrupt gate %d at %04X:%08X (flat %RRv)\n", iGate, pGuestIdte->Gen.u16SegSel, VBOXIDTE_OFFSET(*pGuestIdte), pHandler));
rc = PATMR3InstallPatch(pVM, pHandler - aOpenBsdPushCSOffset[i], PATMFL_CODE32 | PATMFL_GUEST_SPECIFIC);
switch (iGate) {
Log(("Installing %s gate handler for 0x%X at %RRv\n", (pGuestIdte->Gen.u5Type2 == VBOX_IDTE_TYPE2_TRAP_32) ? "trap" : "intr", iGate, pHandler));
if (pNewHandlerGC)
return VINF_SUCCESS;
return VINF_SUCCESS;
return VINF_SUCCESS;
#ifdef VBOX_WITH_DEBUGGER
static DECLCALLBACK(int) csamr3CmdOff(PCDBGCCMD pCmd, PDBGCCMDHLP pCmdHlp, PVM pVM, PCDBGCVAR paArgs, unsigned cArgs)
static DECLCALLBACK(int) csamr3CmdOn(PCDBGCCMD pCmd, PDBGCCMDHLP pCmdHlp, PVM pVM, PCDBGCVAR paArgs, unsigned cArgs)