Bitdefender Hypervisor Memory Introspection
swapmem.c File Reference

This module is responsibility for reading guest memory that may be swapped out. More...

#include "swapmem.h"
#include "winagent.h"
#include "hook.h"
#include "introcpu.h"

Go to the source code of this file.

Data Structures

struct  _SWAPMEM_TRANSACTION
 
struct  _SWAPMEM_PAGE
 
struct  _SWAPMEM_STATE
 

Typedefs

typedef struct _SWAPMEM_TRANSACTION SWAPMEM_TRANSACTION
 
typedef struct _SWAPMEM_TRANSACTIONPSWAPMEM_TRANSACTION
 
typedef struct _SWAPMEM_PAGE SWAPMEM_PAGE
 
typedef struct _SWAPMEM_PAGEPSWAPMEM_PAGE
 
typedef struct _SWAPMEM_STATE SWAPMEM_STATE
 
typedef struct _SWAPMEM_STATEPSWAPMEM_STATE
 

Functions

static INTSTATUS IntSwapMemCleanupCallback (void *DataAddress, QWORD DataInfo)
 Cleans up a transaction, by freeing the data buffer, the context and the transaction itself. More...
 
static INTSTATUS IntSwapMemCancelTransaction (PSWAPMEM_TRANSACTION Transaction)
 Cancels a transaction. More...
 
static INTSTATUS IntSwapMemHandleBreakpointAgent (QWORD GuestVirtualAddress, DWORD AgentTag, void *Context)
 Handles a breakpoint agent. More...
 
static INTSTATUS IntSwapMemInjectMiniSwapper (QWORD VirtualAddress)
 Injects the mini swapper, which is basically just a breakpoint agent. More...
 
static INTSTATUS IntSwapMemPageSwappedIn (void *Context, QWORD VirtualAddress, QWORD OldEntry, QWORD NewEntry, QWORD OldPageSize, QWORD NewPageSize)
 Handle a page swap-in event. More...
 
INTSTATUS IntSwapMemReadData (QWORD Cr3, QWORD VirtualAddress, DWORD Length, DWORD Options, void *Context, DWORD ContextTag, PFUNC_PagesReadCallback Callback, PFUNC_PreInjectCallback PreInject, void **SwapHandle)
 Reads a region of guest virtual memory, and calls the indicated callback when all the data is available. More...
 
static PSWAPMEM_PAGE IntSwapMemFindPendingPage (QWORD VirtualAddress, QWORD Cr3)
 Finds a pending page, given the guest virtual address and the Cr3. More...
 
INTSTATUS IntSwapMemInjectPendingPF (void)
 Inject a PF for a pending page. More...
 
void IntSwapMemCancelPendingPF (QWORD VirtualAddress)
 Cancel a pending PF. More...
 
void IntSwapMemReinjectFailedPF (void)
 Reinject timed-out PFs. More...
 
INTSTATUS IntSwapMemRemoveTransaction (void *Transaction)
 Remove a transaction. More...
 
INTSTATUS IntSwapMemRemoveTransactionsForVaSpace (QWORD Cr3)
 Remove all transactions initiated for a virtual address space. More...
 
INTSTATUS IntSwapMemInit (void)
 Init the swapmem system. More...
 
INTSTATUS IntSwapMemUnInit (void)
 Uninit the swapmem system. More...
 
void IntSwapMemDump (void)
 Dump all active transactions & pages. More...
 

Variables

static SWAPMEM_STATE gSwapState = { 0 }
 

Detailed Description

This module is responsibility for reading guest memory that may be swapped out.

This module handles reading guest virtual memory. Usually, a required range of guest memory will not be present entirely inside the guest physical memory. The missing pages may be swapped in at some time in the future, but there's no guarantee. In order to ensure that we will gain access to the swapped out data, this module will schedule page-fault injections for missing pages. The doesn't have to deal with low-level aspects of paging, all it has to do is call IntSwapMemReadData, and wait for the data read callback to be called. Internally, this module will read all the data already available, and, for the missing pages, it will inject PFs inside the guest. NOTE: We can inject only a single PF at any given moment, even if more than 1 VCPU are present. This greatly simplifies the scheduler and any PF tracking logic, by also avoiding too many PFs to be injected at the same time (for example, on a guest with multiple VCPUs, we may cause a significant performance overhead if we were to inject a PF on each VCPU). NOTE: When reading kernel memory, the PF will be injected in the context of a SYSCALL, where interrupt are enabled, no locks are being held and we are in paged context. NOTE: The caller may wish to avoid injecting a PF. If it desires to, it can use the SWAPMEM_OPT_NO_FAULT option, which will tell this module to not inject any PF in order to read the indicated region of memory. Instead, it will simply wait for the required pages to be naturally swapped in as a result of guest access. NOTE: When we inject a PF, there is no guarantee that the PF will indeed reach the guest; sometimes, the HV may decide to inject something else (for example, an IPI, or another exception), and, as a result, the PF that we tried to inject will be lost. However, in order to address this, there is the event injection callback (IntHandleEventInjection in callbacks.c) which gets called immediately after injecting an event inside the guest, if we had a pending PF (or another pending exception). This callback will receive as arguments the injected vector, the CR2 and the error code; using these arguments, we can check if the PF which we wanted got injected, and if it wasn't, we can simply retry injecting it later. This allows the scheduler to know exactly if a swapmem PF was injected or not.

