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 orbisai0security
May 28, 2026

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.

View the Security Fix

Check out the pull request that fixed this vulnerability

View PR #12

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