Author: fireball Date: Fri Oct 15 13:16:48 2010 New Revision: 49154
URL: http://svn.reactos.org/svn/reactos?rev=49154&view=rev Log: [HEAP] - Implement parameters validation ("DebugHeap") in all used RTL heap APIs. Winetests failures down to 4.
Modified: trunk/reactos/lib/rtl/heap.h trunk/reactos/lib/rtl/heap_rewrite.c trunk/reactos/lib/rtl/heapdbg.c
Modified: trunk/reactos/lib/rtl/heap.h URL: http://svn.reactos.org/svn/reactos/trunk/reactos/lib/rtl/heap.h?rev=49154&am... ============================================================================== --- trunk/reactos/lib/rtl/heap.h [iso-8859-1] (original) +++ trunk/reactos/lib/rtl/heap.h [iso-8859-1] Fri Oct 15 13:16:48 2010 @@ -322,6 +322,15 @@ PHEAP_ENTRY_EXTRA NTAPI RtlpGetExtraStuffPointer(PHEAP_ENTRY HeapEntry);
+BOOLEAN NTAPI +RtlpValidateHeap(PHEAP Heap, BOOLEAN ForceValidation); + +BOOLEAN NTAPI +RtlpValidateHeapEntry(PHEAP Heap, PHEAP_ENTRY HeapEntry); + +BOOLEAN NTAPI +RtlpValidateHeapHeaders(PHEAP Heap, BOOLEAN Recalculate); + /* heapdbg.c */ HANDLE NTAPI RtlDebugCreateHeap(ULONG Flags, @@ -331,7 +340,7 @@ PVOID Lock, PRTL_HEAP_PARAMETERS Parameters);
-HANDLE NTAPI +BOOLEAN NTAPI RtlDebugDestroyHeap(HANDLE HeapPtr);
PVOID NTAPI
Modified: trunk/reactos/lib/rtl/heap_rewrite.c URL: http://svn.reactos.org/svn/reactos/trunk/reactos/lib/rtl/heap_rewrite.c?rev=... ============================================================================== --- trunk/reactos/lib/rtl/heap_rewrite.c [iso-8859-1] (original) +++ trunk/reactos/lib/rtl/heap_rewrite.c [iso-8859-1] Fri Oct 15 13:16:48 2010 @@ -1476,23 +1476,9 @@ TotalSize = ROUND_UP(CommitSize, 16 * PAGE_SIZE); }
- if (RtlpGetMode() == UserMode) - { - /* TODO: Here should be a call to special "Debug" heap, which does parameters validation, - however we're just going to simulate setting correct flags here */ - if (Flags & (HEAP_VALIDATE_ALL_ENABLED | - HEAP_VALIDATE_PARAMETERS_ENABLED | - HEAP_CAPTURE_STACK_BACKTRACES | - HEAP_FLAG_PAGE_ALLOCS | - HEAP_CREATE_ENABLE_TRACING) && - !(Flags & HEAP_SKIP_VALIDATION_CHECKS)) - { - // RtlDebugCreateHeap(Flags, Addr, TotalSize, CommitSize, Lock, Parameters); - Flags |= HEAP_SKIP_VALIDATION_CHECKS | - HEAP_TAIL_CHECKING_ENABLED | - HEAP_FREE_CHECKING_ENABLED; - } - } + /* Call special heap */ + if (RtlpHeapIsSpecial(Flags)) + return RtlDebugCreateHeap(Flags, Addr, TotalSize, CommitSize, Lock, Parameters);
/* Calculate header size */ HeaderSize = sizeof(HEAP); @@ -1736,7 +1722,11 @@
if (!HeapPtr) return NULL;
- // TODO: Check for special heap + /* Call special heap */ + if (RtlpHeapIsSpecial(Heap->Flags)) + { + if (!RtlDebugDestroyHeap(Heap)) return HeapPtr; + }
/* Check for a process heap */ if (RtlpGetMode() == UserMode && @@ -2103,6 +2093,10 @@ /* Force flags */ Flags |= Heap->ForceFlags;
+ /* Call special heap */ + if (RtlpHeapIsSpecial(Flags)) + return RtlDebugAllocateHeap(Heap, Flags, Size); + /* Check for the maximum size */ if (Size >= 0x80000000) { @@ -2111,14 +2105,10 @@ return NULL; }
- if (Flags & ( - HEAP_VALIDATE_ALL_ENABLED | - HEAP_VALIDATE_PARAMETERS_ENABLED | - HEAP_FLAG_PAGE_ALLOCS | - HEAP_CREATE_ENABLE_TRACING | - HEAP_CREATE_ALIGN_16)) - { - DPRINT("HEAP: RtlAllocateHeap is called with unsupported flags %x, ignoring\n", Flags); + if (Flags & (HEAP_CREATE_ENABLE_TRACING | + HEAP_CREATE_ALIGN_16)) + { + DPRINT1("HEAP: RtlAllocateHeap is called with unsupported flags %x, ignoring\n", Flags); }
//DPRINT("RtlAllocateHeap(%p %x %x)\n", Heap, Flags, Size); @@ -2328,6 +2318,10 @@ /* Get pointer to the heap and force flags */ Heap = (PHEAP)HeapPtr; Flags |= Heap->ForceFlags; + + /* Call special heap */ + if (RtlpHeapIsSpecial(Flags)) + return RtlDebugFreeHeap(Heap, Flags, Ptr);
/* Lock if necessary */ if (!(Flags & HEAP_NO_SERIALIZE)) @@ -2733,7 +2727,9 @@ /* Force heap flags */ Flags |= Heap->ForceFlags;
- // Check for special heap + /* Call special heap */ + if (RtlpHeapIsSpecial(Flags)) + return RtlDebugReAllocateHeap(Heap, Flags, Ptr, Size);
/* Make sure size is valid */ if (Size >= 0x80000000) @@ -3222,7 +3218,9 @@ /* Force flags */ Flags |= Heap->Flags;
- // FIXME Special heap + /* Call special heap */ + if (RtlpHeapIsSpecial(Flags)) + return RtlDebugSizeHeap(Heap, Flags, Ptr);
/* Get the heap entry pointer */ HeapEntry = (PHEAP_ENTRY)Ptr - 1; @@ -3825,6 +3823,10 @@ /* Force flags */ Flags |= Heap->Flags;
+ /* Call special heap */ + if (RtlpHeapIsSpecial(Flags)) + return RtlDebugSetUserValueHeap(Heap, Flags, BaseAddress, UserValue); + /* Lock if it's lockable */ if (!(Heap->Flags & HEAP_NO_SERIALIZE)) { @@ -3880,6 +3882,10 @@ /* Force flags */ Flags |= Heap->Flags;
+ /* Call special heap */ + if (RtlpHeapIsSpecial(Flags)) + return RtlDebugSetUserFlagsHeap(Heap, Flags, BaseAddress, UserFlagsReset, UserFlagsSet); + /* Lock if it's lockable */ if (!(Heap->Flags & HEAP_NO_SERIALIZE)) { @@ -3932,6 +3938,10 @@ /* Force flags */ Flags |= Heap->Flags;
+ /* Call special heap */ + if (RtlpHeapIsSpecial(Flags)) + return RtlDebugGetUserInfoHeap(Heap, Flags, BaseAddress, UserValue, UserFlags); + /* Lock if it's lockable */ if (!(Heap->Flags & HEAP_NO_SERIALIZE)) {
Modified: trunk/reactos/lib/rtl/heapdbg.c URL: http://svn.reactos.org/svn/reactos/trunk/reactos/lib/rtl/heapdbg.c?rev=49154... ============================================================================== --- trunk/reactos/lib/rtl/heapdbg.c [iso-8859-1] (original) +++ trunk/reactos/lib/rtl/heapdbg.c [iso-8859-1] Fri Oct 15 13:16:48 2010 @@ -25,18 +25,111 @@ HANDLE NTAPI RtlDebugCreateHeap(ULONG Flags, PVOID Addr, - SIZE_T TotalSize, + SIZE_T ReserveSize, SIZE_T CommitSize, PVOID Lock, PRTL_HEAP_PARAMETERS Parameters) { - return NULL; -} - -HANDLE NTAPI + MEMORY_BASIC_INFORMATION MemoryInfo; + NTSTATUS Status; + PHEAP Heap; + + /* Validate parameters */ + if (ReserveSize <= HEAP_ENTRY_SIZE) + { + DPRINT1("HEAP: Incorrect ReserveSize %x\n", ReserveSize); + return NULL; + } + + if (ReserveSize < CommitSize) + { + DPRINT1("HEAP: Incorrect CommitSize %x\n", CommitSize); + return NULL; + } + + if (Flags & HEAP_NO_SERIALIZE && Lock) + { + DPRINT1("HEAP: Can't specify Lock routine and have HEAP_NO_SERIALIZE flag set\n"); + return NULL; + } + + /* If the address is specified, check it's virtual memory */ + if (Addr) + { + Status = ZwQueryVirtualMemory(NtCurrentProcess(), + Addr, + MemoryBasicInformation, + &MemoryInfo, + sizeof(MemoryInfo), + NULL); + + if (!NT_SUCCESS(Status)) + { + DPRINT1("HEAP: Specified heap base address %p is invalid, Status 0x%08X\n", Addr, Status); + return NULL; + } + + if (MemoryInfo.BaseAddress != Addr) + { + DPRINT1("HEAP: Specified heap base address %p is not really a base one %p\n", Addr, MemoryInfo.BaseAddress); + return NULL; + } + + if (MemoryInfo.State == MEM_FREE) + { + DPRINT1("HEAP: Specified heap base address %p is free\n", Addr); + return NULL; + } + } + + /* All validation performed, now call the real routine with skip validation check flag */ + Flags |= HEAP_SKIP_VALIDATION_CHECKS | + HEAP_TAIL_CHECKING_ENABLED | + HEAP_FREE_CHECKING_ENABLED; + + Heap = RtlCreateHeap(Flags, Addr, ReserveSize, CommitSize, Lock, Parameters); + if (!Heap) return NULL; + + // FIXME: Capture stack backtrace + + RtlpValidateHeapHeaders(Heap, TRUE); + + return Heap; +} + +BOOLEAN NTAPI RtlDebugDestroyHeap(HANDLE HeapPtr) { - return NULL; + SIZE_T Size; + PHEAP Heap = (PHEAP)HeapPtr; + + if (Heap == RtlGetCurrentPeb()->ProcessHeap) + { + DPRINT1("HEAP: It's forbidden delete process heap!"); + return FALSE; + } + + if (Heap->Signature != HEAP_SIGNATURE) + { + DPRINT1("HEAP: Invalid heap %p signature 0x%x\n", Heap, Heap->Signature); + return FALSE; + } + + if (!RtlpValidateHeap(Heap, FALSE)) return FALSE; + + /* Make heap invalid by zeroing its signature */ + Heap->Signature = 0; + + /* Free validate headers copy if it was existing */ + if (Heap->HeaderValidateCopy) + { + ZwFreeVirtualMemory(NtCurrentProcess(), + &Heap->HeaderValidateCopy, + &Size, + MEM_RELEASE); + } + + return TRUE; }
PVOID NTAPI @@ -44,7 +137,64 @@ ULONG Flags, SIZE_T Size) { - return NULL; + PHEAP Heap = (PHEAP)HeapPtr; + SIZE_T AllocSize = 1; + BOOLEAN HeapLocked = FALSE; + PVOID Result; + + //if (Heap->ForceFlags & HEAP_FLAG_PAGE_ALLOCS) + //return RtlpPageHeapAllocateHeap(HeapPtr, Flags, Size); + + if (Heap->Signature != HEAP_SIGNATURE) + { + DPRINT1("HEAP: Invalid heap %p signature 0x%x\n", Heap, Heap->Signature); + return NULL; + } + + /* Add settable user value flag */ + Flags |= Heap->ForceFlags | HEAP_SETTABLE_USER_VALUE | HEAP_SKIP_VALIDATION_CHECKS; + + /* Calculate size */ + if (Size) AllocSize = Size; + AllocSize = ((AllocSize + Heap->AlignRound) & Heap->AlignMask) + sizeof(HEAP_ENTRY_EXTRA); + + /* Check if size didn't exceed max one */ + if (AllocSize < Size || + AllocSize > Heap->MaximumAllocationSize) + { + DPRINT1("HEAP: Too big allocation size %x (max allowed %x)\n", Size, Heap->MaximumAllocationSize); + return NULL; + } + + /* Lock the heap ourselves */ + if (!(Flags & HEAP_NO_SERIALIZE)) + { + RtlEnterHeapLock(Heap->LockVariable); + HeapLocked = TRUE; + + /* Add no serialize flag so that the main routine won't try to acquire the lock again */ + Flags |= HEAP_NO_SERIALIZE; + } + + /* Validate the heap if necessary */ + RtlpValidateHeap(Heap, FALSE); + + /* Call main routine to do the stuff */ + Result = RtlAllocateHeap(HeapPtr, Flags, Size); + + /* Validate heap headers */ + RtlpValidateHeapHeaders(Heap, TRUE); + + if (Result) + { + if (Heap->Flags & HEAP_VALIDATE_ALL_ENABLED) + RtlpValidateHeap(Heap, FALSE); + } + + /* Release the lock */ + if (HeapLocked) RtlLeaveHeapLock(Heap->LockVariable); + + return Result; }
PVOID NTAPI @@ -53,7 +203,70 @@ PVOID Ptr, SIZE_T Size) { - return NULL; + PHEAP Heap = (PHEAP)HeapPtr; + SIZE_T AllocSize = 1; + BOOLEAN HeapLocked = FALSE; + PVOID Result = NULL; + PHEAP_ENTRY HeapEntry; + + //if (Heap->ForceFlags & HEAP_FLAG_PAGE_ALLOCS) + //return RtlpPageHeapReAllocateHeap(HeapPtr, Flags, Size); + + if (Heap->Signature != HEAP_SIGNATURE) + { + DPRINT1("HEAP: Invalid heap %p signature 0x%x\n", Heap, Heap->Signature); + return NULL; + } + + /* Add settable user value flag */ + Flags |= Heap->ForceFlags | HEAP_SETTABLE_USER_VALUE | HEAP_SKIP_VALIDATION_CHECKS; + + /* Calculate size */ + if (Size) AllocSize = Size; + AllocSize = ((AllocSize + Heap->AlignRound) & Heap->AlignMask) + sizeof(HEAP_ENTRY_EXTRA); + + /* Check if size didn't exceed max one */ + if (AllocSize < Size || + AllocSize > Heap->MaximumAllocationSize) + { + DPRINT1("HEAP: Too big allocation size %x (max allowed %x)\n", Size, Heap->MaximumAllocationSize); + return NULL; + } + + /* Lock the heap ourselves */ + if (!(Flags & HEAP_NO_SERIALIZE)) + { + RtlEnterHeapLock(Heap->LockVariable); + HeapLocked = TRUE; + + /* Add no serialize flag so that the main routine won't try to acquire the lock again */ + Flags |= HEAP_NO_SERIALIZE; + } + + /* Validate the heap if necessary */ + RtlpValidateHeap(Heap, FALSE); + + /* Get the existing heap entry */ + HeapEntry = (PHEAP_ENTRY)Ptr - 1; + + /* Validate it */ + if (RtlpValidateHeapEntry(Heap, HeapEntry)) + { + /* Call main routine to do the stuff */ + Result = RtlReAllocateHeap(HeapPtr, Flags, Ptr, Size); + + if (Result) + { + /* Validate heap headers and then heap itself */ + RtlpValidateHeapHeaders(Heap, TRUE); + RtlpValidateHeap(Heap, FALSE); + } + } + + /* Release the lock */ + if (HeapLocked) RtlLeaveHeapLock(Heap->LockVariable); + + return Result; }
BOOLEAN NTAPI @@ -61,7 +274,54 @@ ULONG Flags, PVOID Ptr) { - return FALSE; + PHEAP Heap = (PHEAP)HeapPtr; + BOOLEAN HeapLocked = FALSE; + PHEAP_ENTRY HeapEntry; + BOOLEAN Result = FALSE; + + //if (Heap->ForceFlags & HEAP_FLAG_PAGE_ALLOCS) + //return RtlpPageHeapFreeHeap(HeapPtr, Flags, Size); + + if (Heap->Signature != HEAP_SIGNATURE) + { + DPRINT1("HEAP: Invalid heap %p signature 0x%x\n", Heap, Heap->Signature); + return FALSE; + } + + /* Add skip validation flag */ + Flags |= Heap->ForceFlags | HEAP_SKIP_VALIDATION_CHECKS; + + /* Lock the heap ourselves */ + if (!(Flags & HEAP_NO_SERIALIZE)) + { + RtlEnterHeapLock(Heap->LockVariable); + HeapLocked = TRUE; + + /* Add no serialize flag so that the main routine won't try to acquire the lock again */ + Flags |= HEAP_NO_SERIALIZE; + } + + /* Validate the heap if necessary */ + RtlpValidateHeap(Heap, FALSE); + + /* Get the existing heap entry */ + HeapEntry = (PHEAP_ENTRY)Ptr - 1; + + /* Validate it */ + if (RtlpValidateHeapEntry(Heap, HeapEntry)) + { + /* If it succeeded - call the main routine */ + Result = RtlFreeHeap(HeapPtr, Flags, Ptr); + + /* Validate heap headers and then heap itself */ + RtlpValidateHeapHeaders(Heap, TRUE); + RtlpValidateHeap(Heap, FALSE); + } + + /* Release the lock */ + if (HeapLocked) RtlLeaveHeapLock(Heap->LockVariable); + + return Result; }
BOOLEAN NTAPI @@ -71,7 +331,50 @@ PVOID *UserValue, PULONG UserFlags) { - return FALSE; + PHEAP Heap = (PHEAP)HeapHandle; + BOOLEAN HeapLocked = FALSE; + PHEAP_ENTRY HeapEntry; + BOOLEAN Result = FALSE; + + //if (Heap->ForceFlags & HEAP_FLAG_PAGE_ALLOCS) + //return RtlpPageHeapGetUserInfoHeap(HeapPtr, Flags, Size); + + if (Heap->Signature != HEAP_SIGNATURE) + { + DPRINT1("HEAP: Invalid heap %p signature 0x%x\n", Heap, Heap->Signature); + return FALSE; + } + + /* Add skip validation flag */ + Flags |= Heap->ForceFlags | HEAP_SKIP_VALIDATION_CHECKS; + + /* Lock the heap ourselves */ + if (!(Flags & HEAP_NO_SERIALIZE)) + { + RtlEnterHeapLock(Heap->LockVariable); + HeapLocked = TRUE; + + /* Add no serialize flag so that the main routine won't try to acquire the lock again */ + Flags |= HEAP_NO_SERIALIZE; + } + + /* Validate the heap if necessary */ + RtlpValidateHeap(Heap, FALSE); + + /* Get the existing heap entry */ + HeapEntry = (PHEAP_ENTRY)BaseAddress - 1; + + /* Validate it */ + if (RtlpValidateHeapEntry(Heap, HeapEntry)) + { + /* If it succeeded - call the main routine */ + Result = RtlGetUserInfoHeap(HeapHandle, Flags, BaseAddress, UserValue, UserFlags); + } + + /* Release the lock */ + if (HeapLocked) RtlLeaveHeapLock(Heap->LockVariable); + + return Result; }
BOOLEAN NTAPI @@ -80,7 +383,53 @@ PVOID BaseAddress, PVOID UserValue) { - return FALSE; + PHEAP Heap = (PHEAP)HeapHandle; + BOOLEAN HeapLocked = FALSE; + PHEAP_ENTRY HeapEntry; + BOOLEAN Result = FALSE; + + //if (Heap->ForceFlags & HEAP_FLAG_PAGE_ALLOCS) + //return RtlpPageHeapSetUserValueHeap(HeapPtr, Flags, Size); + + if (Heap->Signature != HEAP_SIGNATURE) + { + DPRINT1("HEAP: Invalid heap %p signature 0x%x\n", Heap, Heap->Signature); + return FALSE; + } + + /* Add skip validation flag */ + Flags |= Heap->ForceFlags | HEAP_SKIP_VALIDATION_CHECKS; + + /* Lock the heap ourselves */ + if (!(Flags & HEAP_NO_SERIALIZE)) + { + RtlEnterHeapLock(Heap->LockVariable); + HeapLocked = TRUE; + + /* Add no serialize flag so that the main routine won't try to acquire the lock again */ + Flags |= HEAP_NO_SERIALIZE; + } + + /* Validate the heap if necessary */ + RtlpValidateHeap(Heap, FALSE); + + /* Get the existing heap entry */ + HeapEntry = (PHEAP_ENTRY)BaseAddress - 1; + + /* Validate it */ + if (RtlpValidateHeapEntry(Heap, HeapEntry)) + { + /* If it succeeded - call the main routine */ + Result = RtlSetUserValueHeap(HeapHandle, Flags, BaseAddress, UserValue); + + /* Validate the heap */ + RtlpValidateHeap(Heap, FALSE); + } + + /* Release the lock */ + if (HeapLocked) RtlLeaveHeapLock(Heap->LockVariable); + + return Result; }
BOOLEAN @@ -91,7 +440,60 @@ ULONG UserFlagsReset, ULONG UserFlagsSet) { - return FALSE; + PHEAP Heap = (PHEAP)HeapHandle; + BOOLEAN HeapLocked = FALSE; + PHEAP_ENTRY HeapEntry; + BOOLEAN Result = FALSE; + + //if (Heap->ForceFlags & HEAP_FLAG_PAGE_ALLOCS) + //return RtlPageHeapSetUserFlagsHeap(HeapPtr, Flags, BaseAddress, UserFlagsReset, UserFlagsSet); + + /* Check if this heap allows flags to be set at all */ + if (UserFlagsSet & ~HEAP_SETTABLE_USER_FLAGS || + UserFlagsReset & ~HEAP_SETTABLE_USER_FLAGS) + { + return FALSE; + } + + if (Heap->Signature != HEAP_SIGNATURE) + { + DPRINT1("HEAP: Invalid heap %p signature 0x%x\n", Heap, Heap->Signature); + return FALSE; + } + + /* Add skip validation flag */ + Flags |= Heap->ForceFlags | HEAP_SKIP_VALIDATION_CHECKS; + + /* Lock the heap ourselves */ + if (!(Flags & HEAP_NO_SERIALIZE)) + { + RtlEnterHeapLock(Heap->LockVariable); + HeapLocked = TRUE; + + /* Add no serialize flag so that the main routine won't try to acquire the lock again */ + Flags |= HEAP_NO_SERIALIZE; + } + + /* Validate the heap if necessary */ + RtlpValidateHeap(Heap, FALSE); + + /* Get the existing heap entry */ + HeapEntry = (PHEAP_ENTRY)BaseAddress - 1; + + /* Validate it */ + if (RtlpValidateHeapEntry(Heap, HeapEntry)) + { + /* If it succeeded - call the main routine */ + Result = RtlSetUserFlagsHeap(HeapHandle, Flags, BaseAddress, UserFlagsReset, UserFlagsSet); + + /* Validate the heap */ + RtlpValidateHeap(Heap, FALSE); + } + + /* Release the lock */ + if (HeapLocked) RtlLeaveHeapLock(Heap->LockVariable); + + return Result; }
SIZE_T NTAPI @@ -99,7 +501,51 @@ ULONG Flags, PVOID Ptr) { - return 0; + PHEAP Heap = (PHEAP)HeapPtr; + BOOLEAN HeapLocked = FALSE; + PHEAP_ENTRY HeapEntry; + SIZE_T Result = ~(SIZE_T)0; + + //if (Heap->ForceFlags & HEAP_FLAG_PAGE_ALLOCS) + //return RtlPageHeapSizeHeap(HeapPtr, Flags, Ptr); + + /* Check heap signature */ + if (Heap->Signature != HEAP_SIGNATURE) + { + DPRINT1("HEAP: Invalid heap %p signature 0x%x\n", Heap, Heap->Signature); + return FALSE; + } + + /* Add skip validation flag */ + Flags |= Heap->ForceFlags | HEAP_SKIP_VALIDATION_CHECKS; + + /* Lock the heap ourselves */ + if (!(Flags & HEAP_NO_SERIALIZE)) + { + RtlEnterHeapLock(Heap->LockVariable); + HeapLocked = TRUE; + + /* Add no serialize flag so that the main routine won't try to acquire the lock again */ + Flags |= HEAP_NO_SERIALIZE; + } + + /* Validate the heap if necessary */ + RtlpValidateHeap(Heap, FALSE); + + /* Get the existing heap entry */ + HeapEntry = (PHEAP_ENTRY)Ptr - 1; + + /* Validate it */ + if (RtlpValidateHeapEntry(Heap, HeapEntry)) + { + /* If it succeeded - call the main routine */ + Result = RtlSizeHeap(HeapPtr, Flags, Ptr); + } + + /* Release the lock */ + if (HeapLocked) RtlLeaveHeapLock(Heap->LockVariable); + + return Result; }