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 Denial of Service via Exponential-Time Complexity Happens in Node.js Dependencies and How to Fix It

A high-severity Denial of Service vulnerability (CVE-2026-13149) was discovered in the brace-expansion npm package, where maliciously crafted input could trigger exponential-time complexity and crash Node.js applications. The fix upgrades brace-expansion from version 5.0.6 to 5.0.9 using npm overrides to ensure all nested dependencies receive the patched version.

high

How Denial of Service via infinite loop happens in Node.js dependencies and how to fix it

A high-severity Denial of Service vulnerability in the nanoid package (CVE-2026-67213) was discovered in the project's dependency tree, where crafted input could trigger an infinite loop during random ID generation. The fix upgrades nanoid from 3.3.17 to 3.3.18 and adds an npm override to ensure all transitive dependencies use the patched version.

high

How Dependabot Missing Cooldown happens in GitHub Actions and how to fix it

A Dependabot configuration in `.github/dependabot.yml` was missing cooldown periods for both its npm and GitHub Actions package ecosystems, meaning newly published — potentially malicious or unstable — package versions could be proposed for adoption immediately after release. Adding a `cooldown` block with `default-days: 7` to each ecosystem entry creates a 7-day buffer, allowing the security community time to identify and flag compromised packages before they reach your codebase.

high

How pnpm Missing Minimum Release Age happens in Node.js workspaces and how to fix it

A missing `minimumReleaseAge` setting in `pnpm-workspace.yaml` left this Node.js workspace vulnerable to immediately installing newly published — potentially malicious — package versions. The fix adds `minimumReleaseAge: 10080` (7 days in minutes) to enforce a quarantine window before any freshly published package can be installed. This single configuration change significantly reduces the risk of supply chain attacks targeting the package publishing pipeline.

high

How Dependabot Missing Cooldown happens in GitHub Actions and how to fix it

A high-severity misconfiguration in `.github/dependabot.yml` left three `package-ecosystem` entries without a cooldown period, meaning Dependabot could immediately propose updates from newly published—potentially malicious—packages. The fix adds a `cooldown` block with `default-days: 7` to each entry, introducing a mandatory waiting period before any newly released package version is surfaced as an update candidate. For a Node.js library whose vulnerabilities ripple downstream to all consumers,

critical

How Unauthenticated Proxy Endpoints Enable DoS Amplification in FastAPI and how to fix it

Public proxy endpoints in `backend/api/proxy.py` had no rate limiting, allowing any attacker to flood the httpx connection pool with unauthenticated requests and amplify denial-of-service attacks against downstream tile and coordinate-conversion services. The fix introduces a per-IP sliding-window rate limiter using environment-configurable thresholds, closing the amplification vector without breaking legitimate usage.