Definition in file swapmem.c.

Typedef Documentation

◆ PSWAPMEM_PAGE

typedef struct _SWAPMEM_PAGE * PSWAPMEM_PAGE

◆ PSWAPMEM_STATE

typedef struct _SWAPMEM_STATE * PSWAPMEM_STATE

◆ PSWAPMEM_TRANSACTION

◆ SWAPMEM_PAGE

typedef struct _SWAPMEM_PAGE SWAPMEM_PAGE

Describes one page that will be read. A transaction contains a page entry for each page of virtual memory it needs to read from guest space.

◆ SWAPMEM_STATE

typedef struct _SWAPMEM_STATE SWAPMEM_STATE

Global swapmem state.

◆ SWAPMEM_TRANSACTION

This context is used only internally. It represents a swapmem transaction. A transaction is a request to read a contiguous portion of guest virtual memory, parts of which may not be present in physical memory.

Function Documentation

◆ IntSwapMemCancelPendingPF()

void IntSwapMemCancelPendingPF ( QWORD  VirtualAddress)

Cancel a pending PF.

Cancel the pending PF on the provided VirtualAddress. This can happen if we requested a PF injection, but the HV had to inject something else. In this case, we cancel the injection, and we mark the page Ready, allowing it to be re-injected later.

Parameters
[in]VirtualAddressThe virtual address the PF was requested for.

Definition at line 879 of file swapmem.c.

Referenced by IntHandleEventInjection(), and IntSwapMemHandleBreakpointAgent().

◆ IntSwapMemCancelTransaction()

static INTSTATUS IntSwapMemCancelTransaction ( PSWAPMEM_TRANSACTION  Transaction)
static

Cancels a transaction.

Parameters
[in]TransactionThe transaction to be canceled.
Return values
INT_STATUS_SUCCESSOn success.

Definition at line 141 of file swapmem.c.

Referenced by IntSwapMemReadData(), IntSwapMemRemoveTransaction(), and IntSwapMemRemoveTransactionsForVaSpace().

◆ IntSwapMemCleanupCallback()

static INTSTATUS IntSwapMemCleanupCallback ( void *  DataAddress,
QWORD  DataInfo 
)
static

Cleans up a transaction, by freeing the data buffer, the context and the transaction itself.

Parameters
[in]DataAddressTransaction address.
[in]DataInfoUnused.
Return values
INT_STATUS_SUCCESSOn success.

Definition at line 105 of file swapmem.c.

Referenced by IntSwapMemCancelTransaction(), and IntSwapMemPageSwappedIn().

◆ IntSwapMemDump()

void IntSwapMemDump ( void  )

Dump all active transactions & pages.

Definition at line 1066 of file swapmem.c.

◆ IntSwapMemFindPendingPage()

static PSWAPMEM_PAGE IntSwapMemFindPendingPage ( QWORD  VirtualAddress,
QWORD  Cr3 
)
static

Finds a pending page, given the guest virtual address and the Cr3.

Parameters
[in]VirtualAddressThe virtual address.
[in]Cr3The Cr3.
Returns
The pending page, or NULL if none is found.

Definition at line 664 of file swapmem.c.

◆ IntSwapMemHandleBreakpointAgent()

static INTSTATUS IntSwapMemHandleBreakpointAgent ( QWORD  GuestVirtualAddress,
DWORD  AgentTag,
void *  Context 
)
static

Handles a breakpoint agent.

This callback is called as soon as a breakpoint agent gets triggered inside the guest. Inside this function, we will simply inject a PF, which is usually for a kernel address. The PF is injected on the SYSCALL flow, where interrupts are enabled, and no locks are held, making it safe.

Parameters
[in]GuestVirtualAddressUnused.
[in]AgentTagUnused.
[in]ContextThe kernel guest virtual address we inject the PF for.
Return values
INT_STATUS_SUCCESSOn success.

Definition at line 195 of file swapmem.c.

