AI-powered crash analysis is now available on all plans — including Free.Read the crash analysis guide

Mobile Native Memory Corruption Debugging: ASan Guide

NFNourin Mahfuj Finick··8 min read

Native memory corruption bugs are among the most elusive and dangerous defects in mobile development. When you're deep in mobile memory corruption debugging, you're hunting crashes that leave cryptic stack traces, corrupt heap metadata, and often manifest in completely unrelated parts of your app — sometimes hours after the original damage occurred. Unlike higher-level crashes from null pointers or unhandled exceptions, native memory corruption silently poisons your app's state until it collapses in ways that traditional crash reporters struggle to explain.

These bugs exploit the gap between managed runtime safety (Kotlin's null safety, Swift's ARC) and the unmanaged reality of C/C++ code in NDK libraries, game engines, media codecs, and third-party SDKs. A single memcpy overshooting by four bytes can corrupt an adjacent allocation, and you won't know until that object gets accessed minutes later in a different thread. This guide covers Android's ASan, HWASan, and GWP-ASan, plus iOS's Address Sanitizer, Malloc Stack Logging, Guard Malloc, and Zombie objects — so you can detect these bugs at development time rather than after they ship.

Understanding Native Memory Corruption

Use-after-free (UAF) occurs when a pointer is dereferenced after the underlying memory has been freed and potentially reallocated. On mobile, Android and iOS allocators aggressively reuse memory, so a UAF often reads or writes arbitrary data structures — producing segfaults at addresses containing unexpected values or, worse, silent data corruption with no crash at all.

Heap buffer overflow happens when writes extend beyond an allocated buffer. Off-by-one errors in loops, strcpy into undersized buffers, and incorrect memcpy sizes are classic culprits. ARM processors have stricter alignment requirements than x86, so overflows that happen to work on a developer's x86 simulator reliably crash on ARM64 devices.

Heap metadata corruption targets the internal bookkeeping structures that malloc/free use to track allocations. When you overflow a buffer adjacent to the allocator's metadata, you corrupt size fields, linked-list pointers, or free-list entries. The crash often appears inside malloc or free itself, misleading developers into thinking the allocator is buggy when the damage happened earlier.

Double-free occurs when the same pointer is passed to free() twice. The allocator may have already reused that memory for a different allocation, so the second free corrupts the new allocation's metadata — creating cascading problems that are notoriously difficult to trace back to the original mistake. Invalid free happens when free() is called on a pointer that was never heap-allocated (a stack address, a static variable, or a garbage value), immediately corrupting the allocator's internal state.

Why Mobile Makes It Harder

Memory overhead is the critical bottleneck. Full ASan on Android can increase your app's memory footprint by 2-3x due to shadow memory tracking every allocation. Apps that normally use 800MB can exhaust available RAM under ASan, triggering low-memory kills that look like crashes but are sanitizer-induced. Google introduced HWASan specifically to address this — it uses ARM's Top Byte Ignore (TBI) feature, reducing overhead to roughly 10-15% instead of 2-3x.

ARM architecture adds complexity: unaligned memory accesses that silently succeed on x86 trigger alignment exceptions on ARM, and weaker memory ordering means race conditions with corrupted memory manifest differently across architectures. The simulator gap compounds this — iOS Simulator runs x86_64 code with clean ASan support, but real ARM64 devices need platform-specific sanitizer configurations. Android x86_64 emulators support ASan well, but ARM emulator images have historically lacked full sanitizer support, forcing teams to test on physical devices.

Android Sanitizers: ASan, HWASan, and GWP-ASan

AddressSanitizer (ASan)

ASan instruments every memory access to detect out-of-bounds reads/writes, use-after-free, and double-free errors. Enable it in your NDK CMakeLists.txt:

set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fsanitize=address -fno-omit-frame-pointer")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=address -fno-omit-frame-pointer")
set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -fsanitize=address")

ASan requires a wrap script to preload the sanitizer runtime via LD_PRELOAD. When it detects a heap buffer overflow, it produces a detailed report with the source location, allocation/deallocation stacks for UAF cases, and a shadow memory map. The 2-3x memory overhead limits it to emulators and high-end devices with 6GB+ RAM — reserve lighter-weight sanitizers for broader device coverage.

Hardware-Assisted Address Sanitizer (HWASan)

HWASan is Google's production-feasible alternative for physical Android devices. It leverages ARM64's Top Byte Ignore to store memory tags in the upper bits of pointers, comparing them against shadow memory on every access. Compile with -fsanitize=hwaddress -fno-omit-frame-pointer. The memory overhead drops to 10-15%, making HWASan viable on 4GB devices. Google uses it extensively in Android platform testing, and the Android NDK documentation provides complete setup instructions.

