Skip to content

AD5012: ValidateSegmentPermissions

Summary

Property Value
ID AD5012
Name ValidateSegmentPermissions
Category Security
Severity Error
Applies to Mach-O (macOS/iOS)

Description

Segments should follow the W^X (Write XOR Execute) principle: memory should either be writable or executable, but not both. The __TEXT segment should be read-execute only (not writable), and __DATA segments should be read-write only (not executable). Violations allow attackers to inject and execute arbitrary code.

How It Works

The rule examines segment memory protections:

  1. __TEXT is read-execute only: Code segment should never be writable
  2. __DATA is read-write only: Data segment should never be executable
  3. No W^X violations: No segment should have both write AND execute permissions

Why This Matters

W^X (Write XOR Execute) is the most fundamental memory protection. Violating it by having writable+executable segments gives attackers a trivial path to code execution—write shellcode, execute it.

The Attack Made Trivial

With proper W^X:
  Attacker: "I have a write primitive"
  Result:   Can write to DATA, but can't execute it
            Can't write to TEXT (it's read-only)
            Must use complex ROP/JOP

With W^X violation (RWX segment):
  Attacker: "I have a write primitive"
  Result:   Write shellcode to RWX segment
            Jump there
            Done. No ROP needed.

Segment Permission Best Practices

Segment Correct Permissions Wrong Permissions
__TEXT R-X RWX (writable code!)
__DATA RW- RWX (executable data!)
__DATA_CONST R-- RW- (modifiable constants!)
__LINKEDIT R-- RW- (modifiable metadata!)

Attack Scenarios

Writable __TEXT:

1. Attacker exploits vulnerability
2. Gets write primitive
3. Writes directly to __TEXT segment
4. Replaces legitimate code with malicious code
5. Legitimate code paths now execute malware

Executable __DATA:

1. Attacker exploits vulnerability
2. Writes shellcode to __DATA
3. Hijacks control flow to __DATA
4. Shellcode executes

Hardware Enforcement

Apple Platform W^X Enforcement
iOS Kernel enforces strictly
macOS Intel CPU enforces via page tables
Apple Silicon Hardware enforces, PAC adds more

Why Violations Might Occur

Cause Better Approach
JIT compilation Use proper mprotect lifecycle
Self-modifying code Redesign architecture
Legacy toolchain Update tools
Bad linker script Fix segment definitions

Proper JIT Pattern

Secure JIT:
  1. Allocate RW memory
  2. Write generated code
  3. mprotect() to R-X
  4. Execute
  5. To modify: mprotect() to RW, write, mprotect() to R-X

Never simultaneously W and X.

Apple's Hardened Runtime

Hardened Runtime enforces proper permissions:

With Hardened Runtime:
  - JIT capability requires entitlement
  - Memory pages can't be W+X simultaneously
  - Runtime checks enforce W^X

W^X Principle

The W^X (Write XOR Execute) principle is a fundamental security protection:

Allowed:
  Read + Execute (code)  ✓
  Read + Write (data)    ✓
  Read only              ✓

Forbidden:
  Write + Execute        ✗  ← Allows code injection

Attack Scenarios

Writable __TEXT:

// Attacker can modify code in memory
void* code_page = get_text_section();
memcpy(code_page, shellcode, sizeof(shellcode));
// Original code replaced with malicious code

Executable __DATA:

// Attacker stores shellcode in data section
char buffer[1024];
strcpy(buffer, shellcode);  // Buffer overflow
jump_to(buffer);  // Execute shellcode from data

Resolution

Default Behavior

Modern linkers create proper segment permissions by default. If you're seeing violations, check for:

  1. Custom linker scripts that modify segment permissions
  2. Inline assembly that requires writable code
  3. Legacy code that self-modifies

Xcode

Ensure no custom linker flags modify segment permissions:

  1. Select your target
  2. Go to Build SettingsLinking
  3. Check Other Linker Flags for segment permission overrides

Linker Flags to Avoid

# Do NOT use these flags:
-segprot __TEXT rwx rwx      # Writable text
-segprot __DATA rwx rwx      # Executable data
-allow_stack_execute         # Executable stack

For JIT Compilers

If you need dynamic code generation (JIT):

// Allocate memory with write permission
void* code = mmap(NULL, size, PROT_READ | PROT_WRITE,
                  MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);

// Generate code
generate_code(code);

// Change to read-execute (remove write)
mprotect(code, size, PROT_READ | PROT_EXEC);

// Execute the code
((void(*)())code)();

When to Suppress

This rule may be suppressed for:

  • JIT compilers: JavaScript engines, bytecode interpreters
  • Dynamic code generation: Runtime code patching
  • Debuggers: May need to modify code for breakpoints

Platform Notes

  • iOS: Strictly enforces W^X; JIT is only allowed for Safari
  • macOS with Hardened Runtime: Enforces W^X
  • macOS without Hardened Runtime: May allow violations
  • AD5002 - Stack execution check
  • AD5005 - Heap execution check
  • AD5011 - Code signature requirement

References