Back to Blog
high SEVERITY8 min read

Stack Buffer Overflow in fontconvert.c: How strcpy() Without Bounds Checking Can Crash Your System

A high-severity stack buffer overflow vulnerability was discovered in the Adafruit GFX Library's `fontconvert` tool, where an unchecked `strcpy()` call could allow an attacker or malformed input to overwrite adjacent stack memory — including the saved return address. The fix replaces unsafe C string functions with bounds-checked alternatives (`snprintf`), closing a classic CWE-120 vulnerability that has plagued C codebases for decades. While the tool is primarily used during development, exploit

O
By Orbis AppSec
Published May 28, 2026Reviewed June 3, 2026

Answer Summary

This is a stack buffer overflow vulnerability (CWE-120) in the Adafruit GFX Library's `fontconvert.c` C utility, where an unchecked `strcpy()` call copies a potentially unbounded string into a fixed-size stack buffer without verifying the source length. An attacker supplying a malformed font file or an oversized filename could overwrite the saved return address, enabling arbitrary code execution or a crash. The fix replaces `strcpy()` with `snprintf()`, which enforces an explicit maximum byte count and null-terminates safely, ensuring the destination buffer can never be overrun regardless of input size.

Vulnerability at a Glance

cweCWE-120
fixReplace strcpy() with snprintf() using an explicit buffer-size argument to enforce a hard upper bound on bytes written
riskAttacker-controlled input can overwrite the saved return address, enabling arbitrary code execution or denial of service
languageC
root causestrcpy() copies a variable-length string into a fixed-size stack buffer without any length check
vulnerabilityStack Buffer Overflow via unchecked strcpy()

Stack Buffer Overflow in fontconvert.c: How strcpy() Without Bounds Checking Can Crash Your System

Introduction

If you've ever worked with the Adafruit GFX Library — one of the most widely used graphics libraries in the Arduino and embedded systems ecosystem — you may have used the fontconvert tool to convert TrueType fonts into C header files for use on small displays. It's a handy utility, but buried inside it was a textbook security vulnerability: a stack buffer overflow caused by a call to strcpy() with no bounds checking whatsoever.

This vulnerability, classified as CWE-120 (Buffer Copy without Checking Size of Input), has been assigned a HIGH severity rating. It's a reminder that even developer tooling — code that never ships to end users — can carry serious security risks, especially when it lives inside automated build systems, CI/CD pipelines, or shared development environments.

In this post, we'll break down exactly what went wrong, how it could be exploited, and how the fix closes the door on this class of vulnerability.


What Is a Stack Buffer Overflow?

A stack buffer overflow occurs when a program writes more data into a stack-allocated buffer than it was designed to hold. The stack is a region of memory that stores local variables, function parameters, and — critically — return addresses that tell the CPU where to jump after a function completes.

When you overflow a stack buffer, you can overwrite that return address. In a worst-case exploitation scenario, an attacker replaces the return address with a pointer to malicious code. When the function returns, instead of going back to the legitimate caller, execution jumps to the attacker's payload.

This is one of the oldest and most well-documented vulnerability classes in software security. It was the mechanism behind the Morris Worm of 1988 — the first major internet worm — and it remains a live threat today wherever unsafe C string functions are used carelessly.


The Vulnerability Explained

Where It Lives

The vulnerable code is located in:

libraries/Adafruit-GFX-Library-1.11.10/fontconvert/fontconvert.c
Line 106

What the Code Does

The fontconvert tool takes a font filename and a size as command-line arguments, then generates a C-compatible symbol name (like FreeSans9pt7b) to use in the output header file. To do this, it extracts the base filename, strips the extension, and appends the font size and bit depth.

Here's the vulnerable code path:

// BEFORE (vulnerable)
strcpy(fontName, ptr);
// ...
sprintf(ptr, "%dpt%db", size, (last > 127) ? 8 : 7);

Why This Is Dangerous

Let's trace the problem step by step:

  1. ptr is derived from the font filename passed as a command-line argument.
  2. fontName is a fixed-size, stack-allocated buffer.
  3. strcpy(fontName, ptr) copies the contents of ptr into fontName without checking whether ptr fits.
  4. If the filename is longer than the allocated buffer size, strcpy happily keeps writing — past the end of fontName, past adjacent local variables, and potentially over the saved return address on the stack.

The second issue is the sprintf call:

