Skip to content

AD3023: ProperLoadSegments

Summary

Property Value
ID AD3023
Name ProperLoadSegments
Category Correctness
Severity Error
Applies to ELF (Linux/Unix)

Description

This rule checks that ELF LOAD segments have proper permission flags and do not violate the W^X (Write XOR Execute) principle. Segments that are both writable and executable represent a significant security risk.

Why This Matters

W^X (Write XOR Execute) is a fundamental security principle: memory should be writable OR executable, never both. Violating this through improper LOAD segments creates trivially exploitable conditions.

The W^X Principle

Secure memory layout:
  .text   R-X  (Code: run but don't modify)
  .rodata R--  (Constants: read only)
  .data   RW-  (Variables: modify but don't run)
  .bss    RW-  (Uninitialized: modify but don't run)

Insecure layout:
  .text   RWX  (Code: can inject and execute)
  ↑ Attacker writes shellcode to .text, executes it

Exploitation Impact

Segment Permissions Exploitation
R-X (code) Must use ROP/JOP
RW- (data) Cannot execute
RWX (writable+exec) Direct shellcode injection

Why RWX Is Trivially Exploitable

With RWX segment:
  1. Attacker finds write primitive
  2. Writes shellcode to RWX region
  3. Jumps to shellcode
  4. Game over

Without RWX:
  1. Attacker finds write primitive
  2. Cannot write executable code
  3. Must use ROP (complex chains)
  4. Eventually call mprotect/VirtualProtect
  5. Then execute payload

RWX removes many exploitation hurdles.

Common Causes of RWX Segments

Cause Why It Happens Fix
Nested functions GCC trampolines on stack Avoid or -fno-trampolines
JIT compilers Dynamic code generation Use mprotect properly
Old linker scripts Legacy defaults Update scripts
Self-modifying code Intentional but dangerous Redesign

Proper JIT Design

Bad JIT:
  1. Allocate RWX memory
  2. Write code
  3. Execute
  4. RWX forever (attackable)

Good JIT:
  1. Allocate RW memory
  2. Write code
  3. mprotect → R-X
  4. Execute
  5. To modify: mprotect → RW, write, mprotect → R-X

Never simultaneously W and X.

Hardened System Enforcement

System RWX Handling
SELinux (strict) Blocks RWX mmap
PaX (grsecurity) MPROTECT prevents W→X
OpenBSD W^X enforced
iOS No RWX for third-party apps

Segment Permission Best Practices

Segment Correct Permissions
.text R-X
.rodata R--
.data RW-
.bss RW-
.plt R-X
.got R-- (with Full RELRO)
  • W^X violation: RWX segments allow code injection attacks
  • Code integrity: Executable code should never be writable
  • Data protection: Writable data should never be executable
  • Exploit mitigation: Proper segment permissions are fundamental to security

What This Rule Checks

  1. No RWX segments: No LOAD segment should be Read+Write+Execute
  2. Code segments: Should be R-X (readable, executable, not writable)
  3. Data segments: Should be RW- (readable, writable, not executable)
  4. Read-only data: Should be R-- (readable only)

How to Fix

Ensure proper compiler/linker flags

# Standard compilation should produce proper segments
gcc -o myapp myapp.c

# Explicitly ensure no executable stack
gcc -Wl,-z,noexecstack -o myapp myapp.c

# Check for RWX segments
readelf -l myapp | grep -E "RWE|RWX"

Common causes of RWX segments

  1. Nested functions (GCC trampolines): Avoid nested functions
  2. JIT compilation: Use proper mprotect() calls
  3. Self-modifying code: Redesign to avoid
  4. Old linker scripts: Update to modern practices

Fix nested functions

// Bad: Nested function requires executable stack
void outer() {
    void inner() { /* ... */ }
    inner();
}

// Good: Use static function or function pointer
static void inner() { /* ... */ }
void outer() {
    inner();
}

Example

Fail: Binary has RWX segment

LOAD    0x001000 0x00401000 0x00401000 0x1000 0x1000 RWE 0x1000

Pass: Proper segment permissions

LOAD    0x000000 0x00400000 0x00400000 0x1000 0x1000 R   0x1000
LOAD    0x001000 0x00401000 0x00401000 0x2000 0x2000 R E 0x1000
LOAD    0x003000 0x00403000 0x00403000 0x1000 0x1000 RW  0x1000

See Also