History of NULL Pointer Dereferences on macOS

Finding a crash while fuzzing is just the beginning of Vulnerability Research. After finding a crash, Exploit Development is often a long journey. Not every bug is exploitable, and my previous article (Case Study: Analyzing macOS IONVMeFamily Driver Denial of Service Issue) is great proof of that. Sometimes, I am sure that something can be exploited, but then I face a wall of mitigations implemented by the OS.
During my recent fuzzing session, I discovered a bug that, after some readings, trial and error, and reverse engineering, was determined to be unexploitable due to various mitigations implemented in macOS over the years. One of these mitigations specifically addresses NULL pointer dereferences.
I have come across numerous valuable resources on this subject, but locating them took considerable time. Therefore, I decided to write an overview article summarizing the key points and include links to these references for others seeking answers to a specific question:
Why NULL Pointer Dereferences on macOS since arm64 is no longer exploitable? ENJOY!
Introduction: NULL Pointer Dereferences on Apple Silicon
A NULL pointer dereference occurs when software attempts to access memory at address 0 (the NULL address) via a pointer set to NULL. On ARM64 (like Apple Silicon), address 0 is typically not mapped in virtual memory as on other architectures. Thus, dereferencing a NULL pointer usually triggers a memory access fault. In kernel context, this fault causes an immediate panic rather than reading or writing valid data. The consequence is a Denial of Service (crash) since the kernel stops the execution to protect itself.

A NULL pointer dereference is a programming error that does not execute attacker-controlled code — it simply faults. Below is a simple userland example you can compile and run to see for yourself:
//clang -o null_dereference null_dereference.c
#include <stdio.h>
int main() {
int *ptr = NULL; // Initialize a pointer to NULL
int value = *ptr; // Attempt to dereference the NULL pointer
printf("Value: %d\n", value); // This line will never be reached if the above dereference causes a crash
return 0;
}
Code language: PHP (php)

Historically, attackers have found ways to exploit kernel NULL dereferences under certain conditions.
How could we exploit it?
The classic exploit scenario involves tricking the kernel into not faulting on address zero by mapping a controlled memory page at that address. If successful, instead of crashing, the kernel might read or execute attacker-supplied data at address 0, potentially leading to code execution in kernel mode.
On ARM64-based systems, Apple has set up memory protections such that the zero address remains unmapped during normal operation. Any attempt to use a NULL pointer results in a data abort (memory access exception). Thus, a NULL dereference in the kernel will generally halt the system for safety.
Only if an attacker can somehow place controlled data at address zero and have the kernel use it could it become an exploitable condition. As we will see, modern macOS make this extremely difficult.
Historical Exploitation of NULL Dereferences in macOS
In earlier macOS versions (on Intel architectures), the general method was to map a fake object at the NULL address in user space so that when the kernel mistakenly dereferenced a NULL pointer, it would access the attacker’s fake data instead of faulting. This required bypassing the OS’s normal prevention of low-address memory mapping.
There were a few cases where this was possible.
__PAGEZERO
On macOS, 64-bit processes by default cannot map memory at address 0 – the system reserves a large unmapped region to catch null pointer bugs. In every Mach-o, we can find __PAGEZERO
segment:
In fact, for 64-bit Mach-O binaries, the first 4GB of address space is reserved and inaccessible.
NULL Page Mapping Technique with 32-bit binaries
However, in older macOS versions, back when 32-bit apps were supported, we could compile a 32-bit binary (-m 32
) with the linker flag to disable the page-zero reservation (-pagezero_size 0x0
).
// clang -o poc poc.c -framework IOKit -m32 -pagezero_size 0x0
Code language: JSON / JSON with Comments (json)
This legacy quirk allowed a 32-bit process to allocate memory successfully at address 0. Attackers used this to place shellcode or fake objects at NULL and then trigger the kernel vulnerability.
// 1. Deallocate the NULL page region:
err = vm_deallocate(mach_task_self(), 0x0, 0x1000);
// 2. Allocate memory at address 0:
vm_address_t addr = 0;
err = vm_allocate(mach_task_self(), &addr, 0x1000, 0);
// 3. Fill the NULL page with controlled data:
char* np = 0;
for (int i = 0; i < 0x1000; i++){
np[i] = '\x41';
}
Code language: JavaScript (javascript)
For instance, Piotr Bania from Cisco Talos demonstrateWd this in 2016 by exploiting an Intel graphics driver bug. He compiled a 32-bit payload with no pagezero
, mapped a page at 0
, and got the kernel to call a function pointer from that page, achieving kernel code execution and even bypassing KASLR.
Notably, this required disabling SMEP protection which is discussed later.
TPWN 0-day (OS X Yosemite, 2015)
But before that, a well-known example was Luca Todesco’s 2015 exploit targeting OS X Yosemite, who combined a NULL pointer dereference in IOKit with an info leak to gain root privileges. The exploit was called “tpwn
” and is publicly available here. Below is a simplified exploit flow:
[Initial User Access]
↓
[Information Leak Vulnerability]
↓
[Obtain kalloc.1024 zone pointer]
↓
[Bypass Kernel ASLR]
↓
[Memory Corruption Primitive]
↓
[NULL Pointer Dereference in IOKit]
↓
[OR 0x10 anywhere in kernel memory]
↓
[Corrupt vtable pointer]
↓
[Trigger IOServiceRelease]
↓
[Execute code at vtable+0x20]
↓
[Stack pivot (RSP = RAX)]
↓
[Execute ROP chain]
↓
[Set UID to 0 (root)]
↓
[Clean up memory corruption]
↓
[Adjust task counts]
↓
[Unlock IOAudioEngine locks]
↓
[Root privileges achieved]
Code language: CSS (css)
Todesco also developed a protection called NULLGuard to defend against NULL pointer dereference bugs, which were later mitigated in OS X 10.11 (El Capitan), which introduced System Integrity Protection (SIP) and other hardening measures.
At the time, NULL dereferences could be a stepping stone to escalation if other conditions were met.
macOS OS X 10.11 (El Capitan) – IOKit Driver Exploits Race (2015–2016)
Many vulnerabilities were discovered during that time in macOS kernel drivers where a NULL pointer was used as an object reference. By mapping the NULL page in a malicious process and then racing or forcing a pointer to become NULL, researchers could achieve control of the instruction pointer.
- For example, CVE-2016-1846 in the NVIDIA GeForce driver found by Ian Beer, allowed a user to map address zero and place a fake C++ object there. The driver then called a virtual function on a NULL object, jumping to an attacker-controlled function pointer from the fake Virtual Table.
┌────────────────────────────────────────────────────────────────┐
│ Process Virtual Memory │
├────────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────────┐ │
│ │ NULL Page │ ◄── 1. vm_deallocate(0x0, 0x1000) │
│ │ (0x0-0xFFF) │ ◄── 2. vm_allocate(&addr=0, 0x1000, 0) │
│ │ │ ◄── 3. Fill with 0x41 bytes │
│ └─────────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────┐ │
│ │ Fake VTable │ │
│ │ 0x41414141 │ ◄── Function pointers in VTable │
│ │ 0x41414141 │ (will be called by kernel) │
│ │ ... │ │
│ └─────────────┘ │
│ │
└────────────────────────────────────────────────────────────────┘
┌────────────────────────────────────────────────────────────────┐
│ Kernel Execution Flow │
├────────────────────────────────────────────────────────────────┤
│ │
│ Thread 1 Thread 2 │
│ ┌─────────────┐ ┌─────────────┐ │
│ │ Open driver │ │ Wait for │ │
│ │ connection │──┐ ┌──│ spinlock │ │
│ └─────────────┘ │ │ └─────────────┘ │
│ │ │ │ │ │
│ ▼ │ │ ▼ │
│ ┌─────────────┐ │ │ ┌─────────────┐ │
│ │ Release │ │ │ │ Acquire │ │
│ │ spinlock │──┼────────┼─▶│ spinlock │ │
│ └─────────────┘ │ │ └─────────────┘ │
│ │ │ │ │ │
│ ▼ │ │ ▼ │
│ ┌─────────────┐ │ │ ┌─────────────┐ │
│ │ Close driver│ │ │ │ Call IOKit │ │
│ │ connection │◄─┘ └─▶│ method │ │
│ └─────────────┘ └─────────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────────────┐ │
│ │ nvCommandQueue:: │ │
│ │ GetHandleIndex accesses │ │
│ │ NULL pointer at +0x5b8 │ │
│ └─────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌───────────────────────────┐ │
│ │ Virtual method call on │ │
│ │ NULL object jumps to │ │
│ │ address 0x4141414141414141| │
│ └───────────────────────────┘ │
│ │
└────────────────────────────────────────────────────────────────┘
Code language: Clean (clean)
- Similarly, a bug in IOAudioEngine (CVE-2016-1821) set an object to NULL but later used it, letting an attacker supply a fake
reserved->streams
array at address zero and redirect execution.
In these cases, mapping the null page gave trivial kernel RIP control to the attacker.
Supervisor Mode Execution Prevention
On some older Macs, hardware protections against executing user memory in kernel were absent or disabled. In the Talos example, the researchers noted that modern CPUs have SMEP, which would prevent the kernel from executing code placed in user pages (like the NULL page). If SMEP was not enabled on the system, the exploit could directly execute shellcode from the mapped NULL page.
On newer hardware, Apple had started using CPU feature called SMEP to forbid executing user-space memory in kernel mode. It was introduced in OS X 10.11 (El Capitan) by late 2016.
Intel Mitigations on macOS
These historical exploits show that when an attacker could map address zero and if the kernel then used that memory as a pointer or code, a NULL dereference bug could lead to arbitrary code execution.
Apple closed these avenues by late 2016. Apple’s security updates reclassified such bugs as Denial of Service issues and fixed them by adding NULL checks or better state handling:

