From c606fd79b1c879bad37e49a69e71986dfbc2052e Mon Sep 17 00:00:00 2001 From: Ahmed Arif Date: Wed, 29 Jul 2026 20:54:44 +0200 Subject: [PATCH] [NTOS:FSRTL] Respect byte-range lock ownership (#9342) FsRtlCheckLockForReadAccess() accepts access through an exclusive byte-range lock when the IRP key matches, even if another process owns the lock. FsRtlFastUnlockAll() likewise removes exclusive locks without checking the Process argument. Require both the key and requestor process for exclusive-read access, and skip exclusive locks owned by other processes in FsRtlFastUnlockAll(). This matches the ownership checks already used by FsRtlFastCheckLockForRead() and FsRtlFastUnlockAllByKey(). Reference: - https://learn.microsoft.com/en-us/windows-hardware/drivers/ddi/ntifs/nf-ntifs-_fsrtl_advanced_fcb_header-fsrtlfastchecklockforread --- ntoskrnl/fsrtl/filelock.c | 6 +++++- sdk/include/xdk/fsrtltypes.h | 2 +- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/ntoskrnl/fsrtl/filelock.c b/ntoskrnl/fsrtl/filelock.c index 6d7a9d8afc8..c4d4394483d 100644 --- a/ntoskrnl/fsrtl/filelock.c +++ b/ntoskrnl/fsrtl/filelock.c @@ -676,6 +676,7 @@ FsRtlCheckLockForReadAccess(IN PFILE_LOCK FileLock, PIO_STACK_LOCATION IoStack = IoGetCurrentIrpStackLocation(Irp); COMBINED_LOCK_ELEMENT ToFind; PCOMBINED_LOCK_ELEMENT Found; + PEPROCESS Process = IoGetRequestorProcess(Irp); DPRINT("CheckLockForReadAccess(%wZ, Offset %08x%08x, Length %x)\n", &IoStack->FileObject->FileName, IoStack->Parameters.Read.ByteOffset.HighPart, @@ -697,7 +698,8 @@ FsRtlCheckLockForReadAccess(IN PFILE_LOCK FileLock, return TRUE; } Result = !Found->Exclusive.FileLock.ExclusiveLock || - IoStack->Parameters.Read.Key == Found->Exclusive.FileLock.Key; + (IoStack->Parameters.Read.Key == Found->Exclusive.FileLock.Key && + Process == Found->Exclusive.FileLock.ProcessId); DPRINT("CheckLockForReadAccess(%wZ) => %s\n", &IoStack->FileObject->FileName, Result ? "TRUE" : "FALSE"); return Result; } @@ -1060,6 +1062,8 @@ FsRtlFastUnlockAll(IN PFILE_LOCK FileLock, Entry = RtlEnumerateGenericTable(&InternalInfo->RangeTable, FALSE)) { LARGE_INTEGER Length; + if (Entry->Exclusive.FileLock.ProcessId != Process) + continue; // We'll take the first one to be the list head, and free the others first... Length.QuadPart = Entry->Exclusive.FileLock.EndingByte.QuadPart - diff --git a/sdk/include/xdk/fsrtltypes.h b/sdk/include/xdk/fsrtltypes.h index b8e8fe9f02d..291079cdab0 100644 --- a/sdk/include/xdk/fsrtltypes.h +++ b/sdk/include/xdk/fsrtltypes.h @@ -190,7 +190,7 @@ typedef struct _FILE_LOCK_INFO { BOOLEAN ExclusiveLock; ULONG Key; PFILE_OBJECT FileObject; - PVOID ProcessId; + PVOID ProcessId; /**< This field actually holds a pointer to EPROCESS structure */ LARGE_INTEGER EndingByte; } FILE_LOCK_INFO, *PFILE_LOCK_INFO;