From 966f207668d19610dae34d5331150e3742815bcf Mon Sep 17 00:00:00 2001 From: Pawel Dziepak Date: Wed, 6 Mar 2013 18:03:54 +0100 Subject: [PATCH] x86: enable data execution prevention Set execute disable bit for any page that belongs to area with neither B_EXECUTE_AREA nor B_KERNEL_EXECUTE_AREA set. In order to take advanage of NX bit in 32 bit protected mode PAE must be enabled. Thus, from now on it is also enabled when the CPU supports NX bit. vm_page_fault() takes additional argument which indicates whether page fault was caused by an illegal instruction fetch. --- headers/private/kernel/arch/x86/arch_cpu.h | 12 +++++ headers/private/kernel/vm/vm_priv.h | 2 +- src/system/kernel/arch/arm/arch_int.cpp | 2 +- src/system/kernel/arch/m68k/arch_int.cpp | 1 + src/system/kernel/arch/ppc/arch_int.cpp | 1 + src/system/kernel/arch/x86/64/syscalls.cpp | 3 +- src/system/kernel/arch/x86/arch_cpu.cpp | 4 +- src/system/kernel/arch/x86/arch_int.cpp | 5 ++- src/system/kernel/arch/x86/arch_vm.cpp | 9 ++++ .../arch/x86/arch_vm_translation_map.cpp | 17 ++++--- .../x86/paging/64bit/X86PagingMethod64Bit.cpp | 14 ++++++ .../x86/paging/64bit/X86PagingMethod64Bit.h | 2 + .../paging/64bit/X86VMTranslationMap64Bit.cpp | 6 ++- .../kernel/arch/x86/paging/64bit/paging.h | 4 +- .../x86/paging/pae/X86PagingMethodPAE.cpp | 8 ++++ .../x86/paging/pae/X86VMTranslationMapPAE.cpp | 12 ++++- .../kernel/arch/x86/paging/pae/paging.h | 3 +- src/system/kernel/vm/vm.cpp | 44 +++++++++++++------ 18 files changed, 118 insertions(+), 31 deletions(-) diff --git a/headers/private/kernel/arch/x86/arch_cpu.h b/headers/private/kernel/arch/x86/arch_cpu.h index 58694f3423..bc9f4c8599 100644 --- a/headers/private/kernel/arch/x86/arch_cpu.h +++ b/headers/private/kernel/arch/x86/arch_cpu.h @@ -39,6 +39,11 @@ #define IA32_MSR_EFER 0xc0000080 +// MSR EFER bits +// reference +#define IA32_MSR_EFER_SYSCALL (1 << 0) +#define IA32_MSR_EFER_NX (1 << 11) + // x86_64 MSRs. #define IA32_MSR_STAR 0xc0000081 #define IA32_MSR_LSTAR 0xc0000082 @@ -131,6 +136,13 @@ #define IA32_FEATURE_AMD_EXT_3DNOWEXT (1 << 30) // 3DNow! extensions #define IA32_FEATURE_AMD_EXT_3DNOW (1 << 31) // 3DNow! +// some of the features from cpuid eax 0x80000001, edx register (AMD) are also +// available on Intel processors +#define IA32_FEATURES_INTEL_EXT (IA32_FEATURE_AMD_EXT_SYSCALL \ + | IA32_FEATURE_AMD_EXT_NX \ + | IA32_FEATURE_AMD_EXT_RDTSCP \ + | IA32_FEATURE_AMD_EXT_LONG) + // x86 defined features from cpuid eax 6, eax register // reference http://www.intel.com/Assets/en_US/PDF/appnote/241618.pdf (Table 5-11) #define IA32_FEATURE_DTS (1 << 0) //Digital Thermal Sensor diff --git a/headers/private/kernel/vm/vm_priv.h b/headers/private/kernel/vm/vm_priv.h index afb15a714a..9091c60c81 100644 --- a/headers/private/kernel/vm/vm_priv.h +++ b/headers/private/kernel/vm/vm_priv.h @@ -28,7 +28,7 @@ extern "C" { // Should only be used by vm internals status_t vm_page_fault(addr_t address, addr_t faultAddress, bool isWrite, - bool isUser, addr_t *newip); + bool isExecute, bool isUser, addr_t *newip); void vm_unreserve_memory(size_t bytes); status_t vm_try_reserve_memory(size_t bytes, int priority, bigtime_t timeout); status_t vm_daemon_init(void); diff --git a/src/system/kernel/arch/arm/arch_int.cpp b/src/system/kernel/arch/arm/arch_int.cpp index b6f18d47f4..b41cc7154d 100644 --- a/src/system/kernel/arch/arm/arch_int.cpp +++ b/src/system/kernel/arch/arm/arch_int.cpp @@ -277,7 +277,7 @@ arch_arm_data_abort(struct iframe *frame) enable_interrupts(); - vm_page_fault(far, frame->pc, isWrite, isUser, &newip); + vm_page_fault(far, frame->pc, isWrite, false, isUser, &newip); if (newip != 0) { // the page fault handler wants us to modify the iframe to set the diff --git a/src/system/kernel/arch/m68k/arch_int.cpp b/src/system/kernel/arch/m68k/arch_int.cpp index a982a75e8a..b80942ac66 100644 --- a/src/system/kernel/arch/m68k/arch_int.cpp +++ b/src/system/kernel/arch/m68k/arch_int.cpp @@ -238,6 +238,7 @@ m68k_exception_entry(struct iframe *iframe) vm_page_fault(fault_address(iframe), iframe->cpu.pc, fault_was_write(iframe), // store or load + false, iframe->cpu.sr & SR_S, // was the system in user or supervisor &newip); if (newip != 0) { diff --git a/src/system/kernel/arch/ppc/arch_int.cpp b/src/system/kernel/arch/ppc/arch_int.cpp index 61ede968ef..ac1c60b284 100644 --- a/src/system/kernel/arch/ppc/arch_int.cpp +++ b/src/system/kernel/arch/ppc/arch_int.cpp @@ -164,6 +164,7 @@ ppc_exception_entry(int vector, struct iframe *iframe) vm_page_fault(iframe->dar, iframe->srr0, iframe->dsisr & (1 << 25), // store or load + false, iframe->srr1 & (1 << 14), // was the system in user or supervisor &newip); if (newip != 0) { diff --git a/src/system/kernel/arch/x86/64/syscalls.cpp b/src/system/kernel/arch/x86/64/syscalls.cpp index 20bf44ef4e..4407498aa2 100644 --- a/src/system/kernel/arch/x86/64/syscalls.cpp +++ b/src/system/kernel/arch/x86/64/syscalls.cpp @@ -20,7 +20,8 @@ static void init_syscall_registers(void* dummy, int cpuNum) { // Enable SYSCALL (EFER.SCE = 1). - x86_write_msr(IA32_MSR_EFER, x86_read_msr(IA32_MSR_EFER) | (1 << 0)); + x86_write_msr(IA32_MSR_EFER, x86_read_msr(IA32_MSR_EFER) + | IA32_MSR_EFER_SYSCALL); // Flags to clear upon entry. Want interrupts disabled and the direction // flag cleared. diff --git a/src/system/kernel/arch/x86/arch_cpu.cpp b/src/system/kernel/arch/x86/arch_cpu.cpp index be6d4889dd..cafc2daf6d 100644 --- a/src/system/kernel/arch/x86/arch_cpu.cpp +++ b/src/system/kernel/arch/x86/arch_cpu.cpp @@ -605,10 +605,12 @@ detect_cpu(int currentCPU) get_current_cpuid(&cpuid, 1); cpu->arch.feature[FEATURE_COMMON] = cpuid.eax_1.features; // edx cpu->arch.feature[FEATURE_EXT] = cpuid.eax_1.extended_features; // ecx - if (cpu->arch.vendor == VENDOR_AMD) { + if (cpu->arch.vendor == VENDOR_AMD || cpu->arch.vendor == VENDOR_INTEL) { get_current_cpuid(&cpuid, 0x80000001); cpu->arch.feature[FEATURE_EXT_AMD] = cpuid.regs.edx; // edx } + if (cpu->arch.vendor == VENDOR_INTEL) + cpu->arch.feature[FEATURE_EXT_AMD] &= IA32_FEATURES_INTEL_EXT; get_current_cpuid(&cpuid, 6); cpu->arch.feature[FEATURE_6_EAX] = cpuid.regs.eax; cpu->arch.feature[FEATURE_6_ECX] = cpuid.regs.ecx; diff --git a/src/system/kernel/arch/x86/arch_int.cpp b/src/system/kernel/arch/x86/arch_int.cpp index 75e50a827e..e1836368cd 100644 --- a/src/system/kernel/arch/x86/arch_int.cpp +++ b/src/system/kernel/arch/x86/arch_int.cpp @@ -319,8 +319,9 @@ x86_page_fault_exception(struct iframe* frame) enable_interrupts(); vm_page_fault(cr2, frame->ip, - (frame->error_code & 0x2) != 0, // write access - (frame->error_code & 0x4) != 0, // userland + (frame->error_code & 0x2)!= 0, // write access + (frame->error_code & 0x10) != 0, // instruction fetch + (frame->error_code & 0x4) != 0, // userland &newip); if (newip != 0) { // the page fault handler wants us to modify the iframe to set the diff --git a/src/system/kernel/arch/x86/arch_vm.cpp b/src/system/kernel/arch/x86/arch_vm.cpp index 0aed76adb7..ae063057a5 100644 --- a/src/system/kernel/arch/x86/arch_vm.cpp +++ b/src/system/kernel/arch/x86/arch_vm.cpp @@ -728,6 +728,15 @@ arch_vm_supports_protection(uint32 protection) return false; } + // Userland and the kernel have the same setting of NX-bit. + // That's why we do not allow any area that user can access, but not execute + // and the kernel can execute. + if ((protection & (B_READ_AREA | B_WRITE_AREA)) != 0 + && (protection & B_EXECUTE_AREA) == 0 + && (protection & B_KERNEL_EXECUTE_AREA) != 0) { + return false; + } + return true; } diff --git a/src/system/kernel/arch/x86/arch_vm_translation_map.cpp b/src/system/kernel/arch/x86/arch_vm_translation_map.cpp index 836262365e..c2abe3f253 100644 --- a/src/system/kernel/arch/x86/arch_vm_translation_map.cpp +++ b/src/system/kernel/arch/x86/arch_vm_translation_map.cpp @@ -86,13 +86,16 @@ arch_vm_translation_map_init(kernel_args *args, gX86PagingMethod = new(&sPagingMethodBuffer) X86PagingMethod64Bit; #elif B_HAIKU_PHYSICAL_BITS == 64 bool paeAvailable = x86_check_feature(IA32_FEATURE_PAE, FEATURE_COMMON); - bool paeNeeded = false; - for (uint32 i = 0; i < args->num_physical_memory_ranges; i++) { - phys_addr_t end = args->physical_memory_range[i].start - + args->physical_memory_range[i].size; - if (end > 0x100000000LL) { - paeNeeded = true; - break; + bool paeNeeded = x86_check_feature(IA32_FEATURE_AMD_EXT_NX, + FEATURE_EXT_AMD); + if (!paeNeeded) { + for (uint32 i = 0; i < args->num_physical_memory_ranges; i++) { + phys_addr_t end = args->physical_memory_range[i].start + + args->physical_memory_range[i].size; + if (end > 0x100000000LL) { + paeNeeded = true; + break; + } } } diff --git a/src/system/kernel/arch/x86/paging/64bit/X86PagingMethod64Bit.cpp b/src/system/kernel/arch/x86/paging/64bit/X86PagingMethod64Bit.cpp index d84199a44d..059f99c3d6 100644 --- a/src/system/kernel/arch/x86/paging/64bit/X86PagingMethod64Bit.cpp +++ b/src/system/kernel/arch/x86/paging/64bit/X86PagingMethod64Bit.cpp @@ -59,6 +59,10 @@ X86PagingMethod64Bit::Init(kernel_args* args, fKernelPhysicalPML4 = args->arch_args.phys_pgdir; fKernelVirtualPML4 = (uint64*)(addr_t)args->arch_args.vir_pgdir; + // enable NX-bit on all CPUs + if (x86_check_feature(IA32_FEATURE_AMD_EXT_NX, FEATURE_EXT_AMD)) + call_all_cpus_sync(&_EnableExecutionDisable, NULL); + // Ensure that the user half of the address space is clear. This removes // the temporary identity mapping made by the boot loader. memset(fKernelVirtualPML4, 0, sizeof(uint64) * 256); @@ -367,6 +371,8 @@ X86PagingMethod64Bit::PutPageTableEntryInTable(uint64* entry, page |= X86_64_PTE_USER; if ((attributes & B_WRITE_AREA) != 0) page |= X86_64_PTE_WRITABLE; + if ((attributes & B_EXECUTE_AREA) == 0) + page |= X86_64_PTE_NOT_EXECUTABLE; } else if ((attributes & B_KERNEL_WRITE_AREA) != 0) page |= X86_64_PTE_WRITABLE; @@ -374,3 +380,11 @@ X86PagingMethod64Bit::PutPageTableEntryInTable(uint64* entry, SetTableEntry(entry, page); } + +void +X86PagingMethod64Bit::_EnableExecutionDisable(void* dummy, int cpu) +{ + x86_write_msr(IA32_MSR_EFER, x86_read_msr(IA32_MSR_EFER) + | IA32_MSR_EFER_NX); +} + diff --git a/src/system/kernel/arch/x86/paging/64bit/X86PagingMethod64Bit.h b/src/system/kernel/arch/x86/paging/64bit/X86PagingMethod64Bit.h index f561d9e995..e834434c78 100644 --- a/src/system/kernel/arch/x86/paging/64bit/X86PagingMethod64Bit.h +++ b/src/system/kernel/arch/x86/paging/64bit/X86PagingMethod64Bit.h @@ -96,6 +96,8 @@ public: uint32 memoryType); private: + static void _EnableExecutionDisable(void* dummy, int cpu); + phys_addr_t fKernelPhysicalPML4; uint64* fKernelVirtualPML4; diff --git a/src/system/kernel/arch/x86/paging/64bit/X86VMTranslationMap64Bit.cpp b/src/system/kernel/arch/x86/paging/64bit/X86VMTranslationMap64Bit.cpp index 0e8800b15a..3f24ae5434 100644 --- a/src/system/kernel/arch/x86/paging/64bit/X86VMTranslationMap64Bit.cpp +++ b/src/system/kernel/arch/x86/paging/64bit/X86VMTranslationMap64Bit.cpp @@ -627,11 +627,13 @@ X86VMTranslationMap64Bit::Query(addr_t virtualAddress, // Translate the page state flags. if ((entry & X86_64_PTE_USER) != 0) { *_flags |= ((entry & X86_64_PTE_WRITABLE) != 0 ? B_WRITE_AREA : 0) - | B_READ_AREA; + | B_READ_AREA + | ((entry & X86_64_PTE_NOT_EXECUTABLE) == 0 ? B_EXECUTE_AREA : 0); } *_flags |= ((entry & X86_64_PTE_WRITABLE) != 0 ? B_KERNEL_WRITE_AREA : 0) | B_KERNEL_READ_AREA + | ((entry & X86_64_PTE_NOT_EXECUTABLE) == 0 ? B_KERNEL_EXECUTE_AREA : 0) | ((entry & X86_64_PTE_DIRTY) != 0 ? PAGE_MODIFIED : 0) | ((entry & X86_64_PTE_ACCESSED) != 0 ? PAGE_ACCESSED : 0) | ((entry & X86_64_PTE_PRESENT) != 0 ? PAGE_PRESENT : 0); @@ -671,6 +673,8 @@ X86VMTranslationMap64Bit::Protect(addr_t start, addr_t end, uint32 attributes, newProtectionFlags = X86_64_PTE_USER; if ((attributes & B_WRITE_AREA) != 0) newProtectionFlags |= X86_64_PTE_WRITABLE; + if ((attributes & B_EXECUTE_AREA) == 0) + newProtectionFlags |= X86_64_PTE_NOT_EXECUTABLE; } else if ((attributes & B_KERNEL_WRITE_AREA) != 0) newProtectionFlags = X86_64_PTE_WRITABLE; diff --git a/src/system/kernel/arch/x86/paging/64bit/paging.h b/src/system/kernel/arch/x86/paging/64bit/paging.h index 2cb4fb4b06..a99afaa44c 100644 --- a/src/system/kernel/arch/x86/paging/64bit/paging.h +++ b/src/system/kernel/arch/x86/paging/64bit/paging.h @@ -59,7 +59,9 @@ #define X86_64_PTE_GLOBAL (1LL << 8) #define X86_64_PTE_NOT_EXECUTABLE (1LL << 63) #define X86_64_PTE_ADDRESS_MASK 0x000ffffffffff000L -#define X86_64_PTE_PROTECTION_MASK (X86_64_PTE_WRITABLE | X86_64_PTE_USER) +#define X86_64_PTE_PROTECTION_MASK (X86_64_PTE_NOT_EXECUTABLE \ + | X86_64_PTE_WRITABLE \ + | X86_64_PTE_USER) #define X86_64_PTE_MEMORY_TYPE_MASK (X86_64_PTE_WRITE_THROUGH \ | X86_64_PTE_CACHING_DISABLED) diff --git a/src/system/kernel/arch/x86/paging/pae/X86PagingMethodPAE.cpp b/src/system/kernel/arch/x86/paging/pae/X86PagingMethodPAE.cpp index 34258f0c1d..d2071bc718 100644 --- a/src/system/kernel/arch/x86/paging/pae/X86PagingMethodPAE.cpp +++ b/src/system/kernel/arch/x86/paging/pae/X86PagingMethodPAE.cpp @@ -165,6 +165,12 @@ private: { x86_write_cr3((addr_t)physicalPDPT); x86_write_cr4(x86_read_cr4() | IA32_CR4_PAE | IA32_CR4_GLOBAL_PAGES); + + // if availalbe enable NX-bit (No eXecute) + if (x86_check_feature(IA32_FEATURE_AMD_EXT_NX, FEATURE_EXT_AMD)) { + x86_write_msr(IA32_MSR_EFER, x86_read_msr(IA32_MSR_EFER) + | IA32_MSR_EFER_NX); + } } void _TranslatePageTable(addr_t virtualBase) @@ -778,6 +784,8 @@ X86PagingMethodPAE::PutPageTableEntryInTable(pae_page_table_entry* entry, page |= X86_PAE_PTE_USER; if ((attributes & B_WRITE_AREA) != 0) page |= X86_PAE_PTE_WRITABLE; + if ((attributes & B_EXECUTE_AREA) == 0) + page |= X86_PAE_PTE_NOT_EXECUTABLE; } else if ((attributes & B_KERNEL_WRITE_AREA) != 0) page |= X86_PAE_PTE_WRITABLE; diff --git a/src/system/kernel/arch/x86/paging/pae/X86VMTranslationMapPAE.cpp b/src/system/kernel/arch/x86/paging/pae/X86VMTranslationMapPAE.cpp index 8d6853689b..c936af436a 100644 --- a/src/system/kernel/arch/x86/paging/pae/X86VMTranslationMapPAE.cpp +++ b/src/system/kernel/arch/x86/paging/pae/X86VMTranslationMapPAE.cpp @@ -687,11 +687,14 @@ X86VMTranslationMapPAE::Query(addr_t virtualAddress, // translate the page state flags if ((entry & X86_PAE_PTE_USER) != 0) { *_flags |= ((entry & X86_PAE_PTE_WRITABLE) != 0 ? B_WRITE_AREA : 0) - | B_READ_AREA; + | B_READ_AREA + | ((entry & X86_PAE_PTE_NOT_EXECUTABLE) == 0 ? B_EXECUTE_AREA : 0); } *_flags |= ((entry & X86_PAE_PTE_WRITABLE) != 0 ? B_KERNEL_WRITE_AREA : 0) | B_KERNEL_READ_AREA + | ((entry & X86_PAE_PTE_NOT_EXECUTABLE) == 0 + ? B_KERNEL_EXECUTE_AREA : 0) | ((entry & X86_PAE_PTE_DIRTY) != 0 ? PAGE_MODIFIED : 0) | ((entry & X86_PAE_PTE_ACCESSED) != 0 ? PAGE_ACCESSED : 0) | ((entry & X86_PAE_PTE_PRESENT) != 0 ? PAGE_PRESENT : 0); @@ -733,11 +736,14 @@ X86VMTranslationMapPAE::QueryInterrupt(addr_t virtualAddress, // translate the page state flags if ((entry & X86_PAE_PTE_USER) != 0) { *_flags |= ((entry & X86_PAE_PTE_WRITABLE) != 0 ? B_WRITE_AREA : 0) - | B_READ_AREA; + | B_READ_AREA + | ((entry & X86_PAE_PTE_NOT_EXECUTABLE) == 0 ? B_EXECUTE_AREA : 0); } *_flags |= ((entry & X86_PAE_PTE_WRITABLE) != 0 ? B_KERNEL_WRITE_AREA : 0) | B_KERNEL_READ_AREA + | ((entry & X86_PAE_PTE_NOT_EXECUTABLE) == 0 + ? B_KERNEL_EXECUTE_AREA : 0) | ((entry & X86_PAE_PTE_DIRTY) != 0 ? PAGE_MODIFIED : 0) | ((entry & X86_PAE_PTE_ACCESSED) != 0 ? PAGE_ACCESSED : 0) | ((entry & X86_PAE_PTE_PRESENT) != 0 ? PAGE_PRESENT : 0); @@ -766,6 +772,8 @@ X86VMTranslationMapPAE::Protect(addr_t start, addr_t end, uint32 attributes, newProtectionFlags = X86_PAE_PTE_USER; if ((attributes & B_WRITE_AREA) != 0) newProtectionFlags |= X86_PAE_PTE_WRITABLE; + if ((attributes & B_EXECUTE_AREA) == 0) + newProtectionFlags |= X86_PAE_PTE_NOT_EXECUTABLE; } else if ((attributes & B_KERNEL_WRITE_AREA) != 0) newProtectionFlags = X86_PAE_PTE_WRITABLE; diff --git a/src/system/kernel/arch/x86/paging/pae/paging.h b/src/system/kernel/arch/x86/paging/pae/paging.h index ae6d45b64a..0567dacc24 100644 --- a/src/system/kernel/arch/x86/paging/pae/paging.h +++ b/src/system/kernel/arch/x86/paging/pae/paging.h @@ -49,7 +49,8 @@ #define X86_PAE_PTE_IGNORED3 0x0000000000000800LL #define X86_PAE_PTE_ADDRESS_MASK 0x000ffffffffff000LL #define X86_PAE_PTE_NOT_EXECUTABLE 0x8000000000000000LL -#define X86_PAE_PTE_PROTECTION_MASK (X86_PAE_PTE_WRITABLE \ +#define X86_PAE_PTE_PROTECTION_MASK (X86_PAE_PTE_NOT_EXECUTABLE \ + |X86_PAE_PTE_WRITABLE \ | X86_PAE_PTE_USER) #define X86_PAE_PTE_MEMORY_TYPE_MASK (X86_PAE_PTE_WRITE_THROUGH \ | X86_PAE_PTE_CACHING_DISABLED) diff --git a/src/system/kernel/vm/vm.cpp b/src/system/kernel/vm/vm.cpp index fb6c2464b5..f9a2f4dc0c 100644 --- a/src/system/kernel/vm/vm.cpp +++ b/src/system/kernel/vm/vm.cpp @@ -267,7 +267,7 @@ static cache_info* sCacheInfoTable; static void delete_area(VMAddressSpace* addressSpace, VMArea* area, bool addressSpaceCleanup); static status_t vm_soft_fault(VMAddressSpace* addressSpace, addr_t address, - bool isWrite, bool isUser, vm_page** wirePage, + bool isWrite, bool isExecute, bool isUser, vm_page** wirePage, VMAreaWiredRange* wiredRange = NULL); static status_t map_backing_store(VMAddressSpace* addressSpace, VMCache* cache, off_t offset, const char* areaName, addr_t size, int wiring, @@ -315,6 +315,7 @@ enum { PAGE_FAULT_ERROR_KERNEL_ONLY, PAGE_FAULT_ERROR_WRITE_PROTECTED, PAGE_FAULT_ERROR_READ_PROTECTED, + PAGE_FAULT_ERROR_EXECUTE_PROTECTED, PAGE_FAULT_ERROR_KERNEL_BAD_USER_MEMORY, PAGE_FAULT_ERROR_NO_ADDRESS_SPACE }; @@ -346,6 +347,10 @@ public: case PAGE_FAULT_ERROR_READ_PROTECTED: out.Print("page fault error: area: %ld, read protected", fArea); break; + case PAGE_FAULT_ERROR_EXECUTE_PROTECTED: + out.Print("page fault error: area: %ld, execute protected", + fArea); + break; case PAGE_FAULT_ERROR_KERNEL_BAD_USER_MEMORY: out.Print("page fault error: kernel touching bad user memory"); break; @@ -3994,8 +3999,8 @@ forbid_page_faults(void) status_t -vm_page_fault(addr_t address, addr_t faultAddress, bool isWrite, bool isUser, - addr_t* newIP) +vm_page_fault(addr_t address, addr_t faultAddress, bool isWrite, bool isExecute, + bool isUser, addr_t* newIP) { FTRACE(("vm_page_fault: page fault at 0x%lx, ip 0x%lx\n", address, faultAddress)); @@ -4038,8 +4043,8 @@ vm_page_fault(addr_t address, addr_t faultAddress, bool isWrite, bool isUser, } if (status == B_OK) { - status = vm_soft_fault(addressSpace, pageAddress, isWrite, isUser, - NULL); + status = vm_soft_fault(addressSpace, pageAddress, isWrite, isExecute, + isUser, NULL); } if (status < B_OK) { @@ -4074,8 +4079,8 @@ vm_page_fault(addr_t address, addr_t faultAddress, bool isWrite, bool isUser, "\"%s\" (%" B_PRId32 ") tried to %s address %#lx, ip %#lx " "(\"%s\" +%#lx)\n", thread->name, thread->id, thread->team->Name(), thread->team->id, - isWrite ? "write" : "read", address, faultAddress, - area ? area->name : "???", faultAddress - (area ? + isWrite ? "write" : (isExecute ? "execute" : "read"), address, + faultAddress, area ? area->name : "???", faultAddress - (area ? area->Base() : 0x0)); // We can print a stack trace of the userland thread here. @@ -4364,7 +4369,8 @@ fault_get_page(PageFaultContext& context) */ static status_t vm_soft_fault(VMAddressSpace* addressSpace, addr_t originalAddress, - bool isWrite, bool isUser, vm_page** wirePage, VMAreaWiredRange* wiredRange) + bool isWrite, bool isExecute, bool isUser, vm_page** wirePage, + VMAreaWiredRange* wiredRange) { FTRACE(("vm_soft_fault: thid 0x%" B_PRIx32 " address 0x%" B_PRIxADDR ", " "isWrite %d, isUser %d\n", thread_get_current_thread_id(), @@ -4419,7 +4425,16 @@ vm_soft_fault(VMAddressSpace* addressSpace, addr_t originalAddress, VMPageFaultTracing::PAGE_FAULT_ERROR_WRITE_PROTECTED)); status = B_PERMISSION_DENIED; break; - } else if (!isWrite && (protection + } else if (isExecute && (protection + & (B_EXECUTE_AREA + | (isUser ? 0 : B_KERNEL_EXECUTE_AREA))) == 0) { + dprintf("instruction fetch attempted on execute-protected area 0x%" + B_PRIx32 " at %p\n", area->id, (void*)originalAddress); + TPF(PageFaultError(area->id, + VMPageFaultTracing::PAGE_FAULT_ERROR_EXECUTE_PROTECTED)); + status = B_PERMISSION_DENIED; + break; + } else if (!isWrite && !isExecute && (protection & (B_READ_AREA | (isUser ? 0 : B_KERNEL_READ_AREA))) == 0) { dprintf("read access attempted on read-protected area 0x%" B_PRIx32 " at %p\n", area->id, (void*)originalAddress); @@ -4756,7 +4771,8 @@ vm_set_area_memory_type(area_id id, phys_addr_t physicalBase, uint32 type) /*! This function enforces some protection properties: - - if B_WRITE_AREA is set, B_WRITE_KERNEL_AREA is set as well + - if B_WRITE_AREA is set, B_KERNEL_WRITE_AREA is set as well + - if B_EXECUTE_AREA is set, B_KERNEL_EXECUTE_AREA is set as well - if only B_READ_AREA has been set, B_KERNEL_READ_AREA is also set - if no protection is specified, it defaults to B_KERNEL_READ_AREA and B_KERNEL_WRITE_AREA. @@ -4770,6 +4786,8 @@ fix_protection(uint32* protection) *protection |= B_KERNEL_READ_AREA | B_KERNEL_WRITE_AREA; else *protection |= B_KERNEL_READ_AREA; + if ((*protection & B_EXECUTE_AREA) != 0) + *protection |= B_KERNEL_EXECUTE_AREA; } } @@ -5222,8 +5240,8 @@ vm_wire_page(team_id team, addr_t address, bool writable, cacheChainLocker.Unlock(); addressSpaceLocker.Unlock(); - error = vm_soft_fault(addressSpace, pageAddress, writable, isUser, - &page, &info->range); + error = vm_soft_fault(addressSpace, pageAddress, writable, false, + isUser, &page, &info->range); if (error != B_OK) { // The page could not be mapped -- clean up. @@ -5401,7 +5419,7 @@ lock_memory_etc(team_id team, void* address, size_t numBytes, uint32 flags) addressSpaceLocker.Unlock(); error = vm_soft_fault(addressSpace, nextAddress, writable, - isUser, &page, range); + false, isUser, &page, range); addressSpaceLocker.Lock(); cacheChainLocker.SetTo(vm_area_get_locked_cache(area));