setKeyboard releases the value it gets from IOHIDDeviceGetValue:
https://github.com/damieng/setledsmac/blob/f117466/Source/SetLEDs/main.c#L149
IOHIDDeviceGetValue is a Get function, so by the CoreFoundation ownership rule
the caller does not own what it returns. The element hands back its own cached
IOHIDValueRef with a retain count of 1, so the release frees an object the
element still points at. The IOHIDDeviceSetValue a few lines later then makes
IOKit release that freed value internally.
I hit this by accident while using setleds on a loop, and found crashes already
sitting in ~/Library/Logs/DiagnosticReports.
Crash
0 libobjc.A.dylib objc_release EXC_BAD_ACCESS / KERN_INVALID_ADDRESS
1 IOKit _IOHIDElementSetValue
2 IOHIDLib
3 IOHIDLib
4 IOKit IOHIDDeviceSetValue
5 setleds setKeyboard
6 setleds setAllKeyboards
7 setleds parseOptions
8 setleds main
Four reports, all identical.
Ownership
Reading the same element three times without releasing:
read 1: ptr=0x7fd1f3028ab0 retainCount=1
read 2: ptr=0x7fd1f3028ab0 retainCount=1
read 3: ptr=0x7fd1f3028ab0 retainCount=1
after CFRelease: retainCount=1152921504606846975 (0x0FFF..., freed)
next iteration: SIGSEGV
Same pointer each time, retain count 1. That single reference belongs to the
element.
Reproduction
Extracted lines 141 to 166 into a standalone file and built it twice, the only
difference being whether that one CFRelease is compiled in:
with CFRelease: 10/10 runs crashed, 0/10 completed 20 cycles
without CFRelease: 0/10 runs crashed, 10/10 completed 20 cycles
With the line removed, the real binary survives 100 alternating sets under
MallocScribble.
Why it looks intermittent
Each run over-releases once per LED element and exits immediately, so whether it
crashes depends on the freed block being reused in time. It also needs an actual
set: setleds -v alone never reaches IOHIDDeviceSetValue and never crashes,
which is why it is easy to miss.
Fix
Deleting line 149 is sufficient. Nothing else needs to change, and there is no
leak, because the value was never owned.
Environment
macOS 26.6.2 (25G83), Intel, setleds 0.4 built from f117466 with the supplied
Makefile.
Not verified
Happy to send a PR with just this one line if it would be useful.
setKeyboardreleases the value it gets fromIOHIDDeviceGetValue:https://github.com/damieng/setledsmac/blob/f117466/Source/SetLEDs/main.c#L149
IOHIDDeviceGetValueis a Get function, so by the CoreFoundation ownership rulethe caller does not own what it returns. The element hands back its own cached
IOHIDValueRefwith a retain count of 1, so the release frees an object theelement still points at. The
IOHIDDeviceSetValuea few lines later then makesIOKit release that freed value internally.
I hit this by accident while using setleds on a loop, and found crashes already
sitting in
~/Library/Logs/DiagnosticReports.Crash
Four reports, all identical.
Ownership
Reading the same element three times without releasing:
Same pointer each time, retain count 1. That single reference belongs to the
element.
Reproduction
Extracted lines 141 to 166 into a standalone file and built it twice, the only
difference being whether that one
CFReleaseis compiled in:With the line removed, the real binary survives 100 alternating sets under
MallocScribble.Why it looks intermittent
Each run over-releases once per LED element and exits immediately, so whether it
crashes depends on the freed block being reused in time. It also needs an actual
set:
setleds -valone never reachesIOHIDDeviceSetValueand never crashes,which is why it is easy to miss.
Fix
Deleting line 149 is sufficient. Nothing else needs to change, and there is no
leak, because the value was never owned.
Environment
macOS 26.6.2 (25G83), Intel, setleds 0.4 built from
f117466with the suppliedMakefile.
Not verified
mechanism fits, but I have not confirmed those reports were this bug.
Happy to send a PR with just this one line if it would be useful.