Back to Blog
critical SEVERITY8 min read

Buffer Overflow in zlib's untgz.c: How strcpy() Puts Your App at Risk

A critical buffer overflow vulnerability was discovered and patched in zlib's `untgz.c` utility, where two unchecked `strcpy()` calls could allow attackers to corrupt memory by supplying an oversized archive name. This class of vulnerability has been responsible for some of the most devastating exploits in software history, making it essential for developers to understand how and why it happens. The fix eliminates unsafe string copying and replaces it with bounds-aware alternatives that prevent

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

Answer Summary

This is a critical buffer overflow vulnerability (CWE-120) in zlib's `untgz.c` C utility, caused by two unchecked `strcpy()` calls that copy a user-supplied archive name into a fixed-size stack buffer without any length validation. An attacker who provides an oversized `.tar.gz` archive name can overwrite adjacent stack memory, potentially enabling arbitrary code execution. The fix replaces both `strcpy()` calls with bounds-checking alternatives (such as `strncpy()` or `snprintf()`) that limit how many bytes are written into the destination buffer, eliminating the overflow condition entirely.

Vulnerability at a Glance

cweCWE-120
fixReplace strcpy() with bounds-aware string copy functions (strncpy/snprintf) that enforce buffer size limits
riskHeap/stack memory corruption, potential arbitrary code execution
languageC
root causeTwo strcpy() calls copy a user-controlled archive name into a fixed-size buffer without length validation
vulnerabilityBuffer Overflow via unchecked strcpy()

Buffer Overflow in zlib's untgz.c: How Two strcpy() Calls Could Crash (or Hijack) Your Application

Introduction

In the world of C programming, few functions carry as much historical baggage as strcpy(). Introduced in the earliest days of the C standard library, it copies a string from one location to another — and it does so with absolutely no concern for whether the destination is large enough to hold the result. This design, innocent-seeming in isolation, has been the root cause of countless critical vulnerabilities across decades of software, from early Unix exploits to modern embedded systems.

A recently patched vulnerability in components/zlib/zlib/contrib/untgz/untgz.c brings this classic problem back into focus. Two calls to strcpy() — at lines 136 and 141 — perform no bounds checking when copying an archive name and a suffix into a fixed-size buffer. The result? A classic stack or heap buffer overflow that an attacker could potentially exploit to crash the application, corrupt data, or execute arbitrary code.

