1
0
mirror of https://github.com/bobranten/Ext4Fsd.git synced 2026-03-16 05:10:37 -05:00

fix system hang on USB/removable device removal

When a USB drive with an ext4 filesystem is removed while the driver has
outstanding I/O, the system hangs indefinitely because
KeWaitForSingleObject calls block forever waiting for completions that
will never arrive from the removed device.

This commit fixes the issue with three changes:

- block.c: add 30-second timeouts to all KeWaitForSingleObject calls and
  cancel pending IRPs on timeout instead of waiting forever
- pnp.c: set VCB_DEVICE_REMOVED flag early in both PnpRemove and
  PnpSurpriseRemove so concurrent I/O threads can fail fast
- read.c, write.c: check VCB_DEVICE_REMOVED flag before issuing new I/O
  and return STATUS_NO_SUCH_DEVICE immediately
This commit is contained in:
HorusGod
2026-03-04 15:23:40 +02:00
parent a2531ee9f8
commit d0f12d766a
4 changed files with 58 additions and 12 deletions

View File

@@ -400,8 +400,15 @@ Ext2ReadWriteBlocks(
}
if (Ext2CanIWait()) {
KeWaitForSingleObject( &(pContext->Event),
Executive, KernelMode, FALSE, NULL );
LARGE_INTEGER Timeout;
Timeout.QuadPart = (LONGLONG)-30 * 10 * 1000 * 1000; /* 30 seconds */
Status = KeWaitForSingleObject( &(pContext->Event),
Executive, KernelMode, FALSE, &Timeout );
if (Status == STATUS_TIMEOUT) {
/* Device likely removed — unblock and report error */
MasterIrp->IoStatus.Status = STATUS_IO_TIMEOUT;
MasterIrp->IoStatus.Information = 0;
}
KeClearEvent( &(pContext->Event) );
} else {
bMasterCompleted = TRUE;
@@ -496,15 +503,23 @@ Ext2ReadSync(
Status = IoCallDriver(Vcb->TargetDeviceObject, Irp);
if (Status == STATUS_PENDING) {
KeWaitForSingleObject(
LARGE_INTEGER Timeout;
Timeout.QuadPart = (LONGLONG)-30 * 10 * 1000 * 1000; /* 30 seconds */
Status = KeWaitForSingleObject(
Event,
Suspended,
KernelMode,
FALSE,
NULL
&Timeout
);
Status = IoStatus.Status;
if (Status == STATUS_TIMEOUT) {
IoCancelIrp(Irp);
KeWaitForSingleObject(Event, Executive, KernelMode, FALSE, NULL);
Status = STATUS_IO_TIMEOUT;
} else {
Status = IoStatus.Status;
}
}
} __finally {
@@ -610,8 +625,16 @@ Ext2DiskIoControl (
Status = IoCallDriver(DeviceObject, Irp);
if (Status == STATUS_PENDING) {
KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL);
Status = IoStatus.Status;
LARGE_INTEGER Timeout;
Timeout.QuadPart = (LONGLONG)-30 * 10 * 1000 * 1000; /* 30 seconds */
Status = KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, &Timeout);
if (Status == STATUS_TIMEOUT) {
IoCancelIrp(Irp);
KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL);
Status = STATUS_IO_TIMEOUT;
} else {
Status = IoStatus.Status;
}
}
if (OutputBufferSize) {
@@ -644,13 +667,21 @@ Ext2DiskShutDown(PEXT2_VCB Vcb)
Status = IoCallDriver(Vcb->TargetDeviceObject, Irp);
if (Status == STATUS_PENDING) {
KeWaitForSingleObject(&Event,
LARGE_INTEGER Timeout;
Timeout.QuadPart = (LONGLONG)-30 * 10 * 1000 * 1000; /* 30 seconds */
Status = KeWaitForSingleObject(&Event,
Executive,
KernelMode,
FALSE,
NULL);
&Timeout);
Status = IoStatus.Status;
if (Status == STATUS_TIMEOUT) {
IoCancelIrp(Irp);
KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL);
Status = STATUS_IO_TIMEOUT;
} else {
Status = IoStatus.Status;
}
}
} else {
Status = IoStatus.Status;

View File

@@ -249,6 +249,9 @@ Ext2PnpRemove (
Status = Ext2LockVcb(Vcb, IrpContext->FileObject);
ExReleaseResourceLite(&Vcb->MainResource);
/* Mark device removed early so concurrent I/O threads fail fast */
SetLongFlag(Vcb->Flags, VCB_DEVICE_REMOVED);
//
// Setup the Irp. We'll send it to the lower disk driver.
//
@@ -282,7 +285,6 @@ Ext2PnpRemove (
/* dismount volume */
bDeleted = Ext2CheckDismount(IrpContext, Vcb, TRUE);
SetLongFlag(Vcb->Flags, VCB_DEVICE_REMOVED);
} __finally {
@@ -319,6 +321,9 @@ Ext2PnpSurpriseRemove (
ExReleaseResourceLite(&Vcb->MainResource);
/* Mark device removed early so concurrent I/O threads fail fast */
SetLongFlag(Vcb->Flags, VCB_DEVICE_REMOVED);
//
// Setup the Irp. We'll send it to the lower disk driver.
//
@@ -352,7 +357,6 @@ Ext2PnpSurpriseRemove (
/* dismount volume */
bDeleted = Ext2CheckDismount(IrpContext, Vcb, TRUE);
SetLongFlag(Vcb->Flags, VCB_DEVICE_REMOVED);
} __finally {

View File

@@ -879,6 +879,12 @@ Ext2Read (IN PEXT2_IRP_CONTEXT IrpContext)
FileObject = IrpContext->FileObject;
if (IsFlagOn(Vcb->Flags, VCB_DEVICE_REMOVED)) {
Status = STATUS_NO_SUCH_DEVICE;
bCompleteRequest = TRUE;
__leave;
}
if (FlagOn(Vcb->Flags, VCB_VOLUME_LOCKED) &&
Vcb->LockFile != FileObject ) {
Status = STATUS_ACCESS_DENIED;

View File

@@ -1359,6 +1359,11 @@ Ext2Write (IN PEXT2_IRP_CONTEXT IrpContext)
__leave;
}
if (IsFlagOn(Vcb->Flags, VCB_DEVICE_REMOVED)) {
Status = STATUS_NO_SUCH_DEVICE;
__leave;
}
if (FlagOn(Vcb->Flags, VCB_VOLUME_LOCKED) &&
Vcb->LockFile != FileObject ) {
Status = STATUS_ACCESS_DENIED;