Most memory corruption exploits end with the processor running bytes the attacker placed in a stack or heap buffer, and Process Segment Execution Prevention stops that last step by refusing to execute anything outside the code segment. MITRE D3FEND names this countermeasure Process Segment Execution Prevention (D3-PSEP) and defines it as "Preventing execution of any address in a memory region other than the code segment." Its synonyms are No Execute and Execute Disable, and in Windows it is known as Data Execution Prevention (DEP). It sits under the Harden tactic as a child of Application Hardening. This card covers what D3-PSEP enforces, which ATT&CK techniques D3FEND relates it to, where it is implemented on Windows, Linux and macOS, how to verify it is on for every process, and why it is a baseline rather than a complete exploit defence. It is written for platform engineers, application security teams and endpoint owners.
Key takeaways
- It is a baseline, not a full exploit defence. Microsoft's own documentation says DEP "is not intended to be a comprehensive defense against all exploits", and D3FEND notes that return-oriented programming and return-to-libc reuse code that is already executable.
- Hardware enforcement is the norm. The NX (AMD) or XD (Intel) bit in each page table entry marks memory as non-executable; D3FEND notes that software-only implementations are easier to turn off for a page.
- 64-bit processes are covered by default. Microsoft states that DEP is enabled for x64, ARM and Arm64 executables and cannot be disabled; legacy 32-bit processes are where gaps remain.
- Exceptions are the real attack surface. JIT compilers, executable stacks set by the linker, and runtime entitlements that permit writable and executable memory are where the control is weakened, so inventory them.
- Verification is cheap. Boot messages, CPU flags, the boot configuration value and ELF program headers show whether the control is active, and a build pipeline can fail on executable stacks.
D3-PSEP at a glance
| Attribute | Value |
|---|---|
| D3FEND ID | D3-PSEP (d3f:ProcessSegmentExecutionPrevention) |
| Tactic | Harden |
| Parent technique | Application Hardening |
| Digital artefact | Process Segment, synonym Process Memory (D3FEND relation: neutralizes Process Segment) |
| Synonyms | No Execute, Execute Disable |
| Common names | NX bit, XD bit, DEP, W^X (write xor execute), non-executable stack |
| What it changes | Stack, heap and data pages are mapped without execute permission, so a jump into them faults |
| Effort | Low: on by default on current 64-bit platforms; effort goes into legacy binaries and exceptions |
| Main limitation | Code-reuse attacks (ROP, return-to-libc) and abuse of legitimately executable memory |
How it works
D3FEND describes the principle simply: during execution the instruction pointer should only point at addresses in the code segment (the .text segment), because that is the only segment that should hold program code. D3-PSEP enforces that by giving memory regions an execute permission and refusing to fetch instructions from regions that lack it.
- Hardware enforcement: the processor's NX or XD bit marks memory as non-executable. D3FEND notes that early implementations set this per segment, while modern flat-memory systems store it in each page table entry, so execution is controlled page by page.
- Software enforcement: D3FEND's example is Windows XP SP2 software-enforced DEP, which did not check every change of the instruction pointer but did check, before calling an exception handler, that the handler sat in executable memory. Combined with SafeSEH it also checked that the handler was registered in the program file.
- What happens on violation: on Windows, code executed from a protected page raises STATUS_ACCESS_VIOLATION, and an unhandled exception terminates the process. D3FEND points out that a detected violation can feed eviction countermeasures such as Process Termination or blocking the faulting executable.
The same idea is often expressed as W^X: a page may be writable or executable, never both at once. Code that genuinely needs to generate instructions at runtime, such as a JIT compiler, has to ask for executable memory explicitly. Microsoft's guidance is to allocate with VirtualAlloc using a PAGE_EXECUTE protection value and then call VirtualProtect to remove write access once the code is generated. Heap memory from malloc and HeapAlloc is non-executable.
ATT&CK techniques D3FEND relates to D3-PSEP
Relationship type: inferred only. D3FEND does not hand-map D3-PSEP to ATT&CK. It infers the rows below because D3-PSEP neutralizes Process Segment and these techniques access or modify a Process Segment or one of its sub-artefacts. D3FEND labels these relations "inferred and experimental" (knowledge graph 1.6.0, checked November 2026). Every ID was checked against ATT&CK v20.
| Tactic (ATT&CK v20) | ATT&CK ID | Technique | Where non-executable memory helps |
|---|---|---|---|
| Initial Access | T1189 | Drive-by Compromise | Browser exploits that place shellcode in heap memory fault instead of running |
| Initial Access | T1190 | Exploit Public-Facing Application | Classic stack or heap overflow payloads in a server process cannot execute in place |
| Execution | T1203 | Exploitation for Client Execution | Document and client exploits must fall back to code reuse |
| Privilege Escalation | T1068 | Exploitation for Privilege Escalation | Injected code in data pages of a privileged process cannot run directly |
| Privilege Escalation, Stealth | T1055.012 | Process Hollowing | Partial: hollowing maps code as executable, so the gain is in forcing explicit, observable permission changes |
| Stealth | T1211 | Exploitation for Stealth | Same as other exploitation techniques |
| Stealth | T1620 | Reflective Code Loading | Partial: the loader must request executable memory, which ACG-style policies can block |
| Stealth | T1218.013 | Mavinject | Partial: injection into another process writes to its memory segments |
| Stealth | T1564.010 | Process Argument Spoofing | Little direct effect: the technique rewrites data, not code, in process memory |
| Lateral Movement | T1210 | Exploitation of Remote Services | Remote service exploits that rely on executing a buffer fail |
| Credential Access | T1212 | Exploitation for Credential Access | Same as other exploitation techniques |
| Credential Access, Collection | T1056.004 | Credential API Hooking | Partial: hooks must be placed in executable memory with explicit permission changes |
| Discovery | T1033 | System Owner/User Discovery | Little direct effect: D3FEND relates it because the technique may read process memory |
The fourth column is NHI Mgmt Group's reading, not D3FEND's. Read the table as "where the artefact overlaps", not as a coverage claim: non-executable memory blocks injected shellcode, not discovery or argument spoofing. Defensive detail for the techniques is in T1190 Exploit Public-Facing Application Defensive Guide, MITRE ATT&CK T1189 Drive-by Compromise, MITRE ATT&CK T1210 Exploitation of Remote Services and MITRE ATT&CK T1620 Reflective Code Loading. The mitigation view is in M1050 Exploit Protection.
Where it is implemented
Platforms are listed alphabetically.
| Platform or layer | Mechanism and setting | Exceptions to watch |
|---|---|---|
| CPU | NX (No eXecute) or XD (eXecute Disable) bit in page table entries. Red Hat notes that some firmware setup menus offer an option to turn it off | Firmware setting disabled on older or misconfigured hardware |
| Linux kernel | NX enabled by default when the CPU supports it; kernel parameter noexec=on is the default and noexec=off disables it, which Red Hat does not recommend. On 32-bit x86 the option needs a PAE kernel | noexec=off on the kernel command line |
| Linux toolchain (GNU ld) | -z noexecstack marks the stack non-executable; -z separate-code puts instructions on pages separate from data; --warn-rwx-segments (on by default) warns on segments that are readable, writable and executable; --error-rwx-segments and --error-execstack turn warnings into build failures | -z execstack, hand-written assembler objects with no .note.GNU-stack section, which on some targets implies an executable stack |
| macOS | Hardened Runtime, required for notarisation, disallows capabilities such as JIT compilation unless an exception entitlement is added | Entitlements com.apple.security.cs.allow-jit, com.apple.security.cs.allow-unsigned-executable-memory and com.apple.security.cs.disable-executable-page-protection (all default false) |
| Windows boot policy | BCDEdit nx value: OptIn (operating system components only), OptOut (all processes, with per-program opt-out), AlwaysOn (all processes, attempts to disable ignored), AlwaysOff (DEP disabled) | OptIn or AlwaysOff on servers; per-program opt-outs under OptOut |
| Windows exploit protection | DEP mitigation: enabled for x64, ARM and Arm64 executables and cannot be disabled; enabled for x86 binaries but can be disabled per process. Arbitrary code guard (ACG) goes further and blocks making memory executable at all (STATUS_DYNAMIC_CODE_BLOCKED), with an audit-only option | Enable ATL Thunk emulation for old ATL components; legacy x86 applications that generate code at runtime |
| Windows applications | Images built DEP-compatible (the MSVC /NXCOMPAT linker option); code in data sections moved to a code section or marked IMAGE_SCN_MEM_EXECUTE | JIT engines and plug-in hosts that need executable heaps |
Implementation steps
- Confirm hardware support and firmware state. On Linux, check for the nx flag in /proc/cpuinfo and the boot message "NX (Execute Disable) protection: active". On Windows, read the system policy (GetSystemDEPPolicy, or the nx entry in the boot configuration). Fix any firmware setting that has turned NX or XD off.
- Set the strongest workable system policy. On Windows servers aim for OptOut or AlwaysOn rather than OptIn. On Linux, make sure no host has noexec=off on the kernel command line.
- Inventory the exceptions. List 32-bit Windows processes with DEP disabled, programs on the OptOut exception list, ELF binaries with an executable stack (a GNU_STACK program header with execute permission) or RWX segments, and macOS apps carrying the three executable memory entitlements. Each needs an owner and a reason.
- Harden the build pipeline. Add --error-execstack and --error-rwx-segments (with the matching warning switches) to release builds, keep -z separate-code, and treat any new executable memory entitlement as a security review item.
- Tighten beyond DEP where it fits. For processes that never generate code, such as many server daemons and document renderers, test ACG in audit mode first, then enforce. Do not enforce it on JIT hosts.
- Pilot and roll back. Move legacy 32-bit applications to DEP enabled in a test ring; a crash on start usually means runtime code generation or old ATL thunks. Roll back with a per-application setting, not by weakening the system policy.
How to verify it works
- Platform check: record the boot NX message or CPU flag on Linux and the nx boot value on Windows in configuration compliance scans, so drift is reported.
- Binary check: scan release artefacts for executable stacks and RWX segments as part of the build; treat a hit as a failed build.
- Process check: for Windows, query each process's DEP state through endpoint tooling and alert on 32-bit processes running with DEP disabled.
- Functional test: in a lab, run a harmless test program that copies a few bytes into a heap buffer and jumps to them. It must crash with an access violation or segmentation fault. Keep it out of production estates.
- Audit evidence: where ACG is in audit mode, review blocked-in-audit events before enforcing.
| Metric | Example target (NHI Mgmt Group suggestion, not a standard) |
|---|---|
| Hosts with NX active and DEP policy OptOut or AlwaysOn | 100% of supported hardware |
| Release binaries with executable stack or RWX segments | Zero, or each with a documented exception |
| 32-bit processes running with DEP disabled | Falling quarter on quarter |
| Apps holding executable memory entitlements or ACG exemptions without an owner | Zero |
Limits, bypasses and failure patterns
- Code reuse: D3FEND states that ROP and return-to-libc can bypass D3-PSEP because they call code that legitimately sits in the .text segment. Pair it with D3-CFI Control Flow Integrity, D3-SFCV Stack Frame Canary Validation, D3-PAN Pointer Authentication and D3-SAOR Segment Address Offset Randomization, which make the reuse step harder.
- Attacker-requested executable memory: an exploit that has gained some control can call the operating system to mark its buffer executable. ACG on Windows and the absence of executable memory entitlements on macOS are the controls that close this path.
- JIT surfaces: browsers, script engines and runtimes that compile code keep writable or executable regions by design. Isolate them in separate processes where the platform allows.
- Legacy exceptions that become permanent: an OptOut entry added for one old application years ago stays forever unless it has an owner and a review date.
- Silent toolchain regressions: one hand-written assembler file without a .note.GNU-stack section can mark a whole binary as needing an executable stack on some targets. Build-time errors catch it; reviews rarely do.
- Software-only enforcement: D3FEND notes that non-hardware enforcement is easier to switch off for a page of memory; rely on hardware NX or XD.
Framework mapping
| Framework | Reference | Relevance |
|---|---|---|
| NIST SP 800-53 Rev 5 | SI-16 Memory Protection | Controls to protect system memory from unauthorised code execution, the direct home of D3-PSEP |
| NIST SP 800-53 Rev 5 | CM-7 Least Functionality; SA-15 Development Process, Standards, and Tools | Removing executable memory exceptions; toolchain flags as development standards |
| NIST CSF 2.0 | PR.PS-01, PR.PS-06 | Configuration management practices; secure software development practices |
| MITRE ATT&CK | M1050 Exploit Protection | The ATT&CK mitigation that covers DEP and related exploit mitigations |
| MITRE D3FEND | Application Hardening, D3-PSEP | Parent and technique |
Related countermeasures
- D3-PCSV Process Code Segment Verification: checks that the code segment itself has not been altered, the complement to keeping code only there. See D3-PCSV Process Code Segment Verification.
- D3-EHPV Exception Handler Pointer Validation: the SafeSEH-style check D3FEND mentions alongside software DEP. See D3-EHPV Exception Handler Pointer Validation.
- Memory-safe languages: remove the memory corruption that D3-PSEP only contains. See the Memory-Safe Languages Guide.
- Overview: the Application Hardening family and the rest of the knowledge base are introduced in the MITRE D3FEND Guide.
Practitioner checklist
- Confirm NX or XD is enabled in firmware and active at boot on every host class.
- Set Windows DEP policy to OptOut or AlwaysOn on servers and record any per-program exceptions.
- Check no Linux host boots with noexec=off.
- Fail release builds on executable stacks and RWX segments.
- Register every JIT host, ATL thunk exception and macOS executable memory entitlement with an owner.
- Test ACG in audit mode for processes that never generate code, then enforce.
- Pair D3-PSEP with control flow integrity, stack canaries and address randomisation.
- Include DEP and NX state in configuration compliance reporting.
Standards and references
- MITRE D3FEND: Process Segment Execution Prevention (D3-PSEP)
- NIST: SP 800-53 Rev. 5 Security and Privacy Controls (SI-16 Memory Protection)
- Microsoft Learn: Data Execution Prevention
- Microsoft Learn: BCDEdit /set (nx option)
- Microsoft Learn: Exploit protection reference
- Red Hat: What is NX/XD feature?
- GNU Binutils: ld command-line options
- Apple Developer: Hardened Runtime
Related NHI Mgmt Group resources: MITRE D3FEND Guide · M1050 Exploit Protection · D3-CFI Control Flow Integrity · Memory Safety Roadmap Guide