Referenced by IntSwapMemInjectMiniSwapper().

◆ IntSwapMemInit()

INTSTATUS IntSwapMemInit ( void  )

Init the swapmem system.

Return values
INT_STATUS_SUCCESSOn success.

Definition at line 1026 of file swapmem.c.

Referenced by IntGuestInit().

◆ IntSwapMemInjectMiniSwapper()

static INTSTATUS IntSwapMemInjectMiniSwapper ( QWORD  VirtualAddress)
static

Injects the mini swapper, which is basically just a breakpoint agent.

Parameters
[in]VirtualAddressThe kernel virtual address to be swapped in.
Return values
INT_STATUS_SUCCESSOn success.
INT_STATUS_OPERATION_NOT_IMPLEMENTEDIf the OS is not Windows.

Definition at line 235 of file swapmem.c.

Referenced by IntSwapMemInjectPendingPF().

◆ IntSwapMemInjectPendingPF()

INTSTATUS IntSwapMemInjectPendingPF ( void  )

Inject a PF for a pending page.

This is the main PF scheduling algorithm. This is called before returning from every callback, and it checks the list of existing transactions & pages, in order to inject a PF inside the guest. Note that only a single PF can be injected at any given time (even if we have multiple VCPUs), as this makes the scheduler much simpler, and it avoids spamming the guest kernel with unexpected PFs. Before injecting a PF, it makes sure the context is right (user/kernel, CR3), and it calls the PreInjectCallback; of the PreInject callback does not return INT_STATUS_SUCCESS, the PF for that page will not be injected, and another page will be selected. PFs can be either injected directly (user-mode PFs, when the context is right) or indirectly (kernel-mode PFs) via the breakpoint agent. No other PFs will be injected until the pending one has been handled (the page has been swapped in).

Return values
INT_STATUS_SUCCESSOn success.

Definition at line 698 of file swapmem.c.

Referenced by IntGuestPreReturnCallback().

◆ IntSwapMemPageSwappedIn()

static INTSTATUS IntSwapMemPageSwappedIn ( void *  Context,
QWORD  VirtualAddress,
QWORD  OldEntry,
QWORD  NewEntry,
QWORD  OldPageSize,
QWORD  NewPageSize 
)
static

Handle a page swap-in event.

This function is called when the translation of a page we wish to read is modified. We will check if the page has been swapped in, and if it has, we will read its contents. Once the entire memory region is read (which may include more than one page), the swap-in callback will be called, sending along a pointer to the read data. After a page has been swapped in and read, the page-table hooks set for that page translation is removed.

Parameters
[in]ContextOptional context, points to the swapped in page structure, SWAPMEM_PAGE.
[in]VirtualAddressThe guest virtual address whose translation has just been modified.
[in]OldEntryOld page table entry.
[in]NewEntryNew page table entry.
[in]OldPageSizeOld page size.
[in]NewPageSizeNew page size.
Return values
INT_STATUS_SUCCESSOn success.
INT_STATUS_INVALID_PARAMETERIf an invalid parameter is supplied.

Definition at line 259 of file swapmem.c.

Referenced by IntSwapMemReadData().

◆ IntSwapMemReadData()

INTSTATUS IntSwapMemReadData ( QWORD  Cr3,
QWORD  VirtualAddress,
DWORD  Length,
DWORD  Options,
void *  Context,
DWORD  ContextTag,
PFUNC_PagesReadCallback  Callback,
PFUNC_PreInjectCallback  PreInject,
void **  SwapHandle 
)

Reads a region of guest virtual memory, and calls the indicated callback when all the data is available.

The function will read Length bytes from VirtualAddress inside Cr3 address space. The function may either read the data directly, if it is present inside the physical memory, or it may inject a page fault in order to force a swap-in of the pages containing the data. Callback will be invoked when all the data has been read. The callback may be invoked synchronously or asynchronously: if a page fault is needed to read parts of the data, it will invoked asynchronously. Otherwise, it will be invoked synchronously. The flag SWAPMEM_FLAG_ASYNC_CALL will be set in the Flags argument of the callback for asynchronously calls. The function can be used to read data of arbitrary length (including data spanning multiple pages). Some pages may be swapped in as a result of us injecting a PF for them, others may be swapped in naturally due to the normal guest activity, and other pages may already be present when calling this function. The PreInject callback will be called before actually injecting a PF inside the guest. This callback is optional, but if it present and it returns INT_STATUS_NOT_NEEDED_HINT, the PF will be inhibited, and therefore, it will not be injected.

