Fix boot panic on macOS 26 (Tahoe) with AMD GPUs - #127
Open
olwimo wants to merge 1 commit into
Open
Conversation
With an AMD GPU present, WhateverGreen panicked the kernel while it was enumerating PCI and USB devices. Four separate defects were involved, each of which only became reachable once the previous one was fixed. Corrupt trampoline. Lilu copies the prologue bytes it displaces into a trampoline verbatim, so an instruction with a relative operand inside that range resolves against the trampoline once moved. On macOS 26, IORegistryEntry::getProperty(const char *) has 13 bytes of movable prologue before a rel32 call into OSSymbol::withCString, so the absolute route added in ed5e710 swallowed that call and retargeted it at unrelated memory. The route now picks a jump type the prologue can survive, falling back to routeMultipleShort, which refuses to patch rather than widening the jump, and skipping the route entirely when neither variant is safe. setProperty (exactly 14 movable bytes) and cs_validate_page (16) were audited the same way and are unaffected. Wrapper re-entrancy. Even with a correct trampoline, wrapGetProperty calls getParentEntry on every dictionary-valued lookup, re-entering the registry while a walk of that same registry is in progress. It can only ever do useful work when CFG/PP/CAIL overrides were injected, so it is no longer routed unless one is present. -radnoprop disables both property routes. Calls through unresolved trampolines. Making getProperty conditional left orgGetProperty null for wrapSetProperty, which called through it whenever a model property was set. updateConnectorsInfo had the same defect via orgGetAtomObjectTableForType, a symbol that does not exist on macOS 26 at all. Both are now checked. Unbounded framebuffer write. The back-copy and zero-fill sized their write from the console vinfo but wrote into the framebuffer's VRAM mapping, with the guard above them comparing against the display mode rather than the mapping. The write is now clamped to the mapped length. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> (cherry picked from commit 32c8815)
|
Does this issue need to be added to Acidanthera's bugtracker here? |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
On macOS 26 (Tahoe), WhateverGreen panics the kernel while it is enumerating PCI and USB devices whenever an AMD GPU is present. The panic lands before the panic log can reach disk, so it leaves nothing behind.
Four separate defects turned out to be involved. Each only became reachable once the previous one was fixed, which is why this is a single PR rather than four.
1. Corrupt trampoline on
IORegistryEntry::getPropertyKernelPatcher::createTrampolinecopies displaced prologue bytes verbatim — the comment there reads// Copy the prologue, assuming it is PIC. An instruction carrying a relative operand inside that range therefore resolves against the trampoline once moved.On Tahoe,
IORegistryEntry::getProperty(const char *)has only 13 bytes of movable prologue:Reaching the 14 bytes an absolute jump needs swallows that
callq. #122 switched this route torouteMultipleLong, which forces an absolute route, so the trampoline's call toOSSymbol::withCStringlands on unrelated memory and the kernel dies on the first property lookup.The route now selects a jump type the prologue can actually survive:
routeMultipleLongwhen a 14-byte displacement is clean, otherwiserouteMultipleShort— which refuses to patch when the callback is out of reach instead of quietly widening the jump — and skipping the route entirely when neither is safe.Every route WhateverGreen makes was audited the same way against the real Tahoe binaries, using Lilu's own hde64 decoder.
getPropertyis the only hazard;setProperty(exactly 14 movable bytes) andcs_validate_page(16) are unaffected.2. Re-entrancy in the
getPropertywrapperA valid trampoline was still fatal.
wrapGetPropertycallsgetParentEntryon every dictionary-valued lookup, re-entering the registry while a walk of that same registry is in progress — including the walk Lilu performs to buildDeviceInfo. On a machine with a long PCI device list it dies partway through.The wrapper can only ever do useful work when
CFG,/PP,/CAIL,overrides were injected, so it is no longer routed unless one is actually present.-radnopropdisables both property routes outright.3. Calls through unresolved trampolines
Making
getPropertyconditional leftorgGetPropertynull, andwrapSetPropertycalled straight through it whenever amodelproperty was set — whichAMDRadeonX6000does when it loads.updateConnectorsInfohas the same defect viaorgGetAtomObjectTableForType. That symbol, along withAtiAtomBiosDceInterface::getConnectorsInfo,AMDLegacyController::start,AppleGraphicsDevicePolicy::start,_dce_driver_set_backlightand_dce_panel_cntl_hw_init, does not exist on macOS 26 at all. Both call sites are now checked.4. Unbounded framebuffer write
wrapFramebufferInitsized its back-copy and zero-fill from the consolevinfobut wrote into the framebuffer's VRAM mapping:The guard above it compares against the display mode's pixel information, which is not the same thing as the mapping. The write is now clamped to
IOMemoryMap::getLength().Testing
Built for Release and Debug. Verified on a Radeon Pro W5700 (Navi 10,
1002:7312) running macOS 26.6 (25G72), booting from an unbootable starting state through to a full desktop on stock settings (agdpmod=pikera unfairgva=1), with each fix bisected by boot argument along the way.Only tested on that one machine and on Navi. The Intel and NVIDIA submodules were not exercised, though nothing here is specific to AMD except the gating in point 2.
Note for maintainers
This includes the
MODULE_VERSIONbump to 1.7.2 and its Changelog entries, since the Changelog text refers to the version. Happy to drop both commits' version handling if you would rather bump separately, as the history suggests you normally do.