if ((Address >= StackBase) || (Address < DeallocationStack))
{
/* That's odd... */
- DPRINT1("Faulting address outside of stack bounds\n");
+ DPRINT1("Faulting address outside of stack bounds. Address=%p, StackBase=%p, DeallocationStack=%p\n",
+ Address, StackBase, DeallocationStack);
return STATUS_GUARD_PAGE_VIOLATION;
}
return STATUS_STACK_OVERFLOW;
}
+FORCEINLINE
+BOOLEAN
+MiIsAccessAllowed(
+ _In_ ULONG ProtectionMask,
+ _In_ BOOLEAN Write,
+ _In_ BOOLEAN Execute)
+{
+ #define _BYTE_MASK(Bit0, Bit1, Bit2, Bit3, Bit4, Bit5, Bit6, Bit7) \
+ (Bit0) | ((Bit1) << 1) | ((Bit2) << 2) | ((Bit3) << 3) | \
+ ((Bit4) << 4) | ((Bit5) << 5) | ((Bit6) << 6) | ((Bit7) << 7)
+ static const UCHAR AccessAllowedMask[2][2] =
+ {
+ { // Protect 0 1 2 3 4 5 6 7
+ _BYTE_MASK(0, 1, 1, 1, 1, 1, 1, 1), // READ
+ _BYTE_MASK(0, 0, 1, 1, 0, 0, 1, 1), // EXECUTE READ
+ },
+ {
+ _BYTE_MASK(0, 0, 0, 0, 1, 1, 1, 1), // WRITE
+ _BYTE_MASK(0, 0, 0, 0, 0, 0, 1, 1), // EXECUTE WRITE
+ }
+ };
+
+ /* We want only the lower access bits */
+ ProtectionMask &= MM_PROTECT_ACCESS;
+
+ /* Look it up in the table */
+ return (AccessAllowedMask[Write != 0][Execute != 0] >> ProtectionMask) & 1;
+}
+
NTSTATUS
NTAPI
MiAccessCheck(IN PMMPTE PointerPte,
IN BOOLEAN StoreInstruction,
IN KPROCESSOR_MODE PreviousMode,
- IN ULONG_PTR ProtectionCode,
+ IN ULONG_PTR ProtectionMask,
IN PVOID TrapFrame,
IN BOOLEAN LockHeld)
{
return STATUS_SUCCESS;
}
- /* Convert any fault flag to 1 only */
- if (StoreInstruction) StoreInstruction = 1;
-
-#if 0
/* Check if the protection on the page allows what is being attempted */
- if ((MmReadWrite[Protection] - StoreInstruction) < 10)
+ if (!MiIsAccessAllowed(ProtectionMask, StoreInstruction, FALSE))
{
return STATUS_ACCESS_VIOLATION;
}
-#endif
/* Check if this is a guard page */
- if (ProtectionCode & MM_DECOMMIT)
+ if ((ProtectionMask & MM_PROTECT_SPECIAL) == MM_GUARDPAGE)
{
+ NT_ASSERT(ProtectionMask != MM_DECOMMIT);
+
/* Attached processes can't expand their stack */
if (KeIsAttachedProcess()) return STATUS_ACCESS_VIOLATION;
(TempPte.u.Soft.Prototype == 0)) == FALSE);
/* Remove the guard page bit, and return a guard page violation */
- PointerPte->u.Soft.Protection = ProtectionCode & ~MM_DECOMMIT;
+ TempPte.u.Soft.Protection = ProtectionMask & ~MM_GUARDPAGE;
+ NT_ASSERT(TempPte.u.Long != 0);
+ MI_WRITE_INVALID_PTE(PointerPte, TempPte);
return STATUS_GUARD_PAGE_VIOLATION;
}
}
#if (_MI_PAGING_LEVELS == 2)
-BOOLEAN
FORCEINLINE
+BOOLEAN
MiSynchronizeSystemPde(PMMPDE PointerPde)
{
MMPDE SystemPde;
/* Release the PFN lock */
KeReleaseQueuedSpinLock(LockQueuePfnLock, OldIrql);
- /* Remove caching bits */
- Protection &= ~(MM_NOCACHE | MM_NOACCESS);
+ /* Remove special/caching bits */
+ Protection &= ~MM_PROTECT_SPECIAL;
/* Setup caching */
if (Pfn1->u3.e1.CacheAttribute == MiWriteCombined)
PMMPFN Pfn1;
MMPTE TempPte;
PMMPTE PointerToPteForProtoPage;
- DPRINT1("Transition fault on 0x%p with PTE 0x%p in process %s\n", FaultingAddress, PointerPte, CurrentProcess->ImageFileName);
+ DPRINT1("Transition fault on 0x%p with PTE 0x%p in process %s\n",
+ FaultingAddress, PointerPte, CurrentProcess->ImageFileName);
/* Windowss does this check */
ASSERT(*InPageBlock == NULL);
return STATUS_ACCESS_VIOLATION;
}
+ /* There is no such thing as a decommitted prototype PTE */
+ NT_ASSERT(TempPte.u.Long != MmDecommittedPte.u.Long);
+
/* Check for access rights on the PTE proper */
PteContents = *PointerPte;
if (PteContents.u.Soft.PageFileHigh != MI_PTE_LOOKUP_NEEDED)
#if (_MI_PAGING_LEVELS >= 3)
(PointerPpe->u.Hard.Valid == 0) ||
#endif
- (PointerPde->u.Hard.Valid == 0))
+ (PointerPde->u.Hard.Valid == 0) ||
+ (PointerPte->u.Hard.Valid == 0))
{
- /* This fault is not valid, printf out some debugging help */
+ /* This fault is not valid, print out some debugging help */
DbgPrint("MM:***PAGE FAULT AT IRQL > 1 Va %p, IRQL %lx\n",
Address,
OldIrql);
}
/* Nothing is actually wrong */
- DPRINT1("Fault at IRQL1 is ok\n");
+ DPRINT1("Fault at IRQL %u is ok (%p)\n", OldIrql, Address);
return STATUS_SUCCESS;
}
/* Bail out, if the fault came from user mode */
if (Mode == UserMode) return STATUS_ACCESS_VIOLATION;
-#if (_MI_PAGING_LEVELS == 4)
- /* AMD64 system, check if PXE is invalid */
- if (PointerPxe->u.Hard.Valid == 0)
- {
- KeBugCheckEx(PAGE_FAULT_IN_NONPAGED_AREA,
- (ULONG_PTR)Address,
- StoreInstruction,
- (ULONG_PTR)TrapInformation,
- 7);
- }
-#endif
-#if (_MI_PAGING_LEVELS == 4)
- /* PAE/AMD64 system, check if PPE is invalid */
- if (PointerPpe->u.Hard.Valid == 0)
- {
- KeBugCheckEx(PAGE_FAULT_IN_NONPAGED_AREA,
- (ULONG_PTR)Address,
- StoreInstruction,
- (ULONG_PTR)TrapInformation,
- 5);
- }
-#endif
#if (_MI_PAGING_LEVELS == 2)
if (MI_IS_SYSTEM_PAGE_TABLE_ADDRESS(Address)) MiSynchronizeSystemPde((PMMPDE)PointerPte);
MiCheckPdeForPagedPool(Address);
#endif
- /* Check if the PDE is invalid */
- if (PointerPde->u.Hard.Valid == 0)
+ /* Check if the higher page table entries are invalid */
+ if (
+#if (_MI_PAGING_LEVELS == 4)
+ /* AMD64 system, check if PXE is invalid */
+ (PointerPxe->u.Hard.Valid == 0) ||
+#endif
+#if (_MI_PAGING_LEVELS >= 3)
+ /* PAE/AMD64 system, check if PPE is invalid */
+ (PointerPpe->u.Hard.Valid == 0) ||
+#endif
+ /* Always check if the PDE is valid */
+ (PointerPde->u.Hard.Valid == 0))
{
- /* PDE (still) not valid, kill the system */
+ /* PXE/PPE/PDE (still) not valid, kill the system */
KeBugCheckEx(PAGE_FAULT_IN_NONPAGED_AREA,
(ULONG_PTR)Address,
StoreInstruction,
ASSERT(MI_IS_PAGE_LARGE(PointerPde) == FALSE);
}
- /* Now capture the PTE. Ignore virtual faults for now */
+ /* Now capture the PTE. */
TempPte = *PointerPte;
- ASSERT(TempPte.u.Hard.Valid == 0);
+
+ /* Check if the PTE is valid */
+ if (TempPte.u.Hard.Valid)
+ {
+ /* Check if this is a write on a readonly PTE */
+ if (StoreInstruction)
+ {
+ /* Is this a copy on write PTE? */
+ if (TempPte.u.Hard.CopyOnWrite)
+ {
+ /* Not supported yet */
+ ASSERT(FALSE);
+ }
+
+ /* Is this a read-only PTE? */
+ if (!TempPte.u.Hard.Write)
+ {
+ /* Return the status */
+ MiUnlockProcessWorkingSet(CurrentProcess, CurrentThread);
+ return STATUS_ACCESS_VIOLATION;
+ }
+ }
+
+ /* FIXME: Execution is ignored for now, since we don't have no-execute pages yet */
+
+ /* The fault has already been resolved by a different thread */
+ MiUnlockProcessWorkingSet(CurrentProcess, CurrentThread);
+ return STATUS_SUCCESS;
+ }
/* Quick check for demand-zero */
if (TempPte.u.Long == (MM_READWRITE << MM_PTE_SOFTWARE_PROTECTION_BITS))
}
/* Is this a guard page? */
- if (ProtectionCode & MM_DECOMMIT)
+ if ((ProtectionCode & MM_PROTECT_SPECIAL) == MM_GUARDPAGE)
{
+ /* The VAD protection cannot be MM_DECOMMIT! */
+ NT_ASSERT(ProtectionCode != MM_DECOMMIT);
+
/* Remove the bit */
- PointerPte->u.Soft.Protection = ProtectionCode & ~MM_DECOMMIT;
+ TempPte.u.Soft.Protection = ProtectionCode & ~MM_GUARDPAGE;
+ MI_WRITE_INVALID_PTE(PointerPte, TempPte);
/* Not supported */
ASSERT(ProtoPte == NULL);
else
{
/* No, create a new PTE. First, write the protection */
- PointerPte->u.Soft.Protection = ProtectionCode;
+ TempPte.u.Soft.Protection = ProtectionCode;
+ MI_WRITE_INVALID_PTE(PointerPte, TempPte);
}
/* Lock the PFN database since we're going to grab a page */
/* Write the prototype PTE */
TempPte = PrototypePte;
TempPte.u.Soft.Protection = ProtectionCode;
+ NT_ASSERT(TempPte.u.Long != 0);
MI_WRITE_INVALID_PTE(PointerPte, TempPte);
}
else