fix: Apple Silicon integer SMC decoding (si8/si16, endianness) + daemon crash on unauthorized XPC write - #4
Merged
Conversation
… add si8/si16/si32 + PSTR Verified on M2 MacBook Air against AppleSmartBattery (IOKit) ground truth: - ui16/ui32 were decoded big-endian (Intel convention); Apple Silicon SMC stores integers little-endian. B0AV read 53039 instead of 12239 mV (IOKit: 12235 mV). Byte order is now a decode parameter defaulting to the build architecture. - si8/si16/si32 were unsupported and fell through to raw bytes. This was not just cosmetic: the daemon's plugged-in detection reads AC-W (si8) via doubleValue, which always returned nil and silently defaulted to plugged-in. AC-W now decodes (-1 = no adapter; rendered as "not connected"); B0AC decodes to -320 mA matching IOKit Amperage. - B0AC/B0AV report mA/mV; scale to the displayed A/V units. - PDTR is DC-in total power and legitimately reads ~0 on battery; add PSTR (system total power) to sensors and power status as the portable-friendly draw signal (reads ~3-6 W on battery, consistent with V*I from the battery rails).
userIsAdmin converted the caller's primary gid with a checked Int32() initializer. System accounts use "negative" ids — nobody's gid -2 is 4294967294 as gid_t — so the conversion trapped and took down the root daemon (EXC_BREAKPOINT in authorizeWrite), turning the authorization gate into a local DoS: any user could crash-loop smctld instead of being rejected. Use Int32(bitPattern:) and add a regression test. Reproduced and verified on-machine: before, `sudo -u nobody smctl battery maintain 50` crashed the daemon (crash report smctld-2026-06-10-133814.ips); after, it gets "Write requests require root or an admin user." and the daemon PID is unchanged.
3 tasks
harryisfish
added a commit
that referenced
this pull request
Jun 10, 2026
Squashed replacement for closed stacked PR #6 after PR #4 and PR #5 landed.\n\nAdditional fixes included before merge:\n- persist battery.force_discharge in writeConfig\n- prevent disabled charge limiting from keeping force-discharge armed\n- document new CLI entries in English and Chinese README files\n\nValidated:\n- swift test --disable-sandbox\n- git diff --check\n- git merge-tree --write-tree --merge-base $(git merge-base HEAD origin/main) HEAD origin/main\n- GitHub CI Build & Test (macOS)
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.
Summary
Two fixes, both found and verified on real hardware (M2 MacBook Air, macOS 15.7.5):
1. Integer SMC keys decoded wrong on Apple Silicon (
4453647)doubleValue, which always returned nil and silently defaulted to plugged-in. Verified live: AC-W now tracks adapter attach/detach (-1 ↔ wattage), B0AC matches IOKit Amperage.sensorsandpower statusas the portable-friendly draw signal.2. Unauthorized XPC write crashed the daemon (
38dc9de)userIsAdminconverted the caller's primary gid with a checkedInt32()init. System accounts use "negative" ids (nobody's gid -2 = 4294967294 as gid_t), so the conversion trapped — turning the authorization gate into a local DoS. Reproduced on-machine:sudo -u nobody smctl battery maintain 50crashed smctld; after the fix it gets a clean "Write requests require root or an admin user." with the daemon PID unchanged. Fixed withInt32(bitPattern:)+ regression tests.Test plan
ioreg/AppleSmartBattery: voltage/current match within sampling noise