windowsx86dep

Does the existence of PAGE_EXECUTE_READWRITE as an option in VirtualAlloc mean that the W^X is only facilitated in Windows by DEP?


W^X ("write xor execute", pronounced W xor X) is a security feature in operating systems and virtual machines. It is a memory protection policy whereby every page in a process's or kernel's address space may be either writable or executable, but not both.

My basic perspective on why this is a good security feature is that the owner of the system theoretically has an opportunity to, within the kernel, or specifically within the VirtualAlloc function, to hook some analysis function to perform some security validation before allowing newly written code to be executed on the machine.

I was already familiar with DEP, but only just now realizing it has something to do with W^X in Windows:

Executable space protection on Windows is called "Data Execution Prevention" (DEP). Under Windows XP or Server 2003 NX protection was used on critical Windows services exclusively by default. If the x86 processor supported this feature in hardware, then the NX features were turned on automatically in Windows XP/Server 2003 by default. If the feature was not supported by the x86 processor, then no protection was given. Early implementations of DEP provided no address space layout randomization (ASLR), which allowed potential return-to-libc attacks that could have been feasibly used to disable DEP during an attack.

It was my impression that W^X applied to Windows in general, without requiring configuration of the process. But I just noticed that VirtualProtect allows the option PAGE_EXECUTE_READWRITE, which is documented as:

Enables execute, read-only, or read/write access to the committed region of pages.

This seems to entirely defy the concept of W^X. So is W^X not an enforced security policy on Windows, except when DEP is enabled?


Solution

  • Early x86 CPUs had no support for pages without eXec permission. In legacy 32-bit x86 page tables, there was only a bit for write permission, the R/W bit. (Read permission is always implicit in the page being valid, whether the page is writeable or not). The PAE format for page-table entries, which x86-64 also uses, added an NX bit ("no exec"), aka XD (eXecute Disable).

    An OS still had to decide which pages to make non-executable. Windows seems to use DEP to describe the feature of actually mapping logical page permissions to the hardware page tables, to be enforced by the CPU.

    Some programs written in the bad old days when every readable page was executable may have been sloppy about telling the OS that they wanted a page to be executable. Especially ones that only targeted 32-bit x86. This is what Windows caters for by requiring executables to opt in to DEP, to indicate that they're aware of and compatible with not having exec permission for pages that aren't explicitly marked that way.


    Some OSes, notably OpenBSD, truly enforce W^X. For example, mmap(..., PROT_WRITE | PROT_EXEC, ...) will return an error on OpenBSD. Their mmap(2) man page documents that such an mmap or mprotect system call will return

    [ENOTSUP] The accesses requested in the prot argument are not allowed. In particular, PROT_WRITE | PROT_EXEC mappings are not permitted unless the filesystem is mounted wxallowed and the process is link-time tagged with wxneeded. (See also kern.wxabort in sysctl(2) for a method to diagnose failure).


    Most other OSes (including Linux and Windows) allow user-space to create pages that are writeable and executable at the same time. But the standard toolchains and dynamic linking mechanisms aim for W^X compliance by default, if you don't use any options like gcc -zexecstack that will get the OS to create a process image with some R|W|X pages.

    Older 32-bit x86 Linux for example used to use PLT entries (dynamic linking stubs) with jmp rel32 direct jumps, and rewrite the machine code to have the right displacement to reach wherever the shared library got loaded in memory. But these days, the PLT code uses indirect jumps (through the GOT = global offset table), so the executable PLT code can be in read-only page(s).

    Changes like this have weeded out any need for write+exec pages in a normal process built with the standard tools.

    But on Windows, MacOS, and Linux, W^X is not enforced by the OS. System calls like Windows VirtualAlloc / VirtualProtect and their POSIX equivalents mmap / mprotect will work just fine.

    @Ander's answer says DEP does not enforce W^X, just gets the OS to respect the exec permission settings in the executable when creating the initial mappings for .text / .data / .bss and stack space, and stuff like that during process startup.