Back to Blog
critical SEVERITY7 min read

How unsafe buffer copying happens in C credential storage and how to fix it

A critical vulnerability in `lib/server.c` allowed attackers to trigger out-of-bounds memory reads when copying credentials via unsafe `memcpy()` calls. By replacing `memcpy()` with bounds-safe `strlcpy()`, the fix ensures credentials are safely stored without buffer overruns or null-termination issues.

O
By Orbis AppSec
Published June 7, 2026Reviewed June 7, 2026

Answer Summary

This is a buffer overflow vulnerability (CWE-119) in C credential storage caused by unsafe `memcpy()` calls that don't validate buffer boundaries or ensure null-termination. The fix replaces `memcpy(server->username, username, sizeof(server->username))` with `strlcpy(server->username, username, sizeof(server->username))`, which automatically truncates to fit and guarantees null-termination, preventing out-of-bounds reads and memory corruption.

Vulnerability at a Glance

cweCWE-119 (Improper Restriction of Operations within the Bounds of a Memory Buffer)
fixReplace memcpy() with strlcpy() to enforce bounds checking and automatic null-termination
riskAttackers can trigger out-of-bounds memory reads, potentially leaking sensitive data or causing denial of service
languageC
root causememcpy() with sizeof(destination) copies without validating source length or ensuring null-termination
vulnerabilityUnsafe Buffer Copying / Out-of-Bounds Read in Credential Storage

How unsafe buffer copying happens in C credential storage and how to fix it

Introduction

In the production codebase of an AFP (Apple Filing Protocol) server implementation, a critical vulnerability was discovered in lib/server.c at lines 39–40. The afp_server_complete_connection() function was using memcpy() to copy username and password credentials into fixed-size buffers without proper bounds checking or null-termination guarantees. This is a classic buffer overflow pattern that could allow attackers to trigger out-of-bounds memory reads by supplying crafted credentials through a malicious AFP server.

The specific problematic code was:

memcpy(server->username, username, sizeof(server->username));
memcpy(server->password, password, sizeof(server->password));

This pattern is dangerous because memcpy() blindly copies the number of bytes specified as the third argument, regardless of whether the source data actually contains that many bytes. If the source string is shorter than the destination buffer size, memcpy() will read past the end of the source string, leaking adjacent memory. Additionally, memcpy() doesn't guarantee null-termination, so if the source exactly fills the buffer, the resulting string won't be null-terminated—causing undefined behavior in any function that expects a C string.


The Vulnerability Explained

What Went Wrong

The vulnerable code in lib/server.c:39-40 makes two critical assumptions that are not guaranteed:

  1. Assumption 1: The source buffer (username and password parameters) is at least as long as the destination buffer size.
  2. Assumption 2: The destination buffer will be properly null-terminated after the copy.

Neither assumption is safe. Here's why:

The Vulnerable Code:

memcpy(server->username, username, sizeof(server->username));
memcpy(server->password, password, sizeof(server->password));

If sizeof(server->username) is 256 bytes (a typical size for credential buffers), but the username parameter points to a 10-byte string in memory, memcpy() will read 256 bytes starting from the username pointer. This causes an out-of-bounds read, pulling data from memory that follows the username string—potentially sensitive data like API keys, tokens, or other credentials stored nearby.

Attack Scenario

Consider this exploitation scenario:

  1. An attacker controls a malicious AFP server or provides crafted input to the client.
  2. The attacker supplies a short username string (e.g., "admin" = 5 bytes) but the code calls memcpy(..., 256).
  3. memcpy() reads 256 bytes from the username pointer, including memory beyond the string boundary.
  4. Adjacent memory contents (heap metadata, other credentials, or sensitive data) are copied into server->username.
  5. If the application logs or returns this buffer, the attacker has leaked sensitive information.
  6. Alternatively, if the buffer is used in string operations without proper length checks, it could trigger a buffer over-read in downstream code.

Real-World Impact

In this AFP server context, credentials are core to authentication. If an attacker can:
- Leak memory via out-of-bounds reads, they might discover other users' credentials or session tokens.
- Trigger a crash by reading from unmapped memory, they achieve denial of service.
- Corrupt the heap by triggering memory protection violations, they could escalate to arbitrary code execution.

The PR description notes this was confirmed exploitable by the security scanner, meaning the vulnerability was not theoretical—it could be reliably triggered.


The Fix

What Changed

The fix replaces the dangerous memcpy() calls with strlcpy(), a bounds-safe string copy function:

Before:

memcpy(server->username, username, sizeof(server->username));
memcpy(server->password, password, sizeof(server->password));

After:

strlcpy(server->username, username, sizeof(server->username));
strlcpy(server->password, password, sizeof(server->password));

Why This Fix Works

strlcpy() is specifically designed for safe string copying. It:

  1. Enforces bounds checking: It will copy at most sizeof(server->username) - 1 bytes from the source.
  2. Guarantees null-termination: It always appends a null byte (\0) to the destination, ensuring the result is a valid C string.
  3. Prevents out-of-bounds reads: It stops reading from the source at the first null terminator or after copying the maximum allowed bytes—whichever comes first.
  4. Returns the intended length: It returns the length of the source string, allowing the caller to detect truncation if needed.

Security Properties Restored:

  • No out-of-bounds reads: strlcpy() will never read past the source string's null terminator.
  • Always null-terminated: The destination buffer is guaranteed to end with \0, even if truncated.
  • Predictable behavior: The function behaves identically regardless of input length.

Example

If username = "admin" (5 bytes + null terminator) and sizeof(server->username) = 256:

  • Old code (memcpy): Copies 256 bytes, reading far past the "admin" string and leaking adjacent memory.
  • New code (strlcpy): Copies the 5-byte string "admin" and appends a null terminator, leaving the rest of the 256-byte buffer unchanged.

Prevention & Best Practices

1. Never use memcpy() for string handling

memcpy() is for raw binary data. For strings, use:
- strlcpy() — Preferred on BSD, macOS, and modern systems. Guaranteed null-termination.
- strncpy() — Available everywhere but requires manual null-termination.
- snprintf() — Flexible, well-defined, and safe.

2. Validate input length before copying

Even with strlcpy(), validate that input meets your expectations:

if (strlen(username) > 255) {
    log_error("Username too long");
    return -1;
}
strlcpy(server->username, username, sizeof(server->username));

3. Use static analysis tools

Tools like Clang Static Analyzer, Coverity, and Semgrep can flag dangerous patterns:
- memcpy() with sizeof() on buffers
- Missing null-termination checks
- Unsafe string functions

4. Apply defense in depth

  • Use AddressSanitizer (ASan) during testing to catch out-of-bounds accesses at runtime.
  • Use Valgrind to detect memory errors in your credential handling code.
  • Implement fuzzing with AFL or libFuzzer to test credential parsing with malformed input.

5. Encrypt credentials at rest

The PR description notes that PBKDF2 is available in dependencies but not used. Beyond fixing the buffer overflow, credentials should be encrypted before storage:

// Pseudocode: encrypt credentials before storing
unsigned char *encrypted = pbkdf2_encrypt(password, salt, iterations);
strlcpy(server->password_encrypted, (char*)encrypted, sizeof(server->password_encrypted));

6. Follow OWASP Secure Coding Guidelines


Key Takeaways

  • Never use memcpy() to copy strings: It doesn't perform bounds checking or null-termination. Use strlcpy() instead.
  • The sizeof(destination) pattern is dangerous: When used as the length argument to memcpy() for strings, it assumes the source is at least that long—a false assumption.
  • Out-of-bounds reads are exploitable: Even if they don't crash immediately, they can leak sensitive data from adjacent memory, especially critical in credential-handling code.
  • The AFP server's credential copying was confirmed exploitable: This wasn't a theoretical issue—attackers could trigger it with malicious input.
  • Bounds-safe functions like strlcpy() are non-negotiable for credential storage: They eliminate entire classes of buffer overflow vulnerabilities.

How Orbis AppSec Detected This

Source: Credentials supplied via AFP protocol input (username and password parameters to afp_server_complete_connection())

Sink: Unsafe memcpy() calls at lib/server.c:39-40 copying into fixed-size buffers (server->username and server->password)

Missing Control: No bounds validation on source data length; no guarantee of null-termination after copy; no use of bounds-safe string functions

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

Fix: Replaced memcpy(server->username, username, sizeof(server->username)) with strlcpy(server->username, username, sizeof(server->username)) to enforce bounds checking and automatic null-termination. The same change was applied to the password field.

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 credential handling code are among the most critical security issues in C applications. The fix in this PR—replacing memcpy() with strlcpy()—is a straightforward but essential change that eliminates out-of-bounds reads and ensures proper null-termination.

For developers maintaining C code that handles sensitive data like credentials, the key lesson is simple: use bounds-safe functions by default. Don't assume input lengths; don't rely on implicit null-termination. Tools like strlcpy(), combined with static analysis and runtime testing, make it possible to write secure credential handling code.

If you're working on similar authentication or credential storage code, audit it now for the same pattern. This vulnerability is both common and easily fixed—but the impact of leaving it unfixed is severe.


References

Frequently Asked Questions

What is unsafe buffer copying in C?

It's when you copy data into a fixed-size buffer without verifying the source data fits, leading to out-of-bounds reads or writes. Functions like memcpy() don't perform bounds checking or null-termination.

How do you prevent buffer overflow in C credential handling?

Use bounds-safe functions like strlcpy() or strncpy() with proper size arguments, validate input lengths before copying, and always ensure null-termination of strings.

What CWE covers this vulnerability?

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

Is input validation enough to prevent this?

No—input validation helps, but the root issue is the unsafe copy function itself. Even with validation, memcpy() doesn't guarantee null-termination, which can cause issues downstream.

Can static analysis detect this?

Yes. Tools like Clang Static Analyzer, Coverity, and Semgrep can flag memcpy() with sizeof(destination) as a high-risk pattern, especially in credential-handling code.

View the Security Fix

Check out the pull request that fixed this vulnerability

View PR #233

Related Articles

medium

How buffer overflow happens in C kernel PTY subsystem (tty_ptmx.c) and how to fix it

A stack buffer overflow vulnerability was discovered in `tty_ptmx.c`, the kernel-level pseudo-terminal multiplexer component, where an unchecked `sprintf()` call at line 293 could overflow the `device_name` buffer by combining `root_path` and `dev_rel_path` without bounds validation. Because this code executes in kernel context during PTY device creation, successful exploitation could lead to kernel memory corruption, privilege escalation, or system crashes. The fix replaces the unbounded `sprin

medium

How buffer overflow happens in C ImageMagick drawing-wand and how to fix it

ImageMagick's drawing-wand component contained a critical buffer overflow vulnerability in the MVGPrintf() function where vsprintf() was used without bounds checking. By switching to snprintf() with proper size constraints, the fix prevents attackers from overflowing the MVG buffer through crafted SVG files and achieving arbitrary code execution.

critical

How buffer overflow in URL parsing happens in C++ HTTP client and how to fix it

A critical buffer overflow vulnerability in the HTTP client's URL parsing function allowed attackers to overflow a stack-allocated host buffer through specially crafted URLs with excessively long hostnames. The vulnerability enabled arbitrary code execution by overwriting the return address. The fix adds proper bounds validation before the memcpy() operation to ensure the hostname length never exceeds the destination buffer size.

critical

How heap buffer overflow happens in C WiFi frame capture and how to fix it

A critical buffer overflow vulnerability in the ESP32 WiFi frame capture feature (feat_capture_hs.c) allowed attackers within WiFi range to craft oversized 802.11 frames that would overflow heap buffers and achieve remote code execution. The fix adds explicit length validation before memcpy operations and rejects oversized frames rather than silently truncating them.

critical

How integer overflow in _wopendir() happens in C Windows dirent and how to fix it

A critical integer overflow vulnerability in `include/compat/dirent_msvc.h` allowed an attacker-controlled directory path length to wrap the `sizeof(wchar_t) * n + 16` allocation calculation, resulting in a dangerously undersized heap buffer. Subsequent writes to that buffer caused a heap overflow, enabling potential memory corruption or code execution on Windows systems. The fix adds a pre-allocation bounds check and proper errno signaling to safely reject overflow-inducing inputs.

critical

How buffer overflow in SCSI command handling happens in C and how to fix it

A critical buffer overflow vulnerability was discovered in libretro-common's CDROM handling code where the `cdrom_send_command_win32()` function copied an arbitrary number of bytes into a fixed 16-byte SCSI Command Descriptor Block (CDB) buffer without validation. This vulnerability could allow an attacker using a malicious CDROM image or USB device to corrupt memory and potentially execute arbitrary code. The fix adds a simple bounds check before the memcpy operation to ensure cmd_len never exc