Skip to content

Fix intermittent tvOS crash enumerating Obj-C classes during URLSession instrumentation#281

Merged
ypopovych merged 4 commits into
mainfrom
fix/tvos-objc-unknown-class-crash
Jun 15, 2026
Merged

Fix intermittent tvOS crash enumerating Obj-C classes during URLSession instrumentation#281
ypopovych merged 4 commits into
mainfrom
fix/tvos-objc-unknown-class-crash

Conversation

@ypopovych

Copy link
Copy Markdown
Contributor

Summary

Fixes an intermittent crash (objc[…]: Attempt to use unknown class 0x… / Bad pointer dereference at 0xffffffffffffffff) during URLSessionInstrumentation init on tvOS, surfaced on Xcode 26.x.

Root cause

InstrumentationUtils.objc_getClassList() used the classic two-call pattern but iterated to the count returned by the second call. objc_getClassList(buf, n) fills at most n slots but returns the total number of registered classes. During app/test launch other threads load frameworks and register classes concurrently, so that total could exceed the buffer sized from the first call. The loop then read uninitialized memory past the buffer, producing garbage "class" pointers that crashed the moment they were dereferenced (in the scan, or in the superclass walk). The race is why it failed only intermittently and didn't reproduce on every toolchain/simulator.

Fix

  • objc_getClassList() now grows and retries: re-query and enlarge the buffer until the returned total fits what we allocated. This captures every class, never reads past the buffer, and settles in a single retry under typical concurrent loading (small headroom).
  • Kept the classConforms optimization, which skips the expensive class_copyMethodList for the thousands of classes that don't adopt URLSessionDelegate.
  • Removed the dead objc_getSafeClassList helper and unused imports; the delegate scan is otherwise back to its original, proven shape.

Testing

  • Builds clean for tvOS.
  • DDNetworkInstrumentationTests passes on the tvOS 26.5 simulator.
  • The original crash could not be reproduced locally (local toolchain is Xcode 26.5 / tvOS 26.5 sim; CI uses 26.2 / 26.4), but the out-of-bounds read fully explains the intermittent failures independent of toolchain. CI integration run is the final confirmation.

🤖 Generated with Claude Code

ypopovych and others added 4 commits June 15, 2026 14:29
…ation

injectInNSURLClasses() enumerated every registered Obj-C class via
objc_getClassList() and called class_copyMethodList on each. That list can
include classes whose superclass chain references an unrealized stub class
(weak-linked classes from unloaded frameworks, or relative-method-list stub
classes). class_copyMethodList realizes the class, realization walks into the
stub superclass, and the runtime aborts with "Attempt to use unknown class".
Newer runtimes (Xcode 26.4 / tvOS) ship more of these stubs, which is why it
surfaced now.

Replace the unused, prefix-based objc_getSafeClassList with a real safety
filter that keeps only classes whose entire superclass chain is itself in the
class list, and use it from injectInNSURLClasses. Reading class_getSuperclass
does not realize the class, so the filter never touches an unknown class.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ound

Root cause: objc_getClassList(buf, n) writes at most n entries but returns the
total number of registered classes. During app/test launch other threads load
frameworks and register classes concurrently, so the second call can return a
count larger than the buffer we sized from the first call. Iterating to that
larger count read uninitialized memory and produced garbage "class" pointers,
which crashed intermittently as "Attempt to use unknown class" / bad access the
moment they were dereferenced. Clamp the loop to the allocated capacity.

This was the actual bug, so the earlier superclass-chain safe-list workaround is
no longer needed and is removed (it also made things worse by dereferencing the
garbage pointers extra times). Keep the classConforms optimization, which skips
class_copyMethodList for the thousands of classes that don't adopt
URLSessionDelegate, and drop the unused objc_getSafeClassList helper and unused
imports.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…opped

Instead of clamping to the initial buffer capacity (which would silently drop
classes registered mid-scan, possibly missing real URLSession delegates), keep
re-querying and growing the buffer until objc_getClassList's returned total fits
what we allocated. This captures every class while still never reading past the
buffer. Small headroom lets it settle in a single retry under typical concurrent
framework loading.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@ypopovych
ypopovych requested review from a team as code owners June 15, 2026 15:30
@ypopovych
ypopovych merged commit 4f6f247 into main Jun 15, 2026
31 checks passed
@ypopovych
ypopovych deleted the fix/tvos-objc-unknown-class-crash branch June 15, 2026 15:44
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants