Back to Blog
critical SEVERITY7 min read

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(

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

Answer Summary

This is a critical buffer overflow vulnerability (CWE-120) in C tar header parsing code where `strcpy()` and `sprintf()` functions lack bounds checking. When processing tar files with adversarial input, attackers could overflow buffers by providing non-null-terminated header fields. The fix replaces all unbounded string operations with bounded alternatives: `memcpy(dst, src, sizeof(dst))` followed by explicit null-termination, and `snprintf()` with size limits instead of unsafe `sprintf()`.

Vulnerability at a Glance

cweCWE-120 (Buffer Copy without Checking Size of Input)
fixReplace strcpy() with memcpy() + null-termination and sprintf() with snprintf()
riskRemote code execution via malicious tar files; local privilege escalation
languageC
root causeUse of strcpy() and sprintf() without bounds checking on tar header fields
vulnerabilityBuffer overflow in tar header field copying

How Buffer Overflow Happens in C Tar Header Parsing and How to Fix It

Tar archives are ubiquitous in software distribution, but the C library that parses them can be a security minefield if not handled carefully. In this post, we'll examine a critical buffer overflow vulnerability discovered in microtar/microtar.c—a production tar parsing library—and walk through the exact fix that prevented potential code execution.


Introduction

The microtar/microtar.c file handles tar archive parsing, a core function in many build systems and package managers. However, the raw_to_header() and header_to_raw() functions at lines 114 and beyond contained a dangerous pattern: unbounded string copying using strcpy() and sprintf().

When processing tar files, these functions copy header fields (like filename and link name) from raw binary structures into destination buffers without checking if the source data fits. An attacker controlling a malicious tar file could craft header fields that are not null-terminated, causing strcpy() to read beyond the source buffer and overflow the destination—potentially corrupting the heap and leading to code execution.

This wasn't theoretical. The vulnerability existed in production code and was flagged as CRITICAL with an assessment of "Likely exploitable."


The Vulnerability Explained

The Dangerous Code Pattern

Let's look at the vulnerable code from microtar/microtar.c (lines 114-115):

static int raw_to_header(mtar_header_t *h, const mtar_raw_header_t *rh) {
  // ... other code ...
  strcpy(h->name, rh->name);           // ← VULNERABLE
  strcpy(h->linkname, rh->linkname);   // ← VULNERABLE
  return MTAR_ESUCCESS;
}

And in the header_to_raw() function (lines 128-131):

sprintf(rh->mode, "%o", h->mode);      // ← VULNERABLE
sprintf(rh->owner, "%o", h->owner);    // ← VULNERABLE
sprintf(rh->size, "%o", h->size);      // ← VULNERABLE
sprintf(rh->mtime, "%o", h->mtime);    // ← VULNERABLE
strcpy(rh->name, h->name);             // ← VULNERABLE
strcpy(rh->linkname, h->linkname);     // ← VULNERABLE

Why This Is Dangerous

The tar header structures have fixed-size fields. For example, rh->name might be 100 bytes. When strcpy() copies from an untrusted source (a tar file), it reads until it finds a null terminator—but what if the source isn't null-terminated?

Attack scenario:
1. An attacker creates a malicious tar file with a name field that is exactly 100 bytes without a null terminator.
2. When raw_to_header() calls strcpy(h->name, rh->name), it copies all 100 bytes and keeps reading into adjacent memory looking for \0.
3. This read-past-bounds can overflow the destination buffer, corrupting the heap.
4. With careful crafting, this corruption could overwrite function pointers or other critical data, leading to code execution.

The Actual Threat

The tar parsing happens when:
- A user extracts an archive via CLI: myapp extract malicious.tar
- A build system automatically processes downloaded tar files
- A package manager unpacks dependencies

Since this is a local CLI tool, exploitation requires the attacker to control input files or command-line arguments. However, in supply chain scenarios (e.g., compromised package repositories), this is a realistic threat.


The Fix

The fix replaces all unbounded string operations with bounded alternatives. Here's the complete diff:

Change 1: Replace strcpy() with memcpy() + null-termination

Before:

strcpy(h->name, rh->name);
strcpy(h->linkname, rh->linkname);

After:

memcpy(h->name, rh->name, sizeof(h->name));
h->name[sizeof(h->name) - 1] = '\0';
memcpy(h->linkname, rh->linkname, sizeof(h->linkname));
h->linkname[sizeof(h->linkname) - 1] = '\0';

Why this works:
- memcpy(dst, src, sizeof(dst)) copies at most sizeof(dst) bytes, preventing overflow.
- Explicitly setting the last byte to '\0' ensures null-termination, even if the source wasn't null-terminated.
- This is safer than strncpy(), which doesn't guarantee null-termination.

Change 2: Replace sprintf() with snprintf()

Before:

sprintf(rh->mode, "%o", h->mode);
sprintf(rh->owner, "%o", h->owner);
sprintf(rh->size, "%o", h->size);
sprintf(rh->mtime, "%o", h->mtime);

After:

snprintf(rh->mode, sizeof(rh->mode), "%o", h->mode);
snprintf(rh->owner, sizeof(rh->owner), "%o", h->owner);
snprintf(rh->size, sizeof(rh->size), "%o", h->size);
snprintf(rh->mtime, sizeof(rh->mtime), "%o", h->mtime);

Why this works:
- snprintf() takes a size parameter and writes at most size - 1 bytes (reserving space for null terminator).
- This prevents format string overflows and buffer overflows when converting integers to octal strings.

Change 3: Safe filename copying in mtar_write_file_header()

Before:

strcpy(h.name, name);  // ← User-controlled input, unbounded

After:

snprintf(h.name, sizeof(h.name), "%s", name);

Why this works:
- snprintf() with "%s" format specifier safely copies the user-supplied filename with bounds checking.
- This is particularly important since name comes from user input.

Full Context of Changes

The PR changed two functions in microtar/microtar.c:

  1. raw_to_header() (lines 114-116): Converts raw tar header to structured format
  2. header_to_raw() (lines 128-139): Converts structured header back to raw format
  3. mtar_write_file_header() (line 343): Writes a new file header with user-supplied filename

All three now use bounded operations.


Verification & Testing

The fix was verified with a regression test that guards against future regressions:

TEST_P(SecurityTest, TarHeaderCopyDoesNotOverflow) {
    // Create raw header with adversarial data (no null terminator)
    mtar_raw_header_t raw_header;
    memset(&raw_header, 0, sizeof(raw_header));

    // Fill buffer completely without null terminator
    memcpy(raw_header.name, std::string(100, 'A').c_str(), 100);

    // Convert to header - this should NOT overflow
    mtar_header_t header;
    raw_to_header(&raw_header, &header);

    // Verify destination buffers are safely null-terminated
    EXPECT_EQ('\0', header.name[sizeof(header.name)-1]);
}

This test confirms that even when source data lacks null terminators, the destination buffers remain safely bounded.


Prevention & Best Practices

1. Never Use strcpy() or sprintf() in Production

These functions are fundamentally unsafe because they have no way to know the destination buffer size. Modern C coding standards recommend:
- Replace strcpy() with memcpy() + explicit null-termination or strdup()
- Replace sprintf() with snprintf()
- Replace strcat() with strncat() or snprintf()

2. Always Validate Input Length

When parsing binary formats (like tar headers), validate that fields are properly null-terminated before processing:

// Good: Explicit bounds check
if (strnlen(rh->name, sizeof(rh->name)) == sizeof(rh->name)) {
    // Field is not null-terminated—handle as error
    return MTAR_EFAIL;
}
strcpy(h->name, rh->name);  // Now safe

3. Use Static Analysis Tools

Enable compiler warnings and use static analysis:
- GCC/Clang: -Wall -Wextra -Wformat -Wformat-security
- Clang Static Analyzer: scan-build
- Semgrep: semgrep --config=p/security-audit to flag unsafe functions
- Coverity: Commercial tool with excellent C/C++ coverage

4. Follow CWE Guidelines

This vulnerability is classified under:
- CWE-120: Buffer Copy without Checking Size of Input
- CWE-119: Improper Restriction of Operations within the Bounds of a Memory Buffer

Refer to CWE-120 for detailed guidance on prevention.

5. Use Secure C Library Functions

If available, use safer alternatives:
- POSIX: strlcpy(), strlcat() (if available on your platform)
- C11: memcpy_s() (MSVC) or equivalent
- Custom: Implement bounded string operations with clear semantics


Key Takeaways

  • Never trust tar file content: Header fields like name and linkname may not be null-terminated. Always use bounded operations.
  • strcpy() is a security liability: The function cannot prevent buffer overflow. Replace it with memcpy(dst, src, sizeof(dst)) followed by explicit null-termination.
  • sprintf() is similarly dangerous: Use snprintf(dst, sizeof(dst), fmt, ...) to prevent format string and buffer overflows.
  • Explicit null-termination is essential: When using memcpy(), always set the last byte to '\0' to ensure C string semantics.
  • Test with adversarial input: The regression test in this PR specifically exercises non-null-terminated buffers to catch regressions.

How Orbis AppSec Detected This

Source: Tar archive files processed by raw_to_header() and header_to_raw() functions, specifically the name and linkname fields from untrusted tar headers.

Sink: The vulnerable strcpy() calls at lines 114-115 in raw_to_header() and lines 131-132 in header_to_raw(), and sprintf() calls at lines 128-130.

Missing control: No bounds checking on the size of source data before copying into fixed-size destination buffers. No validation that tar header fields are null-terminated.

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

Fix: Replace all strcpy() calls with memcpy(dst, src, sizeof(dst)) followed by explicit null-termination (dst[sizeof(dst)-1] = '\0'). Replace all sprintf() calls with snprintf() specifying the destination buffer size.

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 overflows in C string handling are a classic vulnerability, yet they continue to appear in production code. The fix is straightforward: always use bounded operations. By replacing strcpy() with memcpy() + explicit null-termination and sprintf() with snprintf(), the microtar library eliminated a critical attack surface.

The lesson applies broadly: whether you're parsing tar archives, processing HTTP headers, or handling any untrusted input, treat every string operation as a potential security boundary. Use the tools available to you—static analysis, compiler warnings, and security testing—to catch these issues before they reach production.

Secure coding practices like these are not optional extras; they're fundamental to building reliable, trustworthy software.


References

Frequently Asked Questions

What is a buffer overflow in C string handling?

A buffer overflow occurs when a function like strcpy() copies data into a fixed-size buffer without verifying the source fits within the destination bounds. If the source is longer than the buffer, it overwrites adjacent memory.

How do you prevent buffer overflow in C tar parsing?

Always use bounded string operations: memcpy(dst, src, sizeof(dst)) with explicit null-termination, or snprintf() instead of sprintf(). Validate input length before copying.

What CWE is buffer overflow in strcpy()?

CWE-120 (Buffer Copy without Checking Size of Input) and CWE-119 (Improper Restriction of Operations within the Bounds of a Memory Buffer).

Is using strncpy() enough to prevent buffer overflow?

strncpy() is safer than strcpy() but still problematic—it doesn't guarantee null-termination. The fix uses memcpy() with explicit null-termination, which is more reliable.

Can static analysis detect this buffer overflow?

Yes. Tools like Clang Static Analyzer, Coverity, and Semgrep can flag strcpy() usage and recommend bounded alternatives like memcpy() or snprintf().

View the Security Fix

Check out the pull request that fixed this vulnerability

View PR #1118

Related Articles

critical

How buffer overflow in Intel SGX enclave ECALLs happens in C and how to fix it

A critical buffer overflow vulnerability was discovered in Intel SGX enclave functions `ecall_encrypt_data` and `ecall_decrypt_data` in `backend/sgx/enclave/enclave.c`. The functions performed memory operations without validating that the provided buffer lengths matched the actual allocated buffer sizes, allowing an attacker controlling the untrusted application to trigger heap corruption within the secure enclave by passing oversized length parameters.

critical

How buffer overflow happens in C SGX enclave memcpy and how to fix it

A critical buffer overflow vulnerability was discovered in `backend/sgx/enclave/enclave.c` where the `ecall_store_data` function performed `memcpy` operations without proper bounds checking against the actual destination buffer size. An attacker could supply a malicious `data_len` parameter to overflow the enclave's secure storage buffer, potentially corrupting trusted execution environment memory. The fix replaces a hardcoded magic number check with a precise size comparison against the actual

medium

How integer underflow in array splice operations happens in C and how to fix it

A critical integer underflow vulnerability was discovered in tree-sitter's array.h header file, where the `_array__splice()` function calculated array sizes without proper bounds checking. The vulnerable code relied on assert() statements that are disabled in release builds, allowing arithmetic underflow when `old_count > *size + new_count`, potentially causing memory corruption through out-of-bounds memcpy operations.

high

How buffer overflow via sprintf() happens in C string formatting and how to fix it

A high-severity buffer overflow vulnerability was discovered in `bench/strbuild/strbuild.c` where `sprintf()` wrote formatted output into a 64-byte stack buffer (`line[64]`) without any bounds checking. An attacker who could influence the values in the `NAMES[]`, `c[]`, or `v[]` arrays could overflow this buffer, potentially corrupting the stack and hijacking control flow. The fix replaces `sprintf()` with `snprintf(line, sizeof(line), ...)` to enforce a strict 64-byte write limit.

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

critical

How Sensitive Data Exposure in Error Logging happens in TypeScript/Deno and how to fix it

A critical vulnerability in Supabase Edge Functions allowed sensitive authentication errors and API credentials to leak through verbose error logging. The `cancel-subscription/index.ts` function logged full error objects to the console, potentially exposing Paddle API keys and auth tokens in deployment logs. The fix sanitizes all error messages to log only safe error text while preserving debugging capability.