VirtualBox

source: vbox/trunk/src/VBox/VMM/PATM/CSAM.cpp@ 1

Last change on this file since 1 was 1, checked in by vboxsync, 55 years ago

import

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 85.8 KB
Line 
1/** @file
2 *
3 * CSAM - Guest OS Code Scanning and Analysis Manager
4 */
5
6/*
7 * Copyright (C) 2006 InnoTek Systemberatung GmbH
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License as published by the Free Software Foundation,
13 * in version 2 as it comes in the "COPYING" file of the VirtualBox OSE
14 * distribution. VirtualBox OSE is distributed in the hope that it will
15 * be useful, but WITHOUT ANY WARRANTY of any kind.
16 *
17 * If you received this file as part of a commercial VirtualBox
18 * distribution, then only the terms of your commercial VirtualBox
19 * license agreement apply instead of the previous paragraph.
20 */
21
22/*******************************************************************************
23* Header Files *
24*******************************************************************************/
25#define LOG_GROUP LOG_GROUP_CSAM
26#include <VBox/cpum.h>
27#include <VBox/stam.h>
28#include <VBox/patm.h>
29#include <VBox/csam.h>
30#include <VBox/pgm.h>
31#include <VBox/iom.h>
32#include <VBox/sup.h>
33#include <VBox/mm.h>
34#include <VBox/em.h>
35#include <VBox/rem.h>
36#include <VBox/selm.h>
37#include <VBox/trpm.h>
38#include <VBox/param.h>
39#include <iprt/avl.h>
40#include <iprt/asm.h>
41#include <iprt/thread.h>
42#include "CSAMInternal.h"
43#include <VBox/vm.h>
44#include <VBox/dbg.h>
45#include <VBox/err.h>
46#include <VBox/ssm.h>
47#include <VBox/log.h>
48#include <iprt/assert.h>
49#include <VBox/dis.h>
50#include <VBox/disopcode.h>
51#include <string.h>
52#include <stdlib.h>
53#include <stdio.h>
54
55
56/* Enabled by default */
57#define CSAM_ENABLE
58
59/* Enable to monitor code pages for self-modifying code. */
60#define CSAM_MONITOR_CODE_PAGES
61/* Enable to monitor all scanned pages
62#define CSAM_MONITOR_CSAM_CODE_PAGES */
63
64/*******************************************************************************
65* Internal Functions *
66*******************************************************************************/
67static DECLCALLBACK(int) csamr3Save(PVM pVM, PSSMHANDLE pSSM);
68static DECLCALLBACK(int) csamr3Load(PVM pVM, PSSMHANDLE pSSM, uint32_t u32Version);
69static DECLCALLBACK(int) CSAMCodePageWriteHandler(PVM pVM, RTGCPTR GCPtr, void *pvPtr, void *pvBuf, size_t cbBuf, PGMACCESSTYPE enmAccessType, void *pvUser);
70static DECLCALLBACK(int) CSAMCodePageInvalidate(PVM pVM, RTGCPTR GCPtr);
71
72bool csamIsCodeScanned(PVM pVM, RTGCPTR pInstr, PCSAMPAGE *pPage);
73int csamR3FlushPageRecord(PVM pVM, PCSAMPAGE pPage);
74int csamR3CheckPageRecord(PVM pVM, RTGCPTR pInstr);
75static PCSAMPAGE csamCreatePageRecord(PVM pVM, RTGCPTR GCPtr, CSAMTAG enmTag, bool fCode32, bool fMonitorInvalidation = false);
76static int csamRemovePageRecord(PVM pVM, RTGCPTR GCPtr);
77static int csamReinit(PVM pVM);
78static void csamMarkCode(PVM pVM, PCSAMPAGE pPage, RTGCPTR pInstr, uint32_t opsize, bool fScanned);
79static int csamAnalyseCodeStream(PVM pVM, GCPTRTYPE(uint8_t *) pInstrGC, GCPTRTYPE(uint8_t *) pCurInstrGC, bool fCode32,
80 PFN_CSAMR3ANALYSE pfnCSAMR3Analyse, void *pUserData, PCSAMP2GLOOKUPREC pCacheRec);
81
82/** @todo Temporary for debugging. */
83static bool fInCSAMCodePageInvalidate = false;
84
85/*******************************************************************************
86* Global Variables *
87*******************************************************************************/
88#ifdef VBOX_WITH_DEBUGGER
89static DECLCALLBACK(int) csamr3CmdOn(PCDBGCCMD pCmd, PDBGCCMDHLP pCmdHlp, PVM pVM, PCDBGCVAR paArgs, unsigned cArgs, PDBGCVAR pResult);
90static DECLCALLBACK(int) csamr3CmdOff(PCDBGCCMD pCmd, PDBGCCMDHLP pCmdHlp, PVM pVM, PCDBGCVAR paArgs, unsigned cArgs, PDBGCVAR pResult);
91
92/** Command descriptors. */
93static const DBGCCMD g_aCmds[] =
94{
95 /* pszCmd, cArgsMin, cArgsMax, paArgDesc, cArgDescs, pResultDesc, fFlags, pfnHandler pszSyntax, ....pszDescription */
96 { "csamon", 0, 0, NULL, 0, NULL, 0, csamr3CmdOn, "", "Enable CSAM code scanning." },
97 { "csamoff", 0, 0, NULL, 0, NULL, 0, csamr3CmdOff, "", "Disable CSAM code scanning." },
98};
99#endif
100
101
102/**
103 * Initializes the CSAM.
104 *
105 * @returns VBox status code.
106 * @param pVM The VM to operate on.
107 */
108CSAMR3DECL(int) CSAMR3Init(PVM pVM)
109{
110 int rc;
111
112 LogFlow(("CSAMR3Init\n"));
113
114 /* Allocate bitmap for the page directory. */
115 rc = MMR3HyperAllocOnceNoRel(pVM, CSAM_PGDIRBMP_CHUNKS*sizeof(RTHCPTR), 0, MM_TAG_CSAM, (void **)&pVM->csam.s.pPDBitmapHC);
116 AssertRCReturn(rc, rc);
117 rc = MMR3HyperAllocOnceNoRel(pVM, CSAM_PGDIRBMP_CHUNKS*sizeof(RTGCPTR), 0, MM_TAG_CSAM, (void **)&pVM->csam.s.pPDGCBitmapHC);
118 AssertRCReturn(rc, rc);
119 pVM->csam.s.pPDBitmapGC = MMHyperHC2GC(pVM, pVM->csam.s.pPDGCBitmapHC);
120 pVM->csam.s.pPDHCBitmapGC = MMHyperHC2GC(pVM, pVM->csam.s.pPDBitmapHC);
121
122 rc = csamReinit(pVM);
123 AssertRCReturn(rc, rc);
124
125 /*
126 * Register save and load state notificators.
127 */
128 rc = SSMR3RegisterInternal(pVM, "CSAM", 0, CSAM_SSM_VERSION, sizeof(pVM->csam.s) + PAGE_SIZE*16,
129 NULL, csamr3Save, NULL,
130 NULL, csamr3Load, NULL);
131 AssertRCReturn(rc, rc);
132
133 STAM_REG(pVM, &pVM->csam.s.StatNrTraps, STAMTYPE_COUNTER, "/CSAM/PageTraps", STAMUNIT_OCCURENCES, "The number of CSAM page traps.");
134 STAM_REG(pVM, &pVM->csam.s.StatDangerousWrite, STAMTYPE_COUNTER, "/CSAM/DangerousWrites", STAMUNIT_OCCURENCES, "The number of dangerous writes that cause a context switch.");
135
136 STAM_REG(pVM, &pVM->csam.s.StatNrPageNPHC, STAMTYPE_COUNTER, "/CSAM/HC/PageNotPresent", STAMUNIT_OCCURENCES, "The number of CSAM pages marked not present.");
137 STAM_REG(pVM, &pVM->csam.s.StatNrPageNPGC, STAMTYPE_COUNTER, "/CSAM/GC/PageNotPresent", STAMUNIT_OCCURENCES, "The number of CSAM pages marked not present.");
138 STAM_REG(pVM, &pVM->csam.s.StatNrPages, STAMTYPE_COUNTER, "/CSAM/PageRec/AddedRW", STAMUNIT_OCCURENCES, "The number of CSAM page records (RW monitoring).");
139 STAM_REG(pVM, &pVM->csam.s.StatNrPagesInv, STAMTYPE_COUNTER, "/CSAM/PageRec/AddedRWI", STAMUNIT_OCCURENCES, "The number of CSAM page records (RW & invalidation monitoring).");
140 STAM_REG(pVM, &pVM->csam.s.StatNrRemovedPages, STAMTYPE_COUNTER, "/CSAM/PageRec/Removed", STAMUNIT_OCCURENCES, "The number of removed CSAM page records.");
141 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.");
142
143 STAM_REG(pVM, &pVM->csam.s.StatNrPatchPages, STAMTYPE_COUNTER, "/CSAM/PageRec/Patch", STAMUNIT_OCCURENCES, "The number of CSAM patch page records.");
144 STAM_REG(pVM, &pVM->csam.s.StatNrUserPages, STAMTYPE_COUNTER, "/CSAM/PageRec/Ignore/User", STAMUNIT_OCCURENCES, "The number of CSAM user page records (ignored).");
145 STAM_REG(pVM, &pVM->csam.s.StatPagePATM, STAMTYPE_COUNTER, "/CSAM/PageRec/Type/PATM", STAMUNIT_OCCURENCES, "The number of PATM page records.");
146 STAM_REG(pVM, &pVM->csam.s.StatPageCSAM, STAMTYPE_COUNTER, "/CSAM/PageRec/Type/CSAM", STAMUNIT_OCCURENCES, "The number of CSAM page records.");
147 STAM_REG(pVM, &pVM->csam.s.StatPageREM, STAMTYPE_COUNTER, "/CSAM/PageRec/Type/REM", STAMUNIT_OCCURENCES, "The number of REM page records.");
148 STAM_REG(pVM, &pVM->csam.s.StatPageMonitor, STAMTYPE_COUNTER, "/CSAM/PageRec/Monitored", STAMUNIT_OCCURENCES, "The number of monitored pages.");
149
150 STAM_REG(pVM, &pVM->csam.s.StatCodePageModified, STAMTYPE_COUNTER, "/CSAM/Monitor/DirtyPage", STAMUNIT_OCCURENCES, "The number of code page modifications.");
151
152 STAM_REG(pVM, &pVM->csam.s.StatNrFlushes, STAMTYPE_COUNTER, "/CSAM/PageFlushes", STAMUNIT_OCCURENCES, "The number of CSAM page flushes.");
153 STAM_REG(pVM, &pVM->csam.s.StatNrFlushesSkipped, STAMTYPE_COUNTER, "/CSAM/PageFlushesSkipped", STAMUNIT_OCCURENCES, "The number of CSAM page flushes that were skipped.");
154 STAM_REG(pVM, &pVM->csam.s.StatNrKnownPagesHC, STAMTYPE_COUNTER, "/CSAM/HC/KnownPageRecords", STAMUNIT_OCCURENCES, "The number of known CSAM page records.");
155 STAM_REG(pVM, &pVM->csam.s.StatNrKnownPagesGC, STAMTYPE_COUNTER, "/CSAM/GC/KnownPageRecords", STAMUNIT_OCCURENCES, "The number of known CSAM page records.");
156 STAM_REG(pVM, &pVM->csam.s.StatNrInstr, STAMTYPE_COUNTER, "/CSAM/ScannedInstr", STAMUNIT_OCCURENCES, "The number of scanned instructions.");
157 STAM_REG(pVM, &pVM->csam.s.StatNrBytesRead, STAMTYPE_COUNTER, "/CSAM/BytesRead", STAMUNIT_OCCURENCES, "The number of bytes read for scanning.");
158 STAM_REG(pVM, &pVM->csam.s.StatNrOpcodeRead, STAMTYPE_COUNTER, "/CSAM/OpcodeBytesRead", STAMUNIT_OCCURENCES, "The number of opcode bytes read by the recompiler.");
159
160 STAM_REG(pVM, &pVM->csam.s.StatBitmapAlloc, STAMTYPE_COUNTER, "/CSAM/Alloc/PageBitmap", STAMUNIT_OCCURENCES, "The number of page bitmap allocations.");
161
162 STAM_REG(pVM, &pVM->csam.s.StatInstrCacheHit, STAMTYPE_COUNTER, "/CSAM/Cache/Hit", STAMUNIT_OCCURENCES, "The number of dangerous instruction cache hits.");
163 STAM_REG(pVM, &pVM->csam.s.StatInstrCacheMiss, STAMTYPE_COUNTER, "/CSAM/Cache/Miss", STAMUNIT_OCCURENCES, "The number of dangerous instruction cache misses.");
164
165 STAM_REG(pVM, &pVM->csam.s.StatScanNextFunction, STAMTYPE_COUNTER, "/CSAM/Function/Scan/Success", STAMUNIT_OCCURENCES, "The number of found functions beyond the ret border.");
166 STAM_REG(pVM, &pVM->csam.s.StatScanNextFunctionFailed, STAMTYPE_COUNTER, "/CSAM/Function/Scan/Failed", STAMUNIT_OCCURENCES, "The number of refused functions beyond the ret border.");
167
168 STAM_REG(pVM, &pVM->csam.s.StatTime, STAMTYPE_PROFILE, "/PROF/CSAM/Scan", STAMUNIT_TICKS_PER_CALL, "Scanning overhead.");
169 STAM_REG(pVM, &pVM->csam.s.StatTimeCheckAddr, STAMTYPE_PROFILE, "/PROF/CSAM/CheckAddr", STAMUNIT_TICKS_PER_CALL, "Address check overhead.");
170 STAM_REG(pVM, &pVM->csam.s.StatTimeAddrConv, STAMTYPE_PROFILE, "/PROF/CSAM/AddrConv", STAMUNIT_TICKS_PER_CALL, "Address conversion overhead.");
171 STAM_REG(pVM, &pVM->csam.s.StatTimeFlushPage, STAMTYPE_PROFILE, "/PROF/CSAM/FlushPage", STAMUNIT_TICKS_PER_CALL, "Page flushing overhead.");
172 STAM_REG(pVM, &pVM->csam.s.StatTimeDisasm, STAMTYPE_PROFILE, "/PROF/CSAM/Disasm", STAMUNIT_TICKS_PER_CALL, "Disassembly overhead.");
173 STAM_REG(pVM, &pVM->csam.s.StatFlushDirtyPages, STAMTYPE_PROFILE, "/PROF/CSAM/FlushDirtyPage", STAMUNIT_TICKS_PER_CALL, "Dirty page flushing overhead.");
174 STAM_REG(pVM, &pVM->csam.s.StatCheckGates, STAMTYPE_PROFILE, "/PROF/CSAM/CheckGates", STAMUNIT_TICKS_PER_CALL, "CSAMR3CheckGates overhead.");
175
176
177#ifdef CSAM_ENABLE
178 CSAMEnableScanning(pVM);
179#endif
180
181#ifdef VBOX_WITH_DEBUGGER
182 /*
183 * Debugger commands.
184 */
185 static bool fRegisteredCmds = false;
186 if (!fRegisteredCmds)
187 {
188 int rc = DBGCRegisterCommands(&g_aCmds[0], ELEMENTS(g_aCmds));
189 if (VBOX_SUCCESS(rc))
190 fRegisteredCmds = true;
191 }
192#endif
193
194 return VINF_SUCCESS;
195}
196
197/**
198 * (Re)initializes CSAM
199 *
200 * @param pVM The VM.
201 */
202static int csamReinit(PVM pVM)
203{
204 /*
205 * Assert alignment and sizes.
206 */
207 AssertRelease(!(RT_OFFSETOF(VM, csam.s) & 31));
208 AssertRelease(sizeof(pVM->csam.s) <= sizeof(pVM->csam.padding));
209
210 /*
211 * Setup any fixed pointers and offsets.
212 */
213 pVM->csam.s.offVM = RT_OFFSETOF(VM, patm);
214
215 pVM->csam.s.fGatesChecked = false;
216 pVM->csam.s.fScanningStarted = false;
217
218 memset(&pVM->csam.s.aIDT[0], 0, sizeof(pVM->csam.s.aIDT));
219
220 VM_FF_CLEAR(pVM, VM_FF_CSAM_FLUSH_DIRTY_PAGE);
221 pVM->csam.s.cDirtyPages = 0;
222 /* not necessary */
223 memset(pVM->csam.s.pvDirtyBasePage, 0, sizeof(pVM->csam.s.pvDirtyBasePage));
224 memset(pVM->csam.s.pvDirtyFaultPage, 0, sizeof(pVM->csam.s.pvDirtyFaultPage));
225
226 memset(&pVM->csam.s.aDangerousInstr, 0, sizeof(pVM->csam.s.aDangerousInstr));
227 pVM->csam.s.cDangerousInstr = 0;
228 pVM->csam.s.iDangerousInstr = 0;
229
230 /** @note never mess with the pgdir bitmap here! */
231 return VINF_SUCCESS;
232}
233
234/**
235 * Applies relocations to data and code managed by this
236 * component. This function will be called at init and
237 * whenever the VMM need to relocate itself inside the GC.
238 *
239 * The csam will update the addresses used by the switcher.
240 *
241 * @param pVM The VM.
242 * @param offDelta Relocation delta.
243 */
244CSAMR3DECL(void) CSAMR3Relocate(PVM pVM, RTGCINTPTR offDelta)
245{
246 if (offDelta)
247 {
248 /* Adjust pgdir and page bitmap pointers. */
249 pVM->csam.s.pPDBitmapGC = MMHyperHC2GC(pVM, pVM->csam.s.pPDGCBitmapHC);
250 pVM->csam.s.pPDHCBitmapGC = MMHyperHC2GC(pVM, pVM->csam.s.pPDBitmapHC);
251
252 for(int i=0;i<CSAM_PGDIRBMP_CHUNKS;i++)
253 {
254 if (pVM->csam.s.pPDGCBitmapHC[i])
255 {
256 pVM->csam.s.pPDGCBitmapHC[i] += offDelta;
257 }
258 }
259 }
260 return;
261}
262
263/**
264 * Terminates the csam.
265 *
266 * Termination means cleaning up and freeing all resources,
267 * the VM it self is at this point powered off or suspended.
268 *
269 * @returns VBox status code.
270 * @param pVM The VM to operate on.
271 */
272CSAMR3DECL(int) CSAMR3Term(PVM pVM)
273{
274 int rc;
275
276 rc = CSAMR3Reset(pVM);
277 AssertRC(rc);
278
279 /* @todo triggers assertion in MMHyperFree */
280#if 0
281 for(int i=0;i<CSAM_PAGEBMP_CHUNKS;i++)
282 {
283 if (pVM->csam.s.pPDBitmapHC[i])
284 MMHyperFree(pVM, pVM->csam.s.pPDBitmapHC[i]);
285 }
286#endif
287
288 return VINF_SUCCESS;
289}
290
291/**
292 * CSAM reset callback.
293 *
294 * @returns VBox status code.
295 * @param pVM The VM which is reset.
296 */
297CSAMR3DECL(int) CSAMR3Reset(PVM pVM)
298{
299 /* Clear page bitmaps. */
300 for(int i=0;i<CSAM_PGDIRBMP_CHUNKS;i++)
301 {
302 if (pVM->csam.s.pPDBitmapHC[i])
303 {
304 Assert((CSAM_PAGE_BITMAP_SIZE& 3) == 0);
305 ASMMemZero32(pVM->csam.s.pPDBitmapHC[i], CSAM_PAGE_BITMAP_SIZE);
306 }
307 }
308
309 /* Remove all CSAM page records. */
310 while(true)
311 {
312 PCSAMPAGEREC pPageRec = (PCSAMPAGEREC)RTAvlPVGetBestFit(&pVM->csam.s.pPageTree, 0, true);
313 if (pPageRec)
314 {
315 csamRemovePageRecord(pVM, pPageRec->page.pPageGC);
316 }
317 else
318 break;
319 }
320 Assert(!pVM->csam.s.pPageTree);
321
322 csamReinit(pVM);
323
324 return VINF_SUCCESS;
325}
326
327#define CSAM_SUBTRACT_PTR(a, b) *(uintptr_t *)&(a) = (uintptr_t)(a) - (uintptr_t)(b)
328#define CSAM_ADD_PTR(a, b) *(uintptr_t *)&(a) = (uintptr_t)(a) + (uintptr_t)(b)
329
330
331/**
332 * Callback function for RTAvlPVDoWithAll
333 *
334 * Counts the number of records in the tree
335 *
336 * @returns VBox status code.
337 * @param pNode Current node
338 * @param pcPatches Pointer to patch counter
339 */
340static DECLCALLBACK(int) CountRecord(PAVLPVNODECORE pNode, void *pcPatches)
341{
342 *(uint32_t *)pcPatches = *(uint32_t *)pcPatches + 1;
343 return VINF_SUCCESS;
344}
345
346/**
347 * Callback function for RTAvlPVDoWithAll
348 *
349 * Saves the state of the page record
350 *
351 * @returns VBox status code.
352 * @param pNode Current node
353 * @param pVM1 VM Handle
354 */
355static DECLCALLBACK(int) SavePageState(PAVLPVNODECORE pNode, void *pVM1)
356{
357 PVM pVM = (PVM)pVM1;
358 PCSAMPAGEREC pPage = (PCSAMPAGEREC)pNode;
359 CSAMPAGEREC page = *pPage;
360 PSSMHANDLE pSSM = pVM->csam.s.savedstate.pSSM;
361 int rc;
362
363 /* Save the page record itself */
364 rc = SSMR3PutMem(pSSM, &page, sizeof(page));
365 AssertRCReturn(rc, rc);
366
367 if (page.page.pBitmap)
368 {
369 rc = SSMR3PutMem(pSSM, page.page.pBitmap, CSAM_PAGE_BITMAP_SIZE);
370 AssertRCReturn(rc, rc);
371 }
372
373 return VINF_SUCCESS;
374}
375
376/**
377 * Execute state save operation.
378 *
379 * @returns VBox status code.
380 * @param pVM VM Handle.
381 * @param pSSM SSM operation handle.
382 */
383static DECLCALLBACK(int) csamr3Save(PVM pVM, PSSMHANDLE pSSM)
384{
385 CSAM csamInfo = pVM->csam.s;
386 int rc;
387
388 /*
389 * Count the number of page records in the tree (feeling lazy)
390 */
391 csamInfo.savedstate.cPageRecords = 0;
392 RTAvlPVDoWithAll(&pVM->csam.s.pPageTree, true, CountRecord, &csamInfo.savedstate.cPageRecords);
393
394 /*
395 * Save CSAM structure
396 */
397 pVM->csam.s.savedstate.pSSM = pSSM;
398 rc = SSMR3PutMem(pSSM, &csamInfo, sizeof(csamInfo));
399 AssertRCReturn(rc, rc);
400
401 /* Save pgdir bitmap */
402 rc = SSMR3PutMem(pSSM, csamInfo.pPDBitmapHC, CSAM_PGDIRBMP_CHUNKS*sizeof(RTHCPTR));
403 AssertRCReturn(rc, rc);
404
405 for (unsigned i=0;i<CSAM_PGDIRBMP_CHUNKS;i++)
406 {
407 if(csamInfo.pPDBitmapHC[i])
408 {
409 /* Save the page bitmap. */
410 rc = SSMR3PutMem(pSSM, csamInfo.pPDBitmapHC[i], CSAM_PAGE_BITMAP_SIZE);
411 AssertRCReturn(rc, rc);
412 }
413 }
414
415 /*
416 * Save page records
417 */
418 rc = RTAvlPVDoWithAll(&pVM->csam.s.pPageTree, true, SavePageState, pVM);
419 AssertRCReturn(rc, rc);
420
421 /** @note we don't restore aDangerousInstr; it will be recreated automatically. */
422 return VINF_SUCCESS;
423}
424
425/**
426 * Execute state load operation.
427 *
428 * @returns VBox status code.
429 * @param pVM VM Handle.
430 * @param pSSM SSM operation handle.
431 * @param u32Version Data layout version.
432 */
433static DECLCALLBACK(int) csamr3Load(PVM pVM, PSSMHANDLE pSSM, uint32_t u32Version)
434{
435 int rc;
436 CSAM csamInfo;
437
438 if (u32Version != CSAM_SSM_VERSION)
439 return VERR_SSM_UNSUPPORTED_DATA_UNIT_VERSION;
440
441 pVM->csam.s.savedstate.pSSM = pSSM;
442
443 /*
444 * Restore CSAM structure
445 */
446 rc = SSMR3GetMem(pSSM, &csamInfo, sizeof(csamInfo));
447 AssertRCReturn(rc, rc);
448
449 pVM->csam.s.fGatesChecked = csamInfo.fGatesChecked;
450 pVM->csam.s.fScanningStarted = csamInfo.fScanningStarted;
451
452 /* Restore dirty code page info. */
453 pVM->csam.s.cDirtyPages = csamInfo.cDirtyPages;
454 memcpy(pVM->csam.s.pvDirtyBasePage, csamInfo.pvDirtyBasePage, sizeof(pVM->csam.s.pvDirtyBasePage));
455 memcpy(pVM->csam.s.pvDirtyFaultPage, csamInfo.pvDirtyFaultPage, sizeof(pVM->csam.s.pvDirtyFaultPage));
456
457 /* Restore IDT gate info. */
458 memcpy(&pVM->csam.s.aIDT[0], &csamInfo.aIDT[0], sizeof(pVM->csam.s.aIDT));
459
460 /* Restore pgdir bitmap (we'll change the pointers next). */
461 rc = SSMR3GetMem(pSSM, pVM->csam.s.pPDBitmapHC, CSAM_PGDIRBMP_CHUNKS*sizeof(RTHCPTR));
462 AssertRCReturn(rc, rc);
463
464 /*
465 * Restore page bitmaps
466 */
467 for (unsigned i=0;i<CSAM_PGDIRBMP_CHUNKS;i++)
468 {
469 if(pVM->csam.s.pPDBitmapHC[i])
470 {
471 rc = MMHyperAlloc(pVM, CSAM_PAGE_BITMAP_SIZE, 0, MM_TAG_CSAM, (void **)&pVM->csam.s.pPDBitmapHC[i]);
472 if (VBOX_FAILURE(rc))
473 {
474 Log(("MMR3HyperAlloc failed with %d\n", rc));
475 return rc;
476 }
477 /* Convert to GC pointer. */
478 pVM->csam.s.pPDGCBitmapHC[i] = MMHyperHC2GC(pVM, pVM->csam.s.pPDBitmapHC[i]);
479 Assert(pVM->csam.s.pPDGCBitmapHC[i]);
480
481 /* Restore the bitmap. */
482 rc = SSMR3GetMem(pSSM, pVM->csam.s.pPDBitmapHC[i], CSAM_PAGE_BITMAP_SIZE);
483 AssertRCReturn(rc, rc);
484 }
485 else
486 {
487 Assert(!pVM->csam.s.pPDGCBitmapHC[i]);
488 pVM->csam.s.pPDGCBitmapHC[i] = 0;
489 }
490 }
491
492 /*
493 * Restore page records
494 */
495 for (uint32_t i=0;i<csamInfo.savedstate.cPageRecords + csamInfo.savedstate.cPatchPageRecords;i++)
496 {
497 CSAMPAGEREC page;
498 PCSAMPAGE pPage;
499
500 rc = SSMR3GetMem(pSSM, &page, sizeof(page));
501 AssertRCReturn(rc, rc);
502
503 /*
504 * Recreate the page record
505 */
506 pPage = csamCreatePageRecord(pVM, page.page.pPageGC, page.page.enmTag, page.page.fCode32, page.page.fMonitorInvalidation);
507 AssertReturn(pPage, VERR_NO_MEMORY);
508
509 pPage->GCPhys = page.page.GCPhys;
510 pPage->fFlags = page.page.fFlags;
511 pPage->u64Hash = page.page.u64Hash;
512
513 if (page.page.pBitmap)
514 {
515 rc = SSMR3GetMem(pSSM, pPage->pBitmap, CSAM_PAGE_BITMAP_SIZE);
516 AssertRCReturn(rc, rc);
517 }
518 else
519 {
520 MMR3HeapFree(pPage->pBitmap);
521 pPage->pBitmap = 0;
522 }
523 }
524
525 /** @note we don't restore aDangerousInstr; it will be recreated automatically. */
526 memset(&pVM->csam.s.aDangerousInstr, 0, sizeof(pVM->csam.s.aDangerousInstr));
527 pVM->csam.s.cDangerousInstr = 0;
528 pVM->csam.s.iDangerousInstr = 0;
529 return VINF_SUCCESS;
530}
531
532/**
533 * Convert guest context address to host context pointer
534 *
535 * @returns VBox status code.
536 * @param pVM The VM to operate on.
537 * @param pCacheRec Address conversion cache record
538 * @param pGCPtr Guest context pointer
539 *
540 * @returns Host context pointer or NULL in case of an error
541 *
542 */
543static HCPTRTYPE(void *) CSAMGCVirtToHCVirt(PVM pVM, PCSAMP2GLOOKUPREC pCacheRec, GCPTRTYPE(uint8_t *)pGCPtr)
544{
545 int rc;
546 HCPTRTYPE(void *)pHCPtr;
547
548 STAM_PROFILE_START(&pVM->csam.s.StatTimeAddrConv, a);
549
550 pHCPtr = PATMR3GCPtrToHCPtr(pVM, pGCPtr);
551 if (pHCPtr) return pHCPtr;
552
553 if (pCacheRec->pPageLocStartHC)
554 {
555 uint32_t offset = pGCPtr & PAGE_OFFSET_MASK;
556 if (pCacheRec->pGuestLoc == (pGCPtr & PAGE_BASE_GC_MASK))
557 {
558 STAM_PROFILE_STOP(&pVM->csam.s.StatTimeAddrConv, a);
559 return pCacheRec->pPageLocStartHC + offset;
560 }
561 }
562
563 rc = PGMPhysGCPtr2HCPtr(pVM, pGCPtr, &pHCPtr);
564 if (rc != VINF_SUCCESS)
565 {
566//// AssertMsgRC(rc, ("MMR3PhysGCVirt2HCVirtEx failed for %VGv\n", pGCPtr));
567 STAM_PROFILE_STOP(&pVM->csam.s.StatTimeAddrConv, a);
568 return NULL;
569 }
570 Assert(sizeof(HCPTRTYPE(uint8_t*)) == sizeof(uint32_t));
571
572 pCacheRec->pPageLocStartHC = (HCPTRTYPE(uint8_t*))((RTHCUINTPTR)pHCPtr & PAGE_BASE_HC_MASK);
573 pCacheRec->pGuestLoc = pGCPtr & PAGE_BASE_GC_MASK;
574 STAM_PROFILE_STOP(&pVM->csam.s.StatTimeAddrConv, a);
575 return pHCPtr;
576}
577
578/**
579 * Read callback for disassembly function; supports reading bytes that cross a page boundary
580 *
581 * @returns VBox status code.
582 * @param pSrc GC source pointer
583 * @param pDest HC destination pointer
584 * @param size Number of bytes to read
585 * @param dwUserdata Callback specific user data (pCpu)
586 *
587 */
588int32_t CSAMR3ReadBytes(RTHCUINTPTR pSrc, uint8_t *pDest, uint32_t size, RTHCUINTPTR dwUserdata)
589{
590 DISCPUSTATE *pCpu = (DISCPUSTATE *)dwUserdata;
591 PVM pVM = (PVM)pCpu->dwUserData[0];
592 RTHCUINTPTR pInstrHC = pCpu->dwUserData[1];
593 RTGCUINTPTR pInstrGC = pCpu->dwUserData[2];
594 int orgsize = size;
595
596 Assert(sizeof(RTHCUINTPTR) <= sizeof(pCpu->dwUserData[0]));
597 Assert(sizeof(RTGCUINTPTR) <= sizeof(pCpu->dwUserData[0]));
598
599 /* We are not interested in patched instructions, so read the original opcode bytes. */
600 /** @note single instruction patches (int3) are checked in CSAMR3AnalyseCallback */
601 for (int i=0;i<orgsize;i++)
602 {
603 int rc = PATMR3QueryOpcode(pVM, (RTGCPTR)pSrc, pDest);
604 if (VBOX_SUCCESS(rc))
605 {
606 pSrc++;
607 pDest++;
608 size--;
609 }
610 else
611 break;
612 }
613 if (size == 0)
614 return VINF_SUCCESS;
615
616 if (PAGE_ADDRESS(pInstrGC) != PAGE_ADDRESS(pSrc + size - 1) && !PATMIsPatchGCAddr(pVM, pSrc))
617 {
618 return PGMPhysReadGCPtr(pVM, pDest, pSrc, size);
619 }
620 else
621 {
622 Assert(pInstrHC);
623
624 /* pInstrHC is the base address; adjust according to the GC pointer. */
625 pInstrHC = pInstrHC + (pSrc - pInstrGC);
626
627 memcpy(pDest, (void *)pInstrHC, size);
628 }
629
630 return VINF_SUCCESS;
631}
632
633inline bool CSAMR3DISInstr(PVM pVM, DISCPUSTATE *pCpu, RTGCPTR InstrGC, uint8_t *InstrHC, uint32_t *pOpsize, char *pszOutput)
634{
635 (pCpu)->pfnReadBytes = CSAMR3ReadBytes;
636 (pCpu)->dwUserData[0] = (RTHCUINTPTR)pVM;
637 (pCpu)->dwUserData[1] = (RTHCUINTPTR)InstrHC;
638 (pCpu)->dwUserData[2] = (RTHCUINTPTR)InstrGC;
639#ifdef DEBUG
640 return DISInstrEx(pCpu, InstrGC, 0, pOpsize, pszOutput, OPTYPE_ALL);
641#else
642 /* We are interested in everything except harmless stuff */
643 return DISInstrEx(pCpu, InstrGC, 0, pOpsize, pszOutput, ~(OPTYPE_INVALID | OPTYPE_HARMLESS | OPTYPE_RRM_MASK));
644#endif
645}
646
647/**
648 * Analyses the instructions following the cli for compliance with our heuristics for cli
649 *
650 * @returns VBox status code.
651 * @param pVM The VM to operate on.
652 * @param pCpu CPU disassembly state
653 * @param pInstrGC Guest context pointer to privileged instruction
654 * @param pCurInstrGC Guest context pointer to the current instruction
655 * @param pCacheRec GC to HC cache record
656 * @param pUserData User pointer (callback specific)
657 *
658 */
659static int CSAMR3AnalyseCallback(PVM pVM, DISCPUSTATE *pCpu, GCPTRTYPE(uint8_t *) pInstrGC, GCPTRTYPE(uint8_t *) pCurInstrGC,
660 PCSAMP2GLOOKUPREC pCacheRec, void *pUserData)
661{
662 PCSAMPAGE pPage = (PCSAMPAGE)pUserData;
663 int rc;
664
665 switch(pCpu->pCurInstr->opcode)
666 {
667 case OP_INT:
668 Assert(pCpu->param1.flags & USE_IMMEDIATE8);
669 if (pCpu->param1.parval == 3)
670 {
671 //two byte int 3
672 return VINF_SUCCESS;
673 }
674 break;
675
676 case OP_ILLUD2:
677 /* This appears to be some kind of kernel panic in Linux 2.4; no point to continue. */
678 case OP_RETN:
679 case OP_INT3:
680 case OP_INVALID:
681#if 1
682 /* removing breaks win2k guests? */
683 case OP_IRET:
684#endif
685 return VINF_SUCCESS;
686 }
687
688 // Check for exit points
689 switch (pCpu->pCurInstr->opcode)
690 {
691 /* It's not a good idea to patch pushf instructions:
692 * - increases the chance of conflicts (code jumping to the next instruction)
693 * - better to patch the cli
694 * - code that branches before the cli will likely hit an int 3
695 * - in general doesn't offer any benefits as we don't allow nested patch blocks (IF is always 1)
696 */
697 case OP_PUSHF:
698 case OP_POPF:
699 break;
700
701 case OP_CLI:
702 {
703 uint32_t cbInstr = 0;
704 uint32_t opsize = pCpu->opsize;
705
706 PATMR3AddHint(pVM, pCurInstrGC, (pPage->fCode32) ? PATMFL_CODE32 : 0);
707
708 /* Make sure the instructions that follow the cli have not been encountered before. */
709 while (true)
710 {
711 DISCPUSTATE cpu;
712 uint8_t *pCurInstrHC = 0;
713 bool disret;
714
715 if (cbInstr + opsize >= SIZEOF_NEARJUMP32)
716 break;
717
718 if (csamIsCodeScanned(pVM, pCurInstrGC + opsize, &pPage) == true)
719 {
720 /* We've scanned the next instruction(s) already. This means we've followed a branch that ended up there before -> dangerous!! */
721 PATMR3DetectConflict(pVM, pCurInstrGC, pCurInstrGC + opsize);
722 break;
723 }
724 pCurInstrGC += opsize;
725 cbInstr += opsize;
726
727 pCurInstrHC = (uint8_t *)CSAMGCVirtToHCVirt(pVM, pCacheRec, pCurInstrGC);
728 if (pCurInstrHC == NULL)
729 {
730 Log(("CSAMGCVirtToHCVirt failed for %VGv\n", pCurInstrGC));
731 break;
732 }
733 Assert(VALID_PTR(pCurInstrHC));
734
735 cpu.mode = (pPage->fCode32) ? CPUMODE_32BIT : CPUMODE_16BIT;
736 disret = CSAMR3DISInstr(pVM, &cpu, pCurInstrGC, pCurInstrHC, &opsize, NULL);
737 Assert(disret == true);
738 if (disret == false)
739 break;
740 }
741 break;
742 }
743
744 case OP_PUSH:
745 if (pCpu->pCurInstr->param1 != OP_PARM_REG_CS)
746 break;
747
748 /* no break */
749 case OP_STR:
750 case OP_LSL:
751 case OP_LAR:
752 case OP_SGDT:
753 case OP_SLDT:
754 case OP_SIDT:
755 case OP_SMSW:
756 case OP_VERW:
757 case OP_VERR:
758 case OP_CPUID:
759 case OP_IRET:
760#ifdef DEBUG
761 switch(pCpu->pCurInstr->opcode)
762 {
763 case OP_STR:
764 Log(("Privileged instruction at %VGv: str!!\n", pCurInstrGC));
765 break;
766 case OP_LSL:
767 Log(("Privileged instruction at %VGv: lsl!!\n", pCurInstrGC));
768 break;
769 case OP_LAR:
770 Log(("Privileged instruction at %VGv: lar!!\n", pCurInstrGC));
771 break;
772 case OP_SGDT:
773 Log(("Privileged instruction at %VGv: sgdt!!\n", pCurInstrGC));
774 break;
775 case OP_SLDT:
776 Log(("Privileged instruction at %VGv: sldt!!\n", pCurInstrGC));
777 break;
778 case OP_SIDT:
779 Log(("Privileged instruction at %VGv: sidt!!\n", pCurInstrGC));
780 break;
781 case OP_SMSW:
782 Log(("Privileged instruction at %VGv: smsw!!\n", pCurInstrGC));
783 break;
784 case OP_VERW:
785 Log(("Privileged instruction at %VGv: verw!!\n", pCurInstrGC));
786 break;
787 case OP_VERR:
788 Log(("Privileged instruction at %VGv: verr!!\n", pCurInstrGC));
789 break;
790 case OP_CPUID:
791 Log(("Privileged instruction at %VGv: cpuid!!\n", pCurInstrGC));
792 break;
793 case OP_PUSH:
794 Log(("Privileged instruction at %VGv: push cs!!\n", pCurInstrGC));
795 break;
796 case OP_IRET:
797 Log(("Privileged instruction at %VGv: iret!!\n", pCurInstrGC));
798 break;
799 }
800#endif
801
802 if (PATMR3HasBeenPatched(pVM, pCurInstrGC) == false)
803 {
804 rc = PATMR3InstallPatch(pVM, pCurInstrGC, (pPage->fCode32) ? PATMFL_CODE32 : 0);
805 if (VBOX_FAILURE(rc))
806 {
807 Log(("PATMR3InstallPatch failed with %d\n", rc));
808 return VWRN_CONTINUE_ANALYSIS;
809 }
810 }
811 if (pCpu->pCurInstr->opcode == OP_IRET)
812 return VINF_SUCCESS; /* Look no further in this branch. */
813
814 return VWRN_CONTINUE_ANALYSIS;
815
816 case OP_JMP:
817 case OP_CALL:
818 {
819 // return or jump/call through a jump table
820 if (OP_PARM_VTYPE(pCpu->pCurInstr->param1) != OP_PARM_J)
821 {
822#ifdef DEBUG
823 switch(pCpu->pCurInstr->opcode)
824 {
825 case OP_JMP:
826 Log(("Control Flow instruction at %VGv: jmp!!\n", pCurInstrGC));
827 break;
828 case OP_CALL:
829 Log(("Control Flow instruction at %VGv: call!!\n", pCurInstrGC));
830 break;
831 }
832#endif
833 return VWRN_CONTINUE_ANALYSIS;
834 }
835 return VWRN_CONTINUE_ANALYSIS;
836 }
837
838 }
839
840 return VWRN_CONTINUE_ANALYSIS;
841}
842
843#ifdef CSAM_ANALYSE_BEYOND_RET
844/**
845 * Wrapper for csamAnalyseCodeStream for call instructions.
846 *
847 * @returns VBox status code.
848 * @param pVM The VM to operate on.
849 * @param pInstrGC Guest context pointer to privileged instruction
850 * @param pCurInstrGC Guest context pointer to the current instruction
851 * @param fCode32 16 or 32 bits code
852 * @param pfnCSAMR3Analyse Callback for testing the disassembled instruction
853 * @param pUserData User pointer (callback specific)
854 *
855 */
856static int csamAnalyseCallCodeStream(PVM pVM, GCPTRTYPE(uint8_t *) pInstrGC, GCPTRTYPE(uint8_t *) pCurInstrGC, bool fCode32,
857 PFN_CSAMR3ANALYSE pfnCSAMR3Analyse, void *pUserData, PCSAMP2GLOOKUPREC pCacheRec)
858{
859 int rc;
860 CSAMCALLEXITREC CallExitRec;
861 PCSAMCALLEXITREC pOldCallRec;
862 PCSAMPAGE pPage = 0;
863 uint32_t i;
864
865 CallExitRec.cInstrAfterRet = 0;
866
867 pOldCallRec = pCacheRec->pCallExitRec;
868 pCacheRec->pCallExitRec = &CallExitRec;
869
870 rc = csamAnalyseCodeStream(pVM, pInstrGC, pCurInstrGC, fCode32, pfnCSAMR3Analyse, pUserData, pCacheRec);
871
872 for (i=0;i<CallExitRec.cInstrAfterRet;i++)
873 {
874 PCSAMPAGE pPage = 0;
875
876 pCurInstrGC = CallExitRec.pInstrAfterRetGC[i];
877
878 /* Check if we've previously encountered the instruction after the ret. */
879 if (csamIsCodeScanned(pVM, pCurInstrGC, &pPage) == false)
880 {
881 DISCPUSTATE cpu;
882 uint32_t opsize;
883 uint8_t *pCurInstrHC = 0;
884 bool disret;
885#ifdef DEBUG
886 char szOutput[256];
887#endif
888 if (pPage == NULL)
889 {
890 /* New address; let's take a look at it. */
891 pPage = csamCreatePageRecord(pVM, pCurInstrGC, CSAM_TAG_CSAM, fCode32);
892 if (pPage == NULL)
893 {
894 rc = VERR_NO_MEMORY;
895 goto done;
896 }
897 }
898
899 /**
900 * Some generic requirements for recognizing an adjacent function:
901 * - alignment fillers that consist of:
902 * - nop
903 * - lea genregX, [genregX (+ 0)]
904 * - push ebp after the filler (can extend this later); aligned at at least a 4 byte boundary
905 */
906 for (int j=0;j<16;j++)
907 {
908 pCurInstrHC = (uint8_t *)CSAMGCVirtToHCVirt(pVM, pCacheRec, pCurInstrGC);
909 if (pCurInstrHC == NULL)
910 {
911 Log(("CSAMGCVirtToHCVirt failed for %VGv\n", pCurInstrGC));
912 goto done;
913 }
914 Assert(VALID_PTR(pCurInstrHC));
915
916 cpu.mode = (fCode32) ? CPUMODE_32BIT : CPUMODE_16BIT;
917 STAM_PROFILE_START(&pVM->csam.s.StatTimeDisasm, a);
918#ifdef DEBUG
919 disret = CSAMR3DISInstr(pVM, &cpu, pCurInstrGC, pCurInstrHC, &opsize, szOutput);
920 if (disret == true) Log(("CSAM Call Analysis: %s", szOutput));
921#else
922 disret = CSAMR3DISInstr(pVM, &cpu, pCurInstrGC, pCurInstrHC, &opsize, NULL);
923#endif
924 STAM_PROFILE_STOP(&pVM->csam.s.StatTimeDisasm, a);
925 if (disret == false)
926 {
927 Log(("Disassembly failed at %VGv (probably page not present) -> return to caller\n", pCurInstrGC));
928 goto done;
929 }
930
931 STAM_COUNTER_ADD(&pVM->csam.s.StatNrBytesRead, opsize);
932
933 GCPTRTYPE(uint8_t *) addr = 0;
934 PCSAMPAGE pJmpPage = NULL;
935
936 if (PAGE_ADDRESS(pCurInstrGC) != PAGE_ADDRESS(pCurInstrGC + opsize - 1))
937 {
938 if (!PGMGstIsPagePresent(pVM, pCurInstrGC + opsize - 1))
939 {
940 /// @todo fault in the page
941 Log(("Page for current instruction %VGv is not present!!\n", pCurInstrGC));
942 goto done;
943 }
944 //all is fine, let's continue
945 csamR3CheckPageRecord(pVM, pCurInstrGC + opsize - 1);
946 }
947
948 switch (cpu.pCurInstr->opcode)
949 {
950 case OP_NOP:
951 break; /* acceptable */
952
953 case OP_LEA:
954 /* Must be similar to:
955 *
956 * lea esi, [esi]
957 * lea esi, [esi+0]
958 * Any register is allowed as long as source and destination are identical.
959 */
960 if ( cpu.param1.flags != USE_REG_GEN32
961 || ( cpu.param2.flags != USE_REG_GEN32
962 && ( !(cpu.param2.flags & USE_REG_GEN32)
963 || !(cpu.param2.flags & (USE_DISPLACEMENT8|USE_DISPLACEMENT16|USE_DISPLACEMENT32))
964 || cpu.param2.parval != 0
965 )
966 )
967 || cpu.param1.base.reg_gen32 != cpu.param2.base.reg_gen32
968 )
969 {
970 STAM_COUNTER_INC(&pVM->csam.s.StatScanNextFunctionFailed);
971 goto next_function;
972 }
973 break;
974
975 case OP_PUSH:
976 {
977 if ( (pCurInstrGC & 0x3) != 0
978 || cpu.param1.flags != USE_REG_GEN32
979 || cpu.param1.base.reg_gen32 != USE_REG_EBP
980 )
981 {
982 STAM_COUNTER_INC(&pVM->csam.s.StatScanNextFunctionFailed);
983 goto next_function;
984 }
985
986 if (csamIsCodeScanned(pVM, pCurInstrGC, &pPage) == false)
987 {
988 CSAMCALLEXITREC CallExitRec2;
989 CallExitRec2.cInstrAfterRet = 0;
990
991 pCacheRec->pCallExitRec = &CallExitRec2;
992
993 /* Analyse the function. */
994 Log(("Found new function at %VGv\n", pCurInstrGC));
995 STAM_COUNTER_INC(&pVM->csam.s.StatScanNextFunction);
996 csamAnalyseCallCodeStream(pVM, pInstrGC, pCurInstrGC, fCode32, pfnCSAMR3Analyse, pUserData, pCacheRec);
997 }
998 goto next_function;
999 }
1000
1001 case OP_SUB:
1002 {
1003 if ( (pCurInstrGC & 0x3) != 0
1004 || cpu.param1.flags != USE_REG_GEN32
1005 || cpu.param1.base.reg_gen32 != USE_REG_ESP
1006 )
1007 {
1008 STAM_COUNTER_INC(&pVM->csam.s.StatScanNextFunctionFailed);
1009 goto next_function;
1010 }
1011
1012 if (csamIsCodeScanned(pVM, pCurInstrGC, &pPage) == false)
1013 {
1014 CSAMCALLEXITREC CallExitRec2;
1015 CallExitRec2.cInstrAfterRet = 0;
1016
1017 pCacheRec->pCallExitRec = &CallExitRec2;
1018
1019 /* Analyse the function. */
1020 Log(("Found new function at %VGv\n", pCurInstrGC));
1021 STAM_COUNTER_INC(&pVM->csam.s.StatScanNextFunction);
1022 csamAnalyseCallCodeStream(pVM, pInstrGC, pCurInstrGC, fCode32, pfnCSAMR3Analyse, pUserData, pCacheRec);
1023 }
1024 goto next_function;
1025 }
1026
1027 default:
1028 STAM_COUNTER_INC(&pVM->csam.s.StatScanNextFunctionFailed);
1029 goto next_function;
1030 }
1031 /* Mark it as scanned. */
1032 csamMarkCode(pVM, pPage, pCurInstrGC, opsize, true);
1033 pCurInstrGC += opsize;
1034 } /* for at most 16 instructions */
1035next_function:
1036 ; /* MSVC complains otherwise */
1037 }
1038 }
1039done:
1040 pCacheRec->pCallExitRec = pOldCallRec;
1041 return rc;
1042}
1043#else
1044#define csamAnalyseCallCodeStream csamAnalyseCodeStream
1045#endif
1046
1047/**
1048 * Disassembles the code stream until the callback function detects a failure or decides everything is acceptable
1049 *
1050 * @returns VBox status code.
1051 * @param pVM The VM to operate on.
1052 * @param pInstrGC Guest context pointer to privileged instruction
1053 * @param pCurInstrGC Guest context pointer to the current instruction
1054 * @param fCode32 16 or 32 bits code
1055 * @param pfnCSAMR3Analyse Callback for testing the disassembled instruction
1056 * @param pUserData User pointer (callback specific)
1057 *
1058 */
1059static int csamAnalyseCodeStream(PVM pVM, GCPTRTYPE(uint8_t *) pInstrGC, GCPTRTYPE(uint8_t *) pCurInstrGC, bool fCode32,
1060 PFN_CSAMR3ANALYSE pfnCSAMR3Analyse, void *pUserData, PCSAMP2GLOOKUPREC pCacheRec)
1061{
1062 DISCPUSTATE cpu;
1063 PCSAMPAGE pPage = (PCSAMPAGE)pUserData;
1064 int rc = VWRN_CONTINUE_ANALYSIS;
1065 uint32_t opsize;
1066 HCPTRTYPE(uint8_t *)pCurInstrHC = 0;
1067 bool disret;
1068
1069#ifdef DEBUG
1070 char szOutput[256];
1071#endif
1072
1073 LogFlow(("csamAnalyseCodeStream: code at %VGv depth=%d\n", pCurInstrGC, pCacheRec->depth));
1074
1075 pVM->csam.s.fScanningStarted = true;
1076
1077 pCacheRec->depth++;
1078 /*
1079 * Limit the call depth. (rather arbitrary upper limit; too low and we won't detect certain
1080 * cpuid instructions in Linux kernels; too high and we waste too much time scanning code)
1081 * (512 is necessary to detect cpuid instructions in Red Hat EL4; see defect 1355)
1082 * @note we are using a lot of stack here. couple of 100k when we go to the full depth (!)
1083 */
1084 if (pCacheRec->depth > 512)
1085 {
1086 LogFlow(("CSAM: maximum calldepth reached for %VGv\n", pCurInstrGC));
1087 pCacheRec->depth--;
1088 return VINF_SUCCESS; //let's not go on forever
1089 }
1090
1091 Assert(!PATMIsPatchGCAddr(pVM, pCurInstrGC));
1092 csamR3CheckPageRecord(pVM, pCurInstrGC);
1093
1094 while(rc == VWRN_CONTINUE_ANALYSIS)
1095 {
1096 if (csamIsCodeScanned(pVM, pCurInstrGC, &pPage) == false)
1097 {
1098 if (pPage == NULL)
1099 {
1100 /* New address; let's take a look at it. */
1101 pPage = csamCreatePageRecord(pVM, pCurInstrGC, CSAM_TAG_CSAM, fCode32);
1102 if (pPage == NULL)
1103 {
1104 rc = VERR_NO_MEMORY;
1105 goto done;
1106 }
1107 }
1108 }
1109 else
1110 {
1111 LogFlow(("Code at %VGv has been scanned before\n", pCurInstrGC));
1112 rc = VINF_SUCCESS;
1113 goto done;
1114 }
1115
1116 pCurInstrHC = (uint8_t *)CSAMGCVirtToHCVirt(pVM, pCacheRec, pCurInstrGC);
1117 if (pCurInstrHC == NULL)
1118 {
1119 Log(("CSAMGCVirtToHCVirt failed for %VGv\n", pCurInstrGC));
1120 rc = VERR_PATCHING_REFUSED;
1121 goto done;
1122 }
1123 Assert(VALID_PTR(pCurInstrHC));
1124
1125 cpu.mode = (fCode32) ? CPUMODE_32BIT : CPUMODE_16BIT;
1126 STAM_PROFILE_START(&pVM->csam.s.StatTimeDisasm, a);
1127#ifdef DEBUG
1128 disret = CSAMR3DISInstr(pVM, &cpu, pCurInstrGC, pCurInstrHC, &opsize, szOutput);
1129 if (disret == true) Log(("CSAM Analysis: %s", szOutput));
1130#else
1131 disret = CSAMR3DISInstr(pVM, &cpu, pCurInstrGC, pCurInstrHC, &opsize, NULL);
1132#endif
1133 STAM_PROFILE_STOP(&pVM->csam.s.StatTimeDisasm, a);
1134 if (disret == false)
1135 {
1136 Log(("Disassembly failed at %VGv (probably page not present) -> return to caller\n", pCurInstrGC));
1137 rc = VINF_SUCCESS;
1138 goto done;
1139 }
1140
1141 STAM_COUNTER_ADD(&pVM->csam.s.StatNrBytesRead, opsize);
1142
1143 csamMarkCode(pVM, pPage, pCurInstrGC, opsize, true);
1144
1145 GCPTRTYPE(uint8_t *) addr = 0;
1146 PCSAMPAGE pJmpPage = NULL;
1147
1148 if (PAGE_ADDRESS(pCurInstrGC) != PAGE_ADDRESS(pCurInstrGC + opsize - 1))
1149 {
1150 if (!PGMGstIsPagePresent(pVM, pCurInstrGC + opsize - 1))
1151 {
1152 /// @todo fault in the page
1153 Log(("Page for current instruction %VGv is not present!!\n", pCurInstrGC));
1154 rc = VWRN_CONTINUE_ANALYSIS;
1155 goto next_please;
1156 }
1157 //all is fine, let's continue
1158 csamR3CheckPageRecord(pVM, pCurInstrGC + opsize - 1);
1159 }
1160 /*
1161 * If it's harmless, then don't bother checking it (the disasm tables had better be accurate!)
1162 */
1163 if ((cpu.pCurInstr->optype & ~OPTYPE_RRM_MASK) == OPTYPE_HARMLESS)
1164 {
1165 AssertMsg(pfnCSAMR3Analyse(pVM, &cpu, pInstrGC, pCurInstrGC, pCacheRec, (void *)pPage) == VWRN_CONTINUE_ANALYSIS, ("Instruction incorrectly marked harmless?!?!?\n"));
1166 rc = VWRN_CONTINUE_ANALYSIS;
1167 goto next_please;
1168 }
1169
1170#ifdef CSAM_ANALYSE_BEYOND_RET
1171 /* Remember the address of the instruction following the ret in case the parent instruction was a call. */
1172 if ( pCacheRec->pCallExitRec
1173 && cpu.pCurInstr->opcode == OP_RETN
1174 && pCacheRec->pCallExitRec->cInstrAfterRet < CSAM_MAX_CALLEXIT_RET)
1175 {
1176 pCacheRec->pCallExitRec->pInstrAfterRetGC[pCacheRec->pCallExitRec->cInstrAfterRet] = pCurInstrGC + opsize;
1177 pCacheRec->pCallExitRec->cInstrAfterRet++;
1178 }
1179#endif
1180
1181 rc = pfnCSAMR3Analyse(pVM, &cpu, pInstrGC, pCurInstrGC, pCacheRec, (void *)pPage);
1182 if (rc == VINF_SUCCESS)
1183 goto done;
1184
1185 // For our first attempt, we'll handle only simple relative jumps and calls (immediate offset coded in instruction)
1186 if ((cpu.pCurInstr->optype & OPTYPE_CONTROLFLOW) && (OP_PARM_VTYPE(cpu.pCurInstr->param1) == OP_PARM_J))
1187 {
1188 addr = CSAMResolveBranch(&cpu, pCurInstrGC);
1189 if (addr == 0)
1190 {
1191 Log(("We don't support far jumps here!! (%08X)\n", cpu.param1.flags));
1192 rc = VINF_SUCCESS;
1193 break;
1194 }
1195 Assert(!PATMIsPatchGCAddr(pVM, addr));
1196
1197 /* If the target address lies in a patch generated jump, then special action needs to be taken. */
1198 PATMR3DetectConflict(pVM, pCurInstrGC, addr);
1199
1200 /* Same page? */
1201 if (PAGE_ADDRESS(addr) != PAGE_ADDRESS(pCurInstrGC ))
1202 {
1203 if (!PGMGstIsPagePresent(pVM, addr))
1204 {
1205 Log(("Page for current instruction %VGv is not present!!\n", addr));
1206 rc = VWRN_CONTINUE_ANALYSIS;
1207 goto next_please;
1208 }
1209
1210 /* All is fine, let's continue. */
1211 csamR3CheckPageRecord(pVM, addr);
1212 }
1213
1214 pJmpPage = NULL;
1215 if (csamIsCodeScanned(pVM, addr, &pJmpPage) == false)
1216 {
1217 if (pJmpPage == NULL)
1218 {
1219 /* New branch target; let's take a look at it. */
1220 pJmpPage = csamCreatePageRecord(pVM, addr, CSAM_TAG_CSAM, fCode32);
1221 if (pJmpPage == NULL)
1222 {
1223 rc = VERR_NO_MEMORY;
1224 goto done;
1225 }
1226 Assert(pPage);
1227 }
1228 if (cpu.pCurInstr->opcode == OP_CALL)
1229 rc = csamAnalyseCallCodeStream(pVM, pInstrGC, addr, fCode32, pfnCSAMR3Analyse, (void *)pJmpPage, pCacheRec);
1230 else
1231 rc = csamAnalyseCodeStream(pVM, pInstrGC, addr, fCode32, pfnCSAMR3Analyse, (void *)pJmpPage, pCacheRec);
1232
1233 if (rc != VINF_SUCCESS) {
1234 goto done;
1235 }
1236 }
1237 if (cpu.pCurInstr->opcode == OP_JMP)
1238 {//unconditional jump; return to caller
1239 rc = VINF_SUCCESS;
1240 goto done;
1241 }
1242
1243 rc = VWRN_CONTINUE_ANALYSIS;
1244 } //if ((cpu.pCurInstr->optype & OPTYPE_CONTROLFLOW) && (OP_PARM_VTYPE(cpu.pCurInstr->param1) == OP_PARM_J))
1245#ifdef CSAM_SCAN_JUMP_TABLE
1246 else
1247 if ( cpu.pCurInstr->opcode == OP_JMP
1248 && (cpu.param1.flags & (USE_DISPLACEMENT32|USE_INDEX|USE_SCALE)) == (USE_DISPLACEMENT32|USE_INDEX|USE_SCALE)
1249 )
1250 {
1251 RTGCPTR pJumpTableGC = (RTGCPTR)cpu.param1.disp32;
1252 uint8_t *pJumpTableHC;
1253 int rc2;
1254
1255 Log(("Jump through jump table\n"));
1256
1257 rc2 = PGMPhysGCPtr2HCPtr(pVM, pJumpTableGC, (PRTHCPTR)&pJumpTableHC);
1258 if (rc2 == VINF_SUCCESS)
1259 {
1260 for (uint32_t i=0;i<2;i++)
1261 {
1262 uint64_t fFlags;
1263
1264 addr = pJumpTableGC + cpu.param1.scale * i;
1265 /* Same page? */
1266 if (PAGE_ADDRESS(addr) != PAGE_ADDRESS(pJumpTableGC))
1267 break;
1268
1269 addr = *(RTGCPTR *)(pJumpTableHC + cpu.param1.scale * i);
1270
1271 rc2 = PGMGstGetPage(pVM, addr, &fFlags, NULL);
1272 if ( rc2 != VINF_SUCCESS
1273 || (fFlags & X86_PTE_US)
1274 || !(fFlags & X86_PTE_P)
1275 )
1276 break;
1277
1278 Log(("Jump to %VGv\n", addr));
1279
1280 pJmpPage = NULL;
1281 if (csamIsCodeScanned(pVM, addr, &pJmpPage) == false)
1282 {
1283 if (pJmpPage == NULL)
1284 {
1285 /* New branch target; let's take a look at it. */
1286 pJmpPage = csamCreatePageRecord(pVM, addr, CSAM_TAG_CSAM, fCode32);
1287 if (pJmpPage == NULL)
1288 {
1289 rc = VERR_NO_MEMORY;
1290 goto done;
1291 }
1292 Assert(pPage);
1293 }
1294 rc = csamAnalyseCodeStream(pVM, pInstrGC, addr, fCode32, pfnCSAMR3Analyse, (void *)pJmpPage, pCacheRec);
1295 if (rc != VINF_SUCCESS) {
1296 goto done;
1297 }
1298 }
1299 }
1300 }
1301 }
1302#endif
1303 if (rc != VWRN_CONTINUE_ANALYSIS) {
1304 break; //done!
1305 }
1306next_please:
1307 if (cpu.pCurInstr->opcode == OP_JMP)
1308 {
1309 rc = VINF_SUCCESS;
1310 goto done;
1311 }
1312 pCurInstrGC += opsize;
1313 }
1314done:
1315 pCacheRec->depth--;
1316 return rc;
1317}
1318
1319
1320/**
1321 * Calculates the 64 bits hash value for the current page
1322 *
1323 * @returns hash value
1324 * @param pVM The VM to operate on.
1325 * @param pInstr Page address
1326 */
1327uint64_t csamR3CalcPageHash(PVM pVM, RTGCPTR pInstr)
1328{
1329 uint64_t hash = 0;
1330 uint32_t val[5];
1331 int rc;
1332
1333 Assert((pInstr & PAGE_OFFSET_MASK) == 0);
1334
1335 rc = PGMPhysReadGCPtr(pVM, &val[0], pInstr, sizeof(val[0]));
1336 AssertMsg(VBOX_SUCCESS(rc) || rc == VERR_PAGE_NOT_PRESENT || rc == VERR_PAGE_TABLE_NOT_PRESENT, ("rc = %Vrc\n", rc));
1337 if (rc == VERR_PAGE_NOT_PRESENT || rc == VERR_PAGE_TABLE_NOT_PRESENT)
1338 {
1339 Log(("csamR3CalcPageHash: page %VGv not present!!\n", pInstr));
1340 return ~0ULL;
1341 }
1342
1343 rc = PGMPhysReadGCPtr(pVM, &val[1], pInstr+1024, sizeof(val[0]));
1344 AssertMsg(VBOX_SUCCESS(rc) || rc == VERR_PAGE_NOT_PRESENT || rc == VERR_PAGE_TABLE_NOT_PRESENT, ("rc = %Vrc\n", rc));
1345 if (rc == VERR_PAGE_NOT_PRESENT || rc == VERR_PAGE_TABLE_NOT_PRESENT)
1346 {
1347 Log(("csamR3CalcPageHash: page %VGv not present!!\n", pInstr));
1348 return ~0ULL;
1349 }
1350
1351 rc = PGMPhysReadGCPtr(pVM, &val[2], pInstr+2048, sizeof(val[0]));
1352 AssertMsg(VBOX_SUCCESS(rc) || rc == VERR_PAGE_NOT_PRESENT || rc == VERR_PAGE_TABLE_NOT_PRESENT, ("rc = %Vrc\n", rc));
1353 if (rc == VERR_PAGE_NOT_PRESENT || rc == VERR_PAGE_TABLE_NOT_PRESENT)
1354 {
1355 Log(("csamR3CalcPageHash: page %VGv not present!!\n", pInstr));
1356 return ~0ULL;
1357 }
1358
1359 rc = PGMPhysReadGCPtr(pVM, &val[3], pInstr+3072, sizeof(val[0]));
1360 AssertMsg(VBOX_SUCCESS(rc) || rc == VERR_PAGE_NOT_PRESENT || rc == VERR_PAGE_TABLE_NOT_PRESENT, ("rc = %Vrc\n", rc));
1361 if (rc == VERR_PAGE_NOT_PRESENT || rc == VERR_PAGE_TABLE_NOT_PRESENT)
1362 {
1363 Log(("csamR3CalcPageHash: page %VGv not present!!\n", pInstr));
1364 return ~0ULL;
1365 }
1366
1367 rc = PGMPhysReadGCPtr(pVM, &val[4], pInstr+4092, sizeof(val[0]));
1368 AssertMsg(VBOX_SUCCESS(rc) || rc == VERR_PAGE_NOT_PRESENT || rc == VERR_PAGE_TABLE_NOT_PRESENT, ("rc = %Vrc\n", rc));
1369 if (rc == VERR_PAGE_NOT_PRESENT || rc == VERR_PAGE_TABLE_NOT_PRESENT)
1370 {
1371 Log(("csamR3CalcPageHash: page %VGv not present!!\n", pInstr));
1372 return ~0ULL;
1373 }
1374
1375 // don't want to get division by zero traps
1376 val[2] |= 1;
1377 val[4] |= 1;
1378
1379 hash = (uint64_t)val[0] * (uint64_t)val[1] / (uint64_t)val[2] + (val[3]%val[4]);
1380 return (hash == ~0ULL) ? hash - 1 : hash;
1381}
1382
1383
1384/**
1385 * Flush a page record
1386 *
1387 * @returns VBox status code. (trap handled or not)
1388 * @param pVM The VM to operate on.
1389 * @param pPage Page record ptr
1390 */
1391int csamR3FlushPageRecord(PVM pVM, PCSAMPAGE pPage)
1392{
1393 Log(("csamR3FlushPageRecord: page %VGv has changed -> FLUSH\n", pPage->pPageGC));
1394
1395 if (pPage->pBitmap == NULL)
1396 {
1397 pPage->pBitmap = (uint8_t *)MMR3HeapAllocZ(pVM, MM_TAG_CSAM_PATCH, CSAM_PAGE_BITMAP_SIZE);
1398 Assert(pPage->pBitmap);
1399 if (pPage->pBitmap == NULL)
1400 {
1401 return VERR_NO_MEMORY;
1402 }
1403 }
1404 else
1405 {
1406 memset(pPage->pBitmap, 0, CSAM_PAGE_BITMAP_SIZE);
1407 }
1408 pPage->u64Hash = csamR3CalcPageHash(pVM, pPage->pPageGC);
1409
1410 return VINF_SUCCESS;
1411}
1412
1413/**
1414 * Notify CSAM of a page flush
1415 *
1416 * @returns VBox status code
1417 * @param pVM The VM to operate on.
1418 * @param addr GC address of the page to flush
1419 * @param fRemovePage Page removal flag
1420 */
1421static int csamFlushPage(PVM pVM, RTGCPTR addr, bool fRemovePage)
1422{
1423 PCSAMPAGEREC pPageRec;
1424 int rc;
1425 RTGCPHYS GCPhys = 0;
1426 uint64_t fFlags = 0;
1427
1428 if (!CSAMIsEnabled(pVM))
1429 return VINF_SUCCESS;
1430
1431 STAM_PROFILE_START(&pVM->csam.s.StatTimeFlushPage, a);
1432
1433 addr = addr & PAGE_BASE_GC_MASK;
1434
1435 /*
1436 * Note: searching for the page in our tree first is more expensive (skipped flushes are two orders of magnitude more common)
1437 */
1438 if (pVM->csam.s.pPageTree == NULL)
1439 {
1440 STAM_PROFILE_STOP(&pVM->csam.s.StatTimeFlushPage, a);
1441 return VWRN_CSAM_PAGE_NOT_FOUND;
1442 }
1443
1444 rc = PGMGstGetPage(pVM, addr, &fFlags, &GCPhys);
1445 /* Returned at a very early stage (no paging yet presumably). */
1446 if (rc == VERR_NOT_SUPPORTED)
1447 {
1448 STAM_PROFILE_STOP(&pVM->csam.s.StatTimeFlushPage, a);
1449 return rc;
1450 }
1451
1452 if (VBOX_SUCCESS(rc))
1453 {
1454 if ( (fFlags & X86_PTE_US)
1455 || rc == VERR_PGM_PHYS_PAGE_RESERVED
1456 )
1457 {
1458 /* User page -> not relevant for us. */
1459 STAM_COUNTER_ADD(&pVM->csam.s.StatNrFlushesSkipped, 1);
1460 STAM_PROFILE_STOP(&pVM->csam.s.StatTimeFlushPage, a);
1461 return VINF_SUCCESS;
1462 }
1463 }
1464 else
1465 if (rc != VERR_PAGE_NOT_PRESENT && rc != VERR_PAGE_TABLE_NOT_PRESENT)
1466 AssertMsgFailed(("PGMR3GetPage %VGv failed with %Vrc\n", addr, rc));
1467
1468 pPageRec = (PCSAMPAGEREC)RTAvlPVGet(&pVM->csam.s.pPageTree, (AVLPVKEY)addr);
1469 if (pPageRec)
1470 {
1471 if ( GCPhys == pPageRec->page.GCPhys
1472 && (fFlags & X86_PTE_P))
1473 {
1474 STAM_COUNTER_ADD(&pVM->csam.s.StatNrFlushesSkipped, 1);
1475 STAM_PROFILE_STOP(&pVM->csam.s.StatTimeFlushPage, a);
1476 return VINF_SUCCESS;
1477 }
1478
1479 Log(("CSAMR3FlushPage: page %VGv has changed -> FLUSH (rc=%Vrc) (Phys: %VGp vs %VGp)\n", addr, rc, GCPhys, pPageRec->page.GCPhys));
1480
1481 STAM_COUNTER_ADD(&pVM->csam.s.StatNrFlushes, 1);
1482
1483 if (fRemovePage)
1484 csamRemovePageRecord(pVM, addr);
1485 else
1486 {
1487 CSAMMarkPage(pVM, addr, false);
1488 pPageRec->page.GCPhys = 0;
1489 pPageRec->page.fFlags = 0;
1490 rc = PGMGstGetPage(pVM, addr, &pPageRec->page.fFlags, &pPageRec->page.GCPhys);
1491 if (rc == VINF_SUCCESS)
1492 pPageRec->page.u64Hash = csamR3CalcPageHash(pVM, addr);
1493 }
1494
1495
1496 /*
1497 * Inform patch manager about the flush; no need to repeat the above check twice.
1498 */
1499 PATMR3FlushPage(pVM, addr);
1500
1501 STAM_PROFILE_STOP(&pVM->csam.s.StatTimeFlushPage, a);
1502 return VINF_SUCCESS;
1503 }
1504 else
1505 {
1506 STAM_PROFILE_STOP(&pVM->csam.s.StatTimeFlushPage, a);
1507 return VWRN_CSAM_PAGE_NOT_FOUND;
1508 }
1509}
1510
1511/**
1512 * Notify CSAM of a page flush
1513 *
1514 * @returns VBox status code
1515 * @param pVM The VM to operate on.
1516 * @param addr GC address of the page to flush
1517 */
1518CSAMR3DECL(int) CSAMR3FlushPage(PVM pVM, RTGCPTR addr)
1519{
1520 return csamFlushPage(pVM, addr, true /* remove page record */);
1521}
1522
1523/**
1524 * Check a page record in case a page has been changed
1525 *
1526 * @returns VBox status code. (trap handled or not)
1527 * @param pVM The VM to operate on.
1528 * @param pInstrGC GC instruction pointer
1529 */
1530int csamR3CheckPageRecord(PVM pVM, RTGCPTR pInstrGC)
1531{
1532 PCSAMPAGEREC pPageRec;
1533 uint64_t u64hash;
1534
1535 pInstrGC = pInstrGC & PAGE_BASE_GC_MASK;
1536
1537 pPageRec = (PCSAMPAGEREC)RTAvlPVGet(&pVM->csam.s.pPageTree, (AVLPVKEY)pInstrGC);
1538 if (pPageRec)
1539 {
1540 u64hash = csamR3CalcPageHash(pVM, pInstrGC);
1541 if (u64hash != pPageRec->page.u64Hash)
1542 {
1543 csamR3FlushPageRecord(pVM, &pPageRec->page);
1544 }
1545 }
1546 else
1547 {
1548 return VWRN_CSAM_PAGE_NOT_FOUND;
1549 }
1550 return VINF_SUCCESS;
1551}
1552
1553/**
1554 * Returns monitor description based on CSAM tag
1555 *
1556 * @return description string
1557 * @param enmTag Owner tag
1558 */
1559const char *csamGetMonitorDescription(CSAMTAG enmTag)
1560{
1561 if (enmTag == CSAM_TAG_PATM)
1562 return "CSAM-PATM self-modifying code monitor handler";
1563 else
1564 if (enmTag == CSAM_TAG_REM)
1565 return "CSAM-REM self-modifying code monitor handler";
1566 Assert(enmTag == CSAM_TAG_CSAM);
1567 return "CSAM self-modifying code monitor handler";
1568}
1569
1570/**
1571 * Adds page record to our lookup tree
1572 *
1573 * @returns CSAMPAGE ptr or NULL if failure
1574 * @param pVM The VM to operate on.
1575 * @param GCPtr Page address
1576 * @param enmTag Owner tag
1577 * @param fCode32 16 or 32 bits code
1578 * @param fMonitorInvalidation Monitor page invalidation flag
1579 */
1580static PCSAMPAGE csamCreatePageRecord(PVM pVM, RTGCPTR GCPtr, CSAMTAG enmTag, bool fCode32, bool fMonitorInvalidation)
1581{
1582 PCSAMPAGEREC pPage;
1583 int rc;
1584 bool ret;
1585
1586 Log(("New page record for %VGv\n", GCPtr & PAGE_BASE_GC_MASK));
1587
1588 pPage = (PCSAMPAGEREC)MMR3HeapAllocZ(pVM, MM_TAG_CSAM_PATCH, sizeof(CSAMPAGEREC));
1589 if (pPage == NULL)
1590 {
1591 AssertMsgFailed(("csamCreatePageRecord: Out of memory!!!!\n"));
1592 return NULL;
1593 }
1594 /* Round down to page boundary. */
1595 GCPtr = (GCPtr & PAGE_BASE_GC_MASK);
1596 pPage->Core.Key = (AVLPVKEY)GCPtr;
1597 pPage->page.pPageGC = GCPtr;
1598 pPage->page.fCode32 = fCode32;
1599 pPage->page.fMonitorInvalidation = fMonitorInvalidation;
1600 pPage->page.enmTag = enmTag;
1601 pPage->page.fMonitorActive = false;
1602 pPage->page.pBitmap = (uint8_t *)MMR3HeapAllocZ(pVM, MM_TAG_CSAM_PATCH, PAGE_SIZE/sizeof(uint8_t));
1603 rc = PGMGstGetPage(pVM, GCPtr, &pPage->page.fFlags, &pPage->page.GCPhys);
1604 AssertMsg(VBOX_SUCCESS(rc) || rc == VERR_PAGE_NOT_PRESENT || rc == VERR_PAGE_TABLE_NOT_PRESENT, ("rc = %Vrc\n", rc));
1605
1606 pPage->page.u64Hash = csamR3CalcPageHash(pVM, GCPtr);
1607 ret = RTAvlPVInsert(&pVM->csam.s.pPageTree, &pPage->Core);
1608 Assert(ret);
1609
1610#ifdef CSAM_MONITOR_CODE_PAGES
1611 AssertRelease(!fInCSAMCodePageInvalidate);
1612
1613 switch (enmTag)
1614 {
1615 case CSAM_TAG_PATM:
1616 case CSAM_TAG_REM:
1617#ifdef CSAM_MONITOR_CSAM_CODE_PAGES
1618 case CSAM_TAG_CSAM:
1619#endif
1620 {
1621 int rc = PGMR3HandlerVirtualRegister(pVM, PGMVIRTHANDLERTYPE_WRITE, GCPtr, GCPtr + (PAGE_SIZE - 1) /* inclusive! */,
1622 (fMonitorInvalidation) ? CSAMCodePageInvalidate : 0, CSAMCodePageWriteHandler, "CSAMGCCodePageWriteHandler", 0,
1623 csamGetMonitorDescription(enmTag));
1624 AssertMsg(VBOX_SUCCESS(rc) || rc == VERR_PGM_HANDLER_VIRTUAL_CONFLICT, ("PGMR3HandlerVirtualRegisterEx %VGv failed with %Vrc\n", GCPtr, rc));
1625 if (VBOX_FAILURE(rc))
1626 Log(("PGMR3HandlerVirtualRegisterEx for %VGv failed with %Vrc\n", GCPtr, rc));
1627
1628 /* Could fail, because it's already monitored. Don't treat that condition as fatal. */
1629
1630 /* Prefetch it in case it's not there yet. */
1631 rc = PGMPrefetchPage(pVM, GCPtr);
1632 AssertRC(rc);
1633
1634 rc = PGMShwModifyPage(pVM, GCPtr, 1, 0, ~(uint64_t)X86_PTE_RW);
1635 Assert(rc == VINF_SUCCESS || rc == VERR_PAGE_NOT_PRESENT || rc == VERR_PAGE_TABLE_NOT_PRESENT);
1636
1637 pPage->page.fMonitorActive = true;
1638 STAM_COUNTER_INC(&pVM->csam.s.StatPageMonitor);
1639 break;
1640 }
1641 default:
1642 break; /* to shut up GCC */
1643 }
1644
1645 Log(("csamCreatePageRecord %VGv HCPhys=%VGp\n", GCPtr, pPage->page.GCPhys));
1646
1647#ifdef VBOX_WITH_STATISTICS
1648 switch (enmTag)
1649 {
1650 case CSAM_TAG_CSAM:
1651 STAM_COUNTER_INC(&pVM->csam.s.StatPageCSAM);
1652 break;
1653 case CSAM_TAG_PATM:
1654 STAM_COUNTER_INC(&pVM->csam.s.StatPagePATM);
1655 break;
1656 case CSAM_TAG_REM:
1657 STAM_COUNTER_INC(&pVM->csam.s.StatPageREM);
1658 break;
1659 default:
1660 break; /* to shut up GCC */
1661 }
1662#endif
1663
1664#endif
1665
1666 STAM_COUNTER_INC(&pVM->csam.s.StatNrPages);
1667 if (fMonitorInvalidation)
1668 STAM_COUNTER_INC(&pVM->csam.s.StatNrPagesInv);
1669
1670 return &pPage->page;
1671}
1672
1673/**
1674 * Monitors a code page (if not already monitored)
1675 *
1676 * @returns VBox status code
1677 * @param pVM The VM to operate on.
1678 * @param pPageAddrGC The page to monitor
1679 * @param enmTag Monitor tag
1680 */
1681CSAMR3DECL(int) CSAMR3MonitorPage(PVM pVM, RTGCPTR pPageAddrGC, CSAMTAG enmTag)
1682{
1683 PCSAMPAGEREC pPageRec = NULL;
1684 int rc;
1685 bool fMonitorInvalidation;
1686
1687 /* Dirty pages must be handled before calling this function!. */
1688 Assert(!pVM->csam.s.cDirtyPages);
1689
1690 if (pVM->csam.s.fScanningStarted == false)
1691 return VINF_SUCCESS; /* too early */
1692
1693 pPageAddrGC &= PAGE_BASE_GC_MASK;
1694
1695 Log(("CSAMR3MonitorPage %VGv %d\n", pPageAddrGC, enmTag));
1696
1697 /** @todo implicit assumption */
1698 fMonitorInvalidation = (enmTag == CSAM_TAG_PATM);
1699
1700 pPageRec = (PCSAMPAGEREC)RTAvlPVGet(&pVM->csam.s.pPageTree, (AVLPVKEY)pPageAddrGC);
1701 if (pPageRec == NULL)
1702 {
1703 uint64_t fFlags;
1704
1705 rc = PGMGstGetPage(pVM, pPageAddrGC, &fFlags, NULL);
1706 AssertMsg(VBOX_SUCCESS(rc) || rc == VERR_PAGE_NOT_PRESENT || rc == VERR_PAGE_TABLE_NOT_PRESENT, ("rc = %Vrc\n", rc));
1707 if ( rc == VINF_SUCCESS
1708 && (fFlags & X86_PTE_US))
1709 {
1710 /* We don't care about user pages. */
1711 STAM_COUNTER_INC(&pVM->csam.s.StatNrUserPages);
1712 return VINF_SUCCESS;
1713 }
1714
1715 csamCreatePageRecord(pVM, pPageAddrGC, enmTag, true /* 32 bits code */, fMonitorInvalidation);
1716
1717 pPageRec = (PCSAMPAGEREC)RTAvlPVGet(&pVM->csam.s.pPageTree, (AVLPVKEY)pPageAddrGC);
1718 Assert(pPageRec);
1719 }
1720 /** @todo reference count */
1721
1722#ifdef CSAM_MONITOR_CSAM_CODE_PAGES
1723 Assert(pPageRec->page.fMonitorActive);
1724#endif
1725
1726#ifdef CSAM_MONITOR_CODE_PAGES
1727 if (!pPageRec->page.fMonitorActive)
1728 {
1729 Log(("CSAMR3MonitorPage: activate monitoring for %VGv\n", pPageAddrGC));
1730
1731 rc = PGMR3HandlerVirtualRegister(pVM, PGMVIRTHANDLERTYPE_WRITE, pPageAddrGC, pPageAddrGC + (PAGE_SIZE - 1) /* inclusive! */,
1732 (fMonitorInvalidation) ? CSAMCodePageInvalidate : 0, CSAMCodePageWriteHandler, "CSAMGCCodePageWriteHandler", 0,
1733 csamGetMonitorDescription(enmTag));
1734 AssertMsg(VBOX_SUCCESS(rc) || rc == VERR_PGM_HANDLER_VIRTUAL_CONFLICT, ("PGMR3HandlerVirtualRegisterEx %VGv failed with %Vrc\n", pPageAddrGC, rc));
1735 if (VBOX_FAILURE(rc))
1736 Log(("PGMR3HandlerVirtualRegisterEx for %VGv failed with %Vrc\n", pPageAddrGC, rc));
1737
1738 /* Could fail, because it's already monitored. Don't treat that condition as fatal. */
1739
1740 /* Prefetch it in case it's not there yet. */
1741 rc = PGMPrefetchPage(pVM, pPageAddrGC);
1742 AssertRC(rc);
1743
1744 rc = PGMShwModifyPage(pVM, pPageAddrGC, 1, 0, ~(uint64_t)X86_PTE_RW);
1745 Assert(rc == VINF_SUCCESS || rc == VERR_PAGE_NOT_PRESENT || rc == VERR_PAGE_TABLE_NOT_PRESENT);
1746
1747 STAM_COUNTER_INC(&pVM->csam.s.StatPageMonitor);
1748
1749 pPageRec->page.fMonitorActive = true;
1750 pPageRec->page.fMonitorInvalidation = fMonitorInvalidation;
1751 }
1752 else
1753 if ( !pPageRec->page.fMonitorInvalidation
1754 && fMonitorInvalidation)
1755 {
1756 Assert(pPageRec->page.fMonitorActive);
1757 PGMHandlerVirtualChangeInvalidateCallback(pVM, pPageRec->page.pPageGC, CSAMCodePageInvalidate);
1758 pPageRec->page.fMonitorInvalidation = true;
1759 STAM_COUNTER_INC(&pVM->csam.s.StatNrPagesInv);
1760
1761 /* Prefetch it in case it's not there yet. */
1762 rc = PGMPrefetchPage(pVM, pPageAddrGC);
1763 AssertRC(rc);
1764
1765 /* Make sure it's readonly. Page invalidation may have modified the attributes. */
1766 rc = PGMShwModifyPage(pVM, pPageAddrGC, 1, 0, ~(uint64_t)X86_PTE_RW);
1767 Assert(rc == VINF_SUCCESS || rc == VERR_PAGE_NOT_PRESENT || rc == VERR_PAGE_TABLE_NOT_PRESENT);
1768 }
1769
1770#ifdef VBOX_STRICT
1771 if (pPageRec->page.fMonitorActive)
1772 {
1773 uint64_t fPageShw;
1774 RTHCPHYS GCPhys;
1775 rc = PGMShwGetPage(pVM, pPageAddrGC, &fPageShw, &GCPhys);
1776 AssertMsg( (rc == VERR_PAGE_NOT_PRESENT || rc == VERR_PAGE_TABLE_NOT_PRESENT)
1777 || !(fPageShw & X86_PTE_RW)
1778 || (pPageRec->page.GCPhys == 0), ("Shadow page flags for %VGv (%VHp) aren't readonly (%VX64)!!\n", pPageAddrGC, GCPhys, fPageShw));
1779 }
1780#endif
1781
1782 if (pPageRec->page.GCPhys == 0)
1783 {
1784 /* Prefetch it in case it's not there yet. */
1785 rc = PGMPrefetchPage(pVM, pPageAddrGC);
1786 AssertRC(rc);
1787 /* The page was changed behind our back. It won't be made read-only until the next SyncCR3, so force it here. */
1788 rc = PGMShwModifyPage(pVM, pPageAddrGC, 1, 0, ~(uint64_t)X86_PTE_RW);
1789 Assert(rc == VINF_SUCCESS || rc == VERR_PAGE_NOT_PRESENT || rc == VERR_PAGE_TABLE_NOT_PRESENT);
1790 }
1791#endif /* CSAM_MONITOR_CODE_PAGES */
1792 return VINF_SUCCESS;
1793}
1794
1795/**
1796 * Removes a page record from our lookup tree
1797 *
1798 * @returns VBox status code
1799 * @param pVM The VM to operate on.
1800 * @param GCPtr Page address
1801 */
1802static int csamRemovePageRecord(PVM pVM, RTGCPTR GCPtr)
1803{
1804 PCSAMPAGEREC pPageRec;
1805
1806 Log(("csamRemovePageRecord %VGv\n", GCPtr));
1807 pPageRec = (PCSAMPAGEREC)RTAvlPVRemove(&pVM->csam.s.pPageTree, (AVLPVKEY)GCPtr);
1808
1809 if (pPageRec)
1810 {
1811 STAM_COUNTER_INC(&pVM->csam.s.StatNrRemovedPages);
1812
1813#ifdef CSAM_MONITOR_CODE_PAGES
1814 if (pPageRec->page.fMonitorActive)
1815 {
1816 /* @todo -> this is expensive (cr3 reload)!!!
1817 * if this happens often, then reuse it instead!!!
1818 */
1819 Assert(!fInCSAMCodePageInvalidate);
1820 STAM_COUNTER_DEC(&pVM->csam.s.StatPageMonitor);
1821 PGMHandlerVirtualDeregister(pVM, GCPtr);
1822 }
1823 if (pPageRec->page.enmTag == CSAM_TAG_PATM)
1824 {
1825 /* Make sure the recompiler flushes its cache as this page is no longer monitored. */
1826 STAM_COUNTER_INC(&pVM->csam.s.StatPageRemoveREMFlush);
1827 CPUMSetChangedFlags(pVM, CPUM_CHANGED_GLOBAL_TLB_FLUSH);
1828 }
1829#endif
1830
1831#ifdef VBOX_WITH_STATISTICS
1832 switch (pPageRec->page.enmTag)
1833 {
1834 case CSAM_TAG_CSAM:
1835 STAM_COUNTER_DEC(&pVM->csam.s.StatPageCSAM);
1836 break;
1837 case CSAM_TAG_PATM:
1838 STAM_COUNTER_DEC(&pVM->csam.s.StatPagePATM);
1839 break;
1840 case CSAM_TAG_REM:
1841 STAM_COUNTER_DEC(&pVM->csam.s.StatPageREM);
1842 break;
1843 default:
1844 break; /* to shut up GCC */
1845 }
1846#endif
1847
1848 if (pPageRec->page.pBitmap) MMR3HeapFree(pPageRec->page.pBitmap);
1849 MMR3HeapFree(pPageRec);
1850 }
1851 else
1852 AssertFailed();
1853
1854 return VINF_SUCCESS;
1855}
1856
1857/**
1858 * Callback for delayed writes from non-EMT threads
1859 *
1860 * @param pVM VM Handle.
1861 * @param GCPtr The virtual address the guest is writing to. (not correct if it's an alias!)
1862 * @param cbBuf How much it's reading/writing.
1863 */
1864static void CSAMDelayedWriteHandler(PVM pVM, RTGCPTR GCPtr, size_t cbBuf)
1865{
1866 int rc = PATMR3PatchWrite(pVM, GCPtr, cbBuf);
1867 AssertRC(rc);
1868}
1869
1870/**
1871 * #PF Handler callback for virtual access handler ranges.
1872 *
1873 * Important to realize that a physical page in a range can have aliases, and
1874 * for ALL and WRITE handlers these will also trigger.
1875 *
1876 * @returns VINF_SUCCESS if the handler have carried out the operation.
1877 * @returns VINF_PGM_HANDLER_DO_DEFAULT if the caller should carry out the access operation.
1878 * @param pVM VM Handle.
1879 * @param GCPtr The virtual address the guest is writing to. (not correct if it's an alias!)
1880 * @param pvPtr The HC mapping of that address.
1881 * @param pvBuf What the guest is reading/writing.
1882 * @param cbBuf How much it's reading/writing.
1883 * @param enmAccessType The access type.
1884 * @param pvUser User argument.
1885 */
1886static DECLCALLBACK(int) CSAMCodePageWriteHandler(PVM pVM, RTGCPTR GCPtr, void *pvPtr, void *pvBuf, size_t cbBuf, PGMACCESSTYPE enmAccessType, void *pvUser)
1887{
1888 int rc;
1889
1890 Assert(enmAccessType == PGMACCESSTYPE_WRITE);
1891 Log(("CSAMCodePageWriteHandler: write to %VGv size=%d\n", GCPtr, cbBuf));
1892
1893 if (VM_IS_EMT(pVM))
1894 {
1895 rc = PATMR3PatchWrite(pVM, GCPtr, cbBuf);
1896 }
1897 else
1898 {
1899 /* Queue the write instead otherwise we'll get concurrency issues. */
1900 /** @note in theory not correct to let it write the data first before disabling a patch!
1901 * (if it writes the same data as the patch jump and we replace it with obsolete opcodes)
1902 */
1903 Log(("CSAMCodePageWriteHandler: delayed write!\n"));
1904 rc = VMR3ReqCallEx(pVM, NULL, 0, VMREQFLAGS_NO_WAIT | VMREQFLAGS_VOID,
1905 (PFNRT)CSAMDelayedWriteHandler, 3, pVM, GCPtr, cbBuf);
1906 }
1907 AssertRC(rc);
1908
1909 return VINF_PGM_HANDLER_DO_DEFAULT;
1910}
1911
1912/**
1913 * #PF Handler callback for invalidation of virtual access handler ranges.
1914 *
1915 * @param pVM VM Handle.
1916 * @param GCPtr The virtual address the guest has changed.
1917 */
1918static DECLCALLBACK(int) CSAMCodePageInvalidate(PVM pVM, RTGCPTR GCPtr)
1919{
1920 fInCSAMCodePageInvalidate = true;
1921 LogFlow(("CSAMCodePageInvalidate %VGv\n", GCPtr));
1922 /** @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. */
1923 csamFlushPage(pVM, GCPtr, false /* don't remove page! */);
1924 fInCSAMCodePageInvalidate = false;
1925 return VINF_SUCCESS;
1926}
1927
1928/**
1929 * Check if the current instruction has already been checked before
1930 *
1931 * @returns VBox status code. (trap handled or not)
1932 * @param pVM The VM to operate on.
1933 * @param pInstr Instruction pointer
1934 * @param pPage CSAM patch structure pointer
1935 */
1936bool csamIsCodeScanned(PVM pVM, RTGCPTR pInstr, PCSAMPAGE *pPage)
1937{
1938 PCSAMPAGEREC pPageRec;
1939 uint32_t offset;
1940
1941 STAM_PROFILE_START(&pVM->csam.s.StatTimeCheckAddr, a);
1942
1943 offset = pInstr & PAGE_OFFSET_MASK;
1944 pInstr = pInstr & PAGE_BASE_GC_MASK;
1945
1946 Assert(pPage);
1947
1948 if (*pPage && (*pPage)->pPageGC == pInstr)
1949 {
1950 if ((*pPage)->pBitmap == NULL || ASMBitTest((*pPage)->pBitmap, offset))
1951 {
1952 STAM_COUNTER_ADD(&pVM->csam.s.StatNrKnownPagesHC, 1);
1953 STAM_PROFILE_STOP(&pVM->csam.s.StatTimeCheckAddr, a);
1954 return true;
1955 }
1956 STAM_PROFILE_STOP(&pVM->csam.s.StatTimeCheckAddr, a);
1957 return false;
1958 }
1959
1960 pPageRec = (PCSAMPAGEREC)RTAvlPVGet(&pVM->csam.s.pPageTree, (AVLPVKEY)pInstr);
1961 if (pPageRec)
1962 {
1963 if (pPage) *pPage= &pPageRec->page;
1964 if (pPageRec->page.pBitmap == NULL || ASMBitTest(pPageRec->page.pBitmap, offset))
1965 {
1966 STAM_COUNTER_ADD(&pVM->csam.s.StatNrKnownPagesHC, 1);
1967 STAM_PROFILE_STOP(&pVM->csam.s.StatTimeCheckAddr, a);
1968 return true;
1969 }
1970 }
1971 else
1972 {
1973 if (pPage) *pPage = NULL;
1974 }
1975 STAM_PROFILE_STOP(&pVM->csam.s.StatTimeCheckAddr, a);
1976 return false;
1977}
1978
1979/**
1980 * Mark an instruction in a page as scanned/not scanned
1981 *
1982 * @param pVM The VM to operate on.
1983 * @param pPage Patch structure pointer
1984 * @param pInstr Instruction pointer
1985 * @param opsize Instruction size
1986 * @param fScanned Mark as scanned or not
1987 */
1988static void csamMarkCode(PVM pVM, PCSAMPAGE pPage, RTGCPTR pInstr, uint32_t opsize, bool fScanned)
1989{
1990 LogFlow(("csamMarkCodeAsScanned %VGv opsize=%d\n", pInstr, opsize));
1991 CSAMMarkPage(pVM, pInstr, fScanned);
1992
1993 /** @todo should recreate empty bitmap if !fScanned */
1994 if (pPage->pBitmap == NULL)
1995 return;
1996
1997 if (fScanned)
1998 {
1999 // retn instructions can be scanned more than once
2000 if (ASMBitTest(pPage->pBitmap, pInstr & PAGE_OFFSET_MASK) == 0)
2001 {
2002 pPage->uSize += opsize;
2003 STAM_COUNTER_ADD(&pVM->csam.s.StatNrInstr, 1);
2004 }
2005 if (pPage->uSize >= PAGE_SIZE)
2006 {
2007 Log(("Scanned full page (%VGv) -> free bitmap\n", pInstr & PAGE_BASE_GC_MASK));
2008 MMR3HeapFree(pPage->pBitmap);
2009 pPage->pBitmap = NULL;
2010 }
2011 else
2012 ASMBitSet(pPage->pBitmap, pInstr & PAGE_OFFSET_MASK);
2013 }
2014 else
2015 ASMBitClear(pPage->pBitmap, pInstr & PAGE_OFFSET_MASK);
2016}
2017
2018/**
2019 * Mark an instruction in a page as scanned/not scanned
2020 *
2021 * @returns VBox status code.
2022 * @param pVM The VM to operate on.
2023 * @param pInstr Instruction pointer
2024 * @param opsize Instruction size
2025 * @param fScanned Mark as scanned or not
2026 */
2027CSAMDECL(int) CSAMR3MarkCode(PVM pVM, RTGCPTR pInstr, uint32_t opsize, bool fScanned)
2028{
2029 PCSAMPAGE pPage = 0;
2030
2031 Assert(!fScanned); /* other case not implemented. */
2032 Assert(!PATMIsPatchGCAddr(pVM, pInstr));
2033
2034 if (csamIsCodeScanned(pVM, pInstr, &pPage) == false)
2035 {
2036 Assert(fScanned == true); /* other case should not be possible */
2037 return VINF_SUCCESS;
2038 }
2039
2040 Log(("CSAMR3MarkCode: %VGv size=%d fScanned=%d\n", pInstr, opsize, fScanned));
2041 csamMarkCode(pVM, pPage, pInstr, opsize, fScanned);
2042 return VINF_SUCCESS;
2043}
2044
2045
2046/**
2047 * Scan and analyse code starting at specified EIP
2048 *
2049 * @returns VBox status code. (trap handled or not)
2050 * @param pVM The VM to operate on.
2051 * @param pEip Instruction pointer
2052 * @param fCode32 16 of 32 bits code
2053 */
2054CSAMR3DECL(int) CSAMR3CheckEIP(PVM pVM, RTGCPTR pEip, bool fCode32)
2055{
2056 int rc;
2057 PCSAMPAGE pPage = NULL;
2058
2059 if (EMIsRawRing0Enabled(pVM) == false || PATMIsPatchGCAddr(pVM, pEip) == true)
2060 {
2061 // No use
2062 return VINF_SUCCESS;
2063 }
2064
2065 if (CSAMIsEnabled(pVM))
2066 {
2067 // Cache record for PATMGCVirtToHCVirt
2068 CSAMP2GLOOKUPREC cacheRec = {0};
2069
2070 //assuming 32 bits code for now
2071 Assert(fCode32);
2072
2073 STAM_PROFILE_START(&pVM->csam.s.StatTime, a);
2074 rc = csamAnalyseCallCodeStream(pVM, pEip, pEip, fCode32, CSAMR3AnalyseCallback, pPage, &cacheRec);
2075 STAM_PROFILE_STOP(&pVM->csam.s.StatTime, a);
2076 if (rc != VINF_SUCCESS)
2077 {
2078 Log(("csamAnalyseCodeStream failed with %d\n", rc));
2079 return rc;
2080 }
2081 else
2082 {
2083 // Log(("CSAMR3CheckEIP: already scanned page at %VGv\n", pEip));
2084 }
2085 }
2086 return VINF_SUCCESS;
2087}
2088
2089/**
2090 * Flush dirty code pages
2091 *
2092 * @returns VBox status code.
2093 * @param pVM The VM to operate on.
2094 */
2095CSAMR3DECL(int) CSAMR3FlushDirtyPages(PVM pVM)
2096{
2097 STAM_PROFILE_START(&pVM->csam.s.StatFlushDirtyPages, a);
2098
2099 for (uint32_t i=0;i<pVM->csam.s.cDirtyPages;i++)
2100 {
2101 int rc;
2102 PCSAMPAGEREC pPageRec;
2103 RTGCPTR GCPtr = pVM->csam.s.pvDirtyBasePage[i];
2104
2105 GCPtr = GCPtr & PAGE_BASE_GC_MASK;
2106
2107 /* Notify the recompiler that this page has been changed. */
2108 REMR3NotifyCodePageChanged(pVM, GCPtr);
2109
2110 /* Enable write protection again. (use the fault address as it might be an alias) */
2111 rc = PGMShwModifyPage(pVM, pVM->csam.s.pvDirtyFaultPage[i], 1, 0, ~(uint64_t)X86_PTE_RW);
2112 Assert(rc == VINF_SUCCESS || rc == VERR_PAGE_NOT_PRESENT || rc == VERR_PAGE_TABLE_NOT_PRESENT);
2113
2114 Log(("CSAMR3FlushDirtyPages: flush %VGv (modifypage rc=%Vrc)\n", pVM->csam.s.pvDirtyBasePage[i], rc));
2115
2116 pPageRec = (PCSAMPAGEREC)RTAvlPVGet(&pVM->csam.s.pPageTree, (AVLPVKEY)GCPtr);
2117 if (pPageRec && pPageRec->page.enmTag == CSAM_TAG_REM)
2118 {
2119 uint64_t fFlags;
2120
2121 rc = PGMGstGetPage(pVM, GCPtr, &fFlags, NULL);
2122 AssertMsg(VBOX_SUCCESS(rc) || rc == VERR_PAGE_NOT_PRESENT || rc == VERR_PAGE_TABLE_NOT_PRESENT, ("rc = %Vrc\n", rc));
2123 if ( rc == VINF_SUCCESS
2124 && (fFlags & X86_PTE_US))
2125 {
2126 /* We don't care about user pages. */
2127 csamRemovePageRecord(pVM, GCPtr);
2128 STAM_COUNTER_INC(&pVM->csam.s.StatNrUserPages);
2129 }
2130 }
2131 }
2132 pVM->csam.s.cDirtyPages = 0;
2133 VM_FF_CLEAR(pVM, VM_FF_CSAM_FLUSH_DIRTY_PAGE);
2134 STAM_PROFILE_STOP(&pVM->csam.s.StatFlushDirtyPages, a);
2135 return VINF_SUCCESS;
2136}
2137
2138/**
2139 * Analyse interrupt and trap gates
2140 *
2141 * @returns VBox status code.
2142 * @param pVM The VM to operate on.
2143 * @param iGate Start gate
2144 * @param cGates Number of gates to check
2145 */
2146CSAMR3DECL(int) CSAMR3CheckGates(PVM pVM, uint32_t iGate, uint32_t cGates)
2147{
2148 uint16_t cbIDT;
2149 RTGCPTR GCPtrIDT = CPUMGetGuestIDTR(pVM, &cbIDT);
2150 uint32_t iGateEnd;
2151 uint32_t maxGates;
2152 VBOXIDTE aIDT[256];
2153 PVBOXIDTE pGuestIdte;
2154 int rc;
2155
2156 if (EMIsRawRing0Enabled(pVM) == false)
2157 {
2158 /* Enabling interrupt gates only works when raw ring 0 is enabled. */
2159 AssertFailed();
2160 return VINF_SUCCESS;
2161 }
2162
2163 Assert(GCPtrIDT && cGates <= 256);
2164 if (!GCPtrIDT || cGates > 256)
2165 return VERR_INVALID_PARAMETER;
2166
2167 /* Determine valid upper boundary. */
2168 maxGates = (cbIDT+1) / sizeof(VBOXIDTE);
2169 Assert(iGate < maxGates);
2170 if (iGate > maxGates)
2171 return VERR_INVALID_PARAMETER;
2172
2173 if (iGate + cGates > maxGates)
2174 cGates = maxGates - iGate;
2175
2176 GCPtrIDT = GCPtrIDT + iGate * sizeof(VBOXIDTE);
2177 iGateEnd = iGate + cGates;
2178
2179 STAM_PROFILE_START(&pVM->csam.s.StatCheckGates, a);
2180
2181 /*
2182 * Get IDT entries.
2183 */
2184 if (PAGE_ADDRESS(GCPtrIDT) == PAGE_ADDRESS(GCPtrIDT+cGates*sizeof(VBOXIDTE)))
2185 {
2186 /* Just convert the IDT address to a HC pointer. The whole IDT fits in one page. */
2187 rc = PGMPhysGCPtr2HCPtr(pVM, GCPtrIDT, (PRTHCPTR)&pGuestIdte);
2188 if (VBOX_FAILURE(rc))
2189 {
2190 AssertMsgRC(rc, ("Failed to read IDTE! rc=%Vrc\n", rc));
2191 STAM_PROFILE_STOP(&pVM->csam.s.StatCheckGates, a);
2192 return rc;
2193 }
2194 }
2195 else
2196 {
2197 /* Slow method when it crosses a page boundary. */
2198 rc = PGMPhysReadGCPtr(pVM, aIDT, GCPtrIDT, cGates*sizeof(VBOXIDTE));
2199 if (VBOX_FAILURE(rc))
2200 {
2201 AssertMsgRC(rc, ("Failed to read IDTE! rc=%Vrc\n", rc));
2202 STAM_PROFILE_STOP(&pVM->csam.s.StatCheckGates, a);
2203 return rc;
2204 }
2205 pGuestIdte = &aIDT[0];
2206 }
2207
2208 for (/*iGate*/; iGate<iGateEnd; iGate++, pGuestIdte++)
2209 {
2210 if ( pVM->csam.s.fGatesChecked
2211 && cGates != 1 /* applies only when we check the entire IDT */
2212 && pGuestIdte->au64 != pVM->csam.s.aIDT[iGate].au64)
2213 {
2214 /* We can't check the contents here, as it might only have been partially modified at this point.
2215 * TRPMR3InjectEvent will recheck if necessary.
2216 */
2217#ifdef DEBUG
2218 RTGCPTR pHandler = (pGuestIdte->Gen.u16OffsetHigh << 16) | pGuestIdte->Gen.u16OffsetLow;
2219
2220 Log2(("IDT entry %x with handler %VGv refused (1)\n", iGate, pHandler));
2221#endif
2222 TRPMR3SetGuestTrapHandler(pVM, iGate, TRPM_INVALID_HANDLER);
2223 TRPMR3SetGuestTrapHandlerDirty(pVM, iGate, true);
2224 continue;
2225 }
2226
2227 TRPMR3SetGuestTrapHandlerDirty(pVM, iGate, false);
2228
2229 if ( pGuestIdte->Gen.u1Present
2230 && (pGuestIdte->Gen.u5Type2 == VBOX_IDTE_TYPE2_TRAP_32 || pGuestIdte->Gen.u5Type2 == VBOX_IDTE_TYPE2_INT_32)
2231 && (pGuestIdte->Gen.u2DPL == 3 || pGuestIdte->Gen.u2DPL == 0)
2232 && pGuestIdte->au64 != pVM->csam.s.aIDT[iGate].au64
2233 )
2234 {
2235 RTGCPTR pHandler;
2236 CSAMP2GLOOKUPREC cacheRec = {0}; /* Cache record for PATMGCVirtToHCVirt. */
2237 PCSAMPAGE pPage = NULL;
2238
2239 /* Save a copy */
2240 pVM->csam.s.aIDT[iGate].au64 = pGuestIdte->au64;
2241
2242 /*
2243 * Assume it's not safe.
2244 */
2245 TRPMR3SetGuestTrapHandler(pVM, iGate, TRPM_INVALID_HANDLER);
2246
2247 pHandler = (pGuestIdte->Gen.u16OffsetHigh << 16) | pGuestIdte->Gen.u16OffsetLow;
2248 pHandler = SELMToFlat(pVM, pGuestIdte->Gen.u16SegSel, 0, pHandler);
2249
2250 if (pGuestIdte->Gen.u5Type2 == VBOX_IDTE_TYPE2_TRAP_32)
2251 {
2252 Log(("CSAMCheckGates: check trap gate %d at %04X:%08X (flat %VGv)\n", iGate, pGuestIdte->Gen.u16SegSel, (pGuestIdte->Gen.u16OffsetHigh << 16) | pGuestIdte->Gen.u16OffsetLow, pHandler));
2253 }
2254 else
2255 {
2256 Log(("CSAMCheckGates: check interrupt gate %d at %04X:%08X (flat %VGv)\n", iGate, pGuestIdte->Gen.u16SegSel, (pGuestIdte->Gen.u16OffsetHigh << 16) | pGuestIdte->Gen.u16OffsetLow, pHandler));
2257 }
2258
2259 STAM_PROFILE_START(&pVM->csam.s.StatTime, a);
2260 rc = csamAnalyseCodeStream(pVM, pHandler, pHandler, true, CSAMR3AnalyseCallback, pPage, &cacheRec);
2261 STAM_PROFILE_STOP(&pVM->csam.s.StatTime, a);
2262 if (rc != VINF_SUCCESS)
2263 {
2264 Log(("CSAMCheckGates: csamAnalyseCodeStream failed with %d\n", rc));
2265 }
2266 else
2267 {
2268 /* OpenBSD guest specific patch test */
2269 rc = PATMR3InstallPatch(pVM, pHandler - 4, PATMFL_CODE32 | PATMFL_GUEST_SPECIFIC);
2270 if (VBOX_SUCCESS(rc))
2271 {
2272 Log(("Installed OpenBSD interrupt handler prefix instruction (push cs) patch\n"));
2273 if (pGuestIdte->Gen.u5Type2 == VBOX_IDTE_TYPE2_TRAP_32)
2274 {
2275 /* This is actually fatal!!!! */
2276 Log(("Warning: patch jump overwrites trap handler entrypoint!!!!!!!!!!!!!\n"));
2277 }
2278 }
2279 else
2280 /* Trap gates and certain interrupt gates. */
2281// if ( pGuestIdte->Gen.u5Type2 == VBOX_IDTE_TYPE2_TRAP_32
2282// || iGate == 0x2E || iGate == 0xE || iGate == 0x80 || iGate == 0xEF)
2283 {
2284 uint32_t fPatchFlags = PATMFL_CODE32 | PATMFL_IDTHANDLER;
2285
2286 if (pGuestIdte->Gen.u5Type2 == VBOX_IDTE_TYPE2_TRAP_32)
2287 fPatchFlags |= PATMFL_TRAPHANDLER;
2288 else
2289 fPatchFlags |= PATMFL_INTHANDLER;
2290
2291 switch (iGate) {
2292 case 8:
2293 case 10:
2294 case 11:
2295 case 12:
2296 case 13:
2297 case 14:
2298 case 17:
2299 fPatchFlags |= PATMFL_TRAPHANDLER_WITH_ERRORCODE;
2300 break;
2301 default:
2302 /* No error code. */
2303 break;
2304 }
2305
2306 Log(("Installing %s gate handler for 0x%X at %VGv\n", (pGuestIdte->Gen.u5Type2 == VBOX_IDTE_TYPE2_TRAP_32) ? "trap" : "intr", iGate, pHandler));
2307
2308 rc = PATMR3InstallPatch(pVM, pHandler, fPatchFlags);
2309 if (VBOX_SUCCESS(rc) || rc == VERR_PATM_ALREADY_PATCHED)
2310 {
2311 RTGCPTR pNewHandlerGC;
2312
2313 Log(("Gate handler 0x%X is SAFE!\n", iGate));
2314
2315 pNewHandlerGC = PATMR3QueryPatchGCPtr(pVM, pHandler);
2316 if (pNewHandlerGC)
2317 {
2318 rc = TRPMR3SetGuestTrapHandler(pVM, iGate, pNewHandlerGC);
2319 if (VBOX_FAILURE(rc))
2320 Log(("TRPMR3SetGuestTrapHandler %d failed with %Vrc\n", iGate, rc));
2321 }
2322 else
2323 Assert(0);
2324 }
2325 }
2326 }
2327 }
2328 else
2329 if (pGuestIdte->au64 != pVM->csam.s.aIDT[iGate].au64)
2330 {
2331 /* Save a copy */
2332 pVM->csam.s.aIDT[iGate].au64 = pGuestIdte->au64;
2333
2334#ifdef DEBUG
2335 RTGCPTR pHandler = (pGuestIdte->Gen.u16OffsetHigh << 16) | pGuestIdte->Gen.u16OffsetLow;
2336
2337 Log2(("IDT entry %x with handler %VGv refused (2)\n", iGate, pHandler));
2338#endif
2339
2340 /*
2341 * Everything is dangerous unless checked
2342 */
2343 TRPMR3SetGuestTrapHandler(pVM, iGate, TRPM_INVALID_HANDLER);
2344 }
2345#ifdef DEBUG
2346 else
2347 if (TRPMR3GetGuestTrapHandler(pVM, iGate) == TRPM_INVALID_HANDLER)
2348 {
2349 RTGCPTR pHandler = (pGuestIdte->Gen.u16OffsetHigh << 16) | pGuestIdte->Gen.u16OffsetLow;
2350
2351 Log2(("IDT entry %x with handler %VGv refused (3)\n", iGate, pHandler));
2352 }
2353#endif
2354 }
2355 STAM_PROFILE_STOP(&pVM->csam.s.StatCheckGates, a);
2356
2357 pVM->csam.s.fGatesChecked = true;
2358 return VINF_SUCCESS;
2359}
2360
2361/**
2362 * Query CSAM state (enabled/disabled)
2363 *
2364 * @returns 0 - disabled, 1 - enabled
2365 * @param pVM The VM to operate on.
2366 */
2367CSAMR3DECL(int) CSAMR3IsEnabled(PVM pVM)
2368{
2369 return pVM->fCSAMEnabled;
2370}
2371
2372#ifdef VBOX_WITH_DEBUGGER
2373/**
2374 * The '.csamoff' command.
2375 *
2376 * @returns VBox status.
2377 * @param pCmd Pointer to the command descriptor (as registered).
2378 * @param pCmdHlp Pointer to command helper functions.
2379 * @param pVM Pointer to the current VM (if any).
2380 * @param paArgs Pointer to (readonly) array of arguments.
2381 * @param cArgs Number of arguments in the array.
2382 */
2383static DECLCALLBACK(int) csamr3CmdOff(PCDBGCCMD pCmd, PDBGCCMDHLP pCmdHlp, PVM pVM, PCDBGCVAR paArgs, unsigned cArgs, PDBGCVAR pResult)
2384{
2385 /*
2386 * Validate input.
2387 */
2388 if (!pVM)
2389 return pCmdHlp->pfnPrintf(pCmdHlp, NULL, "error: The command requires VM to be selected.\n");
2390
2391 CSAMDisableScanning(pVM);
2392 return pCmdHlp->pfnPrintf(pCmdHlp, NULL, "CSAM Scanning disabled\n");
2393}
2394
2395/**
2396 * The '.csamon' command.
2397 *
2398 * @returns VBox status.
2399 * @param pCmd Pointer to the command descriptor (as registered).
2400 * @param pCmdHlp Pointer to command helper functions.
2401 * @param pVM Pointer to the current VM (if any).
2402 * @param paArgs Pointer to (readonly) array of arguments.
2403 * @param cArgs Number of arguments in the array.
2404 */
2405static DECLCALLBACK(int) csamr3CmdOn(PCDBGCCMD pCmd, PDBGCCMDHLP pCmdHlp, PVM pVM, PCDBGCVAR paArgs, unsigned cArgs, PDBGCVAR pResult)
2406{
2407 /*
2408 * Validate input.
2409 */
2410 if (!pVM)
2411 return pCmdHlp->pfnPrintf(pCmdHlp, NULL, "error: The command requires VM to be selected.\n");
2412
2413 CSAMEnableScanning(pVM);
2414 return pCmdHlp->pfnPrintf(pCmdHlp, NULL, "CSAM Scanning enabled\n");
2415}
2416#endif
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette