[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] [RFC PATCH 5/6] Add gnttab IOCTLs that allow sharing already existing memory
IOCTL_XENIFACE_GNTTAB_PERMIT_FOREIGN_ACCESS shares pages that are newly allocated by the driver. This commit adds new IOCTLs: IOCTL_XENIFACE_GNTTAB_PERMIT_FOREIGN_ACCESS_V2, IOCTL_XENIFACE_GNTTAB_REVOKE_FOREIGN_ACCESS_V2, IOCTL_XENIFACE_GNTTAB_MAP_FOREIGN_PAGES_V2 and IOCTL_XENIFACE_GNTTAB_UNMAP_FOREIGN_PAGES_V2. IOCTL_XENIFACE_GNTTAB_PERMIT_FOREIGN_ACCESS_V2 allows specifying user-mode address of a memory region that will be shared (the region is locked for the duration). All _V2 IOCTLs use user-mode address of the shared region as a unique (per-process) request identifier. This differs from the existing IOCTLs that use client-supplied RequestId. _V2 IOCTLs should be considered as preferable since they don't require the client to manage IDs. Signed-off-by: Rafał Wojdyła <omeg@xxxxxxxxxxxxxxxxxxxxxx> --- include/xeniface_ioctls.h | 94 ++++- src/xeniface/ioctl_gnttab.c | 711 ++++++++++++++++++++++-------------- src/xeniface/ioctls.c | 25 +- src/xeniface/ioctls.h | 75 ++-- src/xeniface/irp_queue.c | 27 +- src/xeniface/irp_queue.h | 2 +- 6 files changed, 596 insertions(+), 338 deletions(-) diff --git a/include/xeniface_ioctls.h b/include/xeniface_ioctls.h index 1a8bcb5..67deedf 100644 --- a/include/xeniface_ioctls.h +++ b/include/xeniface_ioctls.h @@ -245,8 +245,10 @@ typedef enum _XENIFACE_GNTTAB_PAGE_FLAGS { } XENIFACE_GNTTAB_PAGE_FLAGS; /*! \brief Grant permission to access local memory pages to a foreign domain + \deprecated Use the _V2 IOCTLs that don't need RequestId input instead \note This IOCTL must be asynchronous. The driver doesn't complete the request until the grant is explicitly revoked or the calling thread terminates. + Granted memory is allocated by the driver. Input: XENIFACE_GNTTAB_PERMIT_FOREIGN_ACCESS_IN @@ -271,7 +273,35 @@ typedef struct _XENIFACE_GNTTAB_PERMIT_FOREIGN_ACCESS_OUT { ULONG References[ANYSIZE_ARRAY]; /*!< An array of Xen-assigned references for each granted page */ } XENIFACE_GNTTAB_PERMIT_FOREIGN_ACCESS_OUT, *PXENIFACE_GNTTAB_PERMIT_FOREIGN_ACCESS_OUT; -/*! \brief Revoke a foreign domain access to previously granted memory region +/*! \brief Grant permission to access local memory pages to a foreign domain + \note This IOCTL must be asynchronous. The driver doesn't complete the request + until the grant is explicitly revoked or the calling thread terminates. + Granted memory may be already mapped or allocated by the driver. + + Input: XENIFACE_GNTTAB_PERMIT_FOREIGN_ACCESS_IN_V2 + + Output: XENIFACE_GNTTAB_PERMIT_FOREIGN_ACCESS_OUT_V2 +*/ +#define IOCTL_XENIFACE_GNTTAB_PERMIT_FOREIGN_ACCESS_V2 \ + CTL_CODE(FILE_DEVICE_UNKNOWN, 0x824, METHOD_NEITHER, FILE_ANY_ACCESS) + +/*! \brief Input for IOCTL_XENIFACE_GNTTAB_PERMIT_FOREIGN_ACCESS_V2 */ +typedef struct _XENIFACE_GNTTAB_PERMIT_FOREIGN_ACCESS_IN_V2 { + USHORT RemoteDomain; /*!< Remote domain that is being granted access */ + PVOID Address; /*!< Address of the granted memory region, allocated by the driver if NULL */ + ULONG NumberPages; /*!< Number of 4k pages to grant access to */ + XENIFACE_GNTTAB_PAGE_FLAGS Flags; /*!< Additional flags */ + ULONG NotifyOffset; /*!< Offset of a byte in the granted region that will be set to 0 when the grant is revoked */ + ULONG NotifyPort; /*!< Local port number of an open event channel that will be notified when the grant is revoked */ +} XENIFACE_GNTTAB_PERMIT_FOREIGN_ACCESS_IN_V2, *PXENIFACE_GNTTAB_PERMIT_FOREIGN_ACCESS_IN_V2; + +/*! \brief Output for IOCTL_XENIFACE_GNTTAB_PERMIT_FOREIGN_ACCESS_V2 */ +typedef struct _XENIFACE_GNTTAB_PERMIT_FOREIGN_ACCESS_OUT \ + XENIFACE_GNTTAB_PERMIT_FOREIGN_ACCESS_OUT_V2, *PXENIFACE_GNTTAB_PERMIT_FOREIGN_ACCESS_OUT_V2; + +/*! \brief Revoke a foreign domain access to a memory region + previously granted by IOCTL_XENIFACE_GNTTAB_PERMIT_FOREIGN_ACCESS + \deprecated Use the _V2 IOCTLs that don't need RequestId input instead Input: XENIFACE_GNTTAB_REVOKE_FOREIGN_ACCESS_IN @@ -285,7 +315,23 @@ typedef struct _XENIFACE_GNTTAB_REVOKE_FOREIGN_ACCESS_IN { ULONG RequestId; /*! Request ID used in the corresponding IOCTL_XENIFACE_GNTTAB_PERMIT_FOREIGN_ACCESS call */ } XENIFACE_GNTTAB_REVOKE_FOREIGN_ACCESS_IN, *PXENIFACE_GNTTAB_REVOKE_FOREIGN_ACCESS_IN; +/*! \brief Revoke a foreign domain access to a memory region + previously granted by IOCTL_XENIFACE_GNTTAB_PERMIT_FOREIGN_ACCESS_V2 + + Input: XENIFACE_GNTTAB_REVOKE_FOREIGN_ACCESS_IN_V2 + + Output: None +*/ +#define IOCTL_XENIFACE_GNTTAB_REVOKE_FOREIGN_ACCESS_V2 \ + CTL_CODE(FILE_DEVICE_UNKNOWN, 0x825, METHOD_BUFFERED, FILE_ANY_ACCESS) + +/*! \brief Input for IOCTL_XENIFACE_GNTTAB_REVOKE_FOREIGN_ACCESS_V2 */ +typedef struct _XENIFACE_GNTTAB_REVOKE_FOREIGN_ACCESS_IN_V2 { + PVOID Address; /*!< User-mode address of the shared memory region */ +} XENIFACE_GNTTAB_REVOKE_FOREIGN_ACCESS_IN_V2, * PXENIFACE_GNTTAB_REVOKE_FOREIGN_ACCESS_IN_V2; + /*! \brief Map a foreign memory region into the current address space + \deprecated Use the _V2 IOCTLs that don't need RequestId input instead \note This IOCTL must be asynchronous. The driver doesn't complete the request until the memory is explicitly unmapped or the calling thread terminates. @@ -309,10 +355,37 @@ typedef struct _XENIFACE_GNTTAB_MAP_FOREIGN_PAGES_IN { /*! \brief Output for IOCTL_XENIFACE_GNTTAB_MAP_FOREIGN_PAGES */ typedef struct _XENIFACE_GNTTAB_MAP_FOREIGN_PAGES_OUT { - PVOID Address; /*!< User-mode address of the mapped memory region */ + PVOID Address; /*!< User-mode address of the shared memory region */ } XENIFACE_GNTTAB_MAP_FOREIGN_PAGES_OUT, *PXENIFACE_GNTTAB_MAP_FOREIGN_PAGES_OUT; -/*! \brief Unmap a foreign memory region from the current address space +/*! \brief Map a foreign memory region into the current address space + \note This IOCTL must be asynchronous. The driver doesn't complete the request + until the memory is explicitly unmapped or the calling thread terminates. + + Input: XENIFACE_GNTTAB_MAP_FOREIGN_PAGES_IN_V2 + + Output: XENIFACE_GNTTAB_MAP_FOREIGN_PAGES_OUT_V2 +*/ +#define IOCTL_XENIFACE_GNTTAB_MAP_FOREIGN_PAGES_V2 \ + CTL_CODE(FILE_DEVICE_UNKNOWN, 0x826, METHOD_NEITHER, FILE_ANY_ACCESS) + +/*! \brief Input for IOCTL_XENIFACE_GNTTAB_MAP_FOREIGN_PAGES_V2 */ +typedef struct _XENIFACE_GNTTAB_MAP_FOREIGN_PAGES_IN_V2 { + USHORT RemoteDomain; /*!< Remote domain that has granted access to the pages */ + ULONG NumberPages; /*!< Number of 4k pages to map */ + XENIFACE_GNTTAB_PAGE_FLAGS Flags; /*!< Additional flags */ + ULONG NotifyOffset; /*!< Offset of a byte in the mapped region that will be set to 0 when the region is unmapped */ + ULONG NotifyPort; /*!< Local port number of an open event channel that will be notified when the region is unmapped */ + ULONG References[ANYSIZE_ARRAY]; /*!< An array of Xen-assigned references for each granted page */ +} XENIFACE_GNTTAB_MAP_FOREIGN_PAGES_IN_V2, *PXENIFACE_GNTTAB_MAP_FOREIGN_PAGES_IN_V2; + +/*! \brief Output for IOCTL_XENIFACE_GNTTAB_MAP_FOREIGN_PAGES_V2 */ +typedef struct _XENIFACE_GNTTAB_MAP_FOREIGN_PAGES_OUT \ + XENIFACE_GNTTAB_MAP_FOREIGN_PAGES_OUT_V2, *PXENIFACE_GNTTAB_MAP_FOREIGN_PAGES_OUT_V2; + +/*! \brief Unmap a foreign memory region mapped by IOCTL_XENIFACE_GNTTAB_MAP_FOREIGN_PAGES + from the current address space + \deprecated Use the _V2 IOCTLs that don't need RequestId input instead Input: XENIFACE_GNTTAB_UNMAP_FOREIGN_PAGES_IN @@ -326,6 +399,21 @@ typedef struct _XENIFACE_GNTTAB_UNMAP_FOREIGN_PAGES_IN { ULONG RequestId; /*! Request ID used in the corresponding IOCTL_XENIFACE_GNTTAB_MAP_FOREIGN_PAGES call */ } XENIFACE_GNTTAB_UNMAP_FOREIGN_PAGES_IN, *PXENIFACE_GNTTAB_UNMAP_FOREIGN_PAGES_IN; +/*! \brief Unmap a foreign memory region mapped by IOCTL_XENIFACE_GNTTAB_MAP_FOREIGN_PAGES_V2 + from the current address space + + Input: XENIFACE_GNTTAB_UNMAP_FOREIGN_PAGES_IN_V2 + + Output: None +*/ +#define IOCTL_XENIFACE_GNTTAB_UNMAP_FOREIGN_PAGES_V2 \ + CTL_CODE(FILE_DEVICE_UNKNOWN, 0x827, METHOD_BUFFERED, FILE_ANY_ACCESS) + +/*! \brief Input for IOCTL_XENIFACE_GNTTAB_UNMAP_FOREIGN_PAGES_V2 */ +typedef struct _XENIFACE_GNTTAB_UNMAP_FOREIGN_PAGES_IN_V2 { + PVOID Address; /*!< User-mode address of the shared memory region */ +} XENIFACE_GNTTAB_UNMAP_FOREIGN_PAGES_IN_V2, *PXENIFACE_GNTTAB_UNMAP_FOREIGN_PAGES_IN_V2; + /*! \brief Gets the current suspend count. Input: None diff --git a/src/xeniface/ioctl_gnttab.c b/src/xeniface/ioctl_gnttab.c index b6b7327..c496f1b 100644 --- a/src/xeniface/ioctl_gnttab.c +++ b/src/xeniface/ioctl_gnttab.c @@ -47,35 +47,35 @@ CompleteGnttabIrp( PXENIFACE_DX Dx = (PXENIFACE_DX)DeviceObject->DeviceExtension; PXENIFACE_FDO Fdo = Dx->Fdo; PIRP Irp = Context; - PXENIFACE_CONTEXT_ID Id; + PXENIFACE_GNTTAB_CONTEXT GnttabContext; PIO_WORKITEM WorkItem; KAPC_STATE ApcState; BOOLEAN ChangeProcess; ASSERT(Context != NULL); - Id = Irp->Tail.Overlay.DriverContext[0]; + GnttabContext = Irp->Tail.Overlay.DriverContext[0]; WorkItem = Irp->Tail.Overlay.DriverContext[1]; // We are not guaranteed to be in the context of the process that initiated the IRP, // but we need to be there to unmap memory. - ChangeProcess = PsGetCurrentProcess() != Id->Process; + ChangeProcess = PsGetCurrentProcess() != GnttabContext->Process; if (ChangeProcess) { - Trace("Changing process from %p to %p\n", PsGetCurrentProcess(), Id->Process); - KeStackAttachProcess(Id->Process, &ApcState); + Trace("Changing process from %p to %p\n", PsGetCurrentProcess(), GnttabContext->Process); + KeStackAttachProcess(GnttabContext->Process, &ApcState); } Trace("Irp %p, Process %p, Id %lu, Type %d, IRQL %d\n", - Irp, Id->Process, Id->RequestId, Id->Type, KeGetCurrentIrql()); + Irp, GnttabContext->Process, GnttabContext->RequestId, GnttabContext->Type, KeGetCurrentIrql()); - switch (Id->Type) { + switch (GnttabContext->Type) { - case XENIFACE_CONTEXT_GRANT: - GnttabFreeGrant(Fdo, CONTAINING_RECORD(Id, XENIFACE_GRANT_CONTEXT, Id)); + case XENIFACE_GNTTAB_CONTEXT_GRANT: + GnttabFreeGrant(Fdo, GnttabContext); break; - case XENIFACE_CONTEXT_MAP: - GnttabFreeMap(Fdo, CONTAINING_RECORD(Id, XENIFACE_MAP_CONTEXT, Id)); + case XENIFACE_GNTTAB_CONTEXT_MAP: + GnttabFreeMap(Fdo, GnttabContext); break; default: @@ -125,18 +125,194 @@ static PIRP FindGnttabIrp( __in PXENIFACE_FDO Fdo, - __in PXENIFACE_CONTEXT_ID Id + __in PXENIFACE_GNTTAB_CONTEXT Context ) { KIRQL Irql; PIRP Irp; CsqAcquireLock(&Fdo->IrpQueue, &Irql); - Irp = CsqPeekNextIrp(&Fdo->IrpQueue, NULL, Id); + Irp = CsqPeekNextIrp(&Fdo->IrpQueue, NULL, Context); CsqReleaseLock(&Fdo->IrpQueue, Irql); return Irp; } +// Undo (possibly partially done) sharing, free/clear associated context fields. +// Does not trigger notifications if the flags are set. +static +void +GnttabStopSharing( + __in PXENIFACE_FDO Fdo, + __inout PXENIFACE_GNTTAB_CONTEXT Context, + __in ULONG NumberPages +) +{ + if (Context->Grants != NULL) { + for (ULONG Page = 0; Page < NumberPages; Page++) { + ASSERT(NT_SUCCESS(XENBUS_GNTTAB(RevokeForeignAccess, + &Fdo->GnttabInterface, + Fdo->GnttabCache, + FALSE, + Context->Grants[Page]))); + } + + RtlZeroMemory(Context->Grants, Context->NumberPages * sizeof(Context->Grants[0])); + __FreePoolWithTag(Context->Grants, XENIFACE_POOL_TAG); + Context->Grants = NULL; + } + + if (Context->Mdl != NULL) { + if (Context->KernelVa != NULL) { + // driver-allocated memory + MmUnmapLockedPages(Context->UserVa, Context->Mdl); + } else { + // user-supplied memory + try { + MmUnlockPages(Context->Mdl); + } except(EXCEPTION_EXECUTE_HANDLER) { + Error("Failed to unlock user pages: 0x%x\n", GetExceptionCode()); + // this shouldn't happen and will BSOD the system when the user process exits with locked pages + } + } + + IoFreeMdl(Context->Mdl); + Context->Mdl = NULL; + } + + if (Context->KernelVa != NULL) { + __FreePoolWithTag(Context->KernelVa, XENIFACE_POOL_TAG); + Context->KernelVa = NULL; + } +} + +static +NTSTATUS +GnttabPermitForeignAccess( + __in PXENIFACE_FDO Fdo, + __inout PXENIFACE_GNTTAB_CONTEXT Context + ) +{ + NTSTATUS Status; + ULONG Page = 0; + size_t GrantsSize = 0; + ULONG SharedSize = 0; + + Trace("> RemoteDomain %d, UserVa %p, NumberPages %lu, Flags 0x%x, Offset 0x%x, Port %d, Process %p, Id %lu\n", + Context->RemoteDomain, Context->UserVa, Context->NumberPages, Context->Flags, + Context->NotifyOffset, Context->NotifyPort, Context->Process, Context->RequestId); + + // Check if the request ID/address is unique for this process. + // This doesn't protect us from simultaneous requests with the same ID arriving here + // but another check for duplicate ID is performed when the context/IRP is queued at the end. + // Ideally we would lock the whole section but that's not really an option since we touch user memory. + Status = STATUS_INVALID_PARAMETER; + if (FindGnttabIrp(Fdo, Context) != NULL) + goto fail1; + + GrantsSize = Context->NumberPages * sizeof(PXENBUS_GNTTAB_ENTRY); + SharedSize = Context->NumberPages * PAGE_SIZE; + Status = STATUS_NO_MEMORY; + Context->Grants = __AllocatePoolWithTag(NonPagedPool, GrantsSize, XENIFACE_POOL_TAG); + if (Context->Grants == NULL) + goto fail2; + + if (Context->UserVa == NULL) { + // sharing driver-allocated pages + Status = STATUS_NO_MEMORY; + Context->KernelVa = __AllocatePoolWithTag(NonPagedPool, SharedSize, XENIFACE_POOL_TAG); + if (Context->KernelVa == NULL) + goto fail3; + + Context->Mdl = IoAllocateMdl(Context->KernelVa, SharedSize, FALSE, FALSE, NULL); + if (Context->Mdl == NULL) + goto fail4; + + MmBuildMdlForNonPagedPool(Context->Mdl); + ASSERT(MmGetMdlByteCount(Context->Mdl) == SharedSize); + } else { + // sharing existing memory + Context->KernelVa = NULL; + Context->Mdl = IoAllocateMdl(Context->UserVa, SharedSize, FALSE, FALSE, NULL); + if (Context->Mdl == NULL) + goto fail4; + + try { + MmProbeAndLockPages(Context->Mdl, + UserMode, + (Context->Flags & XENIFACE_GNTTAB_READONLY) != 0 ? IoReadAccess : IoWriteAccess); + } except(EXCEPTION_EXECUTE_HANDLER) { + Status = GetExceptionCode(); + Error("Failed to lock user pages: 0x%x\n", Status); + Page = 0; + goto fail5; + } + } + + // perform sharing + for (Page = 0; Page < Context->NumberPages; Page++) { + Status = XENBUS_GNTTAB(PermitForeignAccess, + &Fdo->GnttabInterface, + Fdo->GnttabCache, + FALSE, + Context->RemoteDomain, + MmGetMdlPfnArray(Context->Mdl)[Page], + (Context->Flags & XENIFACE_GNTTAB_READONLY) != 0, + &(Context->Grants[Page])); +#if DBG + Info("Grants[%lu] = %p\n", Page, Context->Grants[Page]); +#endif + if (!NT_SUCCESS(Status)) + goto fail5; + } + + if (Context->KernelVa != NULL) { + // map driver-allocated memory into user mode +#pragma prefast(suppress:6320) // we want to catch all exceptions + try { + Context->UserVa = MmMapLockedPagesSpecifyCache(Context->Mdl, + UserMode, + MmCached, + NULL, + FALSE, + NormalPagePriority); + } except (EXCEPTION_EXECUTE_HANDLER) { + Status = GetExceptionCode(); + goto fail6; + } + } + + Trace("< Context %p, KernelVa %p, UserVa %p\n", + Context, Context->KernelVa, Context->UserVa); + + return STATUS_SUCCESS; + +fail6: + Error("Fail6\n"); + +fail5: + Error("Fail5\n"); + +fail4: + Error("Fail4\n"); + +fail3: + Error("Fail3\n"); + +fail2: + Error("Fail2\n"); + +fail1: + Error("Fail1\n"); + GnttabStopSharing(Fdo, Context, Page); + + if (Context != NULL) { + RtlZeroMemory(Context, sizeof(*Context)); + __FreePoolWithTag(Context, XENIFACE_POOL_TAG); + } + + return Status; +} + DECLSPEC_NOINLINE NTSTATUS IoctlGnttabPermitForeignAccess( @@ -147,123 +323,91 @@ IoctlGnttabPermitForeignAccess( __inout PIRP Irp ) { - NTSTATUS status; - PXENIFACE_GNTTAB_PERMIT_FOREIGN_ACCESS_IN In; + NTSTATUS Status; + PXENIFACE_GNTTAB_PERMIT_FOREIGN_ACCESS_IN In1 = NULL; + PXENIFACE_GNTTAB_PERMIT_FOREIGN_ACCESS_IN_V2 In = NULL; + // XENIFACE_GNTTAB_PERMIT_FOREIGN_ACCESS_OUT_V2 is the same as XENIFACE_GNTTAB_PERMIT_FOREIGN_ACCESS_OUT PXENIFACE_GNTTAB_PERMIT_FOREIGN_ACCESS_OUT Out = Irp->UserBuffer; - PXENIFACE_GRANT_CONTEXT Context; + PXENIFACE_GNTTAB_CONTEXT Context; ULONG Page; + ULONG ControlCode = IoGetCurrentIrpStackLocation(Irp)->Parameters.DeviceIoControl.IoControlCode; - status = STATUS_INVALID_BUFFER_SIZE; - if (InLen != sizeof(XENIFACE_GNTTAB_PERMIT_FOREIGN_ACCESS_IN)) + Status = STATUS_INVALID_BUFFER_SIZE; + if ((InLen != sizeof(XENIFACE_GNTTAB_PERMIT_FOREIGN_ACCESS_IN) && ControlCode == IOCTL_XENIFACE_GNTTAB_PERMIT_FOREIGN_ACCESS) + || (InLen != sizeof(XENIFACE_GNTTAB_PERMIT_FOREIGN_ACCESS_IN_V2) && ControlCode == IOCTL_XENIFACE_GNTTAB_PERMIT_FOREIGN_ACCESS_V2)) goto fail1; // This IOCTL uses METHOD_NEITHER so we directly access user memory. - status = __CaptureUserBuffer(Buffer, InLen, &In); - if (!NT_SUCCESS(status)) - goto fail2; + if (ControlCode == IOCTL_XENIFACE_GNTTAB_PERMIT_FOREIGN_ACCESS) { + // legacy IOCTL, convert the input to v2 + Status = __CaptureUserBuffer(Buffer, InLen, &In1); + if (!NT_SUCCESS(Status)) + goto fail2; + + Status = STATUS_NO_MEMORY; + In = __AllocatePoolWithTag(NonPagedPool, sizeof(*In), XENIFACE_POOL_TAG); + if (!In) + goto fail3; + + In->RemoteDomain = In1->RemoteDomain; + In->Address = NULL; + In->NumberPages = In->NumberPages; + In->Flags = In1->Flags; + In->NotifyOffset = In1->NotifyOffset; + In->NotifyPort = In1->NotifyPort; + } else { + Status = __CaptureUserBuffer(Buffer, InLen, &In); + if (!NT_SUCCESS(Status)) + goto fail2; + } - status = STATUS_INVALID_PARAMETER; - if (In->NumberPages == 0 || - In->NumberPages > 1024 * 1024) { - goto fail3; + Status = STATUS_INVALID_PARAMETER; + if (In->NumberPages == 0 || In->NumberPages > 1024 * 1024) { + goto fail4; } if ((In->Flags & XENIFACE_GNTTAB_USE_NOTIFY_OFFSET) && (In->NotifyOffset >= In->NumberPages * PAGE_SIZE)) { - goto fail4; + goto fail5; } - status = STATUS_INVALID_BUFFER_SIZE; + Status = STATUS_INVALID_BUFFER_SIZE; if (OutLen != (ULONG)FIELD_OFFSET(XENIFACE_GNTTAB_PERMIT_FOREIGN_ACCESS_OUT, References[In->NumberPages])) - goto fail5; + goto fail6; - status = STATUS_NO_MEMORY; - Context = __AllocatePoolWithTag(NonPagedPool, sizeof(XENIFACE_GRANT_CONTEXT), XENIFACE_POOL_TAG); + Status = STATUS_NO_MEMORY; + Context = __AllocatePoolWithTag(NonPagedPool, sizeof(XENIFACE_GNTTAB_CONTEXT), XENIFACE_POOL_TAG); if (Context == NULL) - goto fail6; + goto fail7; - RtlZeroMemory(Context, sizeof(XENIFACE_GRANT_CONTEXT)); - Context->Id.Type = XENIFACE_CONTEXT_GRANT; - Context->Id.Process = PsGetCurrentProcess(); - Context->Id.RequestId = In->RequestId; + Context->Type = XENIFACE_GNTTAB_CONTEXT_GRANT; + Context->Process = PsGetCurrentProcess(); Context->RemoteDomain = In->RemoteDomain; + Context->UserVa = In->Address; Context->NumberPages = In->NumberPages; Context->Flags = In->Flags; Context->NotifyOffset = In->NotifyOffset; Context->NotifyPort = In->NotifyPort; - Trace("> RemoteDomain %d, NumberPages %lu, Flags 0x%x, Offset 0x%x, Port %d, Process %p, Id %lu\n", - Context->RemoteDomain, Context->NumberPages, Context->Flags, Context->NotifyOffset, Context->NotifyPort, - Context->Id.Process, Context->Id.RequestId); - - // Check if the request ID is unique for this process. - // This doesn't protect us from simultaneous requests with the same ID arriving here - // but another check for duplicate ID is performed when the context/IRP is queued at the end. - // Ideally we would lock the whole section but that's not really an option since we touch user memory. - status = STATUS_INVALID_PARAMETER; - if (FindGnttabIrp(Fdo, &Context->Id) != NULL) - goto fail7; - - status = STATUS_NO_MEMORY; - Context->Grants = __AllocatePoolWithTag(NonPagedPool, Context->NumberPages * sizeof(PXENBUS_GNTTAB_ENTRY), XENIFACE_POOL_TAG); - if (Context->Grants == NULL) - goto fail8; - - RtlZeroMemory(Context->Grants, Context->NumberPages * sizeof(PXENBUS_GNTTAB_ENTRY)); - - // allocate memory to share - status = STATUS_NO_MEMORY; - Context->KernelVa = __AllocatePoolWithTag(NonPagedPool, Context->NumberPages * PAGE_SIZE, XENIFACE_POOL_TAG); - if (Context->KernelVa == NULL) - goto fail9; - - RtlZeroMemory(Context->KernelVa, Context->NumberPages * PAGE_SIZE); - Context->Mdl = IoAllocateMdl(Context->KernelVa, Context->NumberPages * PAGE_SIZE, FALSE, FALSE, NULL); - if (Context->Mdl == NULL) - goto fail10; - - MmBuildMdlForNonPagedPool(Context->Mdl); - ASSERT(MmGetMdlByteCount(Context->Mdl) == Context->NumberPages * PAGE_SIZE); - - // perform sharing - for (Page = 0; Page < Context->NumberPages; Page++) { - status = XENBUS_GNTTAB(PermitForeignAccess, - &Fdo->GnttabInterface, - Fdo->GnttabCache, - FALSE, - Context->RemoteDomain, - MmGetMdlPfnArray(Context->Mdl)[Page], - (Context->Flags & XENIFACE_GNTTAB_READONLY) != 0, - &(Context->Grants[Page])); - -// prefast somehow thinks that this call can modify Page... -#pragma prefast(suppress:6385) - Info("Grants[%lu] = %p\n", Page, Context->Grants[Page]); - if (!NT_SUCCESS(status)) - goto fail11; - } - - // map into user mode -#pragma prefast(suppress:6320) // we want to catch all exceptions - __try { - Context->UserVa = MmMapLockedPagesSpecifyCache(Context->Mdl, - UserMode, - MmCached, - NULL, - FALSE, - NormalPagePriority); - } - __except (EXCEPTION_EXECUTE_HANDLER) { - status = GetExceptionCode(); - goto fail12; + __FreeCapturedBuffer(In); + In = NULL; + + if (ControlCode == IOCTL_XENIFACE_GNTTAB_PERMIT_FOREIGN_ACCESS) { + Context->UseRequestId = TRUE; + Context->RequestId = In1->RequestId; + __FreeCapturedBuffer(In1); + In1 = NULL; + } else { + Context->UseRequestId = FALSE; + Context->RequestId = 0; } - status = STATUS_UNSUCCESSFUL; - if (Context->UserVa == NULL) - goto fail13; + Status = GnttabPermitForeignAccess(Fdo, Context); + if (!NT_SUCCESS(Status)) + goto fail8; Trace("< Context %p, Irp %p, KernelVa %p, UserVa %p\n", - Context, Irp, Context->KernelVa, Context->UserVa); + Context, Irp, Context->KernelVa, Context->UserVa); // Pass the result to user mode. #pragma prefast(suppress: 6320) // we want to catch all exceptions @@ -277,64 +421,34 @@ IoctlGnttabPermitForeignAccess( Context->Grants[Page]); } } except(EXCEPTION_EXECUTE_HANDLER) { - status = GetExceptionCode(); - Error("Exception 0x%lx while probing/writing output buffer at %p, size 0x%lx\n", status, Out, OutLen); - goto fail14; + Status = GetExceptionCode(); + Error("Exception 0x%lx while probing/writing output buffer at %p, size 0x%lx\n", Status, Out, OutLen); + goto fail9; } // Insert the IRP/context into the pending queue. - // This also checks (again) if the request ID is unique for the calling process. - Irp->Tail.Overlay.DriverContext[0] = &Context->Id; - status = IoCsqInsertIrpEx(&Fdo->IrpQueue, Irp, NULL, &Context->Id); - if (!NT_SUCCESS(status)) - goto fail15; - - __FreeCapturedBuffer(In); + // This also checks (again) if the request ID/address is unique for the calling process. + Irp->Tail.Overlay.DriverContext[0] = Context; + Status = IoCsqInsertIrpEx(&Fdo->IrpQueue, Irp, NULL, Context); + if (!NT_SUCCESS(Status)) + goto fail10; return STATUS_PENDING; -fail15: - Error("Fail15\n"); - -fail14: - Error("Fail14\n"); - MmUnmapLockedPages(Context->UserVa, Context->Mdl); - -fail13: - Error("Fail13\n"); - -fail12: - Error("Fail12\n"); - -fail11: - Error("Fail11: Page = %lu\n", Page); - - while (Page > 0) { - ASSERT(NT_SUCCESS(XENBUS_GNTTAB(RevokeForeignAccess, - &Fdo->GnttabInterface, - Fdo->GnttabCache, - FALSE, - Context->Grants[Page - 1]))); - - --Page; - } - IoFreeMdl(Context->Mdl); - fail10: Error("Fail10\n"); - __FreePoolWithTag(Context->KernelVa, XENIFACE_POOL_TAG); fail9: Error("Fail9\n"); - __FreePoolWithTag(Context->Grants, XENIFACE_POOL_TAG); + GnttabStopSharing(Fdo, Context, Context->NumberPages); fail8: Error("Fail8\n"); + RtlZeroMemory(Context, sizeof(*Context)); + __FreePoolWithTag(Context, XENIFACE_POOL_TAG); fail7: Error("Fail7\n"); - RtlZeroMemory(Context, sizeof(XENIFACE_GRANT_CONTEXT)); - __FreePoolWithTag(Context, XENIFACE_POOL_TAG); fail6: Error("Fail6\n"); @@ -344,33 +458,34 @@ fail5: fail4: Error("Fail4\n"); + __FreeCapturedBuffer(In); fail3: Error("Fail3\n"); - __FreeCapturedBuffer(In); + __FreeCapturedBuffer(In1); // NULL-safe fail2: Error("Fail2\n"); fail1: - Error("Fail1 (%08x)\n", status); - return status; + Error("Fail1 (%08x)\n", Status); + return Status; } _IRQL_requires_max_(APC_LEVEL) VOID GnttabFreeGrant( - __in PXENIFACE_FDO Fdo, - __inout PXENIFACE_GRANT_CONTEXT Context + __in PXENIFACE_FDO Fdo, + __inout PXENIFACE_GNTTAB_CONTEXT Context ) { NTSTATUS status; - ULONG Page; - - ASSERT(KeGetCurrentIrql() <= APC_LEVEL); Trace("Context %p\n", Context); + ASSERT(Context->Type == XENIFACE_GNTTAB_CONTEXT_GRANT); + ASSERT(KeGetCurrentIrql() <= APC_LEVEL); + if (Context->Flags & XENIFACE_GNTTAB_USE_NOTIFY_OFFSET) { ((PCHAR)Context->KernelVa)[Context->NotifyOffset] = 0; } @@ -382,67 +497,56 @@ GnttabFreeGrant( Error("failed to notify port %lu: 0x%x\n", Context->NotifyPort, status); } - // unmap from user address space - MmUnmapLockedPages(Context->UserVa, Context->Mdl); + GnttabStopSharing(Fdo, Context, Context->NumberPages); - // stop sharing - for (Page = 0; Page < Context->NumberPages; Page++) { - status = XENBUS_GNTTAB(RevokeForeignAccess, - &Fdo->GnttabInterface, - Fdo->GnttabCache, - FALSE, - Context->Grants[Page]); - - ASSERT(NT_SUCCESS(status)); // failure here is fatal, something must've gone catastrophically wrong - } - - IoFreeMdl(Context->Mdl); - - RtlZeroMemory(Context->KernelVa, Context->NumberPages * PAGE_SIZE); - __FreePoolWithTag(Context->KernelVa, XENIFACE_POOL_TAG); - - RtlZeroMemory(Context->Grants, Context->NumberPages * sizeof(PXENBUS_GNTTAB_ENTRY)); - __FreePoolWithTag(Context->Grants, XENIFACE_POOL_TAG); - - RtlZeroMemory(Context, sizeof(XENIFACE_GRANT_CONTEXT)); + RtlZeroMemory(Context, sizeof(*Context)); __FreePoolWithTag(Context, XENIFACE_POOL_TAG); } DECLSPEC_NOINLINE NTSTATUS IoctlGnttabRevokeForeignAccess( - __in PXENIFACE_FDO Fdo, - __in PVOID Buffer, - __in ULONG InLen, - __in ULONG OutLen + __in PXENIFACE_FDO Fdo, + __in PVOID Buffer, + __in ULONG InLen, + __in ULONG OutLen, + __in ULONG ControlCode ) { - NTSTATUS status; - PXENIFACE_GNTTAB_REVOKE_FOREIGN_ACCESS_IN In = Buffer; - PXENIFACE_GRANT_CONTEXT Context = NULL; - XENIFACE_CONTEXT_ID Id; + NTSTATUS Status; + XENIFACE_GNTTAB_CONTEXT SeekContext; PIRP PendingIrp; - PXENIFACE_CONTEXT_ID ContextId; + PXENIFACE_GNTTAB_CONTEXT Context = NULL; UNREFERENCED_PARAMETER(OutLen); - status = STATUS_INVALID_BUFFER_SIZE; - if (InLen != sizeof(XENIFACE_GNTTAB_REVOKE_FOREIGN_ACCESS_IN)) - goto fail1; + Status = STATUS_INVALID_BUFFER_SIZE; - Id.Type = XENIFACE_CONTEXT_GRANT; - Id.Process = PsGetCurrentProcess(); - Id.RequestId = In->RequestId; + SeekContext.Type = XENIFACE_GNTTAB_CONTEXT_GRANT; + SeekContext.Process = PsGetCurrentProcess(); - Trace("> Process %p, Id %lu\n", Id.Process, Id.RequestId); + if (ControlCode == IOCTL_XENIFACE_GNTTAB_REVOKE_FOREIGN_ACCESS) { + if (InLen != sizeof(XENIFACE_GNTTAB_REVOKE_FOREIGN_ACCESS_IN)) + goto fail1; - status = STATUS_NOT_FOUND; - PendingIrp = IoCsqRemoveNextIrp(&Fdo->IrpQueue, &Id); + SeekContext.UseRequestId = TRUE; + SeekContext.RequestId = ((PXENIFACE_GNTTAB_REVOKE_FOREIGN_ACCESS_IN)Buffer)->RequestId; + Trace("> Process %p, Id %lu\n", SeekContext.Process, SeekContext.RequestId); + } else { + if (InLen != sizeof(XENIFACE_GNTTAB_REVOKE_FOREIGN_ACCESS_IN_V2)) + goto fail1; + + SeekContext.UseRequestId = FALSE; + SeekContext.UserVa = ((PXENIFACE_GNTTAB_REVOKE_FOREIGN_ACCESS_IN_V2)Buffer)->Address; + Trace("> Process %p, Address %p\n", SeekContext.Process, SeekContext.UserVa); + } + + Status = STATUS_NOT_FOUND; + PendingIrp = IoCsqRemoveNextIrp(&Fdo->IrpQueue, &SeekContext); if (PendingIrp == NULL) goto fail2; - ContextId = PendingIrp->Tail.Overlay.DriverContext[0]; - Context = CONTAINING_RECORD(ContextId, XENIFACE_GRANT_CONTEXT, Id); + Context = PendingIrp->Tail.Overlay.DriverContext[0]; GnttabFreeGrant(Fdo, Context); PendingIrp->IoStatus.Status = STATUS_SUCCESS; @@ -455,67 +559,103 @@ fail2: Error("Fail2\n"); fail1: - Error("Fail1 (%08x)\n", status); - return status; + Error("Fail1 (%08x)\n", Status); + return Status; } DECLSPEC_NOINLINE NTSTATUS IoctlGnttabMapForeignPages( - __in PXENIFACE_FDO Fdo, - __in PVOID Buffer, - __in ULONG InLen, - __in ULONG OutLen, - __inout PIRP Irp + __in PXENIFACE_FDO Fdo, + __in PVOID Buffer, + __in ULONG InLen, + __in ULONG OutLen, + __inout PIRP Irp ) { NTSTATUS status; - PXENIFACE_GNTTAB_MAP_FOREIGN_PAGES_IN In = Buffer; + PXENIFACE_GNTTAB_MAP_FOREIGN_PAGES_IN In1 = NULL; + PXENIFACE_GNTTAB_MAP_FOREIGN_PAGES_IN_V2 In = NULL; + // XENIFACE_GNTTAB_MAP_FOREIGN_PAGES_OUT_V2 is the same as XENIFACE_GNTTAB_MAP_FOREIGN_PAGES_OUT PXENIFACE_GNTTAB_MAP_FOREIGN_PAGES_OUT Out = Irp->UserBuffer; ULONG NumberPages; - ULONG PageIndex; - PXENIFACE_MAP_CONTEXT Context; + PXENIFACE_GNTTAB_CONTEXT Context; + ULONG ControlCode = IoGetCurrentIrpStackLocation(Irp)->Parameters.DeviceIoControl.IoControlCode; - status = STATUS_INVALID_BUFFER_SIZE; - if (InLen < sizeof(XENIFACE_GNTTAB_MAP_FOREIGN_PAGES_IN) || - OutLen != sizeof(XENIFACE_GNTTAB_MAP_FOREIGN_PAGES_OUT)) { + ASSERT(ControlCode == IOCTL_XENIFACE_GNTTAB_MAP_FOREIGN_PAGES || ControlCode == IOCTL_XENIFACE_GNTTAB_MAP_FOREIGN_PAGES_V2); + + status = STATUS_NO_MEMORY; + Context = __AllocatePoolWithTag(NonPagedPool, sizeof(*Context), XENIFACE_POOL_TAG); + if (Context == NULL) goto fail1; - } - // This IOCTL uses METHOD_NEITHER so we directly access user memory. + Context->Type = XENIFACE_GNTTAB_CONTEXT_MAP; + Context->Process = PsGetCurrentProcess(); - // Calculate the expected number of pages based on input buffer size. - NumberPages = (InLen - (ULONG)FIELD_OFFSET(XENIFACE_GNTTAB_MAP_FOREIGN_PAGES_IN, References)) / sizeof(In->References[0]); + // This IOCTL uses METHOD_NEITHER so we directly access user memory. + if (ControlCode == IOCTL_XENIFACE_GNTTAB_MAP_FOREIGN_PAGES) { + status = STATUS_INVALID_BUFFER_SIZE; + if (InLen < sizeof(XENIFACE_GNTTAB_MAP_FOREIGN_PAGES_IN) + || OutLen != sizeof(XENIFACE_GNTTAB_MAP_FOREIGN_PAGES_OUT)) + goto fail2; + + In1 = Buffer; + NumberPages = (InLen - (ULONG)FIELD_OFFSET(XENIFACE_GNTTAB_MAP_FOREIGN_PAGES_IN, References)) / sizeof(ULONG); + status = __CaptureUserBuffer(Buffer, InLen, &In1); + if (!NT_SUCCESS(status)) + goto fail3; + + Context->UseRequestId = TRUE; + Context->RequestId = In1->RequestId; + + // legacy IOCTL, convert the input to v2 + status = STATUS_NO_MEMORY; + In = __AllocatePoolWithTag(NonPagedPool, sizeof(*In), XENIFACE_POOL_TAG); + if (In == NULL) + goto fail4; + + In->RemoteDomain = In1->RemoteDomain; + In->NumberPages = In1->NumberPages; + In->NotifyOffset = In1->NotifyOffset; + In->NotifyPort = In1->NotifyPort; + In->Flags = In1->Flags; + memcpy(&In->References, &In1->References, NumberPages * sizeof(ULONG)); + + __FreeCapturedBuffer(In1); + In1 = NULL; + } else { + status = STATUS_INVALID_BUFFER_SIZE; + if (InLen < sizeof(XENIFACE_GNTTAB_MAP_FOREIGN_PAGES_IN_V2) + || OutLen != sizeof(XENIFACE_GNTTAB_MAP_FOREIGN_PAGES_OUT_V2)) + goto fail2; + + In = Buffer; + NumberPages = (InLen - (ULONG)FIELD_OFFSET(XENIFACE_GNTTAB_MAP_FOREIGN_PAGES_IN_V2, References)) / sizeof(ULONG); + status = __CaptureUserBuffer(Buffer, InLen, &In); + if (!NT_SUCCESS(status)) + goto fail3; - status = __CaptureUserBuffer(Buffer, InLen, &In); - if (!NT_SUCCESS(status)) - goto fail2; + Context->UseRequestId = FALSE; + Context->RequestId = 0; + } + // At this point we only access In. status = STATUS_INVALID_PARAMETER; if (In->NumberPages == 0 || In->NumberPages > 1024 * 1024 || In->NumberPages != NumberPages) { - goto fail3; + goto fail5; } if ((In->Flags & XENIFACE_GNTTAB_USE_NOTIFY_OFFSET) && (In->NotifyOffset >= In->NumberPages * PAGE_SIZE)) { - goto fail4; + goto fail6; } status = STATUS_INVALID_BUFFER_SIZE; - if (InLen != (ULONG)FIELD_OFFSET(XENIFACE_GNTTAB_MAP_FOREIGN_PAGES_IN, References[In->NumberPages])) - goto fail5; - - status = STATUS_NO_MEMORY; - Context = __AllocatePoolWithTag(NonPagedPool, sizeof(XENIFACE_MAP_CONTEXT), XENIFACE_POOL_TAG); - if (Context == NULL) - goto fail6; + if (InLen != (ULONG)FIELD_OFFSET(XENIFACE_GNTTAB_MAP_FOREIGN_PAGES_IN_V2, References[In->NumberPages])) + goto fail7; - RtlZeroMemory(Context, sizeof(XENIFACE_MAP_CONTEXT)); - Context->Id.Type = XENIFACE_CONTEXT_MAP; - Context->Id.Process = PsGetCurrentProcess(); - Context->Id.RequestId = In->RequestId; Context->RemoteDomain = In->RemoteDomain; Context->NumberPages = In->NumberPages; Context->Flags = In->Flags; @@ -523,15 +663,17 @@ IoctlGnttabMapForeignPages( Context->NotifyPort = In->NotifyPort; Trace("> RemoteDomain %d, NumberPages %lu, Flags 0x%x, Offset 0x%x, Port %d, Process %p, Id %lu\n", - Context->RemoteDomain, Context->NumberPages, Context->Flags, Context->NotifyOffset, Context->NotifyPort, - Context->Id.Process, Context->Id.RequestId); + Context->RemoteDomain, Context->NumberPages, Context->Flags, Context->NotifyOffset, Context->NotifyPort, + Context->Process, Context->RequestId); - for (PageIndex = 0; PageIndex < In->NumberPages; PageIndex++) +#if DBG + for (ULONG PageIndex = 0; PageIndex < In->NumberPages; PageIndex++) Info("> Ref %d\n", In->References[PageIndex]); +#endif status = STATUS_INVALID_PARAMETER; - if (FindGnttabIrp(Fdo, &Context->Id) != NULL) - goto fail7; + if (FindGnttabIrp(Fdo, Context) != NULL) + goto fail8; status = XENBUS_GNTTAB(MapForeignPages, &Fdo->GnttabInterface, @@ -542,41 +684,36 @@ IoctlGnttabMapForeignPages( &Context->Address); if (!NT_SUCCESS(status)) - goto fail8; + goto fail9; - status = STATUS_NO_MEMORY; + status = STATUS_INSUFFICIENT_VIRTUAL_ADDR_RESOURCES; Context->KernelVa = MmMapIoSpace(Context->Address, Context->NumberPages * PAGE_SIZE, MmCached); if (Context->KernelVa == NULL) - goto fail9; + goto fail10; status = STATUS_NO_MEMORY; Context->Mdl = IoAllocateMdl(Context->KernelVa, Context->NumberPages * PAGE_SIZE, FALSE, FALSE, NULL); if (Context->Mdl == NULL) - goto fail10; + goto fail11; MmBuildMdlForNonPagedPool(Context->Mdl); // map into user mode #pragma prefast(suppress: 6320) // we want to catch all exceptions - __try { + try { Context->UserVa = MmMapLockedPagesSpecifyCache(Context->Mdl, UserMode, MmCached, NULL, FALSE, NormalPagePriority); - } - __except (EXCEPTION_EXECUTE_HANDLER) { + } except (EXCEPTION_EXECUTE_HANDLER) { status = GetExceptionCode(); - goto fail11; - } - - status = STATUS_UNSUCCESSFUL; - if (Context->UserVa == NULL) goto fail12; + } Trace("< Context %p, Irp %p, Address %p, KernelVa %p, UserVa %p\n", - Context, Irp, Context->Address, Context->KernelVa, Context->UserVa); + Context, Irp, Context->Address, Context->KernelVa, Context->UserVa); // Pass the result to user mode. #pragma prefast(suppress: 6320) // we want to catch all exceptions @@ -591,8 +728,8 @@ IoctlGnttabMapForeignPages( // Insert the IRP/context into the pending queue. // This also checks (again) if the request ID is unique for the calling process. - Irp->Tail.Overlay.DriverContext[0] = &Context->Id; - status = IoCsqInsertIrpEx(&Fdo->IrpQueue, Irp, NULL, &Context->Id); + Irp->Tail.Overlay.DriverContext[0] = Context; + status = IoCsqInsertIrpEx(&Fdo->IrpQueue, Irp, NULL, Context); if (!NT_SUCCESS(status)) goto fail14; @@ -609,45 +746,46 @@ fail13: fail12: Error("Fail12\n"); + IoFreeMdl(Context->Mdl); fail11: Error("Fail11\n"); - IoFreeMdl(Context->Mdl); + MmUnmapIoSpace(Context->KernelVa, Context->NumberPages * PAGE_SIZE); fail10: Error("Fail10\n"); - MmUnmapIoSpace(Context->KernelVa, Context->NumberPages * PAGE_SIZE); - -fail9: - Error("Fail9\n"); ASSERT(NT_SUCCESS(XENBUS_GNTTAB(UnmapForeignPages, &Fdo->GnttabInterface, Context->Address ))); +fail9: + Error("Fail9\n"); + fail8: Error("Fail8\n"); fail7: Error("Fail7\n"); - RtlZeroMemory(Context, sizeof(XENIFACE_MAP_CONTEXT)); - __FreePoolWithTag(Context, XENIFACE_POOL_TAG); fail6: Error("Fail6\n"); fail5: Error("Fail5\n"); + __FreeCapturedBuffer(In); fail4: Error("Fail4\n"); + __FreeCapturedBuffer(In1); fail3: Error("Fail3\n"); - __FreeCapturedBuffer(In); fail2: Error("Fail2\n"); + RtlZeroMemory(Context, sizeof(*Context)); + __FreePoolWithTag(Context, XENIFACE_POOL_TAG); fail1: Error("Fail1 (%08x)\n", status); @@ -658,12 +796,13 @@ _IRQL_requires_max_(APC_LEVEL) DECLSPEC_NOINLINE VOID GnttabFreeMap( - __in PXENIFACE_FDO Fdo, - __inout PXENIFACE_MAP_CONTEXT Context + __in PXENIFACE_FDO Fdo, + __inout PXENIFACE_GNTTAB_CONTEXT Context ) { NTSTATUS status; + ASSERT(Context->Type == XENIFACE_GNTTAB_CONTEXT_MAP); ASSERT(KeGetCurrentIrql() <= APC_LEVEL); Trace("Context %p\n", Context); @@ -694,45 +833,61 @@ GnttabFreeMap( ASSERT(NT_SUCCESS(status)); - RtlZeroMemory(Context, sizeof(XENIFACE_MAP_CONTEXT)); + RtlZeroMemory(Context, sizeof(*Context)); __FreePoolWithTag(Context, XENIFACE_POOL_TAG); } DECLSPEC_NOINLINE NTSTATUS IoctlGnttabUnmapForeignPages( - __in PXENIFACE_FDO Fdo, - __in PVOID Buffer, - __in ULONG InLen, - __in ULONG OutLen + __in PXENIFACE_FDO Fdo, + __in PVOID Buffer, + __in ULONG InLen, + __in ULONG OutLen, + __in ULONG ControlCode ) { NTSTATUS status; - PXENIFACE_GNTTAB_UNMAP_FOREIGN_PAGES_IN In = Buffer; - PXENIFACE_MAP_CONTEXT Context = NULL; - XENIFACE_CONTEXT_ID Id; + XENIFACE_GNTTAB_CONTEXT SeekContext; + PXENIFACE_GNTTAB_CONTEXT Context; PIRP PendingIrp; - PXENIFACE_CONTEXT_ID ContextId; - status = STATUS_INVALID_BUFFER_SIZE; - if (InLen != sizeof(XENIFACE_GNTTAB_UNMAP_FOREIGN_PAGES_IN) || - OutLen != 0) { - goto fail1; - } + ASSERT(ControlCode == IOCTL_XENIFACE_GNTTAB_UNMAP_FOREIGN_PAGES + || ControlCode == IOCTL_XENIFACE_GNTTAB_UNMAP_FOREIGN_PAGES_V2); + + SeekContext.Type = XENIFACE_GNTTAB_CONTEXT_MAP; + SeekContext.Process = PsGetCurrentProcess(); + + if (ControlCode == IOCTL_XENIFACE_GNTTAB_UNMAP_FOREIGN_PAGES) { + status = STATUS_INVALID_BUFFER_SIZE; + if (InLen != sizeof(XENIFACE_GNTTAB_UNMAP_FOREIGN_PAGES_IN) || OutLen != 0) { + goto fail1; + } - Id.Type = XENIFACE_CONTEXT_MAP; - Id.Process = PsGetCurrentProcess(); - Id.RequestId = In->RequestId; + PXENIFACE_GNTTAB_UNMAP_FOREIGN_PAGES_IN In = Buffer; + SeekContext.UseRequestId = TRUE; + SeekContext.RequestId = In->RequestId; - Trace("> Process %p, Id %lu\n", Id.Process, Id.RequestId); + Trace("> Process %p, Id %lu\n", SeekContext.Process, SeekContext.RequestId); + } else { + status = STATUS_INVALID_BUFFER_SIZE; + if (InLen != sizeof(XENIFACE_GNTTAB_UNMAP_FOREIGN_PAGES_IN_V2) || OutLen != 0) { + goto fail1; + } + + PXENIFACE_GNTTAB_UNMAP_FOREIGN_PAGES_IN_V2 In = Buffer; + SeekContext.UseRequestId = FALSE; + SeekContext.UserVa = In->Address; + + Trace("> Process %p, UserVa %p\n", SeekContext.Process, SeekContext.UserVa); + } status = STATUS_NOT_FOUND; - PendingIrp = IoCsqRemoveNextIrp(&Fdo->IrpQueue, &Id); + PendingIrp = IoCsqRemoveNextIrp(&Fdo->IrpQueue, &SeekContext); if (PendingIrp == NULL) goto fail2; - ContextId = PendingIrp->Tail.Overlay.DriverContext[0]; - Context = CONTAINING_RECORD(ContextId, XENIFACE_MAP_CONTEXT, Id); + Context = PendingIrp->Tail.Overlay.DriverContext[0]; GnttabFreeMap(Fdo, Context); PendingIrp->IoStatus.Status = STATUS_SUCCESS; diff --git a/src/xeniface/ioctls.c b/src/xeniface/ioctls.c index 479f0f6..6282e77 100644 --- a/src/xeniface/ioctls.c +++ b/src/xeniface/ioctls.c @@ -151,7 +151,7 @@ fail1: _IRQL_requires_(PASSIVE_LEVEL) // EvtchnFree calls KeFlushQueuedDpcs VOID XenIfaceCleanup( - __in PXENIFACE_FDO Fdo, + __in PXENIFACE_FDO Fdo, __in_opt PFILE_OBJECT FileObject ) { @@ -247,12 +247,13 @@ XenIfaceIoctl( PVOID Buffer = Irp->AssociatedIrp.SystemBuffer; ULONG InLen = Stack->Parameters.DeviceIoControl.InputBufferLength; ULONG OutLen = Stack->Parameters.DeviceIoControl.OutputBufferLength; + ULONG ControlCode = Stack->Parameters.DeviceIoControl.IoControlCode; status = STATUS_DEVICE_NOT_READY; if (Fdo->InterfacesAcquired == FALSE) goto done; - switch (Stack->Parameters.DeviceIoControl.IoControlCode) { + switch (ControlCode) { // store case IOCTL_XENIFACE_STORE_READ: status = IoctlStoreRead(Fdo, (PCHAR)Buffer, InLen, OutLen, &Irp->IoStatus.Information); @@ -308,16 +309,32 @@ XenIfaceIoctl( status = IoctlGnttabPermitForeignAccess(Fdo, Stack->Parameters.DeviceIoControl.Type3InputBuffer, InLen, OutLen, Irp); break; + case IOCTL_XENIFACE_GNTTAB_PERMIT_FOREIGN_ACCESS_V2: // this is a METHOD_NEITHER IOCTL + status = IoctlGnttabPermitForeignAccess(Fdo, Stack->Parameters.DeviceIoControl.Type3InputBuffer, InLen, OutLen, Irp); + break; + case IOCTL_XENIFACE_GNTTAB_REVOKE_FOREIGN_ACCESS: - status = IoctlGnttabRevokeForeignAccess(Fdo, Buffer, InLen, OutLen); + status = IoctlGnttabRevokeForeignAccess(Fdo, Buffer, InLen, OutLen, ControlCode); + break; + + case IOCTL_XENIFACE_GNTTAB_REVOKE_FOREIGN_ACCESS_V2: + status = IoctlGnttabRevokeForeignAccess(Fdo, Buffer, InLen, OutLen, ControlCode); break; case IOCTL_XENIFACE_GNTTAB_MAP_FOREIGN_PAGES: // this is a METHOD_NEITHER IOCTL status = IoctlGnttabMapForeignPages(Fdo, Stack->Parameters.DeviceIoControl.Type3InputBuffer, InLen, OutLen, Irp); break; + case IOCTL_XENIFACE_GNTTAB_MAP_FOREIGN_PAGES_V2: // this is a METHOD_NEITHER IOCTL + status = IoctlGnttabMapForeignPages(Fdo, Stack->Parameters.DeviceIoControl.Type3InputBuffer, InLen, OutLen, Irp); + break; + case IOCTL_XENIFACE_GNTTAB_UNMAP_FOREIGN_PAGES: - status = IoctlGnttabUnmapForeignPages(Fdo, Buffer, InLen, OutLen); + status = IoctlGnttabUnmapForeignPages(Fdo, Buffer, InLen, OutLen, ControlCode); + break; + + case IOCTL_XENIFACE_GNTTAB_UNMAP_FOREIGN_PAGES_V2: + status = IoctlGnttabUnmapForeignPages(Fdo, Buffer, InLen, OutLen, ControlCode); break; // suspend diff --git a/src/xeniface/ioctls.h b/src/xeniface/ioctls.h index 09946fd..b4c8de2 100644 --- a/src/xeniface/ioctls.h +++ b/src/xeniface/ioctls.h @@ -36,17 +36,6 @@ #include "xeniface_ioctls.h" -typedef enum _XENIFACE_CONTEXT_TYPE { - XENIFACE_CONTEXT_GRANT = 1, - XENIFACE_CONTEXT_MAP -} XENIFACE_CONTEXT_TYPE; - -typedef struct _XENIFACE_CONTEXT_ID { - XENIFACE_CONTEXT_TYPE Type; - ULONG RequestId; - PEPROCESS Process; -} XENIFACE_CONTEXT_ID, *PXENIFACE_CONTEXT_ID; - typedef struct _XENIFACE_STORE_CONTEXT { LIST_ENTRY Entry; PCHAR Path; @@ -72,33 +61,33 @@ typedef struct _XENIFACE_SUSPEND_CONTEXT { PVOID FileObject; } XENIFACE_SUSPEND_CONTEXT, *PXENIFACE_SUSPEND_CONTEXT; -typedef struct _XENIFACE_GRANT_CONTEXT { - XENIFACE_CONTEXT_ID Id; - LIST_ENTRY Entry; - PXENBUS_GNTTAB_ENTRY *Grants; - USHORT RemoteDomain; - ULONG NumberPages; - XENIFACE_GNTTAB_PAGE_FLAGS Flags; - ULONG NotifyOffset; - ULONG NotifyPort; - PVOID KernelVa; - PVOID UserVa; - PMDL Mdl; -} XENIFACE_GRANT_CONTEXT, *PXENIFACE_GRANT_CONTEXT; - -typedef struct _XENIFACE_MAP_CONTEXT { - XENIFACE_CONTEXT_ID Id; - LIST_ENTRY Entry; - USHORT RemoteDomain; - ULONG NumberPages; - XENIFACE_GNTTAB_PAGE_FLAGS Flags; - ULONG NotifyOffset; - ULONG NotifyPort; - PHYSICAL_ADDRESS Address; - PVOID KernelVa; - PVOID UserVa; - PMDL Mdl; -} XENIFACE_MAP_CONTEXT, *PXENIFACE_MAP_CONTEXT; +typedef enum _XENIFACE_GNTTAB_CONTEXT_TYPE { + XENIFACE_GNTTAB_CONTEXT_GRANT = 1, + XENIFACE_GNTTAB_CONTEXT_MAP +} XENIFACE_GNTTAB_CONTEXT_TYPE; + +#pragma warning(push) +#pragma warning(disable:4201) // nonstandard extension used: nameless struct/union +typedef struct _XENIFACE_GNTTAB_CONTEXT { + LIST_ENTRY Entry; + XENIFACE_GNTTAB_CONTEXT_TYPE Type; + BOOLEAN UseRequestId; // true for legacy IOCTLs + ULONG RequestId; + PEPROCESS Process; + USHORT RemoteDomain; + ULONG NumberPages; + XENIFACE_GNTTAB_PAGE_FLAGS Flags; + ULONG NotifyOffset; + ULONG NotifyPort; + union { + PXENBUS_GNTTAB_ENTRY *Grants; // permit + PHYSICAL_ADDRESS Address; // map + }; + PVOID KernelVa; + PVOID UserVa; + PMDL Mdl; +} XENIFACE_GNTTAB_CONTEXT, *PXENIFACE_GNTTAB_CONTEXT; +#pragma warning(pop) NTSTATUS __CaptureUserBuffer( @@ -295,7 +284,8 @@ IoctlGnttabRevokeForeignAccess( __in PXENIFACE_FDO Fdo, __in PVOID Buffer, __in ULONG InLen, - __in ULONG OutLen + __in ULONG OutLen, + __in ULONG ControlCode ); DECLSPEC_NOINLINE @@ -314,7 +304,8 @@ IoctlGnttabUnmapForeignPages( __in PXENIFACE_FDO Fdo, __in PVOID Buffer, __in ULONG InLen, - __in ULONG OutLen + __in ULONG OutLen, + __in ULONG ControlCode ); _Acquires_exclusive_lock_(((PXENIFACE_FDO)Argument)->GnttabCacheLock) @@ -342,14 +333,14 @@ _IRQL_requires_max_(APC_LEVEL) VOID GnttabFreeGrant( __in PXENIFACE_FDO Fdo, - __inout PXENIFACE_GRANT_CONTEXT Context + __inout PXENIFACE_GNTTAB_CONTEXT Context ); _IRQL_requires_max_(APC_LEVEL) VOID GnttabFreeMap( __in PXENIFACE_FDO Fdo, - __inout PXENIFACE_MAP_CONTEXT Context + __inout PXENIFACE_GNTTAB_CONTEXT Context ); NTSTATUS diff --git a/src/xeniface/irp_queue.c b/src/xeniface/irp_queue.c index 37191bc..dcc5ba0 100644 --- a/src/xeniface/irp_queue.c +++ b/src/xeniface/irp_queue.c @@ -40,14 +40,14 @@ NTSTATUS CsqInsertIrpEx( _In_ PIO_CSQ Csq, _In_ PIRP Irp, - _In_ PVOID InsertContext // PXENIFACE_CONTEXT_ID + _In_ PVOID InsertContext // PXENIFACE_GNTTAB_CONTEXT ) { PXENIFACE_FDO Fdo; Fdo = CONTAINING_RECORD(Csq, XENIFACE_FDO, IrpQueue); - // Fail if a request with the same ID already exists. + // Fail if a request with the same ID/address already exists. if (CsqPeekNextIrp(Csq, NULL, InsertContext) != NULL) return STATUS_INVALID_PARAMETER; @@ -70,16 +70,16 @@ PIRP CsqPeekNextIrp( _In_ PIO_CSQ Csq, _In_opt_ PIRP Irp, - _In_opt_ PVOID PeekContext // PXENIFACE_CONTEXT_ID + _In_opt_ PVOID PeekContext // PXENIFACE_GNTTAB_CONTEXT ) { - PXENIFACE_FDO Fdo; - PIRP NextIrp = NULL; - PLIST_ENTRY Head, NextEntry; - PXENIFACE_CONTEXT_ID Id, TargetId; + PXENIFACE_FDO Fdo; + PIRP NextIrp = NULL; + PLIST_ENTRY Head, NextEntry; + PXENIFACE_GNTTAB_CONTEXT Context, TargetContext; Fdo = CONTAINING_RECORD(Csq, XENIFACE_FDO, IrpQueue); - TargetId = PeekContext; + TargetContext = PeekContext; Head = &Fdo->IrpList; // If the IRP is NULL, we will start peeking from the list head, @@ -96,8 +96,15 @@ CsqPeekNextIrp( NextIrp = CONTAINING_RECORD(NextEntry, IRP, Tail.Overlay.ListEntry); if (PeekContext) { - Id = NextIrp->Tail.Overlay.DriverContext[0]; - if (Id->RequestId == TargetId->RequestId && Id->Process == TargetId->Process) + Context = NextIrp->Tail.Overlay.DriverContext[0]; + + BOOL Match = TargetContext->Type == Context->Type; + if (TargetContext->UseRequestId) + Match = Match && (TargetContext->RequestId == Context->RequestId); + else + Match = Match && (TargetContext->UserVa == Context->UserVa); + + if (Match) break; } else { break; diff --git a/src/xeniface/irp_queue.h b/src/xeniface/irp_queue.h index 6b0e28f..04b9877 100644 --- a/src/xeniface/irp_queue.h +++ b/src/xeniface/irp_queue.h @@ -51,7 +51,7 @@ PIRP CsqPeekNextIrp( _In_ PIO_CSQ Csq, _In_opt_ PIRP Irp, - _In_opt_ PVOID PeekContext // PXENIFACE_CONTEXT_ID + _In_opt_ PVOID PeekContext // PXENIFACE_GNTTAB_CONTEXT ); _IRQL_raises_(DISPATCH_LEVEL) -- 2.40.1.windows.1
|
Lists.xenproject.org is hosted with RackSpace, monitoring our |