Back to Blog
critical SEVERITY8 min read

Stack Buffer Overflow via Unbounded sprintf() in HardInfo2 CPU Utility

A critical stack buffer overflow vulnerability was discovered and patched in HardInfo2's cpu_util.c, where six unbounded sprintf() calls wrote locale-translated CPU topology labels into fixed-size stack buffers without length constraints. An attacker supplying a crafted translation file could overflow the stack buffer, overwrite saved return addresses, and potentially achieve arbitrary code execution. The fix replaces these dangerous calls with length-bounded alternatives, eliminating the overfl

O
By orbisai0security
May 13, 2026

Critical Stack Buffer Overflow Fixed in HardInfo2: When Translation Files Become Weapons

Introduction

Most developers are familiar with the classic buffer overflow — it's one of the oldest vulnerabilities in the book. Yet despite decades of awareness, unbounded string formatting functions continue to appear in production C code, quietly waiting for the right conditions to become exploitable. This week, a critical severity stack buffer overflow was patched in HardInfo2's cpu_util.c, serving as a timely reminder that even utility code handling "harmless" display labels can harbor serious security flaws.

This vulnerability is particularly interesting because the attack vector isn't network input or user-supplied data in the traditional sense — it's a crafted locale translation file. If you've never considered your gettext .po files as a security boundary, this post is for you.


The Vulnerability Explained

What Is a Stack Buffer Overflow?

Before diving in, a quick refresher: a stack buffer overflow occurs when a program writes more data into a fixed-size buffer allocated on the stack than the buffer can hold. The excess data spills over into adjacent stack memory, potentially overwriting critical control data like saved return addresses or frame pointers. When the function returns, instead of jumping back to legitimate code, the CPU can be redirected to attacker-controlled instructions.

This class of vulnerability is tracked under CWE-121: Stack-based Buffer Overflow and has been the root cause of countless real-world exploits.

The Vulnerable Code

In hardinfo2/cpu_util.c, around lines 266–278, there were six calls to sprintf() that looked something like this:

char sock_str[32];
char core_str[32];

// Dangerous: no length limit specified
sprintf(sock_str, "%s %d", _("Socket"), socket_id);
sprintf(sock_str, "%s %d", _("Core"),   core_id);
sprintf(core_str, "%s %d", _("Book"),   book_id);
sprintf(core_str, "%s %d", _("Drawer"), drawer_id);
// ... and two more similar calls

Two things make this dangerous:

  1. Fixed-size destination buffers: sock_str and core_str are allocated with a fixed size (e.g., 32 bytes) on the stack.
  2. The _() macro: This is the standard gettext internationalization macro. At runtime, _("Socket") doesn't return the literal string "Socket" — it returns whatever translation is loaded from the active locale file. The length of that string is determined entirely at runtime.

Why the _() Macro Is the Critical Factor

Here's where it gets subtle. If these calls used hardcoded string literals, the maximum output length would be predictable and a developer might (correctly) size the buffer accordingly. But because _() is involved, the actual string length is:

  • Determined at runtime by the loaded .mo/.po translation file
  • Potentially unbounded — nothing in the gettext API limits translation string length
  • Controllable by an attacker who can supply a crafted translation file

Consider this: if a malicious or corrupted translation file maps "Socket" to a 200-character string, the sprintf() call will happily write all 200+ characters plus the formatted integer into a 32-byte buffer, smashing the stack.

The Attack Scenario

Normal execution:
  _("Socket")  "Socket" (6 bytes)
  sprintf result: "Socket 3" (8 bytes)  fits in 32-byte buffer

Malicious translation file:
  _("Socket")  "AAAAAAAAAA...AAA" (200 bytes)
  sprintf result: 200+ bytes written into 32-byte buffer  OVERFLOW
  Overwrites: local variables, saved frame pointer, saved return address
  Effect: Arbitrary code execution when function returns

Real-world impact includes:
- Privilege escalation if HardInfo2 runs with elevated privileges (e.g., to access /proc or hardware sensors)
- Code execution on systems where an attacker can influence locale settings or translation files
- Denial of service via reliable application crash

This is especially relevant in multi-user environments, containerized deployments with shared locale directories, or any scenario where locale files are sourced from untrusted locations.


The Fix

What Changed

The fix replaces all six unbounded sprintf() calls with snprintf(), the length-aware counterpart that guarantees it will never write more than N bytes to the destination buffer (including the null terminator).

Before (vulnerable):

char sock_str[64];
char core_str[64];

