[mono] Secondary SIGSEGV crash in `dump_memory_around_ip` when IP is a near-NULL invalid address (e.g. 0xb5) on iOS
- Dominant language
- C#
- Stars
- 18.3k
- Forks
- 5.6k
- PR merge metrics
- PR metrics pending
Description
### Description
When a C++ crash (e.g. a jump through a corrupted vtable or a null-offset virtual call) results in a Program Counter (PC) value that is a small but non-zero address such as `0xb5`, the crash handler `dump_memory_around_ip` in `mini-posix.c` causes a **secondary SIGSEGV** inside the signal handler itself.
This is triggered because the current NULL guard only checks for exact zero (`if (native_ip)`), but a near-NULL address like `0xb5` passes the check and is subsequently used as the base for a memory dump that reads from address `0xa5` (i.e., `0xb5 - 0x10`), which falls within the null guard page and is not mapped.
### Reproduction Steps
1. Embed Mono runtime on iOS.
2. Trigger a C++ crash that results in an invalid near-NULL PC value, for example:
- A virtual call through a null or corrupted `this` pointer where the vtable offset lands at a small integer (e.g., `((Foo*)nullptr)->virtual_method()` with vtable slot offset `0xb5`)
- A function pointer call through a corrupted/uninitialized variable
3. Observe that the Mono crash handler is entered via:
```
mono_handle_native_crash
→ mono_dump_native_crash_info
→ dump_memory_around_ip
```
### Expected behavior
The crash handler should detect that the PC value is not a valid/readable address, print a diagnostic message, and skip the memory dump gracefully.
### Actual behavior
`dump_memory_around_ip` receives `native_ip = 0xb5`, which passes the `if (native_ip)` null check. It then calls:
```c
mono_dump_mem(((guint8 *) native_ip) - 0x10, 0x40);
// equivalent to: mono_dump_mem(0xa5, 0x40)
```
Inside `mono_dump_mem`, the loop attempts to read memory at addresses `0xa5` through `0xe4`, all of which fall within the null guard page (unmapped memory). This triggers a **secondary SIGSEGV** inside the signal handler, making the original crash effectively undebuggable.
### Regression?
_No response_
### Known Workarounds
_No response_
### Configuration
- **OS**: iOS
- **Architecture**: arm64
- - **Runtime**: 9.0.11
### Other information
## Root Cause
In `mini-posix.c`, `dump_memory_around_ip`:
```c
gpointer native_ip = MONO_CONTEXT_GET_IP (mctx);
if (native_ip) { // ← only guards against exact NULL (0x0)
native_ip = MINI_FTNPTR_TO_ADDR (native_ip);
g_async_safe_printf ("Memory around native instruction pointer (%p):", native_ip);
mono_dump_mem (((guint8 *) native_ip) - 0x10, 0x40); // ← reads 0xb5-0x10 = 0xa5, SIGSEGV
}
```
The guard `if (native_ip)` only rejects `0x0`. Any non-zero but invalid address in the null guard page (typically `0x1` – `0xFFF`) will silently proceed to `mono_dump_mem` and cause a second crash.
Contributor guide
Assessment
This issue has not been assessed yet.