Parameters
[in]Cr3Virtual address space where we wish to read data from.
[in]VirtualAddressGuest virtual address we wish to read from.
[in]LengthLength, in bytes. Can span multiple pages.
[in]OptionsOptions. Check out SWAPMEM_OPT* for more info.
[in]ContextOptional context to be passed to the callbacks.
[in]ContextTagIf Context is provided and ContextTag is not 0, the Context will be freed when removing the transaction.
[in]CallbackCalled once all the data is available.
[in]PreInjectCallback invoked BEFORE injecting the PF. If this returns INT_STATUS_NOT_NEEDED_HINT, the PF will not be injected.
[out]SwapHandleContains, upon return, a handle to the swap object. If SWAPMEM_OPT_NO_DUPS is used and a transaction is already present, SwapHandle will be set to NULL. Since the transactions are not reference counted, it would be problematic to return a handle, as it can then get freed more than once.
Return values
INT_STATUS_SUCCESSOn success.
INT_STATUS_INSUFFICIENT_RESOURCESIf a memory alloc fails.

Definition at line 417 of file swapmem.c.

Referenced by DbgSwap(), IntWinDagentCheckSuspiciousDllLoad(), IntWinDagentHandleVerifierReason(), IntWinDrvProtect(), IntWinGetPrcoCmdLineHandleCmdLineInMemory(), IntWinGetPrcoCmdLineHandleUserParamsInMemory(), IntWinGuestFindDriversNamespace(), IntWinGuestNew(), IntWinGuestReadKernel(), IntWinHalFindPerformanceCounter(), IntWinHalReadHal(), IntWinModBlockBlockModuleLoad(), IntWinModHandleModulePathInMemory(), IntWinObjHandleDirectoryEntryInMemory(), IntWinObjHandleDriverDirectoryEntryInMemory(), IntWinObjHandleObjectInMemory(), IntWinObjHandleRootDirTagInMemory(), IntWinObjParseDriverDirectory(), IntWinProcReadCommandLine(), IntWinStackHandleUserStackPagedOut(), IntWinUmModCacheFillExports(), IntWinVadFetchImageName(), and IntWinVadIsExecSuspicious().

◆ IntSwapMemReinjectFailedPF()

void IntSwapMemReinjectFailedPF ( void  )

Reinject timed-out PFs.

Sometimes, injected PFs may get lost, mainly due to the HV, or may be dropped unexpectedly inside the guest. We detect this by maintaining a time-stamp for each pending PF, and retrying the injection after aprox. 1s. Note that reinjecting a PF more than once is not an issue, as the OS can handle this kind of spurious PFs.

Definition at line 913 of file swapmem.c.

Referenced by IntHandleTimer().

◆ IntSwapMemRemoveTransaction()

INTSTATUS IntSwapMemRemoveTransaction ( void *  Transaction)

Remove a transaction.

Once a transaction is removed, the callback will no longer be called, and the swap hooks will be removed. The data will no longer be available. This should be called, for example, when there is a pending read for a process that is just terminating.

Parameters
[in]TransactionThe transaction to be removed.
Return values
INT_STATUS_SUCCESSOn success.
INT_STATUS_INVALID_PARAMETERIf an invalid parameter is supplied.

Definition at line 942 of file swapmem.c.

Referenced by IntWinDrvUnprotect(), IntWinGuestCancelKernelRead(), IntWinHalCancelRead(), IntWinModBlockRemoveBlockObject(), IntWinModCancelExportTransactions(), IntWinModRemoveModule(), IntWinObjCancelRootTransactions(), IntWinObjCleanup(), IntWinProcRemoveProcess(), IntWinProcSwapOut(), and IntWinVadDestroyObject().

◆ IntSwapMemRemoveTransactionsForVaSpace()

INTSTATUS IntSwapMemRemoveTransactionsForVaSpace ( QWORD  Cr3)

Remove all transactions initiated for a virtual address space.

Will remove all active transactions for the given VA space. The read-data callback will not be called for any of the aborted transactions. Useful when a process is terminating.

Parameters
[in]Cr3The virtual address space.
Return values
INT_STATUS_SUCCESSOn success.

Definition at line 982 of file swapmem.c.

Referenced by IntWinProcRemoveProcess(), and IntWinProcSwapOut().

◆ IntSwapMemUnInit()

INTSTATUS IntSwapMemUnInit ( void  )

Uninit the swapmem system.

Return values
INT_STATUS_SUCCESSOn success.
INT_STATUS_NOT_INITIALIZED_HINTIf the system has not been initialized.

Definition at line 1044 of file swapmem.c.

Referenced by IntGuestUninit().

Variable Documentation

◆ gSwapState

SWAPMEM_STATE gSwapState = { 0 }
static

Definition at line 101 of file swapmem.c.