// No length bound — will overflow if _() returns a long string
sprintf(sock_str, "%s %d", _("Socket"), socket_id);
sprintf(core_str, "%s %d", _("Core"),   core_id);
sprintf(sock_str, "%s %d", _("Book"),   book_id);
sprintf(core_str, "%s %d", _("Drawer"), drawer_id);

After (safe):

char sock_str[64];
char core_str[64];

// Length-bounded — will never overflow the destination buffer
snprintf(sock_str, sizeof(sock_str), "%s %d", _("Socket"), socket_id);
snprintf(core_str, sizeof(core_str), "%s %d", _("Core"),   core_id);
snprintf(sock_str, sizeof(sock_str), "%s %d", _("Book"),   book_id);
snprintf(core_str, sizeof(core_str), "%s %d", _("Drawer"), drawer_id);

Why snprintf() Solves the Problem

snprintf(buf, size, fmt, ...) takes an explicit size argument. No matter how long the formatted output would be, it writes at most size - 1 characters and always null-terminates the buffer. The function also returns the number of characters that would have been written, allowing callers to detect truncation if needed.

Key properties of the fix:
- No stack overflow possible: The write is hard-capped at sizeof(buf)
- Using sizeof(buf) instead of a magic number: This ties the limit directly to the actual buffer size, so if the buffer is ever resized, the limit automatically adjusts
- Consistent pattern: All six calls are fixed with the same idiom, eliminating the vulnerability class entirely from this code section

A Note on Truncation

One might ask: "But what if the translation string is legitimately long and gets truncated?" This is a valid concern for functionality, but it's the correct security trade-off. A truncated label in a UI is a minor display issue. A stack buffer overflow is a critical security vulnerability. When in doubt, truncate safely rather than overflow dangerously.

If full translation strings must be supported, the proper solution is dynamic allocation:

// Even safer: dynamically sized buffer
int needed = snprintf(NULL, 0, "%s %d", _("Socket"), socket_id) + 1;
char *sock_str = malloc(needed);
if (sock_str) {
    snprintf(sock_str, needed, "%s %d", _("Socket"), socket_id);
    // ... use sock_str ...
    free(sock_str);
}

Prevention & Best Practices

1. Treat sprintf() as Deprecated in Security-Sensitive Code

In modern C development, sprintf() and strcpy() should be considered legacy functions that require explicit justification to use. Most static analysis tools will flag them by default. Prefer:

Dangerous Safe Alternative
sprintf() snprintf()
strcpy() strncpy(), strlcpy()
strcat() strncat(), strlcat()
gets() fgets()

2. Treat Locale/Translation Data as Untrusted Input

This is the less obvious lesson from this vulnerability. Translation strings sourced via gettext are external data — they come from files on disk that could be modified or replaced. Apply the same skepticism you'd apply to network input:

  • Never assume a translated string fits in a fixed buffer
  • Always use length-bounded formatting when incorporating translated strings
  • Consider validating translation file integrity (e.g., checksums) in security-sensitive applications

3. Use sizeof(buffer) Not Magic Numbers

// Bad: magic number can drift out of sync with actual buffer size
char buf[64];
snprintf(buf, 32, ...);  // ❌ Wrong limit, still dangerous

// Good: limit is always in sync with buffer declaration
char buf[64];
snprintf(buf, sizeof(buf), ...);  // ✅ Always correct

4. Enable Compiler Protections

Modern compilers and linkers offer several mitigations that can limit the exploitability of buffer overflows:

# GCC/Clang: Stack canaries, ASLR-friendly PIE, FORTIFY_SOURCE
gcc -fstack-protector-strong \
    -D_FORTIFY_SOURCE=2 \
    -fpie -Wformat -Wformat-security \
    -o myprogram myprogram.c
  • -fstack-protector-strong: Inserts stack canary values that detect overflows before function return
  • -D_FORTIFY_SOURCE=2: Enables compile-time and runtime checks for unsafe string functions
  • -Wformat-security: Warns about format string vulnerabilities

Note: These are mitigations, not fixes. The correct fix is always to eliminate the overflow in the code itself.

5. Static Analysis Integration

Several tools can catch unbounded sprintf() calls automatically:

  • Clang Static Analyzer: Detects buffer overflows and unsafe API usage
  • Coverity: Free for open source, excellent C/C++ analysis
  • CodeQL: GitHub's semantic code analysis engine (free for open source)
  • Flawfinder: Lightweight tool specifically targeting dangerous C/C++ functions
  • cppcheck: Open-source static analysis for C/C++

Adding one of these to your CI/CD pipeline would have caught this vulnerability before it ever reached production.

6. Relevant Security Standards


Conclusion

