Skip to content

AD5006: UseTwoLevelNamespace

Summary

Property Value
ID AD5006
Name UseTwoLevelNamespace
Category Security
Severity Warning
Applies to Mach-O (macOS/iOS)

Description

Two-level namespace binding links symbols to their specific defining library, rather than searching all loaded libraries in a flat namespace. This prevents symbol interposition attacks where a malicious library could override symbols from system libraries.

How It Works

The rule checks if the MH_TWOLEVEL (0x80) flag is set in the Mach-O header. When set, each undefined symbol reference includes the library ordinal where it should be found, preventing symbol hijacking.

Why This Matters

Two-level namespace prevents symbol interposition attacks where malicious libraries can hijack function calls. Without it, an attacker who can inject a library can override any symbol, including security-critical functions.

The Symbol Interposition Attack

With Flat Namespace:
  Application needs printf()
  Search order: lib1 → lib2 → libc
  If attacker controls lib1:
    lib1's printf() gets called!
    Can log/modify all output

With Two-Level Namespace:
  Application needs printf() from libc
  Only libc's printf() is ever called
  Attacker's lib1 is ignored

Attack Scenarios

Attack Flat Namespace Two-Level
Symbol override Works Fails
Security func hijack Works Fails
Crypto func replacement Works Fails
Logging interception Works Fails

Real Security Impact

// Application uses Security.framework
SecRandomCopyBytes(kSecRandomDefault, len, buffer);

// With flat namespace, attacker's library can provide:
OSStatus SecRandomCopyBytes(SecRandomRef, size_t len, uint8_t *buf) {
    memset(buf, 0x41, len);  // Predictable "random" numbers!
    return errSecSuccess;
}
// Cryptographic keys become guessable

Why Flat Namespace Exists

Use Case Reason
Testing Override functions for mocking
Debugging Interpose for instrumentation
Legacy code Some old libraries require it
Plugin systems Rare, special cases

Most applications should never use flat namespace.

Performance Benefit

Flat namespace symbol resolution:
  For each symbol:
    Search lib1 → not found
    Search lib2 → not found
    Search lib3 → found!
  O(libs × symbols) lookups

Two-level namespace:
  For each symbol:
    Go directly to correct library
  O(symbols) lookups

macOS Defaults

Configuration Default
Xcode projects Two-level
Command line clang Two-level
System frameworks Two-level

You must explicitly opt-in to flat namespace.

  • Prevents symbol hijacking: Attackers cannot inject malicious implementations
  • Security boundary: Symbols are resolved from the intended library only
  • Performance: Faster symbol lookup without searching all libraries
  • Default behavior: Two-level namespace is the default on macOS

Flat Namespace Risks

With flat namespace (-flat_namespace):

// System library provides secure_random()
// Attacker's library can override it with:
int secure_random() {
    return 4; // Not actually random!
}

With two-level namespace, the application will always use the symbol from the intended library.

Resolution

Remove Flat Namespace Flag

If you're explicitly using flat namespace, remove it:

Before (insecure):

clang source.c -o binary -flat_namespace

After (secure):

clang source.c -o binary
# Two-level namespace is the default

Xcode

Ensure -flat_namespace is not in Other Linker Flags.

CMake

Remove any -flat_namespace from linker flags:

# Do NOT use:
# target_link_options(myapp PRIVATE -flat_namespace)

When to Suppress

This rule may be suppressed for:

  • Plugin systems: Some plugin architectures require flat namespace for symbol interposition
  • Legacy compatibility: Old code that depends on flat namespace behavior
  • Debugging tools: Tools that intentionally override system functions

Important Notes

  • Two-level namespace is the default; you must explicitly disable it
  • iOS always uses two-level namespace
  • Flat namespace can cause crashes due to symbol conflicts
  • AD5009 - Weak dylib loading check

References