HWASan catches use-after-free within 16-byte tagging granularity and detects buffer overflows crossing tag boundaries every 16 bytes — catching the vast majority of real-world overflow bugs.

GWP-ASan (Probabilistic Sampling)

GWP-ASan randomly samples a small percentage of heap allocations and places them next to inaccessible guard pages. An overflow or use-after-free touching the guard page triggers SIGSEGV that GWP-ASan catches and reports. It's enabled by default in Android 11+ for native code with near-zero overhead — no build changes required. Google's GWP-ASan documentation explains its effectiveness at catching rare use-after-free bugs in production.

The optimal strategy: ASan on emulators during development, HWASan on physical devices in pre-release, and GWP-ASan as the production safety net.

iOS Sanitizers: ASan, Malloc Stack Logging, Guard Malloc, and Zombies

Address Sanitizer

Enable ASan in Xcode's scheme editor under Diagnostics. It instruments all C, C++, and Objective-C code. Detected errors produce annotated source, allocation/deallocation history, and a memory graph of the corrupted region. For CI, use xcodebuild with ENABLE_ADDRESS_SANITIZER = YES in a .xcconfig file. Apple's documentation covers complete setup and report interpretation.

Malloc Stack Logging

Set MallocStackLogging=1 in your scheme's environment variables to record the call stack for every malloc/free. The malloc_history tool then queries allocation records. Combined with ASan, you get both the corruption detection and the complete allocation history that led to it — showing exactly when and where memory was freed, often in a completely different component than the crashing code.

Guard Malloc and Zombie Objects

Guard Malloc places each allocation on its own virtual memory page with an inaccessible guard page following. Any overflow triggers an immediate crash at the offending instruction. Enable it in the scheme editor or via libgmalloc.dylib. The massive memory overhead (32KB+ per allocation) makes it practical only for targeted testing of specific code paths.

Zombie objects convert deallocated Objective-C objects into logging proxies instead of freeing them, catching messages sent to deallocated objects. While not detecting raw C/C++ corruption directly, iOS apps often mix Objective-C UI layers with C++ engine code, making zombie detections an early signal of corruption elsewhere.

Integrating Sanitizers into CI/CD

  1. Debug ASan build: Compile with -fsanitize=address and run your full test suite on x86_64 emulators. Fail the build on any ASan report — there are virtually no false positives. Every finding is a genuine bug.

  2. HWASan device testing: Build with -fsanitize=hwaddress and run on physical ARM64 devices via Firebase Test Lab or similar device farms. Even a basic smoke test catches ARM-specific bugs that x86_64 ASan misses.

  3. Production GWP-ASan monitoring: Ensure your crash reporting tool captures native crashes with full register dumps and memory maps. When GWP-ASan triggers SIGSEGV, the report includes allocation metadata and the guard-page fault address for straightforward identification.

Debugging Memory Corruption from Production

Some memory corruption bugs only manifest in production, triggered by specific device configurations or concurrency timings. Native crash forensics bridges this gap — raw crash dumps with stack traces are often insufficient because the crash occurs far from the original corruption.

Look for these signals in your crash data: allocator crashes inside malloc/free indicating heap metadata corruption, wild pointer crashes at addresses containing recognizable data patterns (classic UAF), non-deterministic crashes that move between builds, and multiple unrelated crash signatures tracing to the same native library.

Tools like Bugspulse capture rich native crash reports with annotated memory maps and thread states, helping you work backward from the crash to the corruption source. Pair this with pre-release testing that includes HWASan coverage to create a feedback loop where production findings inform your sanitizer configurations.

Getting Started Today

  1. Add ASan to your debug builds today. The setup is minimal — compiler flags and a wrap script — and the first ASan report in your own codebase is worth the effort.
  2. Run your test suite under ASan. Fix every finding. Every ASan report represents a real crash waiting to happen. If native code coverage is thin, write targeted tests for JNI bridges, media pipelines, and C/C++ SDK wrappers.
  3. Add HWASan to device testing. Upload HWASan APKs alongside production builds to your device farm, or keep one device running HWASan during QA cycles.
  4. Verify GWP-ASan in production. Check crash reports for GWP-ASan in logcat. If absent, confirm your app targets Android 11+ without disabling the feature.

The tools exist, they're mature, and they're free. The gap between teams that catch memory corruption in CI and teams that learn about it from one-star app store reviews is simply whether they configured the sanitizers. Memory corruption has been a thorn in native development for decades, but the tooling available to mobile developers today — across both Android and iOS — can catch these bugs before they ever reach a user's device. The key is to stop treating sanitizers as optional debugging aids and start treating them as mandatory build infrastructure, no different from your compiler or linker.

Start building more resilient mobile apps — sign up for Bugspulse and ensure every native crash, from use-after-free to heap corruption, is captured with the diagnostic detail your team needs to fix it fast.