This vulnerability is a textbook example of how context transforms risk. A sprintf() call writing a short, hardcoded label string seems harmless. The same call writing a runtime-translated string is a critical security vulnerability. The difference is entirely in understanding where your data comes from and whether its length is bounded.

The key takeaways from this patch:

  1. sprintf() is dangerous whenever the output length isn't provably bounded at compile time. Use snprintf() with sizeof(buffer).
  2. Translation files are external data. Strings returned by _() / gettext() can be arbitrarily long and should be treated with the same caution as user input.
  3. Compiler warnings and static analysis catch these issues before they become CVEs. Integrate them into your build pipeline.
  4. Defense in depth matters: Stack canaries and FORTIFY_SOURCE don't replace correct code, but they raise the bar for exploitation.

The fix here is small — swapping sprintf for snprintf and adding a size argument — but the security impact is significant. Six potential stack smashing vectors, eliminated. This is the essence of secure coding: small, disciplined habits that compound into robust, trustworthy software.

Stay safe, and always bound your buffers. 🔒


This vulnerability was identified and patched by OrbisAI Security. If you maintain C/C++ projects, consider running a security scan to catch similar issues before they reach production.

View the Security Fix

Check out the pull request that fixed this vulnerability

View PR #220

Related Articles

critical

Heap Buffer Overflow in Audio Ring Buffer: How a Missing Bounds Check Could Crash Your App

A critical heap buffer overflow vulnerability was discovered in `audio_backend.c`, where the audio ring buffer's `memcpy` operations lacked bounds validation before writing PCM data. Without checking that incoming data sizes fell within the allocated buffer's capacity, a maliciously crafted audio file could corrupt adjacent heap memory, potentially enabling arbitrary code execution. The fix adds a concise pre-flight validation guard that rejects out-of-range write requests before any memory oper

critical

Critical Heap Buffer Overflow in SSDP Control Point: How Unbounded String Operations Put Networks at Risk

A critical heap buffer overflow vulnerability was discovered and patched in the SSDP control point implementation (`ssdp_ctrlpt.c`), where multiple unbounded `strcpy` and `strcat` operations constructed HTTP request buffers without any length validation. Network-received SSDP response fields — including service type strings and location URLs — could be crafted by an attacker to exceed buffer boundaries, potentially enabling arbitrary code execution or denial of service. The fix replaces the unsa

critical

Heap Buffer Overflow in OPDS Parser: How a Misplaced Variable Nearly Opened the Door to Remote Code Execution

A critical heap buffer overflow vulnerability was discovered in `lib/OpdsParser/OpdsParser.cpp`, where the buffer allocation size was calculated *after* a fixed chunk size was used to allocate memory, meaning the actual bytes read could exceed the allocated buffer. On embedded devices parsing untrusted OPDS catalog data from the network, this flaw could allow a remote attacker to corrupt heap memory and potentially achieve arbitrary code execution. The fix was elegantly simple: move the `toRead`

critical

Heap Buffer Overflow in BLE MIDI: How a Missing Bounds Check Opens the Door to Remote Exploitation

A critical heap buffer overflow vulnerability was discovered in the BLE MIDI packet assembly code of `blemidi.c`, where attacker-controlled packet length values could trigger writes beyond allocated heap memory. The fix adds an integer overflow guard before the `malloc` call, ensuring that maliciously crafted BLE MIDI packets can no longer corrupt heap memory. This vulnerability is particularly dangerous because it is remotely exploitable by any nearby Bluetooth device — no physical access requi

critical

Heap Overflow in TOML Parser: How Integer Overflow Leads to Memory Corruption

A critical heap buffer overflow vulnerability was discovered and patched in the centitoml TOML parser, where missing integer overflow validation on a `MALLOC(len+1)` call could allow an attacker to trigger memory corruption via a crafted TOML configuration file. The vulnerability (CWE-190) is reachable through community-distributed mod or map files that the game loads from its `config/` directory, making it a realistic attack vector for remote code execution. A targeted one-line guard now preven

critical

Heap Corruption via Unchecked memcpy: How Integer Overflow Bugs Corrupt Memory in Windows File Operations

A critical buffer overflow vulnerability was discovered in `phlib/nativefile.c`, where multiple `memcpy` calls copied filename and extended-attribute data into fixed-size structures without verifying that source lengths didn't exceed destination buffer boundaries. An attacker supplying an oversized filename or EA name could corrupt adjacent heap memory, potentially enabling arbitrary code execution. The fix replaces unchecked arithmetic with Windows' safe integer helpers (`RtlULongAdd`, `RtlULon