Untrusted Pointer Dereference

ID

swift.untrusted_pointer_dereference

Severity

high

Resource

Memory management

Language

Swift

Tags

CWE:822, CWE:823, NIST.SP.800-53, OWASP:2021:A3, PCI-DSS:6.5.10

Description

Rationale

Swift is generally a safe language with many safety features (e.g. memory safety, automatic reference counting aka ARC, no raw pointer arithmetic in "safe" code). However, there are ways in Swift code (especially when interacting with lower-level APIs or "unsafe" constructs) where pointer misuse can produce memory corruption. Some of the typical cases:

  • Use of UnsafePointer, UnsafeMutablePointer, UnsafeRawPointer, unsafe buffer pointers, etc.

func badPointerAccess() {
    let value: Int = 42
    let ptr = UnsafePointer<Int>(bitPattern: 0xDEADBEEF)! // arbitrary, untrusted address
    print(ptr.pointee) // ❌ Dereferencing arbitrary pointer → memory corruption
}

Here, the pointer is created from an arbitrary integer (0xDEADBEEF). Dereferencing it (pointee) may read invalid memory, crash, or expose sensitive memory.

  • Interfacing with C libraries or APIs ('bridging') where pointers are passed in or returned from untrusted sources.

func unsafeBridging() {
    let str = "hello"

    // Force bridge to C string, but deallocate too early
    let cStr = strdup(str) // allocate C string

    free(cStr) // memory released

    // FLAW -  Still using freed memory
    let swiftStr = String(cString: cStr!)
    print(swiftStr) // FLAW - Use-after-free → undefined behavior
}
  • Pointer arithmetic in raw memory (e.g. moving pointers relative to others) without validating bounds or correct typing.

func unsafeArithmetic() {
    var array = [10, 20, 30]

    array.withUnsafeMutableBufferPointer { buffer in
        let ptr = buffer.baseAddress!

        // Going past the array bounds
        let outOfBoundsPtr = ptr.advanced(by: 10)
        // FLAW - Undefined behavior (reading past allocated memory)
        print(outOfBoundsPtr.pointee)
    }
}
  • Incorrect alignment or aliasing when converting raw memory to typed pointers or vice versa.

func misalignedAccess() {
    var bytes: [UInt8] = [0, 1, 2, 3, 4, 5, 6, 7]

    bytes.withUnsafeBytes { rawBuffer in
        // Misaligned pointer: casting raw pointer to Int32
        let misalignedPtr = rawBuffer.baseAddress!
          .advanced(by: 1)
          .assumingMemoryBound(to: Int32.self)

        print(misalignedPtr.pointee) // ❌ May crash due to alignment fault
    }
}

Advancing by 1 makes the pointer unaligned for Int32. On some CPUs (e.g. ARM), unaligned loads can trap, leading to runtime crash.

  • Use-after-free, double-free, or null-pointer dereference when pointers are not managed carefully.

func useAfterFree() {
    let ptr = UnsafeMutablePointer<Int>.allocate(capacity: 1)
    ptr.initialize(to: 99)
    ptr.deallocate() // memory released
    // ❌ Use-after-free (dereferencing invalid memory)
    print(ptr.pointee)
}

Dereferencing a deallocated pointer can lead to undefined behavior, including crashes, data corruption, or security vulnerabilities.

func doubleFree() {
    let ptr = UnsafeMutablePointer<Int>.allocate(capacity: 1)
    ptr.initialize(to: 7)

    ptr.deallocate()
    // ❌ Double-free → memory allocator corruption
    ptr.deallocate()
}

The second deallocate() corrupts heap metadata. It can destabilize the app or be exploited with crafted input.

func nullPointerDereference() {
    let nullPtr: UnsafePointer<Int>? = nil
    // ❌ Force-unwrapping nil pointer
    print(nullPtr!.pointee)
}

Unwrapping a nil UnsafePointer leads to an immediate crash.

Remediation

Here are best practices / mitigations when dealing with this kind of vulnerabilities in Swift:

  • Minimize unsafe pointer usage

Use the safe standard library APIs as much as possible. Only use UnsafePointer, UnsafeMutablePointer, etc. when absolutely necessary (e.g. for performance, interoperability with C).

  • Validate inputs

If pointer values or offsets come from user input, external data, or anything not completely under your control, validate them. Check bounds, check that pointers are not null, check alignment, etc.

  • Use safe abstraction layers

Build or use wrappers that encapsulate unsafe pointer operations, so that the unsafe code is localized, tested thoroughly, and has good validation.

  • Mind pointer lifetimes

Make sure that the memory pointed to is still valid when you dereference. Avoid use-after-free.

  • Null / optional handling

If you obtain pointers which may be null (or optional), properly handle the null case before dereferencing.

  • Unit tests + fuzzing / memory safety tools

Adding tests that cover invalid pointer / offset edge cases. Also using runtime tooling (instruments, sanitizers such as Address Sanitizer) to catch memory corruption in unsafe code paths.

  • Code reviews / peer audits

Unsafe pointer work should be carefully reviewed. Given the high risk of one error causing serious security or stability issues, peer review is especially valuable.

Configuration

This detector does not need any configuration.

References