PATM.cpp revision e7ba4c7cd25b070ecbaeaefa0854e30320a551db
e2a742da3fbd4b04860305ed39b001a0140a65a3vboxsync * PATM - Dynamic Guest OS Patching Manager
1bb2e7ef5f99f9cb24c39d05f5fa0388085c80bcvboxsync * NOTE: Never ever reuse patch memory!!
* available from http://www.virtualbox.org. This file is free software;
#include "PATMInternal.h"
#include "PATMPatch.h"
#include "PATMA.h"
static int patmDisableUnusablePatch(PVM pVM, RTGCPTR pInstrGC, RTGCPTR pConflictAddr, PPATCHINFO pPatch);
#ifdef VBOX_WITH_STATISTICS
#define patmPatchHCPtr2PatchGCPtr(pVM, pHC) (pVM->patm.s.pPatchMemGC + (pHC - pVM->patm.s.pPatchMemHC))
#define patmPatchGCPtr2PatchHCPtr(pVM, pGC) (pVM->patm.s.pPatchMemHC + (pGC - pVM->patm.s.pPatchMemGC))
static DECLCALLBACK(int) patmVirtPageHandler(PVM pVM, RTGCPTR GCPtr, void *pvPtr, void *pvBuf, size_t cbBuf, PGMACCESSTYPE enmAccessType, void *pvUser);
#ifdef VBOX_WITH_DEBUGGER
static DECLCALLBACK(int) patmr3CmdOn(PCDBGCCMD pCmd, PDBGCCMDHLP pCmdHlp, PVM pVM, PCDBGCVAR paArgs, unsigned cArgs, PDBGCVAR pResult);
static DECLCALLBACK(int) patmr3CmdOff(PCDBGCCMD pCmd, PDBGCCMDHLP pCmdHlp, PVM pVM, PCDBGCVAR paArgs, unsigned cArgs, PDBGCVAR pResult);
/* pszCmd, cArgsMin, cArgsMax, paArgDesc, cArgDescs, pResultDesc, fFlags, pfnHandler pszSyntax, ....pszDescription */
int rc;
AssertReleaseMsg(PATMInterruptFlag == (VM_FF_INTERRUPT_APIC | VM_FF_INTERRUPT_PIC | VM_FF_TIMER | VM_FF_REQUEST), ("Interrupt flags out of sync!!\n"));
rc = MMR3HyperAllocOnceNoRel(pVM, PATCH_MEMORY_SIZE + PAGE_SIZE + PATM_STACK_TOTAL_SIZE + PAGE_SIZE + PATM_STAT_MEMSIZE, PAGE_SIZE, MM_TAG_PATM, (void **)&pVM->patm.s.pPatchMemHC);
return rc;
/* PATM stack page for call instruction execution. (2 parts: one for our private stack and one to store the original return address */
* Note1: This is non-critical data; if trashed by the guest, then it will only cause problems for itself
rc = MMHyperAlloc(pVM, sizeof(*pVM->patm.s.PatchLookupTreeHC), 0, MM_TAG_PATM, (void **)&pVM->patm.s.PatchLookupTreeHC);
# ifdef PATM_DISABLE_ALL
return rc;
rc = SSMR3RegisterInternal(pVM, "PATM", 0, PATM_SSM_VERSION, sizeof(pVM->patm.s) + PATCH_MEMORY_SIZE + PAGE_SIZE + PATM_STACK_TOTAL_SIZE + PAGE_SIZE,
return rc;
#ifdef VBOX_WITH_DEBUGGER
static bool fRegisteredCmds = false;
if (!fRegisteredCmds)
fRegisteredCmds = true;
#ifdef VBOX_WITH_STATISTICS
STAM_REG(pVM, &pVM->patm.s.StatNrOpcodeRead, STAMTYPE_COUNTER, "/PATM/OpcodeBytesRead", STAMUNIT_OCCURENCES, "The number of opcode bytes read by the recompiler.");
STAM_REG(pVM, &pVM->patm.s.StatPATMMemoryUsed,STAMTYPE_COUNTER, "/PATM/MemoryUsed", STAMUNIT_OCCURENCES, "The amount of hypervisor heap used for patches.");
STAM_REG(pVM, &pVM->patm.s.StatDisabled, STAMTYPE_COUNTER, "/PATM/Patch/Disabled", STAMUNIT_OCCURENCES, "Number of times patches were disabled.");
STAM_REG(pVM, &pVM->patm.s.StatEnabled, STAMTYPE_COUNTER, "/PATM/Patch/Enabled", STAMUNIT_OCCURENCES, "Number of times patches were enabled.");
STAM_REG(pVM, &pVM->patm.s.StatDirty, STAMTYPE_COUNTER, "/PATM/Patch/Dirty", STAMUNIT_OCCURENCES, "Number of times patches were marked dirty.");
STAM_REG(pVM, &pVM->patm.s.StatUnusable, STAMTYPE_COUNTER, "/PATM/Patch/Unusable", STAMUNIT_OCCURENCES, "Number of unusable patches (conflicts).");
STAM_REG(pVM, &pVM->patm.s.StatInstalled, STAMTYPE_COUNTER, "/PATM/Patch/Installed", STAMUNIT_OCCURENCES, "Number of installed patches.");
STAM_REG(pVM, &pVM->patm.s.StatInt3Callable, STAMTYPE_COUNTER, "/PATM/Patch/Int3Callable", STAMUNIT_OCCURENCES, "Number of cli patches turned into int3 patches.");
STAM_REG(pVM, &pVM->patm.s.StatInt3BlockRun, STAMTYPE_COUNTER, "/PATM/Patch/Run/Int3", STAMUNIT_OCCURENCES, "Number of times an int3 block patch was executed.");
STAMR3RegisterF(pVM, &pVM->patm.s.pGCStateHC->uPatchCalls, STAMTYPE_U32, STAMVISIBILITY_ALWAYS, STAMUNIT_OCCURENCES, NULL, "/PATM/Patch/Run/Normal");
STAM_REG(pVM, &pVM->patm.s.StatInstalledFunctionPatches, STAMTYPE_COUNTER, "/PATM/Patch/Installed/Function", STAMUNIT_OCCURENCES, "Number of installed function duplication patches.");
STAM_REG(pVM, &pVM->patm.s.StatInstalledTrampoline, STAMTYPE_COUNTER, "/PATM/Patch/Installed/Trampoline", STAMUNIT_OCCURENCES, "Number of installed trampoline patches.");
STAM_REG(pVM, &pVM->patm.s.StatInstalledJump, STAMTYPE_COUNTER, "/PATM/Patch/Installed/Jump", STAMUNIT_OCCURENCES, "Number of installed jump patches.");
STAM_REG(pVM, &pVM->patm.s.StatOverwritten, STAMTYPE_COUNTER, "/PATM/Patch/Overwritten", STAMUNIT_OCCURENCES, "Number of overwritten patches.");
STAM_REG(pVM, &pVM->patm.s.StatFixedConflicts,STAMTYPE_COUNTER, "/PATM/Patch/ConflictFixed", STAMUNIT_OCCURENCES, "Number of fixed conflicts.");
STAM_REG(pVM, &pVM->patm.s.StatFlushed, STAMTYPE_COUNTER, "/PATM/Patch/Flushed", STAMUNIT_OCCURENCES, "Number of flushes of pages with patch jumps.");
STAM_REG(pVM, &pVM->patm.s.StatMonitored, STAMTYPE_COUNTER, "/PATM/Patch/Monitored", STAMUNIT_OCCURENCES, "Number of patches in monitored patch pages.");
STAM_REG(pVM, &pVM->patm.s.StatPageBoundaryCrossed, STAMTYPE_COUNTER, "/PATM/Patch/BoundaryCross", STAMUNIT_OCCURENCES, "Number of refused patches due to patch jump crossing page boundary.");
STAM_REG(pVM, &pVM->patm.s.StatHandleTrap, STAMTYPE_PROFILE, "/PATM/HandleTrap", STAMUNIT_TICKS_PER_CALL, "Profiling of PATMR3HandleTrap");
STAM_REG(pVM, &pVM->patm.s.StatPushTrap, STAMTYPE_COUNTER, "/PATM/HandleTrap/PushWP", STAMUNIT_OCCURENCES, "Number of traps due to monitored stack pages.");
STAM_REG(pVM, &pVM->patm.s.StatSwitchBack, STAMTYPE_COUNTER, "/PATM/SwitchBack", STAMUNIT_OCCURENCES, "Switch back to original guest code when IF=1 & executing PATM instructions");
STAM_REG(pVM, &pVM->patm.s.StatSwitchBackFail,STAMTYPE_COUNTER, "/PATM/SwitchBackFail", STAMUNIT_OCCURENCES, "Failed switch back to original guest code when IF=1 & executing PATM instructions");
STAM_REG(pVM, &pVM->patm.s.StatDuplicateREQFailed, STAMTYPE_COUNTER, "/PATM/Function/DupREQ/Failed", STAMUNIT_OCCURENCES, "Nr of failed PATMR3DuplicateFunctionRequest calls");
STAM_REG(pVM, &pVM->patm.s.StatDuplicateREQSuccess, STAMTYPE_COUNTER, "/PATM/Function/DupREQ/Success", STAMUNIT_OCCURENCES, "Nr of successful PATMR3DuplicateFunctionRequest calls");
STAM_REG(pVM, &pVM->patm.s.StatDuplicateUseExisting,STAMTYPE_COUNTER, "/PATM/Function/DupREQ/UseExist", STAMUNIT_OCCURENCES, "Nr of successful PATMR3DuplicateFunctionRequest calls when using an existing patch");
STAM_REG(pVM, &pVM->patm.s.StatFunctionLookupInsert, STAMTYPE_COUNTER, "/PATM/Function/Lookup/Insert", STAMUNIT_OCCURENCES, "Nr of successful function address insertions");
STAM_REG(pVM, &pVM->patm.s.StatFunctionLookupReplace, STAMTYPE_COUNTER, "/PATM/Function/Lookup/Replace", STAMUNIT_OCCURENCES, "Nr of successful function address replacements");
STAM_REG(pVM, &pVM->patm.s.StatU32FunctionMaxSlotsUsed, STAMTYPE_U32_RESET,"/PATM/Function/Lookup/MaxSlots", STAMUNIT_OCCURENCES, "Maximum nr of lookup slots used in all call patches");
STAM_REG(pVM, &pVM->patm.s.StatFunctionFound, STAMTYPE_COUNTER, "/PATM/Function/Found", STAMUNIT_OCCURENCES, "Nr of successful function patch lookups in GC");
STAM_REG(pVM, &pVM->patm.s.StatFunctionNotFound, STAMTYPE_COUNTER, "/PATM/Function/NotFound", STAMUNIT_OCCURENCES, "Nr of failed function patch lookups in GC");
STAM_REG(pVM, &pVM->patm.s.StatPatchWrite, STAMTYPE_PROFILE, "/PATM/Write/Handle", STAMUNIT_TICKS_PER_CALL, "Profiling of PATMR3PatchWrite");
STAM_REG(pVM, &pVM->patm.s.StatPatchWriteDetect, STAMTYPE_PROFILE, "/PATM/Write/Detect", STAMUNIT_TICKS_PER_CALL, "Profiling of PATMIsWriteToPatchPage");
STAM_REG(pVM, &pVM->patm.s.StatPatchWriteInterpreted, STAMTYPE_COUNTER, "/PATM/Write/Interpreted/Success", STAMUNIT_OCCURENCES, "Nr of interpreted patch writes.");
STAM_REG(pVM, &pVM->patm.s.StatPatchWriteInterpretedFailed, STAMTYPE_COUNTER, "/PATM/Write/Interpreted/Failed", STAMUNIT_OCCURENCES, "Nr of failed interpreted patch writes.");
STAM_REG(pVM, &pVM->patm.s.StatPatchRefreshSuccess, STAMTYPE_COUNTER, "/PATM/Refresh/Success", STAMUNIT_OCCURENCES, "Successful patch refreshes");
STAM_REG(pVM, &pVM->patm.s.StatPatchRefreshFailed, STAMTYPE_COUNTER, "/PATM/Refresh/Failure", STAMUNIT_OCCURENCES, "Failed patch refreshes");
STAM_REG(pVM, &pVM->patm.s.StatPatchPageInserted, STAMTYPE_COUNTER, "/PATM/Page/Inserted", STAMUNIT_OCCURENCES, "Nr of inserted guest pages that were patched");
STAM_REG(pVM, &pVM->patm.s.StatPatchPageRemoved, STAMTYPE_COUNTER, "/PATM/Page/Removed", STAMUNIT_OCCURENCES, "Nr of removed guest pages that were patched");
STAM_REG(pVM, &pVM->patm.s.StatInstrDirty, STAMTYPE_COUNTER, "/PATM/Instr/Dirty/Detected", STAMUNIT_OCCURENCES, "Number of times instructions were marked dirty.");
STAM_REG(pVM, &pVM->patm.s.StatInstrDirtyGood, STAMTYPE_COUNTER, "/PATM/Instr/Dirty/Corrected", STAMUNIT_OCCURENCES, "Number of times instructions were marked dirty and corrected later on.");
STAM_REG(pVM, &pVM->patm.s.StatInstrDirtyBad, STAMTYPE_COUNTER, "/PATM/Instr/Dirty/Failed", STAMUNIT_OCCURENCES, "Number of times instructions were marked dirty and we were not able to correct them.");
STAM_REG(pVM, &pVM->patm.s.StatSysEnter, STAMTYPE_COUNTER, "/PATM/Emul/SysEnter", STAMUNIT_OCCURENCES, "Number of times sysenter was emulated.");
STAM_REG(pVM, &pVM->patm.s.StatSysExit, STAMTYPE_COUNTER, "/PATM/Emul/SysExit" , STAMUNIT_OCCURENCES, "Number of times sysexit was emulated.");
STAM_REG(pVM, &pVM->patm.s.StatEmulIret, STAMTYPE_COUNTER, "/PATM/Emul/Iret/Success", STAMUNIT_OCCURENCES, "Number of times iret was emulated.");
STAM_REG(pVM, &pVM->patm.s.StatEmulIretFailed, STAMTYPE_COUNTER, "/PATM/Emul/Iret/Failed", STAMUNIT_OCCURENCES, "Number of times iret was emulated.");
STAM_REG(pVM, &pVM->patm.s.StatGenRet, STAMTYPE_COUNTER, "/PATM/Gen/Ret" , STAMUNIT_OCCURENCES, "Number of generated ret instructions.");
STAM_REG(pVM, &pVM->patm.s.StatGenRetReused, STAMTYPE_COUNTER, "/PATM/Gen/RetReused" , STAMUNIT_OCCURENCES, "Number of reused ret instructions.");
STAM_REG(pVM, &pVM->patm.s.StatGenCall, STAMTYPE_COUNTER, "/PATM/Gen/Call", STAMUNIT_OCCURENCES, "Number of generated call instructions.");
STAM_REG(pVM, &pVM->patm.s.StatGenJump, STAMTYPE_COUNTER, "/PATM/Gen/Jmp" , STAMUNIT_OCCURENCES, "Number of generated indirect jump instructions.");
STAM_REG(pVM, &pVM->patm.s.StatGenPopf, STAMTYPE_COUNTER, "/PATM/Gen/Popf" , STAMUNIT_OCCURENCES, "Number of generated popf instructions.");
STAM_REG(pVM, &pVM->patm.s.StatCheckPendingIRQ, STAMTYPE_COUNTER, "/PATM/GC/CheckIRQ" , STAMUNIT_OCCURENCES, "Number of traps that ask to check for pending irqs.");
return rc;
/* The GC state, stack and statistics must be read/write for the guest (supervisor only of course). */
int rc = PGMMapSetPage(pVM, pVM->patm.s.pGCStateGC, PAGE_SIZE, X86_PTE_P | X86_PTE_A | X86_PTE_D | X86_PTE_RW);
rc = PGMMapSetPage(pVM, pVM->patm.s.pGCStackGC, PATM_STACK_TOTAL_SIZE, X86_PTE_P | X86_PTE_A | X86_PTE_D | X86_PTE_RW);
rc = PGMMapSetPage(pVM, pVM->patm.s.pStatsGC, PATM_STAT_MEMSIZE, X86_PTE_P | X86_PTE_A | X86_PTE_D | X86_PTE_RW);
return rc;
int rc;
#ifndef RT_ARCH_AMD64 /* would be nice if this was changed everywhere. was driving me crazy on AMD64. */
#ifndef PATM_DISABLE_ALL
AssertReleaseMsg(pVM->patm.s.pGCStateGC, ("Impossible! MMHyperHC2GC(%p) failed!\n", pVM->patm.s.pGCStateGC));
AssertReleaseMsg(pVM->patm.s.pGCStackGC, ("Impossible! MMHyperHC2GC(%p) failed!\n", pVM->patm.s.pGCStackGC));
AssertReleaseMsg(pVM->patm.s.pStatsGC, ("Impossible! MMHyperHC2GC(%p) failed!\n", pVM->patm.s.pStatsGC));
AssertReleaseMsg(pVM->patm.s.pPatchMemGC, ("Impossible! MMHyperHC2GC(%p) failed!\n", pVM->patm.s.pPatchMemHC));
rc = MMHyperAlloc(pVM, sizeof(PATMPATCHREC), 0, MM_TAG_PATM_PATCH, (void **)&pVM->patm.s.pGlobalPatchRec);
return VERR_NO_MEMORY;
return rc;
if (delta)
int rc;
RTAvloGCPtrDoWithAll(&pVM->patm.s.PatchLookupTreeHC->PatchTree, true, RelocatePatches, (void *)pVM);
return VINF_SUCCESS;
PPATMPATCHREC pPatchRec = (PPATMPATCHREC)RTAvloGCPtrRemoveBestFit(&pVM->patm.s.PatchLookupTreeHC->PatchTree, 0, true);
if (pPatchRec)
return rc;
if (size == 0)
return VERR_INVALID_PARAMETER;
* Trap/interrupt handler typically call common code on entry. Which might already have patches inserted.
* As we currently don't support calling patch code from patch code, we'll let it read the original opcode bytes instead.
for (int i=0;i<orgsize;i++)
pSrc++;
pDest++;
size--;
if (size == 0)
return VINF_SUCCESS;
#ifdef VBOX_STRICT
if (PAGE_ADDRESS(pDisInfo->pInstrGC) != PAGE_ADDRESS(pSrc + size - 1) && !PATMIsPatchGCAddr(pDisInfo->pVM, pSrc))
return VINF_SUCCESS;
#ifdef LOG_ENABLED
bool disret;
int rc;
#ifdef LOG_ENABLED
disret = PATMR3DISInstr(pVM, &pPatch->patch, &cpu, pPatch->patch.pPrivInstrGC, pPatch->patch.pPrivInstrHC, &opsize, szOutput, PATMREAD_RAWCODE);
if (pRec == 0)
case FIXUP_ABSOLUTE:
Log(("Absolute fixup at %VGv %VHv -> %VHv at %VGv\n", pRec->pSource, *(RTGCUINTPTR*)pRec->pRelocPos, *(RTGCINTPTR*)pRec->pRelocPos + delta, pRec->pRelocPos));
rc = PGMR3HandlerVirtualRegister(pVM, PGMVIRTHANDLERTYPE_ALL, pPage, pPage + (PAGE_SIZE - 1) /* inclusive! */, 0, patmVirtPageHandler, "PATMGCMonitorPage", 0, "PATMMonitorPatchJump");
case FIXUP_REL_JMPTOPATCH:
Log(("Relative fixup (g2p) %08X -> %08X at %08X (source=%08x, target=%08x)\n", *(int32_t*)pRec->pRelocPos, displ, pRec->pRelocPos, pRec->pSource, pRec->pDest));
rc = PGMR3HandlerVirtualRegister(pVM, PGMVIRTHANDLERTYPE_ALL, pPage, pPage + (PAGE_SIZE - 1) /* inclusive! */, 0, patmVirtPageHandler, "PATMGCMonitorPage", 0, "PATMMonitorPatchJump");
Log(("Skip the guest jump to patch code for this disabled patch %08X - %08X\n", pPatch->patch.pPrivInstrHC, pRec->pRelocPos));
case FIXUP_REL_JMPTOGUEST:
Log(("Relative fixup (p2g) %08X -> %08X at %08X (source=%08x, target=%08x)\n", *(int32_t*)pRec->pRelocPos, displ, pRec->pRelocPos, pRec->pSource, pRec->pDest));
return VERR_INVALID_PARAMETER;
#ifdef LOG_ENABLED
disret = PATMR3DISInstr(pVM, &pPatch->patch, &cpu, pPatch->patch.pPrivInstrGC, pPatch->patch.pPrivInstrHC, &opsize, szOutput, PATMREAD_RAWCODE);
static DECLCALLBACK(int) patmVirtPageHandler(PVM pVM, RTGCPTR GCPtr, void *pvPtr, void *pvBuf, size_t cbBuf, PGMACCESSTYPE enmAccessType, void *pvUser)
return VINF_PGM_HANDLER_DO_DEFAULT;
#ifdef VBOX_WITH_DEBUGGER
#ifdef VBOX_WITH_DEBUGGER
if (pcb)
if (pcb)
return (pAddrHC >= pVM->patm.s.pPatchMemHC && pAddrHC < pVM->patm.s.pPatchMemHC + pVM->patm.s.cbPatchMem) ? true : false;
return VINF_SUCCESS;
if (pVM->patm.s.pPatchMemGC <= pAddrGC && pVM->patm.s.pPatchMemGC + pVM->patm.s.cbPatchMem > pAddrGC)
return NULL;
int rc;
AssertMsg(rc == VINF_SUCCESS || rc == VERR_PAGE_NOT_PRESENT || rc == VERR_PAGE_TABLE_NOT_PRESENT, ("MMR3PhysGCVirt2HCVirtEx failed for %08X\n", pGCPtr));
return NULL;
return pHCPtr;
int nrJumpRecs = 0;
if (pRec == 0)
nrJumpRecs++;
int rc;
if (pBranchTargetGC == 0)
AssertMsgFailed(("patmr3SetBranchTargets: patmGuestGCPtrToPatchGCPtr failed for %08X\n", pRec->pTargetGC));
return VERR_PATCHING_REFUSED;
Log(("Set branch target %d to %08X : %08x - (%08x + %d + %d)\n", nrJumpRecs, displ, pBranchTargetGC, pInstrGC, pRec->offDispl, sizeof(RTGCPTR)));
return VINF_SUCCESS;
if (pRec)
/** @note Be extremely careful with this function. Make absolutely sure the guest address is correct! (to avoid executing instructions twice!) */
void patmr3AddP2GLookupRecord(PVM pVM, PPATCHINFO pPatch, uint8_t *pPatchInstrHC, RTGCPTR pInstrGC, PATM_LOOKUP_TYPE enmType, bool fDirty)
bool ret;
uint32_t PatchOffset = pPatchInstrHC - pVM->patm.s.pPatchMemHC; /* Offset in memory reserved for PATM. */
#ifdef VBOX_STRICT
pPatchToGuestRec = (PRECPATCHTOGUEST)MMR3HeapAllocZ(pVM, MM_TAG_PATM_PATCH, sizeof(RECPATCHTOGUEST) + sizeof(RECGUESTTOPATCH));
if (!pGuestToPatchRec)
uint32_t PatchOffset = pPatchInstrGC - pVM->patm.s.pPatchMemGC; /* Offset in memory reserved for PATM. */
if (pPatchToGuestRec)
static int patmAnalyseBlockCallback(PVM pVM, DISCPUSTATE *pCpu, GCPTRTYPE(uint8_t *) pInstrGC, GCPTRTYPE(uint8_t *) pCurInstrGC, void *pUserData)
bool fIllegalInstr = false;
//- no jumps in the instructions following cli (4+ bytes; enough for the replacement jump (5 bytes))
fIllegalInstr = true;
|| (OP_PARM_VTYPE(pCpu->pCurInstr->param1) != OP_PARM_J && !(pPatch->flags & PATMFL_SUPPORT_INDIRECT_CALLS))
fIllegalInstr = true;
/* An unconditional (short) jump right after a cli is a potential problem; we will overwrite whichever function comes afterwards */
if (pCurInstrGC > pPatch->pPrivInstrGC && pCurInstrGC + pCpu->opsize < pPatch->pPrivInstrGC + SIZEOF_NEARJUMP32) /* hardcoded patch jump size; cbPatchJump is still zero */
Log(("Dangerous unconditional jump ends in our generated patch jump!! (%x vs %x)\n", pCurInstrGC, pPatch->pPrivInstrGC));
fIllegalInstr = true;
fIllegalInstr = true;
if (pCpu->pCurInstr->opcode == OP_INT3 || pCpu->pCurInstr->opcode == OP_INT || pCpu->pCurInstr->opcode == OP_INTO)
fIllegalInstr = true;
return VINF_SUCCESS;
case OP_SYSEXIT:
case OP_SYSENTER:
case OP_ILLUD2:
return VINF_SUCCESS;
case OP_STI:
case OP_POPF:
if (pCurInstrGC > pPatch->pPrivInstrGC && pCurInstrGC < pPatch->pPrivInstrGC + SIZEOF_NEARJUMP32) /* hardcoded patch jump size; cbPatchJump is still zero */
return VERR_PATCHING_REFUSED;
return VINF_SUCCESS;
Log(("WARNING: End of block reached, but we need to duplicate some extra instruction to avoid a conflict with the patch jump\n"));
return VINF_SUCCESS;
case OP_IRET:
case OP_CPUID:
case OP_CALL:
case OP_JMP:
// If single instruction patch, we've copied enough instructions *and* the current instruction is not a relative jump
if ((pPatch->flags & PATMFL_CHECK_SIZE) && pPatch->cbPatchBlockSize > SIZEOF_NEARJUMP32 && !(pCpu->pCurInstr->optype & OPTYPE_RELATIVE_CONTROLFLOW))
return VINF_SUCCESS;
return VWRN_CONTINUE_ANALYSIS;
static int patmAnalyseFunctionCallback(PVM pVM, DISCPUSTATE *pCpu, GCPTRTYPE(uint8_t *) pInstrGC, GCPTRTYPE(uint8_t *) pCurInstrGC, void *pUserData)
bool fIllegalInstr = false;
Log(("Code block too big (%x) for function patch at %VGv!!\n", pPatch->cbPatchBlockSize, pCurInstrGC));
fIllegalInstr = true;
|| (OP_PARM_VTYPE(pCpu->pCurInstr->param1) != OP_PARM_J && !(pPatch->flags & PATMFL_SUPPORT_INDIRECT_CALLS))
fIllegalInstr = true;
fIllegalInstr = true;
if (pCpu->pCurInstr->opcode == OP_INT3 || pCpu->pCurInstr->opcode == OP_INT || pCpu->pCurInstr->opcode == OP_INTO)
fIllegalInstr = true;
return VERR_PATCHING_REFUSED;
return VINF_SUCCESS;
case OP_ILLUD2:
return VINF_SUCCESS;
case OP_IRET:
case OP_RETN:
return VINF_SUCCESS;
case OP_POPF:
case OP_STI:
return VWRN_CONTINUE_ANALYSIS;
return VWRN_CONTINUE_ANALYSIS;
return VWRN_CONTINUE_ANALYSIS;
static int patmRecompileCallback(PVM pVM, DISCPUSTATE *pCpu, GCPTRTYPE(uint8_t *) pInstrGC, GCPTRTYPE(uint8_t *) pCurInstrGC, void *pUserData)
&& !(pPatch->flags & PATMFL_RECOMPILE_NEXT)) /* do not do this when the next instruction *must* be executed! */
return rc;
patmr3AddP2GLookupRecord(pVM, pPatch, PATCHCODE_PTR_HC(pPatch) + pPatch->uCurPatchOffset, pCurInstrGC, PATM_LOOKUP_BOTHDIR);
goto end;
/* For our first attempt, we'll handle only simple relative jumps (immediate offset coded in instruction).
if (pTargetGC == 0)
return VERR_PATCHING_REFUSED;
goto end;
rc = patmPatchGenRelJump(pVM, pPatch, pTargetGC, pCpu->pCurInstr->opcode, !!(pCpu->prefix & PREFIX_OPSIZE));
goto end;
case OP_CLI:
/* If a cli is found while duplicating instructions for another patch, then it's of vital importance to continue
Log(("cli instruction found in other instruction patch block; force it to continue & find an exit point\n"));
case OP_MOV:
&& (pCpu->param1.flags & (USE_REG_GEN32|USE_REG_GEN16))) /** @todo memory operand must in theory be handled too */
goto duplicate_instr;
case OP_POP:
goto duplicate_instr;
case OP_STI:
fInhibitIRQInstr = true;
unsigned opsize;
int disret;
AssertFailed();
return VERR_PATCHING_REFUSED;
if (disret == false)
return VERR_PATCHING_REFUSED;
case OP_POPF:
fGenerateJmpBack = false;
rc = patmPatchGenPopf(pVM, pPatch, pCurInstrGC + pCpu->opsize, !!(pCpu->prefix & PREFIX_OPSIZE), fGenerateJmpBack);
if (fGenerateJmpBack == false)
case OP_PUSHF:
case OP_PUSH:
goto duplicate_instr;
case OP_IRET:
case OP_ILLUD2:
case OP_CPUID:
case OP_STR:
case OP_SLDT:
case OP_SGDT:
case OP_SIDT:
case OP_RETN:
case OP_SYSEXIT:
case OP_CALL:
/* In interrupt gate handlers it's possible to encounter jumps or calls when IF has been enabled again.
* In that case we'll jump to the original instruction and continue from there. Otherwise an int 3 is executed.
goto gen_illegal_instr;
case OP_JMP:
/* In interrupt gate handlers it's possible to encounter jumps or calls when IF has been enabled again.
* In that case we'll jump to the original instruction and continue from there. Otherwise an int 3 is executed.
goto gen_illegal_instr;
case OP_INT3:
case OP_INT:
case OP_INTO:
goto gen_illegal_instr;
case OP_MOV_DR:
/** @note: currently we let DRx writes cause a trap d; our trap handler will decide to interpret it or not. */
goto duplicate_instr;
case OP_MOV_CR:
/** @note: currently we let CRx writes cause a trap d; our trap handler will decide to interpret it or not. */
goto duplicate_instr;
end:
if ( !fInhibitIRQInstr
int rc2;
Log(("patmRecompileCallback: generate jump back to guest (%VGv) after fused instruction\n", pNextInstrGC));
// If single instruction patch, we've copied enough instructions *and* the current instruction is not a relative jump
&& !(pPatch->flags & PATMFL_RECOMPILE_NEXT) /* do not do this when the next instruction *must* be executed! */
Log(("patmRecompileCallback: end found for single instruction patch at %VGv opsize %d\n", pNextInstrGC, pCpu->opsize));
return rc;
#ifdef LOG_ENABLED
if (pRec)
int patmr3DisasmCallback(PVM pVM, DISCPUSTATE *pCpu, GCPTRTYPE(uint8_t *) pInstrGC, GCPTRTYPE(uint8_t *) pCurInstrGC, void *pUserData)
return VINF_SUCCESS;
return VWRN_CONTINUE_ANALYSIS;
/* the indirect call patch contains an 0xF/0xB illegal instr to call for assistance; check for this and continue */
return VWRN_CONTINUE_ANALYSIS;
return VINF_SUCCESS;
return VINF_SUCCESS;
return VWRN_CONTINUE_ANALYSIS;
* Disassembles the code stream until the callback function detects a failure or decides everything is acceptable
int patmr3DisasmCode(PVM pVM, GCPTRTYPE(uint8_t *) pInstrGC, GCPTRTYPE(uint8_t *) pCurInstrGC, PFN_PATMR3ANALYSE pfnPATMR3Disasm, void *pUserData)
bool disret;
goto end;
disret = PATMR3DISInstr(pVM, pPatch, &cpu, pCurInstrGC, pCurInstrHC, &opsize, szOutput, PATMREAD_RAWCODE);
goto end;
if (disret == false)
goto end;
/* For our first attempt, we'll handle only simple relative jumps and calls (immediate offset coded in instruction) */
if (pTargetGC == 0)
goto end;
goto end;
goto end;
end:
return rc;
* Disassembles the code stream until the callback function detects a failure or decides everything is acceptable
int patmr3DisasmCodeStream(PVM pVM, GCPTRTYPE(uint8_t *) pInstrGC, GCPTRTYPE(uint8_t *) pCurInstrGC, PFN_PATMR3ANALYSE pfnPATMR3Disasm, void *pUserData)
return rc;
* @note also checks for patch hints to make sure they can never be enabled if a conflict is present.
PPATCHINFO pTargetPatch = PATMFindActivePatchByEntrypoint(pVM, pConflictGC, true /* include patch hints */);
if (pTargetPatch)
return VERR_PATCH_NO_CONFLICT;
* Recompile the code stream until the callback function detects a failure or decides everything is acceptable
static int patmRecompileCodeStream(PVM pVM, GCPTRTYPE(uint8_t *) pInstrGC, GCPTRTYPE(uint8_t *) pCurInstrGC, PFN_PATMR3ANALYSE pfnPATMR3Recompile, void *pUserData)
bool disret;
#ifdef LOG_ENABLED
goto end;
#ifdef LOG_ENABLED
if (disret == false)
patmr3AddP2GLookupRecord(pVM, pPatch, PATCHCODE_PTR_HC(pPatch) + pPatch->uCurPatchOffset, pCurInstrGC, PATM_LOOKUP_BOTHDIR);
goto end;
/* If irqs are inhibited because of the current instruction, then we must make sure the next one is executed! */
/* Certain instructions (e.g. sti) force the next instruction to be executed before any interrupts can occur.
goto end;
if (disret == false)
goto end;
case OP_HLT:
goto end;
/** @todo continue with the instructions following the jump and then recompile the jump target code */
/* For our first attempt, we'll handle only simple relative jumps and calls (immediate offset coded in instruction). */
if (addr == 0)
/* We don't check if the branch target lies in a valid page as we've already done that in the analysis phase. */
* If we are jumping to an existing patch (or within 5 bytes of the entrypoint), then we must temporarily disable
if(pTargetPatch)
Log(("Found active patch at target %VGv (%VGv) -> temporarily disabling it!!\n", addr, pTargetPatch->pPrivInstrGC));
if(pTargetPatch)
goto end;
goto end;
end:
return rc;
int rc;
if (fAddFixup)
if (patmPatchAddReloc32(pVM, pPatch, &pPB[1], FIXUP_REL_JMPTOPATCH, pPatch->pPrivInstrGC + pPatch->cbPatchJump, pPatch->pPatchJumpDestGC) != VINF_SUCCESS)
return VERR_PATCHING_REFUSED;
*(uint32_t *)&temp[1] = (uint32_t)pPatch->pPatchJumpDestGC - ((uint32_t)pPatch->pPrivInstrGC + pPatch->cbPatchJump); //return address
if (fAddFixup)
if (patmPatchAddReloc32(pVM, pPatch, &pPB[2], FIXUP_REL_JMPTOPATCH, pPatch->pPrivInstrGC + pPatch->cbPatchJump, pPatch->pPatchJumpDestGC) != VINF_SUCCESS)
return VERR_PATCHING_REFUSED;
*(uint32_t *)&temp[2] = (uint32_t)pPatch->pPatchJumpDestGC - ((uint32_t)pPatch->pPrivInstrGC + pPatch->cbPatchJump); //return address
Assert(0);
return VERR_PATCHING_REFUSED;
if (fAddFixup)
if (patmPatchAddReloc32(pVM, pPatch, &pPB[1], FIXUP_REL_JMPTOPATCH, pPatch->pPrivInstrGC + SIZEOF_NEARJUMP32, PATCHCODE_PTR_GC(pPatch)) != VINF_SUCCESS)
return VERR_PATCHING_REFUSED;
*(uint32_t *)&temp[1] = (RTGCUINTPTR)PATCHCODE_PTR_GC(pPatch) - ((RTGCUINTPTR)pPatch->pPrivInstrGC + SIZEOF_NEARJUMP32); //return address
return rc;
#ifdef DEBUG
bool disret;
disret = PATMR3DISInstr(pVM, pPatch, &cpu, pPatch->pPrivInstrGC + i, &pPatch->pPrivInstrHC[i], &opsize, szOutput);
if (disret == false)
i += opsize;
/* Restore original code (privileged instruction + following instructions that were overwritten because of the 5/6 byte jmp). */
int rc = PGMPhysWriteGCPtrDirty(pVM, pPatch->pPrivInstrGC, pPatch->aPrivInstr, pPatch->cbPatchJump);
#ifdef DEBUG
bool disret;
disret = PATMR3DISInstr(pVM, pPatch, &cpu, pPatch->pPrivInstrGC + i, &pPatch->pPrivInstrHC[i], &opsize, szOutput);
if (disret == false)
i += opsize;
return rc;
int rc;
if (fAddFixup)
if (patmPatchAddReloc32(pVM, pPatch, &pPB[1], FIXUP_REL_JMPTOPATCH, pPatch->pPrivInstrGC + SIZEOF_NEARJUMP32, pTargetGC) != VINF_SUCCESS)
return VERR_PATCHING_REFUSED;
*(uint32_t *)&temp[1] = (uint32_t)pTargetGC - ((uint32_t)pPatch->pPrivInstrGC + SIZEOF_NEARJUMP32); //return address
return rc;
#ifdef LOG_ENABLED
bool disret;
switch (uOpcode)
case OP_MOV:
case OP_CLI:
case OP_PUSHF:
/* We can 'call' a cli or pushf patch. It will either return to the original guest code when IF is set again, or fault. */
return VERR_INVALID_PARAMETER;
if (!(pPatch->flags & (PATMFL_IDTHANDLER|PATMFL_IDTHANDLER_WITHOUT_ENTRYPOINT|PATMFL_SYSENTER|PATMFL_INT3_REPLACEMENT_BLOCK)))
/* If we're going to insert a patch jump, then the jump itself is not allowed to cross a page boundary. */
#ifdef DEBUG_sandervl
goto failure;
#ifdef PATM_ENABLE_CALL
if ((pPatch->flags & (PATMFL_IDTHANDLER|PATMFL_IDTHANDLER_WITHOUT_ENTRYPOINT|PATMFL_SYSENTER)) == PATMFL_IDTHANDLER)
goto failure;
/***************************************************************************************************************************/
/** @note We can't insert *any* code before a sysenter handler; some linux guests have an invalid stack at this point!!!!! */
/***************************************************************************************************************************/
#ifdef VBOX_WITH_STATISTICS
goto failure;
goto failure;
goto failure;
LogFlow(("Insert %VGv patch offset %VGv\n", pPatchRec->patch.pPrivInstrGC, pPatch->pPatchBlockOffset));
rc = RTAvloGCPtrInsert(&pVM->patm.s.PatchLookupTreeHC->PatchTreeByPatchAddr, &pPatchRec->CoreOffset);
if (!rc)
goto failure;
goto failure;
#ifdef LOG_ENABLED
patmr3DisasmCodeStream(pVM, PATCHCODE_PTR_GC(pPatch), PATCHCODE_PTR_GC(pPatch), patmr3DisasmCallback, pPatch);
goto failure;
Assert(!(pPatch->flags & (PATMFL_IDTHANDLER|PATMFL_IDTHANDLER_WITHOUT_ENTRYPOINT|PATMFL_SYSENTER|PATMFL_INT3_REPLACEMENT_BLOCK)));
goto failure;
#ifdef LOG_ENABLED
disret = PATMR3DISInstr(pVM, pPatch, &cpu, pPatch->pPrivInstrGC, pPatch->pPrivInstrHC, &opsize, szOutput, PATMREAD_RAWCODE);
Log(("Successfully installed %s patch at %VGv\n", patmGetInstructionString(pPatch->opcode, pPatch->flags), pInstrGC));
return VINF_SUCCESS;
return rc;
bool disret;
* and then jump to a common entrypoint. In order not to waste a lot of memory, we will check for this
int rc;
if ( disret
PPATMPATCHREC pJmpPatch = (PPATMPATCHREC)RTAvloGCPtrGet(&pVM->patm.s.PatchLookupTreeHC->PatchTree, pJmpInstrGC);
if (pJmpPatch == 0)
goto failure;
goto failure;
#ifdef VBOX_WITH_STATISTICS
goto failure;
goto failure;
patmr3AddP2GLookupRecord(pVM, pPatch, PATCHCODE_PTR_HC(pPatch) + pPatch->uCurPatchOffset, pInstrGC, PATM_LOOKUP_BOTHDIR);
goto failure;
goto failure;
#ifdef LOG_ENABLED
patmr3DisasmCodeStream(pVM, PATCHCODE_PTR_GC(pPatch), PATCHCODE_PTR_GC(pPatch), patmr3DisasmCallback, pPatch);
LogFlow(("Insert %VGv patch offset %VGv\n", pPatchRec->patch.pPrivInstrGC, pPatch->pPatchBlockOffset));
rc = RTAvloGCPtrInsert(&pVM->patm.s.PatchLookupTreeHC->PatchTreeByPatchAddr, &pPatchRec->CoreOffset);
return VINF_SUCCESS;
#ifdef LOG_ENABLED
bool disret;
#ifdef VBOX_WITH_STATISTICS
goto failure;
goto failure;
#ifdef LOG_ENABLED
patmr3DisasmCodeStream(pVM, PATCHCODE_PTR_GC(pPatch), PATCHCODE_PTR_GC(pPatch), patmr3DisasmCallback, pPatch);
#ifdef LOG_ENABLED
disret = PATMR3DISInstr(pVM, pPatch, &cpu, pPatch->pPrivInstrGC, pPatch->pPrivInstrHC, &opsize, szOutput);
LogFlow(("Insert %VGv patch offset %VGv\n", pPatchRec->patch.pPrivInstrGC, pPatch->pPatchBlockOffset));
rc = RTAvloGCPtrInsert(&pVM->patm.s.PatchLookupTreeHC->PatchTreeByPatchAddr, &pPatchRec->CoreOffset);
return VINF_SUCCESS;
return rc;
#ifdef DEBUG
return VERR_PATCHING_REFUSED;
#ifdef PATM_ENABLE_CALL
goto failure;
#ifdef VBOX_WITH_STATISTICS
goto failure;
goto failure;
LogFlow(("Insert %VGv patch offset %VGv\n", pPatchRec->patch.pPrivInstrGC, pPatch->pPatchBlockOffset));
rc = RTAvloGCPtrInsert(&pVM->patm.s.PatchLookupTreeHC->PatchTreeByPatchAddr, &pPatchRec->CoreOffset);
if (!rc)
goto failure;
goto failure;
#ifdef LOG_ENABLED
patmr3DisasmCodeStream(pVM, PATCHCODE_PTR_GC(pPatch), PATCHCODE_PTR_GC(pPatch), patmr3DisasmCallback, pPatch);
return VINF_SUCCESS;
return rc;
/* First we check if the duplicate function target lies in some existing function patch already. Will save some space. */
PPATMPATCHPAGE pPatchPage = (PPATMPATCHPAGE)RTAvloGCPtrGet(&pVM->patm.s.PatchLookupTreeHC->PatchTreeByPage, (RTGCPTR)pPage);
if (pPatchPage)
uint32_t i;
if (pPatchTargetGC)
PRECPATCHTOGUEST pPatchToGuestRec = (PRECPATCHTOGUEST)RTAvlU32GetBestFit(&pPatch->Patch2GuestAddrTree, offsetPatch, false);
Log(("patmCreateTrampoline: generating jump to code inside patch at %VGv\n", pPatch->pPrivInstrGC));
goto failure;
#ifdef VBOX_WITH_STATISTICS
goto failure;
goto failure;
LogFlow(("Insert %VGv patch offset %VGv\n", pPatchRec->patch.pPrivInstrGC, pPatch->pPatchBlockOffset));
rc = RTAvloGCPtrInsert(&pVM->patm.s.PatchLookupTreeHC->PatchTreeByPatchAddr, &pPatchRec->CoreOffset);
if (!rc)
goto failure;
return VINF_SUCCESS;
return rc;
int rc;
/* First we check if the duplicate function target lies in some existing function patch already. Will save some space. */
PPATMPATCHPAGE pPatchPage = (PPATMPATCHPAGE)RTAvloGCPtrGet(&pVM->patm.s.PatchLookupTreeHC->PatchTreeByPage, (RTGCPTR)pPage);
if (pPatchPage)
uint32_t i;
if (pPatchTargetGC)
if (pPatchTargetGC)
if (pPatchTargetGC)
/* We add a dummy entry into the lookup cache so we won't get bombarded with the same requests over and over again. */
return VINF_SUCCESS;
bool disret;
#ifdef LOG_ENABLED
Assert((pCpu->pCurInstr->opcode == OP_CALL || pCpu->pCurInstr->opcode == OP_JMP) && pCpu->opsize == SIZEOF_NEARJUMP32);
if ((pCpu->pCurInstr->opcode != OP_CALL && pCpu->pCurInstr->opcode != OP_JMP) || pCpu->opsize != SIZEOF_NEARJUMP32)
goto failure;
if (pTargetGC == 0)
goto failure;
pPatchFunction = (PPATMPATCHREC)RTAvloGCPtrGet(&pVM->patm.s.PatchLookupTreeHC->PatchTree, pTargetGC);
if (pTmpInstrHC == 0)
if (pTargetGC == 0)
pPatchFunction = (PPATMPATCHREC)RTAvloGCPtrGet(&pVM->patm.s.PatchLookupTreeHC->PatchTree, pTargetGC);
if (pPatchFunction == 0)
goto failure;
goto failure;
#ifdef LOG_ENABLED
disret = PATMR3DISInstr(pVM, pPatch, &cpu, pPatch->pPrivInstrGC, pPatch->pPrivInstrHC, &opsize, szOutput);
return VINF_SUCCESS;
return rc;
#ifdef LOG_ENABLED
bool disret;
goto failure;
goto failure;
if (patmPatchAddReloc32(pVM, pPatch, &pPB[pCpu->opsize - sizeof(RTGCPTR)], FIXUP_ABSOLUTE, pPatch->pPrivInstrGC, pVM->patm.s.mmio.pCachedData) != VINF_SUCCESS)
return VERR_PATCHING_REFUSED;
#ifdef LOG_ENABLED
disret = PATMR3DISInstr(pVM, pPatch, &cpu, pPatch->pPrivInstrGC, pPatch->pPrivInstrHC, &opsize, szOutput);
pPatch->cbPatchJump = pPatch->cbPrivInstr; /* bit of a misnomer in this case; size of replacement instruction. */
rc = PGMPhysWriteGCPtrDirty(pVM, pInstrGC + pCpu->opsize - sizeof(RTGCPTR), &pVM->patm.s.mmio.pCachedData, sizeof(RTGCPTR));
goto failure;
#ifdef LOG_ENABLED
disret = PATMR3DISInstr(pVM, pPatch, &cpu, pPatch->pPrivInstrGC, pPatch->pPrivInstrHC, &opsize, szOutput);
return VINF_SUCCESS;
return rc;
* Replace the address in an MMIO instruction with the cached version. (instruction is part of an existing patch)
bool disret;
#ifdef LOG_ENABLED
if (disret == false)
return VERR_PATCHING_REFUSED;
return VERR_PATCHING_REFUSED;
return VERR_PATCHING_REFUSED;
if (patmPatchAddReloc32(pVM, pPatch, &pInstrHC[cpu.opsize - sizeof(RTGCPTR)], FIXUP_ABSOLUTE) != VINF_SUCCESS)
return VERR_PATCHING_REFUSED;
#ifdef LOG_ENABLED
return VINF_SUCCESS;
int rc;
return rc;
int rc;
return rc;
* Replace an instruction with a breakpoint (0xCC), that is handled dynamically in the guest context.
PATMR3DECL(int) PATMR3PatchInstrInt3(PVM pVM, RTGCPTR pInstrGC, HCPTRTYPE(uint8_t *) pInstrHC, DISCPUSTATE *pCpu, PPATCHINFO pPatch)
int rc;
#ifdef LOG_ENABLED
pPatch->cbPatchJump = sizeof(ASMInt3); /* bit of a misnomer in this case; size of replacement instruction. */
goto failure;
return VINF_SUCCESS;
return VERR_PATCHING_REFUSED;
int patmPatchJump(PVM pVM, RTGCPTR pInstrGC, HCPTRTYPE(uint8_t *) pInstrHC, DISCPUSTATE *pCpu, PPATMPATCHREC pPatchRec)
#ifdef LOG_ENABLED
bool disret;
* Instruction replacements such as these should never be interrupted. I've added code to EM.cpp to
case OP_JO:
case OP_JNO:
case OP_JC:
case OP_JNC:
case OP_JE:
case OP_JNE:
case OP_JBE:
case OP_JNBE:
case OP_JS:
case OP_JNS:
case OP_JP:
case OP_JNP:
case OP_JL:
case OP_JNL:
case OP_JLE:
case OP_JNLE:
case OP_JMP:
goto failure;
goto failure;
goto failure;
goto failure;
* A conflict jump patch needs to be treated differently; we'll just replace the relative jump address with one that
RTGCPTR pJmpDest = PATMR3GuestGCPtrToPatchGCPtr(pVM, pInstrGC + pCpu->opsize + (int32_t)pCpu->param1.parval);
AssertMsg(pJmpDest, ("PATMR3GuestGCPtrToPatchGCPtr failed for %VGv\n", pInstrGC + pCpu->opsize + (int32_t)pCpu->param1.parval));
goto failure;
#ifdef LOG_ENABLED
disret = PATMR3DISInstr(pVM, pPatch, &cpu, pPatch->pPrivInstrGC, pPatch->pPrivInstrHC, &opsize, szOutput);
Log(("Successfully installed %s patch at %VGv\n", patmGetInstructionString(pPatch->opcode, pPatch->flags), pInstrGC));
return VINF_SUCCESS;
return rc;
bool disret;
int rc;
if (!pVM || pInstrGC == 0 || (flags & ~(PATMFL_CODE32|PATMFL_IDTHANDLER|PATMFL_INTHANDLER|PATMFL_SYSENTER|PATMFL_TRAPHANDLER|PATMFL_DUPLICATE_FUNCTION|PATMFL_REPLACE_FUNCTION_CALL|PATMFL_GUEST_SPECIFIC|PATMFL_INT3_REPLACEMENT|PATMFL_TRAPHANDLER_WITH_ERRORCODE|PATMFL_IDTHANDLER_WITHOUT_ENTRYPOINT|PATMFL_MMIO_ACCESS|PATMFL_TRAMPOLINE|PATMFL_INSTR_HINT|PATMFL_JUMP_CONFLICT)))
AssertFailed();
return VERR_INVALID_PARAMETER;
return VERR_PATCHING_REFUSED;
AssertReleaseMsg(pConflictPatch == 0, ("Unable to patch overwritten instruction at %VGv (%VGv)\n", pInstrGC, pConflictPatch->pPrivInstrGC));
if (pConflictPatch != 0)
return VERR_PATCHING_REFUSED;
return VERR_NOT_IMPLEMENTED;
return VERR_PATCHING_REFUSED;
Log(("PATMR3InstallPatch: code selector not wide open: %04x:%VGv != %VGv eflags=%08x\n", pCtx->cs, pInstrGCFlat, pInstrGC, pCtx->eflags.u32));
return VERR_PATCHING_REFUSED;
/** @note the OpenBSD specific check will break if we allow additional patches to be installed (int 3)) */
void *pvPatchCoreOffset;
pvPatchCoreOffset = RTAvloGCPtrGetBestFit(&pVM->patm.s.PatchLookupTreeHC->PatchTreeByPatchAddr, offset, false);
if (pPatchRec)
return VERR_PATCHING_REFUSED;
Log(("PATMR3InstallPatch: disable operation is pending for patch at %VGv\n", pPatchRec->patch.pPrivInstrGC));
/* A patch, for which we previously received a hint, will be enabled and turned into a normal patch. */
/** @todo we shouldn't disable and enable patches too often (it's relatively cheap, but pointless if it always happens) */
return VWRN_PATCH_ENABLED;
return rc;
if (pPatchRec->patch.flags & (PATMFL_DUPLICATE_FUNCTION|PATMFL_IDTHANDLER|PATMFL_MMIO_ACCESS|PATMFL_INT3_REPLACEMENT|PATMFL_INT3_REPLACEMENT_BLOCK))
pPatchRec->patch.flags |= (flags & (PATMFL_IDTHANDLER|PATMFL_TRAPHANDLER|PATMFL_INTHANDLER)); /* update the type */
AssertMsg(pPatchRec->patch.uState == PATCH_REFUSED || pPatchRec->patch.uState == PATCH_UNUSABLE, ("Patch an existing patched instruction?!? (%VGv, state=%d)\n", pInstrGC, pPatchRec->patch.uState));
return VERR_PATCHING_REFUSED;
return VERR_NO_MEMORY;
return rc;
/* Disallow patching instructions inside ROM code; complete function duplication is allowed though. */
return VERR_PATCHING_REFUSED;
if (!(pPatchRec->patch.flags & (PATMFL_DUPLICATE_FUNCTION | PATMFL_IDTHANDLER | PATMFL_SYSENTER | PATMFL_TRAMPOLINE)))
* Close proximity to an unusable patch is a possible hint that this patch would turn out to be dangerous too!
PPATMPATCHREC pPatchNear = (PPATMPATCHREC)RTAvloGCPtrGetBestFit(&pVM->patm.s.PatchLookupTreeHC->PatchTree, (pInstrGC + SIZEOF_NEARJUMP32 - 1), false);
if (pPatchNear)
if (pPatchNear->patch.uState == PATCH_UNUSABLE && pInstrGC < pPatchNear->patch.pPrivInstrGC && pInstrGC + SIZEOF_NEARJUMP32 > pPatchNear->patch.pPrivInstrGC)
Log(("Dangerous patch; would overwrite the ususable patch at %VGv\n", pPatchNear->patch.pPrivInstrGC));
* Leave the new patch active as it's marked unusable; to prevent us from checking it over and over again
return VERR_PATCHING_REFUSED;
pPatchRec->patch.pTempInfo = (PPATCHINFOTEMP)MMR3HeapAllocZ(pVM, MM_TAG_PATM_PATCH, sizeof(PATCHINFOTEMP));
return VERR_NO_MEMORY;
if (disret == false)
return VERR_PATCHING_REFUSED;
return VERR_PATCHING_REFUSED;
#ifdef VBOX_WITH_STATISTICS
case OP_SYSENTER:
case OP_PUSH:
Log(("PATMR3InstallPatch GUEST: %s %VGv code32=%d\n", patmGetInstructionString(pPatchRec->patch.opcode, pPatchRec->patch.flags), pInstrGC, (flags & PATMFL_CODE32) ? 1 : 0));
return rc;
case OP_SYSENTER:
Log(("PATMR3InstallPatch GUEST: %s %VGv code32=%d\n", patmGetInstructionString(pPatchRec->patch.opcode, pPatchRec->patch.flags), pInstrGC, (flags & PATMFL_CODE32) ? 1 : 0));
return VINF_SUCCESS;
case OP_JO:
case OP_JNO:
case OP_JC:
case OP_JNC:
case OP_JE:
case OP_JNE:
case OP_JBE:
case OP_JNBE:
case OP_JS:
case OP_JNS:
case OP_JP:
case OP_JNP:
case OP_JL:
case OP_JNL:
case OP_JLE:
case OP_JNLE:
case OP_JECXZ:
case OP_LOOP:
case OP_LOOPNE:
case OP_LOOPE:
case OP_JMP:
return VERR_NOT_IMPLEMENTED;
case OP_PUSHF:
case OP_CLI:
Log(("PATMR3InstallPatch %s %VGv code32=%d\n", patmGetInstructionString(pPatchRec->patch.opcode, pPatchRec->patch.flags), pInstrGC, (flags & PATMFL_CODE32) ? 1 : 0));
case OP_STR:
case OP_SGDT:
case OP_SLDT:
case OP_SIDT:
case OP_CPUID:
case OP_LSL:
case OP_LAR:
case OP_SMSW:
case OP_VERW:
case OP_VERR:
case OP_IRET:
return VERR_NOT_IMPLEMENTED;
Log(("Patch lowest %VGv highest %VGv\n", pPatchRec->patch.pInstrGCLowest, pPatchRec->patch.pInstrGCHighest));
Log(("Global lowest %VGv highest %VGv\n", pVM->patm.s.pPatchedInstrGCLowest, pVM->patm.s.pPatchedInstrGCHighest));
#ifdef VBOX_WITH_STATISTICS
STAMR3RegisterCallback(pVM, &pPatchRec->patch, STAMVISIBILITY_NOT_GUI, STAMUNIT_GOOD_BAD, patmResetStat, patmPrintStat, "Patch statistics",
#ifndef DEBUG_sandervl
STAMR3RegisterF(pVM, &pVM->patm.s.pStatsHC[pPatchRec->patch.uPatchIdx], STAMTYPE_RATIO_U32, STAMVISIBILITY_ALWAYS, STAMUNIT_GOOD_BAD, PATMPatchType(pVM, &pPatchRec->patch),
STAMR3RegisterF(pVM, &pPatchRec->patch.cbPatchBlockSize,STAMTYPE_U32, STAMVISIBILITY_ALWAYS, STAMUNIT_BYTES, NULL, "/PATM/Stats/PatchBD/0x%VGv/cbPatchBlockSize", pPatchRec->patch.pPrivInstrGC);
STAMR3RegisterF(pVM, &pPatchRec->patch.cbPatchJump, STAMTYPE_U32, STAMVISIBILITY_ALWAYS, STAMUNIT_BYTES, NULL, "/PATM/Stats/PatchBD/0x%VGv/cbPatchJump", pPatchRec->patch.pPrivInstrGC);
STAMR3RegisterF(pVM, &pPatchRec->patch.cbPrivInstr, STAMTYPE_U32, STAMVISIBILITY_ALWAYS, STAMUNIT_BYTES, NULL, "/PATM/Stats/PatchBD/0x%VGv/cbPrivInstr", pPatchRec->patch.pPrivInstrGC);
STAMR3RegisterF(pVM, &pPatchRec->patch.cCodeWrites, STAMTYPE_U32, STAMVISIBILITY_ALWAYS, STAMUNIT_OCCURENCES, NULL, "/PATM/Stats/PatchBD/0x%VGv/cCodeWrites", pPatchRec->patch.pPrivInstrGC);
STAMR3RegisterF(pVM, &pPatchRec->patch.cInvalidWrites, STAMTYPE_U32, STAMVISIBILITY_ALWAYS, STAMUNIT_OCCURENCES, NULL, "/PATM/Stats/PatchBD/0x%VGv/cInvalidWrites", pPatchRec->patch.pPrivInstrGC);
STAMR3RegisterF(pVM, &pPatchRec->patch.cTraps, STAMTYPE_U32, STAMVISIBILITY_ALWAYS, STAMUNIT_OCCURENCES, NULL, "/PATM/Stats/PatchBD/0x%VGv/cTraps", pPatchRec->patch.pPrivInstrGC);
STAMR3RegisterF(pVM, &pPatchRec->patch.flags, STAMTYPE_X32, STAMVISIBILITY_ALWAYS, STAMUNIT_NONE, NULL, "/PATM/Stats/PatchBD/0x%VGv/flags", pPatchRec->patch.pPrivInstrGC);
STAMR3RegisterF(pVM, &pPatchRec->patch.nrJumpRecs, STAMTYPE_U32, STAMVISIBILITY_ALWAYS, STAMUNIT_OCCURENCES, NULL, "/PATM/Stats/PatchBD/0x%VGv/nrJumpRecs", pPatchRec->patch.pPrivInstrGC);
STAMR3RegisterF(pVM, &pPatchRec->patch.nrFixups, STAMTYPE_U32, STAMVISIBILITY_ALWAYS, STAMUNIT_OCCURENCES, NULL, "/PATM/Stats/PatchBD/0x%VGv/nrFixups", pPatchRec->patch.pPrivInstrGC);
STAMR3RegisterF(pVM, &pPatchRec->patch.opcode, STAMTYPE_U32, STAMVISIBILITY_ALWAYS, STAMUNIT_OCCURENCES, NULL, "/PATM/Stats/PatchBD/0x%VGv/opcode", pPatchRec->patch.pPrivInstrGC);
STAMR3RegisterF(pVM, &pPatchRec->patch.uOldState, STAMTYPE_U32, STAMVISIBILITY_ALWAYS, STAMUNIT_NONE, NULL, "/PATM/Stats/PatchBD/0x%VGv/uOldState", pPatchRec->patch.pPrivInstrGC);
STAMR3RegisterF(pVM, &pPatchRec->patch.uOpMode, STAMTYPE_U32, STAMVISIBILITY_ALWAYS, STAMUNIT_NONE, NULL, "/PATM/Stats/PatchBD/0x%VGv/uOpMode", pPatchRec->patch.pPrivInstrGC);
STAMR3RegisterF(pVM, &pPatchRec->patch.uState, STAMTYPE_U32, STAMVISIBILITY_ALWAYS, STAMUNIT_NONE, NULL, "/PATM/Stats/PatchBD/0x%VGv/uState", pPatchRec->patch.pPrivInstrGC);
return rc;
bool disret;
disret = PATMR3DISInstr(pVM, pPatch, &cpu, pInstrGC, pInstrHC, &opsize, NULL, PATMREAD_ORGCODE | PATMREAD_NOCHECK);
if (disret)
return opsize;
int rc;
pPatchPage = (PPATMPATCHPAGE)RTAvloGCPtrGet(&pVM->patm.s.PatchLookupTreeHC->PatchTreeByPage, pPage);
if (pPatchPage)
rc = MMHyperAlloc(pVM, sizeof(PPATCHINFO)*pPatchPage->cMaxPatches, 0, MM_TAG_PATM_PATCH, (void **)&pPatchPage->aPatch);
return VERR_NO_MEMORY;
return VERR_NO_MEMORY;
rc = MMHyperAlloc(pVM, sizeof(PPATCHINFO)*PATMPATCHPAGE_PREALLOC_INCREMENT, 0, MM_TAG_PATM_PATCH, (void **)&pPatchPage->aPatch);
return VERR_NO_MEMORY;
PRECGUESTTOPATCH pGuestToPatchRec = (PRECGUESTTOPATCH)RTAvlGCPtrGetBestFit(&pPatch->Guest2PatchAddrTree, pPage, true);
if (pGuestToPatchRec)
/* If we're too close to the page boundary, then make sure an instruction from the previous page doesn't cross the boundary itself. */
pGuestToPatchRec = (PRECGUESTTOPATCH)RTAvlGCPtrGetBestFit(&pPatch->Guest2PatchAddrTree, pPage-1, false);
if (pGuestToPatchRec)
pGuestToPatchRec = (PRECGUESTTOPATCH)RTAvlGCPtrGetBestFit(&pPatch->Guest2PatchAddrTree, pPage+PAGE_SIZE-1, false);
if (pGuestToPatchRec)
return VINF_SUCCESS;
int rc;
pPatchPage = (PPATMPATCHPAGE)RTAvloGCPtrGet(&pVM->patm.s.PatchLookupTreeHC->PatchTreeByPage, pPage);
if (!pPatchPage)
return VERR_INVALID_PARAMETER;
uint32_t i;
memcpy(&pPatchPage->aPatch[i], &pPatchPage->aPatch[i+1], sizeof(PPATCHINFO)*(pPatchPage->cCount - (i+1)));
pPatchNode = (PPATMPATCHPAGE)RTAvloGCPtrRemove(&pVM->patm.s.PatchLookupTreeHC->PatchTreeByPage, pPage);
return VINF_SUCCESS;
* Insert page records for all guest pages that contain instructions that were recompiled for this patch
int rc;
/* Insert the pages that contain patched instructions into a lookup tree for detecting self-modifying code. */
PRECGUESTTOPATCH pGuestToPatchRec = (PRECGUESTTOPATCH)RTAvlGCPtrGetBestFit(&pPatch->Guest2PatchAddrTree, pPage, true);
if ( pGuestToPatchRec
return VINF_SUCCESS;
* Remove page records for all guest pages that contain instructions that were recompiled for this patch
int rc;
/* Insert the pages that contain patched instructions into a lookup tree for detecting self-modifying code. */
PRECGUESTTOPATCH pGuestToPatchRec = (PRECGUESTTOPATCH)RTAvlGCPtrGetBestFit(&pPatch->Guest2PatchAddrTree, pPage, true);
if ( pGuestToPatchRec
&& PAGE_ADDRESS(pGuestToPatchRec->Core.Key) == PAGE_ADDRESS(pPage) /** @todo bird: PAGE_ADDRESS is for the current context really. check out these. */
return VINF_SUCCESS;
return VINF_SUCCESS;
PPATMPATCHPAGE pPatchPage = (PPATMPATCHPAGE)RTAvloGCPtrGet(&pVM->patm.s.PatchLookupTreeHC->PatchTreeByPage, (RTGCPTR)pPage);
if (pPatchPage)
uint32_t i;
bool fValidPatchWrite = false;
goto loop_start;
if (pPatchInstrGC)
uint32_t PatchOffset = pPatchInstrGC - pVM->patm.s.pPatchMemGC; /* Offset in memory reserved for PATM. */
fValidPatchWrite = true;
PRECPATCHTOGUEST pPatchToGuestRec = (PRECPATCHTOGUEST)RTAvlU32Get(&pPatch->Patch2GuestAddrTree, PatchOffset);
LogRel(("PATM: Disable block at %VGv - write %VGv-%VGv\n", pPatch->pPrivInstrGC, pGuestPtrGC, pGuestPtrGC+cbWrite));
goto loop_start;
/* Replace the patch instruction with a breakpoint; when it's hit, then we'll attempt to recompile the instruction again. */
if (fValidPatchWrite == false)
pPatchPage = (PPATMPATCHPAGE)RTAvloGCPtrGet(&pVM->patm.s.PatchLookupTreeHC->PatchTreeByPage, (RTGCPTR)pPage);
if (pPatchPage)
LogRel(("PATM: Stop monitoring IDT handler pages at %VGv - invalid write %VGv-%VGv \n", pPatch->pPrivInstrGC, GCPtr, GCPtr+cbWrite));
LogRel(("PATM: Disable block at %VGv - invalid write %VGv-%VGv \n", pPatch->pPrivInstrGC, GCPtr, GCPtr+cbWrite));
goto invalid_write_loop_start;
return VINF_SUCCESS;
/** @note Currently only called by CSAMR3FlushPage; optimization to avoid having to double check if the physical address has changed
PPATMPATCHPAGE pPatchPage = (PPATMPATCHPAGE)RTAvloGCPtrGet(&pVM->patm.s.PatchLookupTreeHC->PatchTreeByPage, addr);
if (pPatchPage)
return VINF_SUCCESS;
return VERR_PATCH_NOT_FOUND;
pPatchRec = (PPATMPATCHREC)RTAvloGCPtrGetBestFit(&pVM->patm.s.PatchLookupTreeHC->PatchTree, pInstrGC, false);
// if the patch is enabled and the pointer lies within 5 bytes of this priv instr ptr, then we've got a hit!
if ( pPatchRec
return VINF_SUCCESS;
return VERR_PATCH_NOT_FOUND;
if (pPatchRec)
return VINF_SUCCESS;
/* Mark the entry with a breakpoint in case somebody else calls it later on (cli patch used as a function, function, trampoline or idt patches) */
Assert(!(pPatch->flags & (PATMFL_DUPLICATE_FUNCTION|PATMFL_IDTHANDLER|PATMFL_TRAMPOLINE|PATMFL_INT3_REPLACEMENT|PATMFL_INT3_REPLACEMENT_BLOCK)));
rc = PGMPhysGCPtr2HCPtr(pVM, pPatchRec->patch.pPrivInstrGC, (PRTHCPTR)&pPatchRec->patch.pPrivInstrHC);
RTGCINTPTR displ = (RTGCUINTPTR)PATCHCODE_PTR_GC(pPatch) - ((RTGCUINTPTR)pPatch->pPrivInstrGC + SIZEOF_NEARJUMP32);
return VWRN_PATCH_REMOVED;
return VERR_PATCH_ALREADY_DISABLED;
return VWRN_PATCH_REMOVED;
/* Just to be safe, let's make sure this one can never be reused; the patch might be marked dirty already (int3 at start) */
return VINF_SUCCESS;
return VERR_PATCH_NOT_FOUND;
static int patmDisableUnusablePatch(PVM pVM, RTGCPTR pInstrGC, RTGCPTR pConflictAddr, PPATCHINFO pConflictPatch)
bool disret;
int rc;
* If it's a 5 byte relative jump, then we can work around the problem by replacing the 32 bits relative offset
if ( disret == true
return rc;
return VINF_SUCCESS;
Log(("PATM -> CONFLICT: Found active patch at instruction %VGv with target %VGv -> turn into int 3 patch!!\n", pInstrGC, pConflictPatch->pPrivInstrGC));
return VINF_SUCCESS;
return VINF_SUCCESS;
Log(("PATM -> CONFLICT: Found active patch at instruction %VGv with target %VGv -> DISABLING it!!\n", pInstrGC, pConflictPatch->pPrivInstrGC));
return VINF_SUCCESS;
return VERR_PATCH_DISABLED;
if (pPatchRec)
rc = PGMPhysGCPtr2HCPtr(pVM, pPatchRec->patch.pPrivInstrGC, (PRTHCPTR)&pPatchRec->patch.pPrivInstrHC);
#ifdef DEBUG
return VERR_PATCH_NOT_FOUND;
return rc;
#ifdef DEBUG
bool disret;
disret = PATMR3DISInstr(pVM, pPatch, &cpu, pPatch->pPrivInstrGC + i, &pPatch->pPrivInstrHC[i], &opsize, szOutput);
i += opsize;
return VERR_PATCH_NOT_FOUND;
return rc;
return rc;
return VERR_PATCH_NOT_FOUND;
pNode = RTAvloGCPtrRemove(&pVM->patm.s.PatchLookupTreeHC->PatchTreeByPatchAddr, pPatchRec->patch.pPatchBlockOffset);
#ifdef VBOX_WITH_STATISTICS
#ifndef DEBUG_sandervl
/** @note no need to free Guest2PatchAddrTree as those records share memory with Patch2GuestAddrTree records. */
return VINF_SUCCESS;
int rc;
AssertReturn(pPatch->flags & (PATMFL_DUPLICATE_FUNCTION|PATMFL_IDTHANDLER|PATMFL_TRAPHANDLER), VERR_PATCHING_REFUSED);
/** Note: quite ugly to enable/disable/remove/insert old and new patches, but there's no easy way around it. */
#ifdef VBOX_WITH_STATISTICS
#ifndef DEBUG_sandervl
/** Note: We don't attempt to reuse patch memory here as it's quite common that the new code block requires more memory. */
rc = PATMR3InstallPatch(pVM, pInstrGC, pPatch->flags & (PATMFL_CODE32|PATMFL_IDTHANDLER|PATMFL_INTHANDLER|PATMFL_TRAPHANDLER|PATMFL_DUPLICATE_FUNCTION|PATMFL_TRAPHANDLER_WITH_ERRORCODE|PATMFL_IDTHANDLER_WITHOUT_ENTRYPOINT));
if (!pPatchTargetGC)
goto failure;
goto failure;
/* Put the new patch back into the tree, because removing the old one kicked this one out. (hack alert) */
LogRel(("PATM: patmR3RefreshPatch: failed to refresh patch at %VGv. Reactiving old one. \n", pInstrGC));
return rc;
* @param pInstr Guest context point to instruction that might lie within 5 bytes of an existing patch jump
PPATMPATCHREC pPatchRec = (PPATMPATCHREC)RTAvloGCPtrGetBestFit(&pVM->patm.s.PatchLookupTreeHC->PatchTree, pInstrGC, false);
/* if the patch is enabled, the pointer is not indentical to the privileged patch ptr and it lies within 5 bytes of this priv instr ptr, then we've got a hit! */
if (pPatchRec)
if ( fIncludeHints
return NULL;
*pPatchAddr = 0;
if (pPatch)
return *pPatchAddr == 0 ? false : true;
if (pPatchRec)
return VINF_SUCCESS;
AssertFailed();
return VERR_PATCH_NOT_FOUND;
return VINF_SUCCESS;
/* Paranoia; make sure this patch is not somewhere in the callchain, so prevent ret instructions from succeeding. */
return VINF_SUCCESS;
PRECPATCHTOGUEST pPatchToGuestRec = (PRECPATCHTOGUEST)RTAvlU32GetBestFit(&pPatch->Patch2GuestAddrTree, pPatchGC - pVM->patm.s.pPatchMemGC, false);
if (pPatchToGuestRec)
PRECGUESTTOPATCH pGuestToPatchRec = (PRECGUESTTOPATCH)RTAvlGCPtrGet(&pPatch->Guest2PatchAddrTree, pInstrGC);
if (pGuestToPatchRec)
PPATMPATCHREC pPatchRec = (PPATMPATCHREC)RTAvloGCPtrGetBestFit(&pVM->patm.s.PatchLookupTreeHC->PatchTree, pInstrGC, false);
if (pPatchRec && pPatchRec->patch.uState == PATCH_ENABLED && pInstrGC >= pPatchRec->patch.pPrivInstrGC)
void *pvPatchCoreOffset;
pvPatchCoreOffset = RTAvloGCPtrGetBestFit(&pVM->patm.s.PatchLookupTreeHC->PatchTreeByPatchAddr, pPatchGC - pVM->patm.s.pPatchMemGC, false);
if (pvPatchCoreOffset == 0)
Log(("PATMR3PatchToGCPtr failed for %VGv offset %x\n", pPatchGC, pPatchGC - pVM->patm.s.pPatchMemGC));
if (pEnmState)
if ( !pPrivInstrGC
pPrivInstrGC = 0;
return pPrivInstrGC;
if (pPatchRec && (pPatchRec->patch.uState == PATCH_ENABLED || pPatchRec->patch.uState == PATCH_DIRTY))
static int patmR3HandleDirtyInstr(PVM pVM, PCPUMCTX pCtx, PPATMPATCHREC pPatch, PRECPATCHTOGUEST pPatchToGuestRec, RTGCPTR pEip)
int rc;
Log(("patmR3HandleDirtyInstr: dirty instruction at %VGv (%VGv)\n", pEip, pPatchToGuestRec->pOrgInstrGC));
cbDirty = 0;
LogRel(("PATM: patmR3HandleDirtyInstr: dirty instruction at %VGv (%VGv) ignored, because instruction in function was reused as target of jump\n", pEip, pPatchToGuestRec->pOrgInstrGC));
return VERR_PATCHING_REFUSED;
#ifdef DEBUG
pRec = (PRECPATCHTOGUEST)RTAvlU32GetBestFit(&pPatch->patch.Patch2GuestAddrTree, pCurPatchInstrGC - pVM->patm.s.pPatchMemGC, true);
bool fValidInstr;
if ( !fValidInstr
fValidInstr = true;
&& fValidInstr
#ifdef DEBUG
#ifdef DEBUG
/** @todo in theory we need to restore the lookup records for the remaining dirty instructions too! */
LogRel(("PATM: Failed to refresh dirty patch at %VGv. Disabling it.\n", pPatch->patch.pPrivInstrGC));
/* Even if we succeed, we must go back to the original instruction as the patched one could be invalid. */
return rc;
void *pvPatchCoreOffset;
int rc ;
pNewEip = 0;
*ppNewEip = 0;
pvPatchCoreOffset = RTAvloGCPtrGetBestFit(&pVM->patm.s.PatchLookupTreeHC->PatchTreeByPatchAddr, offset, false);
if (pvPatchCoreOffset)
Log(("PATMR3HandleTrap: disable operation is pending for patch at %VGv\n", pPatch->patch.pPrivInstrGC));
AssertReleaseMsg(rc != VWRN_PATCH_REMOVED, ("PATMR3DisablePatch removed patch at %VGv\n", pPrivInstrGC));
AssertMsg(pPatch->patch.uState == PATCH_DISABLED || pPatch->patch.uState == PATCH_UNUSABLE, ("Unexpected failure to disable patch state=%d rc=%Vrc\n", pPatch->patch.uState, rc));
pPatchToGuestRec = (PRECPATCHTOGUEST)RTAvlU32GetBestFit(&pPatch->patch.Patch2GuestAddrTree, offset, false);
AssertReleaseMsg(pPatchToGuestRec, ("PATMR3HandleTrap: Unable to find corresponding guest address for %VGv (offset %x)\n", pEip, offset));
AssertReleaseMsg(pVM->patm.s.pGCStateHC->fPIF == 0, ("PATMR3HandleTrap: Unable to find translation record for %VGv (PIF=0)\n", pEip));
return VINF_SUCCESS;
return VINF_PATCH_CONTINUE;
if (pPatch)
("Crash in patch code %VGv (%VGv) esp=%RX32\nPatch state=%x flags=%x fDirty=%d\n%s\n", pEip, pNewEip, CPUMGetGuestESP(pVM), pPatch->patch.uState, pPatch->patch.flags, pPatchToGuestRec->fDirty, szBuf));
if (pvPatchCoreOffset == 0)
return rc;
#ifdef VBOX_STRICT
bool disret;
disret = PATMR3DISInstr(pVM, &pPatch->patch, &cpu, pNewEip, PATMGCVirtToHCVirt(pVM, &pPatch->patch, pNewEip), &opsize, NULL, PATMREAD_RAWCODE);
int rc;
Log(("Expected return address %VGv found address %VGv Psp=%x\n", pVM->patm.s.pGCStackHC[(pVM->patm.s.pGCStateHC->Psp+PATM_STACK_SIZE)/sizeof(RTGCPTR)], retaddr, pVM->patm.s.pGCStateHC->Psp));
#ifdef VBOX_STRICT
bool disret;
disret = PATMR3DISInstr(pVM, &pPatch->patch, &cpu, pNewEip, PATMGCVirtToHCVirt(pVM, &pPatch->patch, pNewEip), &opsize, NULL, PATMREAD_ORGCODE);
if (disret && (cpu.pCurInstr->opcode == OP_SYSEXIT || cpu.pCurInstr->opcode == OP_HLT || cpu.pCurInstr->opcode == OP_INT3))
disret = PATMR3DISInstr(pVM, &pPatch->patch, &cpu, pNewEip, PATMGCVirtToHCVirt(pVM, &pPatch->patch, pNewEip), &opsize, NULL, PATMREAD_RAWCODE);
Assert(cpu.pCurInstr->opcode == OP_SYSEXIT || cpu.pCurInstr->opcode == OP_HLT || cpu.pCurInstr->opcode == OP_IRET);
Log2(("pPatchBlockGC %VGv - pEip %VGv corresponding GC address %VGv\n", PATCHCODE_PTR_GC(&pPatch->patch), pEip, pNewEip));
if (pNewEip >= pPatch->patch.pPrivInstrGC && pNewEip < pPatch->patch.pPrivInstrGC + pPatch->patch.cbPatchJump)
Log(("Disabling patch at location %VGv due to trap too close to the privileged instruction \n", pPatch->patch.pPrivInstrGC));
return VERR_PATCH_DISABLED;
/** @todo compare to nr of successful runs. add some aging algorithm and determine the best time to disable the patch */
Log(("Disabling patch at location %VGv due to too many traps inside patch code\n", pPatch->patch.pPrivInstrGC));
return VERR_PATCH_DISABLED;
return VINF_SUCCESS;
PPATMPATCHREC pPatchRec = (PPATMPATCHREC)RTAvloGCPtrGetBestFit(&pVM->patm.s.PatchLookupTreeHC->PatchTree, addr, false);
if (pPatchRec && pPatchRec->patch.uState == PATCH_ENABLED && PAGE_ADDRESS(pPatchRec->patch.pPrivInstrGC) == PAGE_ADDRESS(addr))
return VINF_SUCCESS;
addr++;
pPatchRec = (PPATMPATCHREC)RTAvloGCPtrGetBestFit(&pVM->patm.s.PatchLookupTreeHC->PatchTree, addr, true);
return VINF_SUCCESS;
#ifdef VBOX_WITH_STATISTICS
RTStrPrintf(szTrap, sizeof(szTrap), (pPatch->flags & PATMFL_INTHANDLER) ? "INT-%2X" : "TRAP-%2X", iGate);
return szTrap;
case PATCH_ENABLED:
case PATCH_DISABLED:
case PATCH_DIRTY:
case PATCH_UNUSABLE:
case PATCH_REFUSED:
case PATCH_DISABLE_PENDING:
AssertFailed();
return pVM->patm.s.pStatsGC + sizeof(STAMRATIOU32) * pPatch->uPatchIdx + RT_OFFSETOF(STAMRATIOU32, u32A);
#ifdef VBOX_WITH_DEBUGGER
static DECLCALLBACK(int) patmr3CmdOff(PCDBGCCMD pCmd, PDBGCCMDHLP pCmdHlp, PVM pVM, PCDBGCVAR paArgs, unsigned cArgs, PDBGCVAR pResult)
if (!pVM)
static DECLCALLBACK(int) patmr3CmdOn(PCDBGCCMD pCmd, PDBGCCMDHLP pCmdHlp, PVM pVM, PCDBGCVAR paArgs, unsigned cArgs, PDBGCVAR pResult)
if (!pVM)