reactos/ntoskrnl/io/iomgr/iocomp.c
George Bișoc d0d86ab588
[NTOSKRNL] Force a probe against ReturnLength on query & Misc ICIF stuff
NtQueryInformationToken is by far the only system call in NT where ReturnLength simply cannot be optional. On Windows this parameter is always probed and an argument to NULL directly leads to an access violation exception.
This is due to the fact of how tokens work, as its information contents (token user, owner, primary group, et al) are dynamic and can vary throughout over time in memory.

What happens on current ReactOS master however is that ReturnLength is only probed if the parameter is not NULL. On a NULL case scenario the probing checks succeed and NtQueryInformationToken fails later. For this, just get rid of CompleteProbing
parameter and opt in for a bit mask flag based approach, with ICIF_FORCE_RETURN_LENGTH_PROBE being set on DefaultQueryInfoBufferCheck which NtQueryInformationToken calls it to do sanity checks.

In addition to that...

- Document the ICIF probe helpers
- Annotate the ICIF prope helpers with SAL
- With the riddance of CompleteProbing and adoption of flags based approach, add ICIF_PROBE_READ_WRITE and ICIF_PROBE_READ flags alongside with ICIF_FORCE_RETURN_LENGTH_PROBE
2022-06-12 11:05:05 +02:00

602 lines
18 KiB
C