Apple removed 32-bit process support entirely in macOS 10.15 and enabed SMEP in fact eliminating the primary trick to map the NULL page completely for Intel based machines.
Modern Mitigations on macOS Apple Silicon
With the transition to Apple Silicon (ARM64 architecture) and modern macOS versions (11.0 Big Sur and later), Apple has introduced strong mitigations that make NULL pointer dereferences unexploitable.
Strict NULL Page Mapping Protections
All user processes on Apple Silicon are 64-bit, and the kernel will not map any memory at address 0 in user space. As noted in __PAGEZERO
the Mach-O format reserves the low address region (4GB).

Even without this reservation, the macOS kernel enforces a minimum address (like mmap_min_addr
in Linux) that user code cannot allocate. In short, we cannot place data at or near the NULL address.
This means any kernel dereference of a NULL pointer will hit an unmapped region and immediately trigger a fault rather than accessing attacker-controlled memory.
Hardware Execution and Access Controls (PXN/PAN)
Apple’s Silicon leverages ARMv8 features analogous to SMEP/SMAP on Intel. ARM64 has Privileged Execute Never (PXN), which ensures the kernel cannot execute code from pages marked as user space.
In contrast, Privileged Access Never (PAN) prevents the kernel from accidentally reading/writing user space memory without an explicit override. Kernel marks all user memory as PXN, so even if an exploit somehow redirected execution to a user page (e.g., address 0), the processor would refuse to execute it.
Likewise, PAN would stop the kernel from reading malicious data placed in a user-controlled NULL page (it would require a special instruction sequence to access user memory intentionally).
1. Attacker maps shellcode at user-space NULL (0x0).
2. Exploit hijacks kernel control flow to 0x0.
3. Hardware checks:
├─ PXN=1 → Instruction Fetch Fault (Execution denied)
└─ PAN=1 → Data Access Fault (Access denied if kernel reads 0x0).
4. System halts exploit via fault.
Code language: PHP (php)
These hardware protections make the classic scenario of executing shellcode from a NULL page nonviable – the attempt would just cause another fault.
Pointer Authentication Codes (PAC)
A major enhancement with Apple’s ARM64e architecture is Pointer Authentication, introduced on the A12/M1 chips (ARMv8.3-A). Pointer authentication codes add a cryptographic signature to pointer values (such as return addresses and function pointers) verified upon use.
macOS extensively uses PAC in the kernel to protect against pointer corruption. If a NULL dereference bug is exploited by manipulating a kernel pointer, PAC will likely detect the tampering.
Regular pointer: 0x00007FFFFFFFFFF8 (64-bit address)
└────────────────┘
address
PAC pointer: 0xA23F7FFFFFFFFFF8
└────┘└──────────┘
│ │
│ └─ original address
└─────────── cryptographic signature (PAC)
The kernel’s function pointers are signed with secret keys. An attacker’s fabricated pointer will not have a valid signature. The check will fail when the kernel attempts to authenticate the fake pointer before use.
In effect, PAC thwarts attempts to forge control-flow pointers. This is highly relevant for NULL deref exploits, which historically relied on making the kernel jump to an attacker’s fake function pointer.
Removal of Legacy Exploit Vectors
As mentioned, macOS no longer supports 32-bit executables (since macOS 10.15 Catalina), closing the loophole of disabling the __PAGEZERO
region. Additionally, System Integrity Protection (SIP) and Kernel Integrity mechanisms prevent even root users from modifying certain memory or loading unsigned kernel code, indirectly making setting up a NULL page attack more difficult.
An attacker cannot simply disable these protections without already having kernel privileges.
Improved Kernel Memory Management and Checks
Apple has made significant improvements to the XNU kernel’s handling of pointers. Many kernel interfaces now perform stricter validation of pointers. Following high-profile bugs between 2015 and 2016, Apple audited drivers to introduce NULL checks and ensure that pointers are only set to NULL when it is safe to do so after freeing objects. When examining the decompiled code of various drivers’ external methods, many of them are found to have prefixes in their names, which may imply that they are “sanitized” or “secure.” These functions serve as wrappers for the original functions.
For instance: <span style="background-color: initial; font-family: inherit; font-size: 1em;">IOMobileFramebufferUserClient::s_set_matrix</span> wraps IOMobileFramebufferUserClient::set_matrix
Code language: HTML, XML (xml)
Nevertheless, even after the audit these bugs still exist, but are they still danger?
Are NULL Pointer Dereferences Still Exploitable on Apple Silicon?
In modern macOS on Apple Silicon, a NULL pointer dereference in the kernel is almost certainly not exploitable for code execution. It will result in a kernel panic (Denial of Service), but as we saw, multiple layers of defense prevent leveraging it for a privilege escalation.
To summarize why exploitation is effectively dead on Apple Silicon macOS:
- Cannot control memory at NULL: Unlike a decade ago, an attacker cannot map or place controlled data at address 0 in the relevant address space. The OS and hardware ensure the NULL page is unmapped and inaccessible to user programs.
- Kernel will not execute user memory: Even if the kernel somehow jumped to address 0 (or any user address), it would hit PXN enforcement and refuse to execute code there, causing a fault.
- Kernel will not read user memory: The kernel cannot read memory from address 0 (or any user address). It would hit PAN enforcement and refuse to load code from there, causing a fault.
- Pointer authenticity checks: Pointer authentication defeats forging a pointer to trick the kernel (e.g., a fake function pointer as payload). The attacker would need to bypass PAC, which is non-trivial and typically requires a separate side-channel or info leak attack (e.g., the PACMAN vulnerability). The kernel will detect the invalid pointer and crash safely without a PAC bypass.
These mitigations make the exploit chain unrealistic to complete, but lets “theoretize” how it could look.
Theoretical Exploitation Chain
At minimum, we need five separate vulnerabilities:
- Page Table Modification: A vulnerability allowing the modification of kernel page tables to map the NULL page with writeable permissions. This alone requires a sophisticated kernel memory corruption primitive specifically targeting page table entries. I have never seen that.
- PAC Bypass: This would require either:
- Leaking the secret PAC keys (complicated as they are stored in system registers)
- Finding an oracle that can generate valid signatures
- Exploiting a side-channel attack like PACMAN
- Finding a PAC-bypass vulnerability in the kernel implementation
- Write Primitive: Vulnerability to place a payload at address 0.
- PXN Bypass: Vulnerability to manipulate page table attributes to mark the NULL page as kernel-accessible and executable (theoretically, the first primitive used for remaping could do that).
- NULL Pointer Dereference: Finally, out bug to dereference NULL and execute our instruction.
This complexity makes NULL pointer dereferences primarily useful only as Denial of Service vulnerabilities in modern Apple Silicon systems.
Does Apple Still Patch It?
Yes, Apple is still fixing such issues and accepting such reports, but it does not always classify them as security issues. I do not know why they do and sometimes do not, but I will discuss this further in the following article. We can see in the recent macOS security updates that Apple classifies NULL pointer dereference fixes as Denial of Service fixes, not “arbitrary code execution” issues:

There have been no public reports of a NULL dereference exploited for escalation since macOS Big Sur, which aligns with the expectation that these are no longer a viable attack vector on Apple Silicon.
Apple’s overall security architecture has closed this chapter of this exploit history.
Exploitability Checklist – Before Reporting a NULL Pointer Dereference
Not every NULL pointer dereference is equal—sometimes the bug’s root cause can be leveraged to redirect a pointer to attacker-controlled kernel memory. Before reporting, verify the following:
- Check if the pointer is derived from a heap allocation and if its source can be manipulated
- Test whether you can spray or groom the heap to influence the memory location in question
- Determine if the pointer is computed using an index or offset that might be user-controllable
- Look for use-after-free conditions where freed memory could be reallocated with controlled data
- Assess if missing or insufficient null checks allow for unintended pointer values
- Explore alternative control flows that might bypass security checks and lead to pointer corruption
In short, verify if any user-controlled input influences the pointer value used in the dereference. Ticking each point from the above list ensures the NULL pointer dereference is indeed NULL.
Final Words
Although NULL Pointer Dereference is no longer a viable option for Exploit Development, I hope you liked the history behind the creation of mitigations against it and why today it is an unexploitable issue.
If you find this blog post interesting and want to learn more about Cybersecurity in general, I encourage you to visit our AFINE blog regularly for new insights. If you are specifically interested in macOS, bookmark the Snake_Apple repository, where you will find all my articles about macOS.
I hope you learned something new here!