CVE-2021-46940 - Fixing an Offset Overflow Vulnerability in Linux Kernel’s Turbostat Tool
In early 2021, a subtle—but important—vulnerability was discovered and patched in the Linux kernel’s Turbostat tool, used for reporting processor frequency and power statistics. Tracked as CVE-2021-46940, this issue was related to incorrect handling of type conversions in the idx_to_offset() and related functions, leading to overflow and logical errors that affected background updates of MSR (Model Specific Register) energy statistics.
This post will break down what happened, how it could be exploited, and how it was fixed, using no-nonsense language and simple code examples.
What is Turbostat?
Turbostat is a kernel utility tool found in /usr/bin/ on many Linux systems. It reports CPU core frequency, temperature, C-states, and energy consumed, by reading special registers (MSRs). It's most useful for performance tuning, energy optimization, and troubleshooting.
Where's the Problem?
The Turbostat codebase includes a function idx_to_offset() that converts an index to an offset. This function's return type is int (32-bit signed integer). However, elsewhere in the code, it is used with MSRs like MSR_PKG_ENERGY_STAT, which are unsigned 32-bit numbers.
Here’s the problem: If you try to store a large unsigned value in a signed integer, it might become negative due to integer overflow. This triggers a safety check that prevents periodic energy stats updates when running Turbostat for a long period.
update_msr_sum()
All these relied on int types to represent offsets or indices, but some data was u32 (unsigned 32-bit). If, for example, MSR_PKG_ENERGY_STAT has a value over 2,147,483,647 (the max for signed int), it's interpreted as negative.
Example Code Before the Fix
// Returns a signed int, but used with u32 inputs
int idx_to_offset(int idx) {
return MSR_BASE + idx * MSR_STRIDE;
}
// Somewhere else:
int offset = idx_to_offset(index);
if (offset < ) {
// Don't update stats!
return;
}
What happens if idx_to_offset() returns a value >= x80000000? It becomes a negative number in int.
Can it be abused by attackers?
No direct privilege escalation or code execution is possible here; it’s a logic failure, not a classic security hole. However, it could cause misleading or missing stats in environments where accurate CPU energy measurements are needed, such as:
Any program depending on accurate Turbostat output
In essence, while you cannot gain unauthorized access, you *can* prevent monitoring software from updating stats, potentially hiding real system energy usage or thermal trends.
Patch: How Was It Fixed?
The developers resolved the issue by changing variable types to properly represent offsets as off_t (which is a type meant for file offsets, and typically 64-bit or at least never negative for unsigned register addresses).
Example Code After the Fix
off_t idx_to_offset(int idx) {
return MSR_BASE + (off_t)idx * MSR_STRIDE;
}
// And matching update in other functions:
off_t offset = idx_to_offset(index);
if (offset < ) {
// This check will only fail for true logic errors, not for type overflows
return;
}
Now, even if the offset is large (u32 range), it's preserved correctly and does not flip negative due to overflow.
References
- Linux Kernel Patch Commit
- CVE-2021-46940 at MITRE
- Original Turbostat Source
Conclusion
CVE-2021-46940 is a classic example of the importance of correct data types—especially when mixing signed and unsigned integers in system code. While it wasn’t a remote code execution bug, it could impact accurate CPU/system monitoring in Linux.
If you maintain scripts or services relying on Turbostat:
Stay safe—and always check your types!
Further reading:
- When to use 'int', 'unsigned int', and 'off_t': Stack Overflow
Have questions about CVE-2021-46940 or Turbostat monitoring? [Ask here!]
Timeline
Published on: 02/27/2024 19:04:05 UTC
Last modified on: 04/10/2024 19:44:37 UTC