Back to Blog
critical SEVERITY7 min read

How buffer overflow happens in C gdb-server and how to fix it

A critical buffer overflow vulnerability was discovered in `src/st-util/gdb-server.c` where unbounded `memcpy()` and `strcpy()` calls could write beyond allocated buffer boundaries when processing user-supplied command-line arguments. The fix replaces all unsafe string operations with bounds-checked alternatives like `snprintf()` and `memcpy()` with explicit length validation.

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

Answer Summary

This vulnerability is a classic buffer overflow (CWE-120) in C caused by unbounded string operations (`strcpy()`, `memcpy()` without length checks) in the st-util GDB server. The vulnerability exists in `gdb-server.c` at lines 212, 367, 376, and 994 where user input or template strings are copied into fixed-size buffers without validation. The fix replaces all unsafe operations with `snprintf()` (which enforces buffer boundaries) and proper `memcpy()` with explicit size limits, ensuring no writes exceed allocated buffer space.

Vulnerability at a Glance

cweCWE-120 (Buffer Copy without Checking Size of Input)
fixReplace strcpy()/memcpy() with snprintf() and bounded memcpy() calls that enforce STLINK_SERIAL_BUFFER_SIZE limits
riskHeap corruption, denial of service, potential code execution via stack/heap overflow
languageC
root causeUsing strcpy() and memcpy() with user-controlled or template data without validating that the source fits in the destination buffer
vulnerabilityBuffer overflow via unbounded strcpy() and memcpy()

How Buffer Overflow Happens in C gdb-server and How to Fix It

Introduction

In the st-util GDB server component (src/st-util/gdb-server.c), a critical buffer overflow vulnerability was discovered that could allow an attacker to corrupt heap memory by supplying a maliciously long --serial command-line argument. The vulnerability stems from four locations in the code where unbounded string operations—strcpy() and memcpy()—copy data into fixed-size buffers without checking if the source fits. Specifically:

  • Line 212: memcpy(st->serialnumber, optarg, STLINK_SERIAL_BUFFER_SIZE) incorrectly assumes optarg is smaller than the buffer size
  • Line 367 & 376: Two strcpy() calls copy template strings into a dynamically allocated map buffer without size validation
  • Line 994: strncpy() is used but lacks null-termination guarantee

This is a classic memory safety issue in C that affects any developer working with command-line tools, embedded systems, or low-level utilities. Understanding this vulnerability and its fix is essential for writing secure C code.


The Vulnerability Explained

The Problematic Code

Let's examine the vulnerable code at line 212 in the parse_options() function:

case SERIAL_OPTION:
    printf("use serial %s\n", optarg);
    memcpy(st->serialnumber, optarg, STLINK_SERIAL_BUFFER_SIZE);
    break;

The issue is subtle but dangerous: the code uses memcpy() with STLINK_SERIAL_BUFFER_SIZE as the copy length, but STLINK_SERIAL_BUFFER_SIZE defines the destination buffer size, not the source length. The optarg pointer comes directly from the command-line argument parser and has no length validation.

If an attacker runs:

./st-util --serial AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA

with a serial string longer than STLINK_SERIAL_BUFFER_SIZE (typically 16 bytes), the memcpy() call will write beyond the st->serialnumber buffer boundary, corrupting adjacent heap memory.

Similar Issues in Memory Map Functions

The same pattern appears in the make_memory_map() function at lines 367 and 376:

} else if(sl->chip_id == STM32_CHIPID_F4_DE) {
    strcpy(map, memory_map_template_F4_DE);  // Line 367 - DANGEROUS!
} else if(sl->chip_id == STM32_CHIPID_F4_HD) {
    strcpy(map, memory_map_template_F4_HD);  // Line 376 - DANGEROUS!

Here, strcpy() copies a template string into the map buffer. While map is dynamically allocated with calloc(), the allocation size is computed earlier in the function. If the template string is larger than the allocated size (a possible scenario in firmware updates or configuration changes), the strcpy() will overflow.

Attack Scenario

An attacker who can control the command-line arguments to st-util can trigger this vulnerability:

# Craft a serial number exceeding STLINK_SERIAL_BUFFER_SIZE
./st-util --serial "$(python3 -c 'print("A" * 100)')"

The memcpy() call writes 16 bytes from a 100-byte string, but the destination buffer is only 16 bytes. The extra 84 bytes corrupt:
- Adjacent heap metadata
- Other heap-allocated structures
- Potentially enabling a heap exploit or denial of service

Real-World Impact

For a tool like st-util (used for STM32 microcontroller debugging), this vulnerability could:
- Crash the debugger during firmware upload/download operations
- Corrupt debug session state, making debugging unreliable
- Escalate privileges if st-util runs with elevated permissions (less common but possible in embedded development environments)
- Enable remote code execution if the heap corruption is leveraged with a heap spray attack


The Fix

What Changed

The fix replaces all four unsafe string operations with bounds-checked alternatives:

Fix 1: Line 212 - Replace memcpy with snprintf

Before:

memcpy(st->serialnumber, optarg, STLINK_SERIAL_BUFFER_SIZE);

After:

snprintf(st->serialnumber, STLINK_SERIAL_BUFFER_SIZE, "%s", optarg);

Why this works: snprintf() enforces the buffer size limit (second parameter) and automatically null-terminates the string. Even if optarg is 1000 bytes, snprintf() will copy at most STLINK_SERIAL_BUFFER_SIZE - 1 bytes and add a null terminator. The string is safely truncated, not overflowed.

Fix 2 & 3: Lines 367 & 376 - Replace strcpy with snprintf

Before (Line 367):

strcpy(map, memory_map_template_F4_DE);

After:

snprintf(map, sz, "%s", memory_map_template_F4_DE);

Why this works: The variable sz holds the allocated size of map. By passing sz to snprintf(), we ensure the template string cannot exceed the buffer boundary. If the template is larger than sz, it's truncated safely.

Fix 4: Line 994 - Replace strncpy with memcpy + explicit size

Before:

strncpy(queryName, &packet[1], queryNameLength);

After:

memcpy(queryName, &packet[1], queryNameLength);

Why this works: The original code used strncpy(), which doesn't guarantee null-termination if the source is exactly queryNameLength bytes. The buffer queryName is allocated with calloc(1, queryNameLength + 1), which zeros memory. Using memcpy() with the exact size and relying on the zero-initialized buffer is safe and more efficient.

Security Improvement

The fixes enforce a security invariant: no string operation will write beyond its destination buffer boundary, even with adversarial input.

Aspect Before After
Buffer boundary check None Enforced by snprintf()
Null termination Assumed Guaranteed
Truncation behavior Overflow Safe truncation
Input validation None Implicit in function

Prevention & Best Practices

1. Never Use strcpy() in Production Code

strcpy() has no bounds checking. Modern C standards (C99, C11) and security guidelines universally recommend against it.

Alternative: Use snprintf() or strlcpy() (if available on your platform).

2. Always Validate Buffer Sizes

When using memcpy(), verify that the source size does not exceed the destination:

// WRONG
memcpy(dest, src, DEST_SIZE);

// RIGHT
size_t copy_size = (src_len < DEST_SIZE) ? src_len : DEST_SIZE;
memcpy(dest, src, copy_size);

3. Use Bounded String Functions

  • snprintf() – Bounded formatted output (C99 standard)
  • strlcpy() – Bounded string copy (BSD, not standard C but widely available)
  • strncpy() – Bounded string copy (C standard, but doesn't guarantee null termination)

4. Enable Compiler Protections

Compile with security flags to catch overflow attempts at runtime:

gcc -fstack-protector-strong -D_FORTIFY_SOURCE=2 -fsanitize=address gdb-server.c
  • -fstack-protector-strong – Detects stack buffer overflows
  • -D_FORTIFY_SOURCE=2 – Enables compile-time and runtime checks for dangerous functions
  • -fsanitize=address – AddressSanitizer detects heap overflows during testing

5. Use Static Analysis Tools

Integrate security scanners into your CI/CD pipeline:

  • Clang Static Analyzer – Detects many buffer overflow patterns
  • Coverity – Commercial static analysis with high accuracy
  • Orbis AppSec – Automated security scanning that detects and fixes issues like this
  • Semgrep – Open-source pattern matching for security rules

6. Reference CWE-120 and OWASP Guidelines

Understand the root cause:
- CWE-120: Buffer Copy without Checking Size of Input
- OWASP: Secure Coding Practices - Input Validation


Key Takeaways

  • Never copy user-controlled data with unbounded functions. The --serial argument in line 212 is attacker-controlled; memcpy() with no length check is a direct buffer overflow.

  • strcpy() is unsafe in all contexts. Even if you "know" the string is small, future code changes or configuration updates can introduce longer strings. Use snprintf() instead.

  • snprintf() is your friend. It enforces buffer boundaries, null-terminates automatically, and safely truncates. It's the standard way to format and copy strings in modern C.

  • Template strings must be validated against buffer size. The memory map templates in lines 367 and 376 are "internal" strings, but they can still overflow if the buffer size calculation is wrong. Always use bounded functions.

  • Compiler protections catch what code review misses. Stack canaries and AddressSanitizer would have detected this vulnerability during testing. Use them in development and CI/CD.


How Orbis AppSec Detected This

Source: Command-line argument optarg from getopt() parser, passed directly to memcpy() without length validation.

Sink:
- memcpy(st->serialnumber, optarg, STLINK_SERIAL_BUFFER_SIZE) at line 212
- strcpy(map, memory_map_template_F4_DE) at line 367
- strcpy(map, memory_map_template_F4_HD) at line 376
- strncpy(queryName, &packet[1], queryNameLength) at line 994

Missing Control: No validation that source data length fits within destination buffer size. No use of bounded string functions.

CWE: CWE-120 – Buffer Copy without Checking Size of Input

Fix: Replaced strcpy() and memcpy() with snprintf() (which enforces the buffer size parameter) and corrected strncpy() to use memcpy() with explicit size validation from buffer allocation.

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 are among the most critical security issues, with a history spanning decades of exploits and breaches. The fix in st-util demonstrates a fundamental principle: use bounded functions by default, never unbounded ones.

The shift from strcpy() to snprintf(), from memcpy() without checks to memcpy() with validated sizes, and from strncpy() without null-termination guarantees to safer alternatives represents a maturation of secure C coding practices.

If you maintain C code, especially low-level tools like debuggers, embedded systems, or security-critical applications:

  1. Audit your codebase for strcpy(), sprintf(), and unbounded memcpy() calls
  2. Replace them with snprintf(), strlcpy(), or bounded alternatives
  3. Enable compiler protections during development and testing
  4. Use static analysis in your CI/CD pipeline to catch new issues before they reach production

The developers of st-util made the right choice in fixing this issue proactively. Your code can be just as secure.


References

Frequently Asked Questions

What is a buffer overflow in C?

A buffer overflow occurs when a program writes data beyond the boundaries of an allocated buffer, potentially corrupting adjacent memory, causing crashes, or enabling code execution attacks.

How do you prevent buffer overflow in C?

Always validate input length before copying, use bounded functions like `snprintf()`, `strncpy()`, or `memcpy()` with explicit size parameters, and enable compiler protections like stack canaries and AddressSanitizer during development.

What CWE is buffer overflow?

Buffer overflow is primarily CWE-120 (Buffer Copy without Checking Size of Input), though related issues include CWE-119 (Improper Restriction of Operations within the Bounds of a Memory Buffer) and CWE-787 (Out-of-bounds Write).

Is input validation alone enough to prevent buffer overflow?

No—input validation is important but insufficient. You must also use bounded string functions. An attacker might bypass validation logic, so the function itself must enforce size limits.

Can static analysis detect buffer overflow?

Yes. Modern static analyzers like Clang Static Analyzer, Coverity, and automated security scanners can detect many buffer overflow patterns, particularly obvious cases like `strcpy()` with unbounded sources.

View the Security Fix

Check out the pull request that fixed this vulnerability

View PR #1495

Related Articles

critical

How integer overflow in buffer size calculations happens in C and how to fix it

A critical integer overflow vulnerability was discovered in `src/api.c`'s `find_config_path()` function, where string lengths were added together without overflow checks before allocating a buffer. An attacker controlling environment variables like `APPDATA`, `HOME`, or `XDG_DATA_HOME` could supply extremely long values to trigger an integer overflow, resulting in an undersized buffer allocation and a subsequent heap buffer overflow. The fix adds explicit overflow guards using `SIZE_MAX` compari

critical

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

A classic buffer overflow vulnerability was discovered in `bld/pbide/dlldbg/dlldbg.c` at line 80, where an unbounded `sprintf()` call wrote a user-influenced `dllName` string into a fixed-size `fmtBuffer` without any length checking. An attacker supplying a maliciously crafted DLL name could overflow the buffer, overwrite adjacent memory, and potentially achieve arbitrary code execution. The fix replaces `sprintf()` with `snprintf()`, passing `sizeof(fmtBuffer)` as an explicit bound to ensure th

critical

How buffer overflow in strcpy() happens in C bin2coff tool and how to fix it

A critical buffer overflow vulnerability was discovered in `tools/bin2coff.c` where multiple `strcpy()` operations copied user-controlled label strings into fixed-size buffers without bounds checking. An attacker could provide maliciously long labels to overflow destination buffers and corrupt adjacent memory structures, potentially leading to arbitrary code execution. The fix replaced unsafe string operations with bounded alternatives like `strlcpy()` and `snprintf()`.

critical

How buffer overflow in handle_interlink_event() happens in C terminal event handling and how to fix it

A critical buffer overflow vulnerability was discovered in `src/terminal/event.c` at line 250, where `memcpy()` blindly copied `info->cwd` into a fixed-size `term->cwd` buffer without verifying the source string's actual length. An attacker who could supply a crafted working directory path longer than `MAX_CWD_LEN` could corrupt adjacent heap memory, potentially leading to code execution. The fix replaces the unsafe `memcpy()` call with `safe_strncpy()`, which enforces the destination buffer bou

high

How buffer overflow happens in C string operations with strcpy/strncpy and how to fix it

A critical buffer overflow vulnerability in `src/pomoc.c` was discovered where `strncpy()` was used unsafely to copy a socket path into a fixed-size buffer. The fix replaces the dangerous string copy with `snprintf()`, which provides automatic bounds checking and null-termination. This prevents attackers from exploiting the CLI tool through oversized input arguments.

high

How integer overflow in malloc happens in C libregexp and how to fix it

A high-severity integer overflow vulnerability was discovered in QuickJS's libregexp.c where multiplication to compute allocation size could wrap around, causing a heap overflow. The fix replaces the unsafe `malloc(sizeof(capture[0]) * lre_get_alloc_count(bc))` pattern with `calloc(lre_get_alloc_count(bc), sizeof(capture[0]))`, which safely handles the multiplication internally and prevents exploitation.