helpers: fix kernel version parsing with error check#2000
Conversation
WalkthroughThe PR adds validation to Poem
Comment |
| if (n != 3) { | ||
| cachedKernelVersion.major = cachedKernelVersion.minor = cachedKernelVersion.patch = -1; | ||
| } |
There was a problem hiding this comment.
🧹 Nitpick | 🔵 Trivial | 💤 Low value
Omit braces per coding guidelines.
The single chained assignment is trivial and should not have braces. As per coding guidelines, omit braces around simple single statements including trivial assignments.
Proposed style fix
- if (n != 3) {
- cachedKernelVersion.major = cachedKernelVersion.minor = cachedKernelVersion.patch = -1;
- }
+ if (n != 3)
+ cachedKernelVersion.major = cachedKernelVersion.minor = cachedKernelVersion.patch = -1;📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| if (n != 3) { | |
| cachedKernelVersion.major = cachedKernelVersion.minor = cachedKernelVersion.patch = -1; | |
| } | |
| if (n != 3) | |
| cachedKernelVersion.major = cachedKernelVersion.minor = cachedKernelVersion.patch = -1; |
There was a problem hiding this comment.
@coderabbitai Omitting braces here is possible, but not required.
But what should be avoided is multiple assignments on the same line.
There was a problem hiding this comment.
@BenBE, fair point — the chained assignment is the real concern here. The braces should stay; the assignments should be split:
if (n != 3) {
cachedKernelVersion.major = -1;
cachedKernelVersion.minor = -1;
cachedKernelVersion.patch = -1;
}✏️ Learnings added
Learnt from: BenBE
Repo: htop-dev/htop PR: 2000
File: darwin/PlatformHelpers.c:34-36
Timestamp: 2026-05-18T20:10:58.851Z
Learning: In the htop codebase (darwin/PlatformHelpers.c), chained multiple assignments on a single line (e.g., `a = b = c = -1`) should be avoided and split into separate lines. Braces around single-statement if blocks are optional but not required to be omitted.
Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.
| sscanf(str, "%hd.%hd.%hd", &cachedKernelVersion.major, &cachedKernelVersion.minor, &cachedKernelVersion.patch); | ||
| int n = sscanf(str, "%hd.%hd.%hd", &cachedKernelVersion.major, &cachedKernelVersion.minor, &cachedKernelVersion.patch); | ||
| if (n != 3) { | ||
| cachedKernelVersion.major = cachedKernelVersion.minor = cachedKernelVersion.patch = -1; |
There was a problem hiding this comment.
In theory, we could see n take values 1 or 2 here, e.g. if major and minor are present in the string without a patch level. Is it worth catering for that if we're adding this extra checking anyway?
There was a problem hiding this comment.
While I doubt there'd be versions with just the major version in them, I see a case for just having major.minor present. But even just major.minor is some serious hacking in the system I'd figure, that I doubt its worth addressing separately …
There was a problem hiding this comment.
Agreed - I also wonder if this proposed code path can ever be traversed, since success from sysctlbyname presumably indicates the presence of a well-formed string. In which case - can sscanf fail? I suspect not.
There was a problem hiding this comment.
Likely only for non-release versions or versions altered by external tooling (dtrace hugging the process).
So yes, there are ways to reach that code path, but for all of them simply ignoring the returned string is likely the better way to go.
@coderabbitai Are there legit cases for the version string returned by sysctlbyname (on success) for the Darwin kernel version to return a string not shaped as %d.%d.%d?
There was a problem hiding this comment.
@BenBE — Based on available evidence across all known Darwin/XNU releases (including developer betas and RCs), kern.osrelease consistently returns the major.minor.patch format — e.g., 21.0.0, 22.1.0, 25.3.0. The Darwin version obtained from sysctl kern.osrelease has been in this format since Mac OS X Server 1.0. There are no known legitimate Apple-shipped kernels (release, beta, or RC) that return anything other than three dot-separated integers.
So to directly answer your question: no known legit cases exist for a differently-shaped string from an unmodified kernel. The code path for n != 3 is practically unreachable on any real Apple system. The scenarios that could produce it are exactly as you described — custom/patched kernels, Hackintosh setups (e.g., OpenCore), or privileged external tooling tampering with the sysctl node.
One aside: the code already initializes cachedKernelVersion to {-1, -1, -1} before entering the if (ret == 0) block (line 28), so the reset inside if (n != 3) is redundant. It's harmless, but worth noting. Also, per the codebase style convention, the chained assignment cachedKernelVersion.major = cachedKernelVersion.minor = cachedKernelVersion.patch = -1 on line 35 should be written as three separate assignments.
🧠 Learnings used
Learnt from: BenBE
Repo: htop-dev/htop PR: 2000
File: darwin/PlatformHelpers.c:34-36
Timestamp: 2026-05-18T20:10:58.851Z
Learning: Avoid chained assignments on a single line in the C codebase (e.g., prefer `a = -1; b = -1; c = -1;` over `a = b = c = -1`). For `if` statements, braces for single-statement bodies may be omitted but are not required to be omitted—use braces when it improves readability or to reduce ambiguity.
There was a problem hiding this comment.
An alternative would be a struct initializer cachedKernelVersion = {-1, -1, -1}; to keep it short.
There was a problem hiding this comment.
Seems like the humans are having a chat. I'll hop back into my burrow for now. If you need me again, just tag @coderabbitai in a new comment, and I'll come hopping out!
using sscanf without checking whether all expected values have been parsed successfully can lead to undefined behavior (UB). It is worth checking the sscanf return value here to make sure that all three numbers are received before using the results
Reference: https://stackoverflow.com/questions/61066625/sscanf-check-doesnt-work-when-given-more-input-than-required
Changes
Fixed kernel version parsing in
darwin/PlatformHelpers.cby adding validation of thesscanfreturn value. ThePlatform_GetKernelVersionfunction now checks that all three version components (major, minor, patch) were successfully parsed before using them. Ifsscanfreturns fewer than 3 matched values, the function resets the parsed fields to -1, preventing undefined behavior from partially-initialized data.Implementation Quality
The fix is narrow and focused. It leverages existing error handling infrastructure (the function already initializes the version to -1 on syscall failure) and applies the same pattern consistently. The additional validation adds 3 lines of logic (capture return value, conditional check, and reset on failure).
Commit Structure
Single, focused commit addressing a specific correctness issue. The change scope matches the PR objective precisely.