If your project bundles zlib (and many do — it's one of the most widely used compression libraries in existence), this is a vulnerability you need to understand.


The Vulnerability Explained

What Is a Buffer Overflow?

A buffer overflow occurs when a program writes more data into a memory buffer than that buffer was allocated to hold. The excess data spills over into adjacent memory, overwriting whatever was stored there — which might be other variables, return addresses, function pointers, or critical control data.

In C, fixed-size stack buffers are particularly dangerous targets because they sit right next to the function's return address on the stack. Overwrite that return address with an attacker-controlled value, and you can redirect program execution anywhere you want.

The Vulnerable Code

The vulnerability lives in the TGZfname() function (or similar archive name construction logic) in untgz.c. Here's a simplified representation of what the vulnerable code looks like:

// Vulnerable code (before fix)
char buffer[1024];   // Fixed-size destination buffer
int origlen;

// Line 136: No bounds check — what if arcname is longer than 1024 bytes?
strcpy(buffer, arcname);

origlen = strlen(buffer);

// Line 141: No bounds check on remaining capacity
// What if origlen + strlen(TGZsuffix[i]) > 1024?
strcpy(buffer + origlen, TGZsuffix[i]);

Two distinct problems exist here:

  1. First overflow (line 136): strcpy(buffer, arcname) copies the archive name directly into buffer without checking whether arcname fits. If an attacker (or even a well-meaning user) provides an archive name longer than the buffer, the copy will overflow.

  2. Second overflow (line 141): Even if the first copy somehow fits, the code then appends a suffix (like .tgz or .tar.gz) starting at buffer + origlen — again without checking whether the remaining capacity in buffer is sufficient.

Either overflow can corrupt adjacent stack or heap memory.

How Could It Be Exploited?

The exploitability depends on how arcname is sourced:

  • Direct attacker control: If the application accepts archive names from user input, network data, or filenames in a crafted archive, an attacker can supply a string carefully sized to overwrite the stack return address.
  • Heap corruption: If buffer is heap-allocated, overflowing it can corrupt heap metadata, enabling use-after-free or arbitrary write primitives.
  • Crash / Denial of Service: Even without achieving code execution, a sufficiently large input will crash the process — a reliable denial-of-service vector.

A Real-World Attack Scenario

Imagine an application that uses untgz to extract user-uploaded .tgz files. An attacker uploads a file whose filename (embedded in the archive metadata) is 2,000 characters long. When the application calls the vulnerable function to reconstruct the output filename:

  1. strcpy(buffer, arcname) copies 2,000 bytes into a 1,024-byte buffer.
  2. The extra 976 bytes overwrite adjacent stack memory.
  3. Depending on the platform and compiler protections, this could:
    - Trigger a segmentation fault (DoS)
    - Overwrite the saved return address (code execution)
    - Corrupt a neighboring variable, causing silent logic errors

On systems without stack canaries or ASLR, exploitation is straightforward. Even with modern mitigations, a determined attacker can often bypass them given a reliable overflow primitive.

CWE Classification

This vulnerability maps to:
- CWE-121: Stack-based Buffer Overflow
- CWE-120: Buffer Copy without Checking Size of Input ('Classic Buffer Overflow')
- OWASP A03:2021 – Injection (memory injection via unsafe copy)


The Fix

What Changed?

The fix replaces the unchecked strcpy() calls with bounds-aware alternatives that verify input length before copying. The safest approach in C is to use strncpy(), snprintf(), or — better yet — explicit length validation before any copy operation.

A safe replacement looks like this:

// Safe code (after fix)
char buffer[1024];
size_t arcname_len;
size_t suffix_len;

arcname_len = strlen(arcname);

// Guard: ensure arcname fits in the buffer (leave room for suffix + null terminator)
if (arcname_len >= sizeof(buffer)) {
    // Handle error: name too long
    return NULL;
}

// Safe copy with explicit length bound
strncpy(buffer, arcname, sizeof(buffer) - 1);
buffer[sizeof(buffer) - 1] = '\0';  // Guarantee null termination

suffix_len = strlen(TGZsuffix[i]);

// Guard: ensure suffix fits in remaining space
if (arcname_len + suffix_len >= sizeof(buffer)) {
    // Handle error: combined name too long
    return NULL;
}

// Safe append
strncpy(buffer + arcname_len, TGZsuffix[i], sizeof(buffer) - arcname_len - 1);
buffer[sizeof(buffer) - 1] = '\0';

Alternatively, snprintf() provides an even cleaner solution:

// Even cleaner: use snprintf for the whole operation
char buffer[1024];
int written;

written = snprintf(buffer, sizeof(buffer), "%s%s", arcname, TGZsuffix[i]);

if (written < 0 || (size_t)written >= sizeof(buffer)) {
    // Truncation or error occurred — handle appropriately
    return NULL;
}

Why This Fix Works

The key improvements are:

Problem Before After
No length check before copy strcpy(buffer, arcname) Length validated before copy
No remaining capacity check strcpy(buffer+origlen, suffix) Remaining space explicitly calculated
No null termination guarantee Implicit (and wrong if truncated) Explicit null termination
Silent overflow Undefined behavior Explicit error handling

By explicitly checking lengths before performing any copy, the code fails safely with an error rather than silently corrupting memory.


Prevention & Best Practices

1. Never Use strcpy() or strcat() in New Code

These functions are fundamentally unsafe. Most modern coding standards (MISRA-C, SEI CERT C, etc.) ban them outright. Use these safer alternatives instead:

Unsafe Function Safer Alternative
strcpy(dst, src) strncpy(dst, src, size) + manual null term, or snprintf
strcat(dst, src) strncat(dst, src, remaining) or snprintf
sprintf(buf, fmt, ...) snprintf(buf, size, fmt, ...)
gets(buf) fgets(buf, size, stdin)

2. Always Validate Input Length Before Buffer Operations

// Pattern: validate BEFORE you copy
if (strlen(input) >= BUFFER_SIZE) {
    log_error("Input too long");
    return ERROR_TOO_LONG;
}
// Now safe to copy

3. Enable Compiler and Platform Protections

Modern compilers offer several mitigations that make exploitation harder (though not impossible):

# GCC/Clang: Enable stack canaries
gcc -fstack-protector-strong -o myapp myapp.c

# Enable FORTIFY_SOURCE (detects some unsafe calls at compile time)
gcc -D_FORTIFY_SOURCE=2 -O2 -o myapp myapp.c

# AddressSanitizer: Detect overflows at runtime during testing
gcc -fsanitize=address -o myapp myapp.c

4. Use Static Analysis Tools

Integrate static analysis into your CI/CD pipeline to catch these issues before they ship:

A simple Cppcheck scan would flag this exact vulnerability:

cppcheck --enable=all untgz.c
# Output: [untgz.c:136]: (error) Buffer overrun: strcpy destination size is 1024...

5. Consider Memory-Safe Languages for New Projects

If you're starting a new project and don't have a hard requirement for C, consider languages with memory safety guarantees:

  • Rust — Zero-cost abstractions with compile-time memory safety
  • Go — Garbage collected, no manual memory management
  • Zig — Low-level control with explicit bounds checking

For existing C codebases, consider wrapping unsafe operations in well-tested utility functions with consistent bounds checking.

6. Follow Secure Coding Standards


Conclusion

The buffer overflow vulnerability in untgz.c is a textbook example of a problem that has existed since the dawn of C programming — and continues to appear in real-world code today. Two strcpy() calls without bounds checking, in a utility function processing archive names, created a critical memory corruption vulnerability that could enable denial-of-service or, under the right conditions, arbitrary code execution.

The fix is conceptually simple: always know how much space you have before you write to it. Use snprintf() or explicitly validate lengths before any copy. Enable compiler protections. Run static analysis. And if you're maintaining a C codebase, audit every use of strcpy(), strcat(), sprintf(), and gets() — they are all unsafe by default.

Buffer overflows are not a new problem, but they remain one of the most exploited vulnerability classes year after year. The lesson from this patch isn't just about these two lines of code — it's a reminder that memory safety requires deliberate, consistent effort at every level of development.

Secure coding isn't a feature you add at the end. It's a habit you build from the start.


This vulnerability was identified and patched by OrbisAI Security. Automated security scanning and remediation can help catch issues like this before they reach production.

Frequently Asked Questions

What is a buffer overflow vulnerability?

A buffer overflow occurs when a program writes more data into a fixed-size memory buffer than it can hold, overwriting adjacent memory. In C, functions like strcpy() perform no bounds checking, making them a common source of this flaw.

How do you prevent buffer overflow in C?

Replace unsafe functions like strcpy(), strcat(), and sprintf() with their bounds-checking counterparts: strncpy(), strncat(), snprintf(), or use strlcpy()/strlcat() where available. Always validate input length before copying into fixed-size buffers.

What CWE is buffer overflow?

Buffer overflow is classified under CWE-120 (Buffer Copy without Checking Size of Input, also known as 'Classic Buffer Overflow'). Related identifiers include CWE-121 (Stack-based Buffer Overflow) and CWE-122 (Heap-based Buffer Overflow).

Is strncpy() enough to prevent buffer overflow in C?

strncpy() limits the number of bytes written but has its own pitfalls — it does not guarantee null-termination if the source string is longer than the limit. A safer pattern is snprintf(dest, sizeof(dest), "%s", src), which always null-terminates the result.

Can static analysis detect buffer overflow from strcpy()?

Yes. Static analysis tools such as Semgrep, Coverity, CodeQL, and clang-tidy can flag unsafe uses of strcpy() and similar functions. Orbis AppSec automatically detected this specific vulnerability and opened a pull request with the fix.

View the Security Fix

Check out the pull request that fixed this vulnerability

View PR #44

Related Articles

high

How missing Dependabot cooldown happens in GitHub Actions and how to fix it

A high-severity configuration vulnerability was discovered in a `.github/dependabot.yml` file that lacked a cooldown period for package updates. Without this safeguard, Dependabot could immediately propose updates to newly published package versions—including potentially malicious or unstable releases. The fix adds a simple `cooldown` block with a 7-day waiting period before any new package version is suggested.

high

How Server-Sent Events Injection via Unsanitized Newlines happens in Node.js h3 and how to fix it

A high-severity Server-Sent Events (SSE) injection vulnerability (CVE-2026-33128) was discovered in the h3 HTTP framework, where unsanitized newline characters in event stream fields could allow attackers to inject arbitrary SSE messages. The fix upgrades h3 from version 1.15.5 to 1.15.6 in the frontend's dependency tree, ensuring that newline characters are properly sanitized before being written to event streams.

high

How Memory Exhaustion via Large Comma-Separated Selector Lists happens in Python Soup Sieve and how to fix it

A high-severity memory exhaustion vulnerability (CVE-2026-49476) was discovered in Soup Sieve version 2.8.3, affecting Python applications that parse CSS selectors from user-controlled input. The vulnerability allows attackers to craft malicious selector lists that consume excessive memory, potentially causing denial of service. The fix involves upgrading to soupsieve 2.8.4, which implements proper resource limits on selector parsing.

high

How prototype pollution via `__proto__` key happens in Node.js defu and how to fix it

A high-severity prototype pollution vulnerability (CVE-2026-35209) was discovered in the `defu` package version 6.1.4, which allowed attackers to inject properties into JavaScript's `Object.prototype` via the `__proto__` key in defaults arguments. The fix upgrades `defu` to version 6.1.5 in the frontend's dependency tree, protecting downstream consumers like `c12` and `dotenv` configuration loaders from malicious property injection.

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.

high

How memory exhaustion via large comma-separated selector lists happens in Python soupsieve and how to fix it

A high-severity memory exhaustion vulnerability (CVE-2026-49476) was discovered in soupsieve 2.8.3, a CSS selector library used by BeautifulSoup in Python. An attacker who could influence CSS selector input could craft large comma-separated selector lists to exhaust system memory, causing denial of service. The fix upgrades soupsieve from 2.8.3 to 2.8.4 in the backend's `uv.lock` dependency file.