Back to Blog
critical SEVERITY6 min read

How buffer overflow happens in C sprintf() and how to fix it

A critical buffer overflow vulnerability was discovered in the ArrowTest() function in main/main.c, where sprintf() was writing formatted strings to a 24-byte buffer without bounds checking. By replacing sprintf() with snprintf() and specifying the buffer size, the vulnerability was eliminated, preventing attackers from corrupting heap memory through oversized width or height parameters.

O
By Orbis AppSec
Published July 12, 2026Reviewed July 12, 2026

Answer Summary

This is a buffer overflow vulnerability (CWE-120) in C code where sprintf() writes to a fixed 24-byte buffer without size limits. The vulnerability occurs in main/main.c:147, 155, and 162 where width and height parameters are formatted into coordinates. The fix replaces all three sprintf() calls with snprintf(ascii, sizeof(ascii), ...) to enforce buffer boundaries, preventing heap corruption when large values are provided.

Vulnerability at a Glance

cweCWE-120 (Buffer Copy without Checking Size of Input)
fixReplace sprintf() with snprintf() and specify buffer size limit
riskHeap corruption, potential code execution via memory layout manipulation
languageC
root causesprintf() writes to fixed-size buffer without bounds checking on format string output
vulnerabilityBuffer Overflow in sprintf()

How buffer overflow happens in C sprintf() and how to fix it

The Vulnerability in Context

In the ArrowTest() function of main/main.c, a critical buffer overflow vulnerability was lurking in embedded firmware code that renders graphical arrows on an LCD display. The function accepts width and height parameters from callers and uses them to calculate screen coordinates for drawing. However, these coordinates were being formatted into a small 24-byte buffer using sprintf() without any size checking—a classic recipe for heap corruption.

The vulnerability exists because an attacker with control over width or height values (perhaps through a compromised network peer on the same LAN segment, given this is embedded firmware) could provide large values like 999999, causing the formatted output to exceed the buffer boundary and overwrite adjacent heap memory.

The Vulnerability Explained

Let's examine the vulnerable code from lines 147, 155, and 162 in main/main.c:

// Line 147 - VULNERABLE
sprintf((char *)ascii, "%d,0", width-1);

// Line 155 - VULNERABLE  
sprintf((char *)ascii, "0,%d", height-1);

// Line 162 - VULNERABLE
sprintf((char *)ascii, "%d,%d", width-1, height-1);

The problem is clear when you understand the buffer constraints:

char ascii[24];  // Fixed 24-byte buffer

The sprintf() function has no knowledge of the buffer size and will write as many bytes as needed to format the output. If width or height are large values, the formatted string can easily exceed 24 bytes.

Attack scenario: Suppose an attacker controls the width and height parameters through a network message or configuration file. They provide:
- width = 2147483647 (INT_MAX)
- height = 2147483647

The formatted string "2147483646,2147483646" is 20 characters—still within bounds. But consider:
- width = 999999999999 (12 digits)
- height = 999999999999

The formatted string "999999999998,999999999998" is 25 characters, exceeding the 24-byte buffer by 1 byte. This single byte overflow corrupts the next heap structure, potentially leading to:
- Heap metadata corruption
- Use-after-free vulnerabilities
- Arbitrary code execution if the heap layout is predictable

In embedded firmware contexts, exploiting heap corruption is often more reliable than in general-purpose systems because memory layouts are more predictable and there are fewer mitigations like ASLR.

The Fix

The fix is straightforward but critical: replace all three sprintf() calls with snprintf() and explicitly pass the buffer size:

-   sprintf((char *)ascii, "%d,0",width-1);
+   snprintf((char *)ascii, sizeof(ascii), "%d,0",width-1);

-   sprintf((char *)ascii, "0,%d",height-1);
+   snprintf((char *)ascii, sizeof(ascii), "0,%d",height-1);

-   sprintf((char *)ascii, "%d,%d",width-1, height-1);
+   snprintf((char *)ascii, sizeof(ascii), "%d,%d",width-1, height-1);

What changed:
- sprintf()snprintf(): The size-aware variant
- Added second parameter: sizeof(ascii) = 24 bytes
- Format string and arguments remain unchanged

How this solves the problem:

snprintf() guarantees that it will write at most sizeof(ascii) bytes (including the null terminator). If the formatted output would exceed this limit, snprintf() truncates the string and null-terminates it safely. The buffer is protected from overflow.

For example, with the attack scenario above:
- snprintf() attempts to format "999999999998,999999999998"
- It detects this would require 26 bytes (25 characters + 1 null terminator)
- It truncates to 23 characters: "999999999998,9999999" (fits in 24-byte buffer)
- No overflow occurs

The trade-off is that the coordinate string is truncated, but this is far preferable to heap corruption and potential code execution.

Prevention & Best Practices

1. Never use sprintf() with dynamic input

The C standard library provides safer alternatives:
- snprintf(): Size-limited, returns number of characters that would have been written
- asprintf(): Dynamically allocates buffer (GNU extension)
- strncpy(): For string copying with size limits

2. Use compiler warnings and static analysis

Enable compiler warnings that flag unsafe functions:

gcc -Wall -Wformat -Wformat-overflow -Wformat-truncation main.c

These flags catch many sprintf/strcpy issues at compile time.

3. Apply the "sizeof() pattern"

Always use sizeof() when working with fixed buffers:

char buffer[256];
// Good:
snprintf(buffer, sizeof(buffer), format, args);
// Bad:
snprintf(buffer, 256, format, args);  // Magic number, breaks if buffer size changes

4. Validate input ranges

Even with snprintf(), consider validating that width and height values are within expected ranges:

if (width < 0 || width > MAX_WIDTH || height < 0 || height > MAX_HEIGHT) {
    // Reject invalid values
    return ERROR;
}

5. Use static analysis tools

Tools like:
- Semgrep: Can detect sprintf() without size limits
- Clang Static Analyzer: Finds buffer overflow patterns
- Orbis AppSec: Automatically detects and fixes these issues

CWE Reference: This vulnerability is classified as:
- CWE-120: Buffer Copy without Checking Size of Input
- CWE-119: Improper Restriction of Operations within the Bounds of a Memory Buffer

OWASP Reference: Falls under OWASP A06:2021 – Vulnerable and Outdated Components when unsafe C functions are used in production code.

Key Takeaways

  • sprintf() is never safe with dynamic input: Always use snprintf() with explicit buffer size limits in production code
  • The ArrowTest() function now enforces buffer boundaries: All three formatting calls at lines 147, 155, and 162 are now protected by snprintf() with sizeof(ascii)
  • Embedded firmware is particularly vulnerable: With predictable memory layouts and limited mitigations, heap overflow exploitation is more reliable than in general-purpose systems
  • sizeof() is your friend: Using sizeof(buffer) instead of hardcoded numbers prevents future buffer size changes from reintroducing vulnerabilities
  • Truncation is acceptable: snprintf() truncating output is far better than heap corruption; the LCD will display partial coordinates rather than crashing

How Orbis AppSec Detected This

  • Source: The width and height function parameters to ArrowTest() in main/main.c
  • Sink: Three sprintf() calls at lines 147, 155, and 162 that write to the 24-byte ascii buffer without bounds checking
  • Missing control: No validation of parameter values and no size-limiting wrapper around the formatting function
  • CWE: CWE-120 (Buffer Copy without Checking Size of Input)
  • Fix: Replaced all sprintf() calls with snprintf(ascii, sizeof(ascii), ...) to enforce buffer boundaries

Orbis AppSec automatically detected this vulnerability and opened a pull request with the fix. Try Orbis AppSec on your repositories to find and fix issues like this automatically.

Conclusion

Buffer overflow vulnerabilities in C formatting functions represent a critical security risk, especially in embedded firmware where exploitation is more reliable due to predictable memory layouts. The transition from sprintf() to snprintf() in the ArrowTest() function demonstrates that fixing these issues is often as simple as adding two parameters—the buffer size and sizeof().

This vulnerability highlights why security scanning tools are essential for C codebases: unsafe functions can easily slip past code review and testing because they only fail under specific input conditions. By adopting snprintf() as a standard practice, validating input ranges, and using static analysis tools, developers can prevent entire classes of buffer overflow vulnerabilities before they reach production.

For teams maintaining embedded firmware and systems code, treating sprintf() as a code smell and automatically flagging it for review is a practical defense strategy that catches these issues early.