/*
* PROJECT: ReactOS Kernel
* LICENSE: GPL - See COPYING in the top level directory
* FILE: ntoskrnl/io/iomgr/iocomp.c
* PURPOSE: I/O Wrappers (called Completion Ports) for Kernel Queues
* PROGRAMMERS: Alex Ionescu (alex.ionescu@reactos.org)
* Thomas Weidenmueller (w3seek@reactos.org)
*/
/* INCLUDES *****************************************************************/
#include <ntoskrnl.h>
#define NDEBUG
#include <debug.h>
POBJECT_TYPE IoCompletionType;
GENERAL_LOOKASIDE IoCompletionPacketLookaside;
GENERIC_MAPPING IopCompletionMapping =
{
STANDARD_RIGHTS_READ | IO_COMPLETION_QUERY_STATE,
STANDARD_RIGHTS_WRITE | IO_COMPLETION_MODIFY_STATE,
STANDARD_RIGHTS_EXECUTE | SYNCHRONIZE,
IO_COMPLETION_ALL_ACCESS
};
static const INFORMATION_CLASS_INFO IoCompletionInfoClass[] =
{
/* IoCompletionBasicInformation */
IQS_SAME(IO_COMPLETION_BASIC_INFORMATION, ULONG, ICIF_QUERY),
};
/* PRIVATE FUNCTIONS *********************************************************/
NTSTATUS
NTAPI
IopUnloadSafeCompletion(IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp,
IN PVOID Context)
{
NTSTATUS Status;
PIO_UNLOAD_SAFE_COMPLETION_CONTEXT UnsafeContext = Context;
/* Reference the device object */
ObReferenceObject(UnsafeContext->DeviceObject);
/* Call the completion routine */
Status= UnsafeContext->CompletionRoutine(DeviceObject,
Irp,
UnsafeContext->Context);
/* Dereference the device object */
ObDereferenceObject(UnsafeContext->DeviceObject);
/* Free our context */
ExFreePool(UnsafeContext);
return Status;
}
VOID
NTAPI
IopFreeMiniPacket(PIOP_MINI_COMPLETION_PACKET Packet)
{
PKPRCB Prcb = KeGetCurrentPrcb();
PNPAGED_LOOKASIDE_LIST List;
/* Use the P List */
List = (PNPAGED_LOOKASIDE_LIST)Prcb->
PPLookasideList[LookasideCompletionList].P;
List->L.TotalFrees++;
/* Check if the Free was within the Depth or not */
if (ExQueryDepthSList(&List->L.ListHead) >= List->L.Depth)
{
/* Let the balancer know */
List->L.FreeMisses++;
/* Use the L List */
List = (PNPAGED_LOOKASIDE_LIST)Prcb->
PPLookasideList[LookasideCompletionList].L;
List->L.TotalFrees++;
/* Check if the Free was within the Depth or not */
if (ExQueryDepthSList(&List->L.ListHead) >= List->L.Depth)
{
/* All lists failed, use the pool */
List->L.FreeMisses++;
ExFreePool(Packet);
return;
}
}
/* The free was within dhe Depth */
InterlockedPushEntrySList(&List->L.ListHead, (PSLIST_ENTRY)Packet);
}
VOID
NTAPI
IopDeleteIoCompletion(PVOID ObjectBody)
{
PKQUEUE Queue = ObjectBody;
PLIST_ENTRY FirstEntry;
PLIST_ENTRY CurrentEntry;
PIRP Irp;
PIOP_MINI_COMPLETION_PACKET Packet;
/* Rundown the Queue */
FirstEntry = KeRundownQueue(Queue);
if (FirstEntry)
{
/* Loop the packets */
CurrentEntry = FirstEntry;
do
{
/* Get the Packet */
Packet = CONTAINING_RECORD(CurrentEntry,
IOP_MINI_COMPLETION_PACKET,
ListEntry);
/* Go to next Entry */
CurrentEntry = CurrentEntry->Flink;
/* Check if it's part of an IRP, or a separate packet */
if (Packet->PacketType == IopCompletionPacketIrp)
{
/* Get the IRP and free it */
Irp = CONTAINING_RECORD(Packet, IRP, Tail.Overlay.ListEntry);
IoFreeIrp(Irp);
}
else
{
/* Use common routine */
IopFreeMiniPacket(Packet);
}
} while (FirstEntry != CurrentEntry);
}
}
/* PUBLIC FUNCTIONS **********************************************************/
/*
* @implemented
*/
NTSTATUS
NTAPI
IoSetIoCompletion(IN PVOID IoCompletion,
IN PVOID KeyContext,
IN PVOID ApcContext,
IN NTSTATUS IoStatus,
IN ULONG_PTR IoStatusInformation,
IN BOOLEAN Quota)
{
PKQUEUE Queue = (PKQUEUE)IoCompletion;
PNPAGED_LOOKASIDE_LIST List;
PKPRCB Prcb = KeGetCurrentPrcb();
PIOP_MINI_COMPLETION_PACKET Packet;
/* Get the P List */
List = (PNPAGED_LOOKASIDE_LIST)Prcb->
PPLookasideList[LookasideCompletionList].P;
/* Try to allocate the Packet */
List->L.TotalAllocates++;
Packet = (PVOID)InterlockedPopEntrySList(&List->L.ListHead);
/* Check if that failed, use the L list if it did */
if (!Packet)
{
/* Let the balancer know */
List->L.AllocateMisses++;
/* Get L List */
List = (PNPAGED_LOOKASIDE_LIST)Prcb->
PPLookasideList[LookasideCompletionList].L;
/* Try to allocate the Packet */
List->L.TotalAllocates++;
Packet = (PVOID)InterlockedPopEntrySList(&List->L.ListHead);
}
/* Still failed, use pool */
if (!Packet)
{
/* Let the balancer know */
List->L.AllocateMisses++;
/* Allocate from Nonpaged Pool */
Packet = ExAllocatePoolWithTag(NonPagedPool, sizeof(*Packet), IOC_TAG);
}
/* Make sure we have one by now... */
if (Packet)
{
/* Set up the Packet */
Packet->PacketType = IopCompletionPacketMini;
Packet->KeyContext = KeyContext;
Packet->ApcContext = ApcContext;
Packet->IoStatus = IoStatus;
Packet->IoStatusInformation = IoStatusInformation;
/* Insert the Queue */
KeInsertQueue(Queue, &Packet->ListEntry);
}
else
{
/* Out of memory, fail */
return STATUS_INSUFFICIENT_RESOURCES;
}
/* Return Success */
return STATUS_SUCCESS;
}
/*
* @implemented
*/
NTSTATUS
NTAPI
IoSetCompletionRoutineEx(IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp,
IN PIO_COMPLETION_ROUTINE CompletionRoutine,
IN PVOID Context,
IN BOOLEAN InvokeOnSuccess,
IN BOOLEAN InvokeOnError,
IN BOOLEAN InvokeOnCancel)
{
PIO_UNLOAD_SAFE_COMPLETION_CONTEXT UnloadContext;
/* Allocate the context */
UnloadContext = ExAllocatePoolWithTag(NonPagedPool,
sizeof(*UnloadContext),
'sUoI');
if (!UnloadContext) return STATUS_INSUFFICIENT_RESOURCES;
/* Set up the context */
UnloadContext->DeviceObject = DeviceObject;
UnloadContext->Context = Context;
UnloadContext->CompletionRoutine = CompletionRoutine;
/* Now set the completion routine */
IoSetCompletionRoutine(Irp,
IopUnloadSafeCompletion,
UnloadContext,
InvokeOnSuccess,
InvokeOnError,
InvokeOnCancel);
return STATUS_SUCCESS;
}
NTSTATUS
NTAPI
NtCreateIoCompletion(OUT PHANDLE IoCompletionHandle,
IN ACCESS_MASK DesiredAccess,
IN POBJECT_ATTRIBUTES ObjectAttributes,
IN ULONG NumberOfConcurrentThreads)
{
PKQUEUE Queue;
HANDLE hIoCompletionHandle;
KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
NTSTATUS Status;
PAGED_CODE();
/* Check if this was a user-mode call */
if (PreviousMode != KernelMode)
{
/* Wrap probing in SEH */
_SEH2_TRY
{
/* Probe the handle */
ProbeForWriteHandle(IoCompletionHandle);
}
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
{
/* Return the exception code */
_SEH2_YIELD(return _SEH2_GetExceptionCode());
}
_SEH2_END;
}
/* Create the Object */
Status = ObCreateObject(PreviousMode,
IoCompletionType,
ObjectAttributes,
PreviousMode,
NULL,
sizeof(KQUEUE),
0,
0,
(PVOID*)&Queue);
if (NT_SUCCESS(Status))
{
/* Initialize the Queue */
KeInitializeQueue(Queue, NumberOfConcurrentThreads);
/* Insert it */
Status = ObInsertObject(Queue,
NULL,
DesiredAccess,
0,
NULL,
&hIoCompletionHandle);
if (NT_SUCCESS(Status))
{
/* Protect writing the handle in SEH */
_SEH2_TRY
{
/* Write the handle back */
*IoCompletionHandle = hIoCompletionHandle;
}
_SEH2_EXCEPT(ExSystemExceptionFilter())
{
/* Get the exception code */
Status = _SEH2_GetExceptionCode();
}
_SEH2_END;
}
}
/* Return Status */
return Status;
}
NTSTATUS
NTAPI
NtOpenIoCompletion(OUT PHANDLE IoCompletionHandle,
IN ACCESS_MASK DesiredAccess,
IN POBJECT_ATTRIBUTES ObjectAttributes)
{
KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
HANDLE hIoCompletionHandle;
NTSTATUS Status;
PAGED_CODE();
/* Check if this was a user-mode call */
if (PreviousMode != KernelMode)
{
/* Wrap probing in SEH */
_SEH2_TRY
{
/* Probe the handle */
ProbeForWriteHandle(IoCompletionHandle);
}
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
{
/* Return the exception code */
_SEH2_YIELD(return _SEH2_GetExceptionCode());
}
_SEH2_END;
}
/* Open the Object */
Status = ObOpenObjectByName(ObjectAttributes,
IoCompletionType,
PreviousMode,
NULL,
DesiredAccess,
NULL,
&hIoCompletionHandle);
if (NT_SUCCESS(Status))
{
/* Protect writing the handle in SEH */
_SEH2_TRY
{
/* Write the handle back */
*IoCompletionHandle = hIoCompletionHandle;
}
_SEH2_EXCEPT(ExSystemExceptionFilter())
{
/* Get the exception code */
Status = _SEH2_GetExceptionCode();
}
_SEH2_END;
}
/* Return Status */
return Status;
}
NTSTATUS
NTAPI
NtQueryIoCompletion(IN HANDLE IoCompletionHandle,
IN IO_COMPLETION_INFORMATION_CLASS IoCompletionInformationClass,
OUT PVOID IoCompletionInformation,
IN ULONG IoCompletionInformationLength,
OUT PULONG ResultLength OPTIONAL)
{
PKQUEUE Queue;
KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
NTSTATUS Status;
PAGED_CODE();
/* Check buffers and parameters */
Status = DefaultQueryInfoBufferCheck(IoCompletionInformationClass,
IoCompletionInfoClass,
sizeof(IoCompletionInfoClass) /
sizeof(IoCompletionInfoClass[0]),
ICIF_PROBE_READ_WRITE,
IoCompletionInformation,
IoCompletionInformationLength,
ResultLength,
NULL,
PreviousMode);
if (!NT_SUCCESS(Status)) return Status;
/* Get the Object */
Status = ObReferenceObjectByHandle(IoCompletionHandle,
IO_COMPLETION_QUERY_STATE,
IoCompletionType,
PreviousMode,
(PVOID*)&Queue,
NULL);
if (NT_SUCCESS(Status))
{
/* Protect write in SEH */
_SEH2_TRY
{
/* Return Info */
((PIO_COMPLETION_BASIC_INFORMATION)IoCompletionInformation)->
Depth = KeReadStateQueue(Queue);
/* Return Result Length if needed */
if (ResultLength)
{
*ResultLength = sizeof(IO_COMPLETION_BASIC_INFORMATION);
}
}
_SEH2_EXCEPT(ExSystemExceptionFilter())
{
/* Get exception code */
Status = _SEH2_GetExceptionCode();
}
_SEH2_END;
/* Dereference the queue */
ObDereferenceObject(Queue);
}
/* Return Status */
return Status;
}
NTSTATUS
NTAPI
NtRemoveIoCompletion(IN HANDLE IoCompletionHandle,
OUT PVOID *KeyContext,
OUT PVOID *ApcContext,
OUT PIO_STATUS_BLOCK IoStatusBlock,
IN PLARGE_INTEGER Timeout OPTIONAL)
{
LARGE_INTEGER SafeTimeout;
PKQUEUE Queue;
PIOP_MINI_COMPLETION_PACKET Packet;
PLIST_ENTRY ListEntry;
KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
NTSTATUS Status;
PIRP Irp;
PVOID Apc, Key;
IO_STATUS_BLOCK IoStatus;
PAGED_CODE();
/* Check if the call was from user mode */
if (PreviousMode != KernelMode)
{
/* Protect probes in SEH */
_SEH2_TRY
{
/* Probe the pointers */
ProbeForWritePointer(KeyContext);
ProbeForWritePointer(ApcContext);
/* Probe the I/O Status Block */
ProbeForWriteIoStatusBlock(IoStatusBlock);
if (Timeout)
{
/* Probe and capture the timeout */
SafeTimeout = ProbeForReadLargeInteger(Timeout);
Timeout = &SafeTimeout;
}
}
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
{
/* Return the exception code */
_SEH2_YIELD(return _SEH2_GetExceptionCode());
}
_SEH2_END;
}
/* Open the Object */
Status = ObReferenceObjectByHandle(IoCompletionHandle,
IO_COMPLETION_MODIFY_STATE,
IoCompletionType,
PreviousMode,
(PVOID*)&Queue,
NULL);
if (NT_SUCCESS(Status))
{
/* Remove queue */
ListEntry = KeRemoveQueue(Queue, PreviousMode, Timeout);
/* If we got a timeout or user_apc back, return the status */
if (((NTSTATUS)(ULONG_PTR)ListEntry == STATUS_TIMEOUT) ||
((NTSTATUS)(ULONG_PTR)ListEntry == STATUS_USER_APC))
{
/* Set this as the status */
Status = (NTSTATUS)(ULONG_PTR)ListEntry;
}
else
{
/* Get the Packet Data */
Packet = CONTAINING_RECORD(ListEntry,
IOP_MINI_COMPLETION_PACKET,
ListEntry);
/* Check if this is piggybacked on an IRP */
if (Packet->PacketType == IopCompletionPacketIrp)
{
/* Get the IRP */
Irp = CONTAINING_RECORD(ListEntry,
IRP,
Tail.Overlay.ListEntry);
/* Save values */
Key = Irp->Tail.CompletionKey;
Apc = Irp->Overlay.AsynchronousParameters.UserApcContext;
IoStatus = Irp->IoStatus;
/* Free the IRP */
IoFreeIrp(Irp);
}
else
{
/* Save values */
Key = Packet->KeyContext;
Apc = Packet->ApcContext;
IoStatus.Status = Packet->IoStatus;
IoStatus.Information = Packet->IoStatusInformation;
/* Free the packet */
IopFreeMiniPacket(Packet);
}
/* Enter SEH to write back the values */
_SEH2_TRY
{
/* Write the values to caller */
*ApcContext = Apc;
*KeyContext = Key;
*IoStatusBlock = IoStatus;
}
_SEH2_EXCEPT(ExSystemExceptionFilter())
{
/* Get the exception code */
Status = _SEH2_GetExceptionCode();
}
_SEH2_END;
}
/* Dereference the Object */
ObDereferenceObject(Queue);
}
/* Return status */
return Status;
}
NTSTATUS
NTAPI
NtSetIoCompletion(IN HANDLE IoCompletionPortHandle,
IN PVOID CompletionKey,
IN PVOID CompletionContext,
IN NTSTATUS CompletionStatus,
IN ULONG CompletionInformation)
{
NTSTATUS Status;
PKQUEUE Queue;
PAGED_CODE();
/* Get the Object */
Status = ObReferenceObjectByHandle(IoCompletionPortHandle,
IO_COMPLETION_MODIFY_STATE,
IoCompletionType,
ExGetPreviousMode(),
(PVOID*)&Queue,
NULL);
if (NT_SUCCESS(Status))
{
/* Set the Completion */
Status = IoSetIoCompletion(Queue,
CompletionKey,
CompletionContext,
CompletionStatus,
CompletionInformation,
TRUE);
/* Dereference the object */
ObDereferenceObject(Queue);
}
/* Return status */
return Status;
}