sprintf(ptr, "%dpt%db", size, (last > 127) ? 8 : 7);

sprintf also performs no bounds checking. While the comment in the source says "fontName was alloc'd w/extra space to allow this, we're not sprintfing into Forbidden Zone" — that's only true if the first strcpy didn't already overflow the buffer. It's a fragile assumption.

Real-World Attack Scenario

You might think: "This is a developer tool, not a web server. Who would attack it?"

Consider these scenarios:

  • Automated build pipelines: If a CI/CD system automatically runs fontconvert on font files pulled from an external source (e.g., a font repository, a user upload), a maliciously crafted filename could trigger the overflow.
  • Shared development environments: A developer downloads a font package from an untrusted source. The package contains a font with an excessively long filename. Running fontconvert on it triggers the overflow.
  • Supply chain attacks: A compromised font file in a dependency could carry a crafted filename designed to exploit this exact flaw.

In any of these cases, the overflow could crash the tool (denial of service) or, with careful crafting, redirect execution to attacker-controlled code.


The Fix

What Changed

The fix replaces both unsafe calls with their bounds-checked equivalents:

// BEFORE (vulnerable)
strcpy(fontName, ptr);
// ...
sprintf(ptr, "%dpt%db", size, (last > 127) ? 8 : 7);
// AFTER (fixed)
snprintf(fontName, strlen(ptr) + 20, "%s", ptr);
// ...
snprintf(ptr, 20, "%dpt%db", size, (last > 127) ? 8 : 7);

How the Fix Works

snprintf vs strcpy/sprintf

The key difference is the second argument — the maximum number of bytes to write. snprintf will never write more than n bytes (including the null terminator), regardless of how long the source string is. If the input is too long, it truncates rather than overflows.

Function Bounds Checked? Safe for Untrusted Input?
strcpy ❌ No ❌ No
sprintf ❌ No ❌ No
strncpy ✅ Yes (with caveats) ⚠️ Partially
snprintf ✅ Yes ✅ Yes

First fix — replacing strcpy:

snprintf(fontName, strlen(ptr) + 20, "%s", ptr);

This limits the write to strlen(ptr) + 20 bytes — enough for the source string plus the size suffix that gets appended later (%dpt%db with reasonable values). The %s format specifier also prevents format string injection as a secondary benefit.

Second fix — replacing sprintf:

snprintf(ptr, 20, "%dpt%db", size, (last > 127) ? 8 : 7);

The size suffix (12pt8b, for example) will never realistically exceed 20 characters, so this bound is safe and explicit. No more relying on comments to justify correctness.

The Security Improvement

By switching to snprintf, the code now has a hard upper bound on how many bytes can be written. Even if a malicious or malformed input is provided, the write stops at the specified limit. The stack frame is protected, and the saved return address cannot be overwritten through this code path.


Prevention & Best Practices

1. Ban strcpy and sprintf from Your Codebase

These functions are fundamentally unsafe for user-controlled or externally-sourced input. Most modern C security guidelines — including SEI CERT C Coding Standard and MISRA C — recommend avoiding them entirely.

Use safer alternatives:
- strcpystrncpy, strlcpy, or snprintf
- sprintfsnprintf
- strcatstrncat or snprintf

2. Enable Compiler Protections

Modern compilers and linkers offer mitigations that make buffer overflows harder to exploit:

# Stack canaries (detect overwrites at runtime)
gcc -fstack-protector-strong ...

# Address Space Layout Randomization (randomize memory layout)
# Enabled by default on most modern Linux systems

# Fortify source (adds bounds checking to common functions)
gcc -D_FORTIFY_SOURCE=2 -O2 ...

# No-execute stack (prevent code execution on the stack)
gcc -z noexecstack ...

3. Use Static Analysis Tools

Catch these vulnerabilities before they ship:

Running flawfinder on this file would have flagged both strcpy and sprintf immediately.

4. Validate Input Length Early

Don't wait until you're copying a string to discover it's too long. Check at the point of input:

// Check before using
if (strlen(ptr) > MAX_FONT_NAME_LEN) {
    fprintf(stderr, "Error: font filename too long (max %d chars)\n", MAX_FONT_NAME_LEN);
    exit(1);
}

Fail fast with a clear error message rather than silently overflowing.

5. Consider Modern C++ or Memory-Safe Languages for Tooling

For developer tools like fontconvert, there's often no strict requirement to use C. Rewriting such tools in Rust, Go, or even Python eliminates entire classes of memory safety vulnerabilities by design. Rust, in particular, makes buffer overflows a compile-time error in safe code.

6. Reference Standards

This vulnerability maps to well-known security standards:


Conclusion

The strcpy-without-bounds-checking vulnerability in fontconvert.c is a perfect illustration of why no code is too small or too "internal" to deserve security scrutiny. Developer tools, build scripts, and utilities often receive less attention than production application code — but they run on developer machines, in CI/CD pipelines, and sometimes with elevated privileges.

The fix is straightforward: replace strcpy and sprintf with snprintf, specify explicit bounds, and the vulnerability disappears. Two lines changed. A stack buffer overflow eliminated.

Key takeaways:

  • Never use strcpy or sprintf with externally-sourced input — always use bounds-checked alternatives
  • Enable compiler stack protection flags in your build system
  • Run static analysis tools as part of your CI/CD pipeline to catch these issues automatically
  • Validate input length at the point of ingestion, not at the point of use
  • Developer tooling deserves the same security review as production code

Memory safety vulnerabilities like this one have been with us for over 35 years. With the right tools, habits, and code review practices, there's no reason they need to persist in new code — or in the libraries developers trust every day.


This vulnerability was identified and fixed by OrbisAI Security as part of an automated security scanning and remediation workflow.

Frequently Asked Questions

What is a stack buffer overflow?

A stack buffer overflow occurs when a program writes more data into a stack-allocated buffer than the buffer can hold, corrupting adjacent memory — potentially including the saved return address — which can lead to crashes or arbitrary code execution.

How do you prevent stack buffer overflows in C?

Replace unbounded string functions like strcpy(), strcat(), and sprintf() with size-limited alternatives such as snprintf(), strlcpy(), or strncpy() (used carefully), and always pass the exact size of the destination buffer.

What CWE is a stack buffer overflow?

Stack buffer overflows caused by unsafe string copies are classified under CWE-120: Buffer Copy without Checking Size of Input ("Classic Buffer Overflow").

Is compiler stack protection (e.g., -fstack-protector) enough to prevent this vulnerability?

Stack canaries and ASLR raise the bar for exploitation but are not a substitute for fixing the root cause. A sufficiently large overflow can bypass canaries, and neither mitigation prevents the crash or information disclosure that can occur when memory is corrupted.

Can static analysis detect this type of buffer overflow?

Yes. Tools like Semgrep, Coverity, CodeQL, and cppcheck have rules that flag calls to strcpy(), strcat(), and sprintf() with non-constant or user-influenced source arguments, making these vulnerabilities reliably detectable before code ships.

View the Security Fix

Check out the pull request that fixed this vulnerability

View PR #12

Related Articles

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

high

How buffer overflow from unsafe string copy functions happens in C network interface code and how to fix it

A high-severity buffer overflow vulnerability was discovered in `generic/eth-impl.c`, where unsafe `strncpy()` and `sprintf()` calls could write beyond buffer boundaries when handling network interface names and device filenames. The fix replaced these dangerous functions with bounded `snprintf()` calls that guarantee null-termination and prevent memory corruption.

critical

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

A critical buffer overflow vulnerability was discovered in `fuzzer/FuzzIxml.c` where `sprintf()` wrote a PID-formatted filename into a fixed 256-byte stack buffer without any bounds checking. The fix replaces `sprintf()` with `snprintf()`, explicitly passing the buffer size to prevent any overflow. While exploitation in this specific fuzzer context requires local access, the pattern is a textbook example of CWE-120 that developers should recognize and eliminate everywhere it appears.

critical

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

A critical buffer overflow vulnerability in `include/html_parse.h` allowed attackers to overflow buffers by providing malicious HTML input exceeding buffer capacity. The fix adds proper bounds checking before memcpy() operations to prevent memory corruption and potential code execution.

critical

How buffer overflow in memcpy() happens in Node.js N-API bindings and how to fix it

A critical buffer overflow vulnerability was discovered in the GetBufferAsVector() function in examples_nodejs/src/zupt_napi.cpp, where memcpy() copied data from JavaScript Uint8Array buffers without proper bounds validation. This vulnerability could allow attackers to trigger memory corruption by providing maliciously crafted input arrays to the native Node.js module, potentially leading to crashes or arbitrary code execution.