References

Frequently Asked Questions

What is a buffer overflow in C sprintf()?

A buffer overflow occurs when sprintf() writes more data to a buffer than it can hold. Since sprintf() doesn't limit output size, large input values can overflow the buffer and corrupt adjacent memory.

How do you prevent buffer overflow in C formatting functions?

Always use size-limited functions like snprintf() instead of sprintf(), and pass the buffer size as a parameter. For example: snprintf(buf, sizeof(buf), format, args) instead of sprintf(buf, format, args).

What CWE is buffer overflow in sprintf()?

CWE-120 (Buffer Copy without Checking Size of Input) and CWE-119 (Improper Restriction of Operations within the Bounds of a Memory Buffer) both apply to this vulnerability.

Is using strlen() to check buffer space enough to prevent sprintf() overflow?

No, strlen() checks the input string length, not the output of sprintf(). The formatted output can be much longer than the input, so you must use snprintf() with the buffer size.

Can static analysis detect sprintf() buffer overflow?

Yes, static analysis tools can detect sprintf() calls with fixed buffers and flag them as high-risk. Orbis AppSec's multi_agent_ai scanner detected this exact pattern and confirmed the fix.

View the Security Fix

Check out the pull request that fixed this vulnerability

View PR #57

Related Articles

critical

How buffer overflow in Intel SGX enclave ECALLs happens in C and how to fix it

A critical buffer overflow vulnerability was discovered in Intel SGX enclave functions `ecall_encrypt_data` and `ecall_decrypt_data` in `backend/sgx/enclave/enclave.c`. The functions performed memory operations without validating that the provided buffer lengths matched the actual allocated buffer sizes, allowing an attacker controlling the untrusted application to trigger heap corruption within the secure enclave by passing oversized length parameters.

critical

How buffer overflow happens in C SGX enclave memcpy and how to fix it

A critical buffer overflow vulnerability was discovered in `backend/sgx/enclave/enclave.c` where the `ecall_store_data` function performed `memcpy` operations without proper bounds checking against the actual destination buffer size. An attacker could supply a malicious `data_len` parameter to overflow the enclave's secure storage buffer, potentially corrupting trusted execution environment memory. The fix replaces a hardcoded magic number check with a precise size comparison against the actual

medium

How integer underflow in array splice operations happens in C and how to fix it

A critical integer underflow vulnerability was discovered in tree-sitter's array.h header file, where the `_array__splice()` function calculated array sizes without proper bounds checking. The vulnerable code relied on assert() statements that are disabled in release builds, allowing arithmetic underflow when `old_count > *size + new_count`, potentially causing memory corruption through out-of-bounds memcpy operations.

high

How buffer overflow via sprintf() happens in C string formatting and how to fix it

A high-severity buffer overflow vulnerability was discovered in `bench/strbuild/strbuild.c` where `sprintf()` wrote formatted output into a 64-byte stack buffer (`line[64]`) without any bounds checking. An attacker who could influence the values in the `NAMES[]`, `c[]`, or `v[]` arrays could overflow this buffer, potentially corrupting the stack and hijacking control flow. The fix replaces `sprintf()` with `snprintf(line, sizeof(line), ...)` to enforce a strict 64-byte write limit.

critical

How buffer overflow happens in C tar header parsing and how to fix it

A critical buffer overflow vulnerability was discovered in `microtar/microtar.c` where the `raw_to_header()` and `header_to_raw()` functions used unbounded `strcpy()` and `sprintf()` calls to copy tar header fields. Malicious tar files with non-null-terminated name fields could overflow destination buffers, potentially leading to code execution. The fix replaces all unsafe string operations with bounded alternatives: `memcpy()` with explicit null-termination and `snprintf()` instead of `sprintf(

critical

How buffer overflow happens in C ieee80211_input() and how to fix it

A critical buffer overflow vulnerability was discovered in `src/firmware/src/net/ieee80211.c` at line 1584, where the `ieee80211_input()` function processed raw 802.11 data frames without verifying that the incoming frame was large enough to contain a valid `ieee80211_frame` header. An attacker within wireless range could craft undersized or malformed frames to trigger memory corruption, potentially leading to remote code execution on the firmware. The fix adds a single, targeted bounds check th