# IDA 9.3 b1 macOS arm64 UAF crash

**URL:** <https://community.hex-rays.com/t/ida-9-3-b1-macos-arm64-uaf-crash/646>\
**Category:** Bugs\
**Created:** [January 13, 2026, 4:11pm UTC](https://community.hex-rays.com/t/ida-9-3-b1-macos-arm64-uaf-crash/646 "2026-01-13T16:11:54Z")\
**Posts on this page:** 4\
**Page:** 1

<div class="post-metadata">

**Author:** ![int](https://sea2.discourse-cdn.com/flex002/user_avatar/community.hex-rays.com/int/32/368_2.png) [@int](https://community.hex-rays.com/u/int)\
**Post date:** [January 13, 2026, 4:11pm UTC](https://community.hex-rays.com/t/ida-9-3-b1-macos-arm64-uaf-crash/646/1 "2026-01-13T16:11:54Z")

</div>

Hey,

I think I have identified a use-after-free bug in libshiboken that’s bundled with IDA and is highly timing dependent.

Ignored it until now because I thought it was my own plugin causing this, but when I started looking into this, it became clear this isn’t inside my plugins (or any plugin for that matter, since this also happened with all plugins disabled).

IDA / OS / hardware context:

- IDA Pro 9.3.251224
- Apple M4 Max / 128GB memory / macOS Tahoe
- Memory pressure was around 85GB - 90GB at time of testing
- CPU pressure around 10% - 15%

IDA’s error handler consistently caught the error, but I figured running IDA in autonomous mode skips the error handler registration. Running under lldb didn’t yield any crash after 20 iterations, indicating a race condition.

After a bit of fiddling I figured I’ll run `MallocGuardEdges=1 /Applications/IDA Professional 9.3.app/Contents/MacOS/ida` to check if there’s some kind of buffer overflow, which yielded zero crashes. This reinforced that it was likely due to timing (since MallocGuardEdges has a non-trivial impact on timing).

I realised if the error was a segfault and it’s timing related, there must be a ref to uninitialized memory somewhere.

To make sure this wasn’t an initialization issue, and given the fact that the crash happens around 35% of the time, I suspected this crash is due to a use after free in memory that happens to be in a contested memory region. Lo and behold, `MallocScribble=1 /Applications/IDA Professional 9.3.app/Contents/MacOS/ida` made IDA crash 100% of the time (MallocScribble makes malloc’s free overwrite all freed memory with 0x55).

At this point my observations looked like this:

| Environment | Crash Rate |
| --- | --- |
| Normal | ~35% |
| MallocScribble=1 | **100%** (5/5) |
| MallocGuardEdges=1 | **0%** (0/5) |

This was pretty annoying to debug since I couldn’t debug it, so I tried dtrace tracing it at 5 kHz and found the last good stack to be `ida -> libida.dylib -> idapython3.dylib -> Python -> PyImport_ImportModuleLevelObject -> dlopen`.

Out of sheer bruteforce debugging at this point I ran `MallocScribble=1 MallocStackLogging=1 MallocStackLoggingNoCompact=1 /Applications/IDA Professional 9.3.app/Contents/MacOS/ida`.

This yielded:

```auto
qt.qpa.fonts: Populating font family aliases took 1043 ms. Replace uses of missing font family "Courier" with one that exists to avoid this cost. 
Assertion failed: (previous_refcount >= refs), function uniquing_table_node_release_internal, file uniquing_table_mutator.c, line 1288.

```

Which is interesting because now it looked like a clear use-after-free or double-free bug.

I kept brute forcing and figured since lldb disabled ASLR and MallocScribble made it crash 100% of the time, I’ll try that:

```auto
for i in $(seq 1 5); do
    lldb -b \
        -o "settings set target.disable-aslr false" \
        -o "env MallocScribble=1" \
        -o "process launch -- -B" \
        -o "process handle SIGSEGV -n true -p true -s true" \
        -o "continue" \
        -o "bt all" \
        "/Applications/IDA Professional 9.3.app/Contents/MacOS/ida" 2>&1 | tail -80
    echo "=== Run $i done ==="
done

```

That gave me:

```auto
Trying lldb with proper settings...
(lldb) target create "/Applications/IDA Professional 9.3.app/Contents/MacOS/ida"
Current executable set to '/Applications/IDA Professional 9.3.app/Contents/MacOS/ida' (arm64).
(lldb) settings set target.disable-aslr false
(lldb) env MallocScribble=1
(lldb) process launch -- -B
ida(90897,0x1ee2bf000) malloc: enabling scribbling to detect mods to free blocks
2026-01-13 16:13:17.041001+0100 ida[90897:35867907] [qt.qpa.fonts] Populating font family aliases took 620 ms. Replace uses of missing font family "Courier" with one that exists to avoid this cost.
Process 90897 launched: '/Applications/IDA Professional 9.3.app/Contents/MacOS/ida' (arm64)
Process 90897 stopped
* thread #1, queue = 'com.apple.main-thread', stop reason = EXC_BAD_ACCESS (code=1, address=0x5f5f796f72740060)
    frame #0: 0x000000018151daa4 libsystem_platform.dylib`_platform_strlen + 4
libsystem_platform.dylib`_platform_strlen:
-> 0x18151daa4 <+4>: ldr q0, [x1]
    0x18151daa8 <+8>: adr x3, 0x18151d9e0 ; ___lldb_unnamed_symbol329
    0x18151daac <+12>: ldr q2, [x3], #0x10
    0x18151dab0 <+16>: and x2, x0, #0xf
Target 0: (ida) stopped.

... etc. ..

```

Lets try again for the stack traces:

```auto
cat > /tmp/lldb_crash.lldb << 'EOF'
settings set target.disable-aslr false
env MallocScribble=1
breakpoint set -n main
run -B
breakpoint delete 1
continue
EOF

# Run with the script and capture all output
for i in $(seq 1 10); do
    echo "=== Attempt $i ===" 
    lldb --source /tmp/lldb_crash.lldb \
        "/Applications/IDA Professional 9.3.app/Contents/MacOS/ida" << 'CMDS' 2>&1 | grep -A100 "EXC_BAD_ACCESS\|stop reason" | head -60
thread backtrace
frame info
register read x0 x1 x19 x20 pc lr
quit
CMDS
    
    if [${PIPESTATUS[0]} -eq 0 ]; then
        echo "Exited normally"
    fi
done

```

That gave me:

```auto
=== Attempt 2 ===
* thread #1, queue = 'com.apple.main-thread', stop reason = EXC_BAD_ACCESS (code=1, address=0xffffff006e6f6970)
    frame #0: 0x000000018151daa4 libsystem_platform.dylib`_platform_strlen + 4
    ...
(lldb) thread backtrace
* thread #1, queue = 'com.apple.main-thread', stop reason = EXC_BAD_ACCESS (code=1, address=0xffffff006e6f6970)
  * frame #0: 0x000000018151daa4 libsystem_platform.dylib`_platform_strlen + 4
    frame #1: 0x000000012b393e84 libshiboken6.abi3.6.8.dylib`___lldb_unnamed_symbol876 + 52
    frame #2: 0x000000012b395c0c libshiboken6.abi3.6.8.dylib`___lldb_unnamed_symbol883 + 128
    frame #3: 0x0000000124fe1df4 Python`builtin_getattr + 136
    ...

=== Attempt 3 ===
* thread #1, queue = 'com.apple.main-thread', stop reason = EXC_BAD_ACCESS (code=1, address=0x1)
    frame #0: 0x00000001292d391c libshiboken6.abi3.6.8.dylib`___lldb_unnamed_symbol945 + 112
    ...
(lldb) thread backtrace
* thread #1, queue = 'com.apple.main-thread', stop reason = EXC_BAD_ACCESS (code=1, address=0x1)
  * frame #0: 0x00000001292d391c libshiboken6.abi3.6.8.dylib`___lldb_unnamed_symbol945 + 112
    frame #1: 0x0000000122e5a758 Python`cfunction_call + 72
    ...
    frame #26: 0x0000000122df37a4 Python`PyObject_CallFunctionObjArgs + 56
    frame #27: 0x00000001292b7aac libshiboken6.abi3.6.8.dylib`___lldb_unnamed_symbol772 + 1544
    frame #28: 0x00000001292bbe68 libshiboken6.abi3.6.8.dylib`Shiboken::initShibokenSupport(_object*) + 112
    frame #29: 0x0000000129121460 Shiboken.abi3.so`PyInit_Shiboken + 304
    frame #30: 0x0000000122f7858c Python`_PyImport_RunModInitFunc + 88
    ...

=== Attempt 10 ===
* thread #1, queue = 'com.apple.main-thread', stop reason = EXC_BAD_ACCESS (code=1, address=0x5f657361635f656b)
    frame #0: 0x000000012b02391c libshiboken6.abi3.6.8.dylib`___lldb_unnamed_symbol945 + 112
    ...

```

I can see two different crash sites here:

1. **Crash in `_platform_strlen`** (Attempt 2): Shiboken calls `strlen` on a freed string pointer
2. **Crash in `___lldb_unnamed_symbol945`** (Attempts 3, 7, 10): Shiboken’s custom import hook dereferences freed memory directly

Findings from the invalid memory addresses:

- `0xffffff006e6f6970` - scribbled memory containing “pion”
- `0x1` - near-NULL pointer
- `0x5f657361635f656b` - scribbled memory containing “ke\_case\_” (part of `make_snake_case_name` inside libshiboken6.abi3.6.8.dylib)

_Edit:_ I really wanted to find out what “pion” / “noip” was, but only found references in `./libclang.dylib`, i.e. `BLRNoIP`, `GPR64common_and_GPR64noip`, etc. Looks like the pointer was originally initialized to `-1` (i.e. 0xFF…). Maybe Qt’s `QVariant` /`QString`’s tagged representations (i.e. `struct QString { Data *d; ... }`, if LSB set, maybe small string inlining? idk but neither LE / BE variants of this string appear in any binary)?

Reading the backtrace again (attempt 3):

```auto
frame #30: Python`_PyImport_RunModInitFunc <- Python importing a C extension
frame #29: Shiboken.abi3.so`PyInit_Shiboken <- Shiboken module init entry point
frame #28: Shiboken::initShibokenSupport() <- Setting up Shiboken infrastructure
frame #27: ___lldb_unnamed_symbol772 <- Internal Shiboken function
frame #26: PyObject_CallFunctionObjArgs <- Calls back into Python
  ... (recursive Python import machinery via Shiboken's custom __import__ hook) ...
frame #19: ___lldb_unnamed_symbol945 + 28 <- Shiboken's import hook (earlier call)
  ... (more nested imports) ...
frame #0: ___lldb_unnamed_symbol945 + 112 <- CRASH in same hook (later call)

```

The crash occurs in `___lldb_unnamed_symbol945`, which appears to be Shiboken’s custom import hook. It’s called recursively during nested module imports, and at some point it dereferences a string pointer that has already been freed.

tl;dr

The use-after-free occurs during `Shiboken::initShibokenSupport()` when Python is initializing PySide6. It’s a race condition in the library’s startup code where a string gets freed while Shiboken’s import hook still holds a reference to it.

* * *

But why stop here?

I wanted to find out if it’s related to threading:

```auto
CRASHES=0
for i in $(seq 1 10); do
    rm -f /Users/int/dev/isa-harvester/armgen/programs/template_madness.i64 2>/dev/null
    PYTHONDONTWRITEBYTECODE=1 PYTHONNOUSERSITE=1 timeout 30 /Applications/IDA\ Professional\ 9.3.app/Contents/MacOS/ida -A /Users/int/dev/isa-harvester/armgen/programs/template_madness >/dev/null 2>&1
    [$? -eq 139] && ((CRASHES++))
done
echo "Crashes with PYTHONDONTWRITEBYTECODE: $CRASHES/10"

```

Output:

```auto
Testing with PYTHONDONTWRITEBYTECODE...
Crashes with PYTHONDONTWRITEBYTECODE: 10/10

```

These env vars mainly impact timing, making the crash more likely.

## Final Summary

### What I found:

1. **Bug Type** : Use-after-free race condition
2. **Location** : `libshiboken6.abi3.6.8.dylib` (PySide6 6.8.0) during `Shiboken::initShibokenSupport()`
3. **Trigger** : Python module import initialization at IDA startup
4. **Observation** : Happens even with all plugins disabled and no input file

### Reliable Reproduction:

```bash
MallocScribble=1 /Applications/IDA\ Professional\ 9.3.app/Contents/MacOS/ida -B

```

### Backtrace (bottom-up):

```auto
Python`_PyImport_RunModInitFunc + 88
  -> Shiboken.abi3.so`PyInit_Shiboken + 304
    -> libshiboken6`Shiboken::initShibokenSupport(_object*) + 112
      -> libshiboken6`___lldb_unnamed_symbol772 + 1544
        -> Python`PyObject_CallFunctionObjArgs + 56
          -> ... (recursive imports via Shiboken's __import__ hook) ...
            -> libshiboken6`___lldb_unnamed_symbol945 + 112 <- CRASH
               (or _platform_strlen when the hook passes a freed string)

```

🦆🦆🦆

---

<div class="post-metadata">

**Author:** ![int](https://sea2.discourse-cdn.com/flex002/user_avatar/community.hex-rays.com/int/32/368_2.png) [@int](https://community.hex-rays.com/u/int)\
**Post date:** [January 16, 2026, 3:55pm UTC](https://community.hex-rays.com/t/ida-9-3-b1-macos-arm64-uaf-crash/646/2 "2026-01-16T15:55:45Z")

</div>

I traced it down, and it was my fault – idapythonrc.py:

```python
import os
import sys
import site

print("[idapythonrc.py] Starting initialization...")

# Enable PyQt5 compatibility shim (IDA 9.2 uses PySide6 by default)
os.environ["IDAPYTHON_USE_PYQT5_SHIM"] = "1"
print(f"[idapythonrc.py] Set IDAPYTHON_USE_PYQT5_SHIM={os.environ.get('IDAPYTHON_USE_PYQT5_SHIM')}")

# IDA's Python directory with built-in modules (PyQt5, PySide6)
ida_python_dir = "/Applications/IDA Professional 9.2.app/Contents/MacOS/python"

# Ensure IDA's Python modules are accessible (they should be by default)
if ida_python_dir not in sys.path:
    sys.path.insert(0, ida_python_dir)

# Add the virtual environment's site-packages to sys.path
# Use append instead of insert so IDA's built-in modules take precedence
ida_venv = os.path.join(os.path.expanduser("~"), ".idapro", "pythonvenv")
site_packages = os.path.join(ida_venv, "lib", "python3.13", "site-packages")
if os.path.exists(site_packages):
    sys.path.append(site_packages)
    print(f"[idapythonrc.py] Added venv site-packages: {site_packages}")

print("[idapythonrc.py] Initialization complete.")

```

Note the `ida_python_dir` – the python integration was actually loading `libshiboken6.abi3.6.8.dylib` from IDA 9.2’s python path. Apparently the library has changed its ABI slightly enough in 9.3 b1 (custom patches?) for it to be compatible enough with IDA 9.2’s variant for it to work … most of the time.

Apologies for the noise.

---

<div class="post-metadata">

**Author:** ![igor](https://sea2.discourse-cdn.com/flex002/user_avatar/community.hex-rays.com/igor/32/35_2.png) [@igor](https://community.hex-rays.com/u/igor)\
**Post date:** [January 16, 2026, 6:16pm UTC](https://community.hex-rays.com/t/ida-9-3-b1-macos-arm64-uaf-crash/646/3 "2026-01-16T18:16:00Z")

</div>

Thank you for the update!

---

<div class="post-metadata">

**Author:** ![deadnull](https://avatars.discourse-cdn.com/v4/letter/d/b9e5f3/32.png) [@deadnull](https://community.hex-rays.com/u/deadnull)\
**Post date:** [January 21, 2026, 12:17am UTC](https://community.hex-rays.com/t/ida-9-3-b1-macos-arm64-uaf-crash/646/4 "2026-01-21T00:17:03Z")

</div>

Incredible detail. Don’t be sorry for the noise! Thank you for taking the time to write this up! I learned a good bit here and fixed a lingering bug in my end.
