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.
This commit is contained in:
Pawel Dziepak
2013-04-04 15:22:23 +02:00
parent 211f71325a
commit 966f207668
18 changed files with 118 additions and 31 deletions
@@ -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
+1 -1
View File
@@ -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);
+1 -1
View File
@@ -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
+1
View File
@@ -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) {
+1
View File
@@ -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) {
+2 -1
View File
@@ -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.
+3 -1
View File
@@ -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;
+3 -2
View File
@@ -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
+9
View File
@@ -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;
}
@@ -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;
}
}
}
@@ -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);
}
@@ -96,6 +96,8 @@ public:
uint32 memoryType);
private:
static void _EnableExecutionDisable(void* dummy, int cpu);
phys_addr_t fKernelPhysicalPML4;
uint64* fKernelVirtualPML4;
@@ -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;
@@ -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)
@@ -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;
@@ -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;
@@ -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)
+31 -13
View File
@@ -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));