Skip to content

helpers: fix kernel version parsing with error check#2000

Closed
GermanAizek wants to merge 1 commit into
htop-dev:mainfrom
GermanAizek:patch-1
Closed

helpers: fix kernel version parsing with error check#2000
GermanAizek wants to merge 1 commit into
htop-dev:mainfrom
GermanAizek:patch-1

Conversation

@GermanAizek
Copy link
Copy Markdown
Contributor

@GermanAizek GermanAizek commented May 17, 2026

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.c by adding validation of the sscanf return value. The Platform_GetKernelVersion function now checks that all three version components (major, minor, patch) were successfully parsed before using them. If sscanf returns 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.

Review Change Stack

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 17, 2026

Walkthrough

The PR adds validation to Platform_GetKernelVersion in darwin/PlatformHelpers.c. Previously, when sscanf failed to parse all three kernel version components from kern.osrelease, the cachedKernelVersion struct could remain partially or uninitialized. The change checks the return value of sscanf and, on parse failure, explicitly sets all version fields to -1, ensuring consistent state across all code paths.

Poem

In Darwin's depths where versions hide,
sscanf once left fields to decide,
Now validation stands its ground,
When parsing fails, all fields are found—
At minus one, a truth defined.


Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1


ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 18038740-89d7-4908-8bb0-12e56acf95ea

📥 Commits

Reviewing files that changed from the base of the PR and between b7f9df9 and 2539e82.

📒 Files selected for processing (1)
  • darwin/PlatformHelpers.c

Comment thread darwin/PlatformHelpers.c
Comment on lines +34 to +36
if (n != 3) {
cachedKernelVersion.major = cachedKernelVersion.minor = cachedKernelVersion.patch = -1;
}
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot May 17, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 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.

Suggested change
if (n != 3) {
cachedKernelVersion.major = cachedKernelVersion.minor = cachedKernelVersion.patch = -1;
}
if (n != 3)
cachedKernelVersion.major = cachedKernelVersion.minor = cachedKernelVersion.patch = -1;

Copy link
Copy Markdown
Member

@BenBE BenBE May 18, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@coderabbitai Omitting braces here is possible, but not required.

But what should be avoided is multiple assignments on the same line.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@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.

Comment thread darwin/PlatformHelpers.c
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;
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 …

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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?

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@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.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

An alternative would be a struct initializer cachedKernelVersion = {-1, -1, -1}; to keep it short.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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!

@BenBE BenBE added enhancement Extension or improvement to existing feature MacOS 🍏 MacOS / Darwin related issues labels May 18, 2026
@natoscott natoscott closed this May 20, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement Extension or improvement to existing feature MacOS 🍏 MacOS / Darwin related issues

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants