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 |
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.
References
-
CWE-822 : Untrusted Pointer Dereference.
-
CWE-823 : Use of Out-of-range Pointer Offset.
-
OWASP Top-10 2021: A08 - Software and Data Integrity Failures.
-
UnsafePointer in Swift Documentation.
-
Memory Safety in the Swift Book.
-
Unsafe Swift - WWDC 2020 video.