https://git.reactos.org/?p=reactos.git;a=commitdiff;h=abe89d7cdecd173628f6fa67819547887a085664

commit abe89d7cdecd173628f6fa67819547887a085664
Author:     George Bișoc <[email protected]>
AuthorDate: Sat Jan 1 20:23:28 2022 +0100
Commit:     George Bișoc <[email protected]>
CommitDate: Tue Jan 11 10:11:08 2022 +0100

    [NTOS:FSRTL] Assign the buffer length to ThisBufferLength field
    
    This fixes an issue where ReactOS would assert on QuotaUsage == 0 as the 
process was still taking up quotas during a quota block de-reference with root 
cause of ThisBufferLength member field being 0 which made process quota 
charging/returning flow unbalanced.
    In addition to that, on FsRtlCancelNotify routine API all we must ensure 
that if PsChargePoolQuota or ExAllocatePoolWithTag function fails we have to 
handle the raised exceptions accordingly and return the charged quota back (if 
we actually charged quotas that is). With said, wrap that part of code with SEH.
    
    === DOCUMENTATION REMARKS ===
    The cause of the assert is due to the fact ThisBufferLength was being 
handled wrongly ever since, until this commit. When FsRtl of the Executive has 
to filter reported changes (with logic algorithm implemented in 
FsRtlNotifyFilterReportChange function), the said function will charge the 
quota of a given process
    with an amount that is represented as the buffer length whose size is 
expressed in bytes. This length buffer is preserved in a local variable called 
NumberOfBytes, which is initialized from BufferLength member field of 
notification structure or from the length from stack parameters pointed from an 
IRP.
    
    As it currently stands, the code is implemented in such a way that 
FsRtlNotifyFilterReportChange will charge quotas to a process but it doesn't 
assign the buffer length to ThisBufferLength. On the first glimpse 
ThisBufferLength and BufferLength are very similar members that serve exact 
same purpose but in reality there's a subtle distinction between the two.
    
    BufferLength is a member whose length size is given by FSDs (filesystem 
drivers) during a notification dispatching. Whenever FsRtl receives the 
notification structure packed with data from the filesystem, the length pointed 
by BufferLength gets passed to ThisBufferLength and from now on the kernel has 
to use this member for the whole time of its task to accomplish
    whatever request it's been given by the filesystem. In other words, 
BufferLength is strictly used only to pass length size data to the kernel by 
initializing ThisBufferLength based on that length and unequivocally the kernel 
uses this member field. What we're doing is that ThisBufferLength never 
receives the length from BufferLength therefore whenever FsRtl component
    has to return quotas back it'll return an amount of 0 (which means no 
amount to return) and that's a bug in the kernel.
---
 ntoskrnl/fsrtl/notify.c | 49 +++++++++++++++++++++++++++++++++++++++----------
 1 file changed, 39 insertions(+), 10 deletions(-)

diff --git a/ntoskrnl/fsrtl/notify.c b/ntoskrnl/fsrtl/notify.c
index e7b73771232..f69475c9984 100644
--- a/ntoskrnl/fsrtl/notify.c
+++ b/ntoskrnl/fsrtl/notify.c
@@ -85,6 +85,7 @@ FsRtlCancelNotify(IN PDEVICE_OBJECT DeviceObject,
     PIO_STACK_LOCATION Stack;
     PNOTIFY_CHANGE NotifyChange;
     PREAL_NOTIFY_SYNC RealNotifySync;
+    BOOLEAN PoolQuotaCharged;
     PSECURITY_SUBJECT_CONTEXT _SEH2_VOLATILE SubjectContext = NULL;
 
     /* Get the NOTIFY_CHANGE struct and reset it */
@@ -165,15 +166,38 @@ FsRtlCancelNotify(IN PDEVICE_OBJECT DeviceObject,
                /* If we have a buffer length, but no buffer then allocate one 
*/
                if (Buffer == NULL)
                {
-                   PsChargePoolQuota(NotifyChange->OwningProcess, PagedPool, 
BufferLength);
-                   Buffer = ExAllocatePoolWithTag(PagedPool | 
POOL_RAISE_IF_ALLOCATION_FAILURE, BufferLength, TAG_FS_NOTIFICATIONS);
-                   NotifyChange->AllocatedBuffer = Buffer;
-               }
+                   /* Assume we haven't charged quotas */
+                   PoolQuotaCharged = FALSE;
 
-               /* Copy data in that buffer */
-               RtlCopyMemory(Buffer, NotifyChange->Buffer, 
NotifyChange->DataLength);
-               NotifyChange->ThisBufferLength = BufferLength;
-               NotifyChange->Buffer = Buffer;
+                   _SEH2_TRY
+                   {
+                       /* Charge quotas */
+                       PsChargePoolQuota(NotifyChange->OwningProcess, 
PagedPool, BufferLength);
+                       PoolQuotaCharged = TRUE;
+
+                       /* Allocate buffer */
+                       Buffer = ExAllocatePoolWithTag(PagedPool | 
POOL_RAISE_IF_ALLOCATION_FAILURE, BufferLength, TAG_FS_NOTIFICATIONS);
+                       NotifyChange->AllocatedBuffer = Buffer;
+
+                       /* Copy data in that buffer */
+                       RtlCopyMemory(Buffer, NotifyChange->Buffer, 
NotifyChange->DataLength);
+                       NotifyChange->ThisBufferLength = BufferLength;
+                       NotifyChange->Buffer = Buffer;
+                   }
+                   _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
+                   {
+                       /* Something went wrong, have we charged quotas? */
+                       if (PoolQuotaCharged)
+                       {
+                           /* We did, return quotas */
+                           
PsReturnProcessPagedPoolQuota(NotifyChange->OwningProcess, BufferLength);
+                       }
+
+                       /* And notify immediately */
+                       NotifyChange->Flags |= NOTIFY_IMMEDIATELY;
+                   }
+                   _SEH2_END;
+               }
            }
 
            /* If we have to notify immediately, ensure that any buffer is 0-ed 
out */
@@ -1322,10 +1346,15 @@ FsRtlNotifyFilterReportChange(IN PNOTIFY_SYNC 
NotifySync,
                         /* If we couldn't find one, then allocate one */
                         if (NotifyChange->Buffer == NULL)
                         {
+                            /* Assign the length buffer */
+                            NotifyChange->ThisBufferLength = NumberOfBytes;
+
+                            /* Assume we have not charged quotas */
                             PoolQuotaCharged = FALSE;
                             _SEH2_TRY
                             {
-                                PsChargePoolQuota(NotifyChange->OwningProcess, 
PagedPool, NumberOfBytes);
+                                /* And charge quotas */
+                                PsChargePoolQuota(NotifyChange->OwningProcess, 
PagedPool, NotifyChange->ThisBufferLength);
                                 PoolQuotaCharged = TRUE;
                                 OutputBuffer = ExAllocatePoolWithTag(PagedPool 
| POOL_RAISE_IF_ALLOCATION_FAILURE,
                                                                      
NumberOfBytes, TAG_FS_NOTIFICATIONS);
@@ -1337,7 +1366,7 @@ FsRtlNotifyFilterReportChange(IN PNOTIFY_SYNC NotifySync,
                             {
                                 if (PoolQuotaCharged)
                                 {
-                                    
PsReturnProcessPagedPoolQuota(NotifyChange->OwningProcess, NumberOfBytes);
+                                    
PsReturnProcessPagedPoolQuota(NotifyChange->OwningProcess, 
NotifyChange->ThisBufferLength);
                                 }
                                 NotifyChange->Flags |= NOTIFY_IMMEDIATELY;
                             }

Reply via email to