Back to Blog
critical SEVERITY8 min read

How buffer overflow happens in C RTSPSession.h and how to fix it

A critical buffer overflow vulnerability in `src/AudioTools/Communication/RTSP/RTSPSession.h` allowed an attacker to send a crafted RTSP request with an oversized payload, triggering a heap overflow via an unchecked `memcpy()` call at line 408. The fix adds a single bounds check before the copy and replaces several unsafe `strcpy`/`strncpy` calls with `snprintf`, closing multiple paths to memory corruption and potential remote code execution.

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

Answer Summary

This is a classic CWE-120 buffer overflow in C, found in `RTSPSession.h` inside an RTSP server handler. The vulnerable code called `memcpy(mCurRequest.data(), aRequest, aRequestSize)` without first verifying that `aRequestSize` did not exceed `mCurRequest.size()`, meaning a crafted RTSP request could overflow the heap buffer and enable remote code execution. The fix inserts a single guard — `if (aRequestSize > mCurRequest.size()) return false;` — directly before the `memcpy`, and replaces multiple unsafe `strcpy`/`strncpy` calls with bounded `snprintf` equivalents to eliminate the entire class of unsafe string operations in this file.

Vulnerability at a Glance

cweCWE-120
fixAdd `if (aRequestSize > mCurRequest.size()) return false;` before memcpy; replace strcpy/strncpy with snprintf
riskRemote code execution via crafted RTSP request
languageC/C++
root causememcpy() called with attacker-controlled size before validating against buffer capacity
vulnerabilityBuffer Overflow (unchecked memcpy in RTSP request handler)

How Buffer Overflow Happens in C RTSPSession.h and How to Fix It

Introduction

The RTSPSession.h file is the heart of an RTSP server implementation — it handles incoming client requests, parses headers, and manages transport negotiation. But a subtle flaw in how it copies incoming request data into an internal buffer created a critical security hole: an attacker who could send a single crafted network packet could potentially take over the entire process.

The culprit was a memcpy() call at line 408 with no bounds check — a pattern so common in C codebases that it often slips past code review unnoticed. This post walks through exactly what was wrong, how it could be exploited, and how the fix closes the door.


The Vulnerability Explained

What the code was doing

Inside RtspSession, incoming RTSP requests are stored in mCurRequest, a buffer with a fixed allocated capacity. When a new request arrives, the handler copies it in with:

// VULNERABLE — before the fix (RTSPSession.h, ~line 408)
const unsigned CurRequestSize = aRequestSize;
memcpy(mCurRequest.data(), aRequest, aRequestSize);

The problem is stark: aRequestSize is derived from the network — it reflects whatever size the client claims the request is. There is no check that aRequestSize <= mCurRequest.size() before the copy executes.

Why this is dangerous

memcpy() is a blunt instrument. It copies exactly as many bytes as you tell it to, regardless of whether the destination has room. When aRequestSize exceeds the capacity of mCurRequest, the function writes past the end of the buffer into adjacent heap memory. Depending on what lives there, this can:

  • Corrupt heap metadata, causing a crash (denial of service)
  • Overwrite adjacent objects, changing program logic
  • In a carefully staged attack, overwrite function pointers or return addresses to redirect execution — remote code execution

The attack scenario

An attacker with network access to the RTSP server constructs a malformed RTSP request. The Content-Length or raw packet size is set to a value larger than mCurRequest's allocated capacity — say, 8 MB when the buffer holds 4 KB. The server receives the packet, reads aRequestSize directly from the stream, and calls:

memcpy(mCurRequest.data(), aRequest, 8388608); // buffer is only 4096 bytes

The overflow begins immediately, writing attacker-controlled bytes into the heap beyond mCurRequest. On a modern system with heap hardening this may crash the process; on a less-hardened target, or with careful heap grooming, it can yield a shell.

Additional unsafe string operations

The same file contained three more unsafe string operations that compounded the risk:

// strcpy with no length bound — destination size unknown to caller
strcpy(CP, ClientPortPtr);
strcpy(CP, eq);

// strncpy — truncates but does NOT guarantee null termination
strncpy(CP, TransportPtr, m_Response.size() - 1);
CP[m_Response.size() - 1] = '\0';

// Hard-coded limit of 256 — ignores actual buffer size
strncpy(m_Buf1.data(), m_URLHostPort.data(), 256);

Each of these is a potential overflow or truncation bug depending on input length and buffer layout.


The Fix

Primary fix: guard the memcpy

The core change is a single line inserted immediately before the memcpy:

// FIXED — RTSPSession.h, line 407 (after fix)
if (aRequestSize > mCurRequest.size()) return false;
const unsigned CurRequestSize = aRequestSize;
memcpy(mCurRequest.data(), aRequest, aRequestSize);

Before:

const unsigned CurRequestSize = aRequestSize;
memcpy(mCurRequest.data(), aRequest, aRequestSize);

After:

if (aRequestSize > mCurRequest.size()) return false;
const unsigned CurRequestSize = aRequestSize;
memcpy(mCurRequest.data(), aRequest, aRequestSize);

This is the minimal, correct fix. If the incoming size exceeds the buffer's actual capacity, the function returns false immediately — the oversized data is never touched. The check uses mCurRequest.size() (the actual runtime capacity of the container) rather than a hardcoded constant, so it remains correct even if the buffer size changes in the future.

Secondary fixes: replace strcpy/strncpy with snprintf

The three unsafe string operations were each replaced with snprintf, which always respects the destination size and guarantees null termination:

Client port parsing — before:

strcpy(CP, ClientPortPtr);
// ... later ...
strcpy(CP, eq);

After:

snprintf(CP, m_Response.size(), "%s", ClientPortPtr);
// ... later ...
snprintf(CP, m_Response.size(), "%s", eq);

Transport parsing — before:

strncpy(CP, TransportPtr, m_Response.size() - 1);
CP[m_Response.size() - 1] = '\0';

After:

snprintf(CP, m_Response.size(), "%s", TransportPtr);

Host/port parsing — before:

strncpy(m_Buf1.data(), m_URLHostPort.data(), 256);

After:

snprintf(m_Buf1.data(), m_Buf1.size(), "%s", m_URLHostPort.data());

Note the last change is doubly important: the original code used a hardcoded 256 as the limit, which would be wrong if m_Buf1 were ever resized. The fix uses m_Buf1.size() — the actual runtime size of the buffer — making it self-consistent.

Why these changes work together

The memcpy guard stops the most severe attack vector: an oversized request payload overflowing the main request buffer. The snprintf replacements close the secondary paths: even if an attacker gets past the first check (or exploits a different entry point), the string operations downstream can no longer be made to overflow their destinations.


Prevention & Best Practices

1. Always validate size before memcpy

Any time memcpy is called with a size that comes from network input, user input, or any external source, validate it first:

// Pattern to follow everywhere
if (inputSize > buffer.size()) {
    return ERROR_BUFFER_TOO_SMALL;
}
memcpy(buffer.data(), input, inputSize);

2. Prefer snprintf over strcpy and strncpy

strcpy has no length limit. strncpy does not guarantee null termination. snprintf does both correctly and is the right default for string copies where the destination size is known:

// Instead of:
strcpy(dest, src);
strncpy(dest, src, n);

// Use:
snprintf(dest, sizeof(dest), "%s", src);

3. Use AddressSanitizer during development and testing

Compile with -fsanitize=address during development. AddressSanitizer (ASan) catches heap and stack overflows at runtime with minimal overhead, turning silent memory corruption into immediate, actionable crashes.

clang++ -fsanitize=address -g -o rtsp_server RTSPSession.cpp

4. Apply static analysis to catch these patterns before review

Tools like Semgrep, Coverity, and CodeQL have rules that flag unchecked memcpy calls and unsafe strcpy/strncpy usage. Run them in CI so these patterns are caught before they reach code review.

5. Enforce a maximum message size at the protocol layer

For RTSP servers, define and enforce a maximum request size at the point where data is read from the socket — before it ever reaches the copy logic. This provides defense in depth even if a bounds check is accidentally removed later.

Relevant standards

  • CWE-120: Buffer Copy without Checking Size of Input ("Classic Buffer Overflow")
  • CWE-122: Heap-based Buffer Overflow
  • OWASP: Buffer Overflow
  • SEI CERT C: STR31-C (guarantee string destination has sufficient space), ARR38-C (guarantee library functions do not form invalid pointers)

Key Takeaways

  • memcpy with an attacker-controlled size and no bounds check is a remote code execution primitive — the single missing guard in RTSPSession.h was all it took.
  • The fix is one line, but the right one line: if (aRequestSize > mCurRequest.size()) return false; placed before the copy, using the runtime size of the actual buffer.
  • strncpy is not a safe replacement for strcpy — it can leave buffers without null termination. snprintf(dest, dest_size, "%s", src) is the correct pattern.
  • Hardcoded size limits like 256 in strncpy(m_Buf1.data(), ..., 256) are a maintenance hazard — always use the actual buffer's runtime size so the check stays correct if the buffer is resized.
  • Network-facing C/C++ code deserves extra scrutiny on every copy operation — every memcpy, strcpy, strncpy, and sprintf that touches externally-supplied data is a potential vulnerability.

How Orbis AppSec Detected This

  • Source: Incoming RTSP network request — the aRequest pointer and aRequestSize value are both attacker-controlled, arriving directly from the network socket.
  • Sink: memcpy(mCurRequest.data(), aRequest, aRequestSize) at RTSPSession.h:408, where the attacker-controlled size is used without validation.
  • Missing control: No check that aRequestSize <= mCurRequest.size() before the copy. The buffer's capacity was never consulted.
  • CWE: CWE-120 — Buffer Copy without Checking Size of Input ("Classic Buffer Overflow")
  • Fix: Inserted if (aRequestSize > mCurRequest.size()) return false; immediately before the memcpy, and replaced four unsafe strcpy/strncpy calls with bounded snprintf equivalents throughout the same file.

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 are old, well-understood, and still showing up in production network code in 2024. The vulnerability in RTSPSession.h is a textbook example: a single memcpy call that trusts the caller to supply a safe size. The fix is equally textbook — validate first, copy second. What makes this case instructive is the compound nature of the problem: the primary memcpy overflow was accompanied by three additional unsafe string operations in the same file, each one a separate path to memory corruption. Fixing the whole class of issues together, using snprintf with runtime buffer sizes, is the right approach.

If you maintain C or C++ code that handles network input, audit every memcpy, strcpy, strncpy, and sprintf call that touches externally-supplied data. The check is cheap; the exploit is not.


References

Frequently Asked Questions

What is a buffer overflow in C?

A buffer overflow occurs when a program writes more data into a buffer than it can hold, overwriting adjacent memory. In C, functions like memcpy(), strcpy(), and strncpy() do not automatically enforce bounds, so if the size argument is attacker-controlled and unchecked, the program can be made to corrupt heap or stack memory — potentially enabling code execution.

How do you prevent buffer overflow in C?

Always validate that the size of data to be copied does not exceed the destination buffer's capacity before calling memcpy() or any string copy function. Prefer snprintf() over strcpy()/strncpy() for string operations, and use static analysis tools to flag unchecked copy calls.

What CWE is buffer overflow?

Buffer overflow is classified under CWE-120 ("Buffer Copy without Checking Size of Input"). Related entries include CWE-122 (Heap-based Buffer Overflow) and CWE-787 (Out-of-bounds Write).

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

Not always. strncpy() can still leave buffers without null termination if the source is longer than the specified limit, and it does not prevent all forms of overflow if the size argument itself is wrong. snprintf() with the correct buffer size is a safer and more predictable alternative.

Can static analysis detect buffer overflow?

Yes. Tools like Semgrep, Coverity, CodeQL, and AddressSanitizer (at runtime) can detect unchecked memcpy/strcpy patterns. Orbis AppSec's multi-agent AI scanner flagged this exact pattern in RTSPSession.h before it reached production.

View the Security Fix

Check out the pull request that fixed this vulnerability

View PR #2352

Related Articles

high

How buffer overflow via insecure strcpy/strncpy happens in C textbox widgets and how to fix it

A high-severity buffer overflow vulnerability was discovered in the Aroma UI framework's textbox widget where `strncpy()` was used to copy user-provided text without guaranteed null-termination safety. The fix replaces the dangerous `strncpy()` pattern with `snprintf()`, which automatically handles buffer boundaries and null-termination in a single, safer operation.

critical

How buffer overflow via sprintf happens in C++ fuzzer code and how to fix it

A critical buffer overflow vulnerability was discovered in `prog/fuzzing/recog_basic_fuzzer.cc` where `sprintf` writes to a fixed 256-byte buffer without bounds checking. An attacker providing crafted fuzzer input could exploit this to corrupt memory. The fix replaces `sprintf` with `snprintf`, enforcing the buffer size limit and preventing overflow.

critical

How buffer overflow in memcpy happens in C bios_disk.h and how to fix it

A critical buffer overflow vulnerability was discovered in `include/bios_disk.h` at line 474, where a `memcpy` operation copies 512 bytes from a source buffer without properly validating that the calculated offset from the `sectnum` parameter stays within bounds. An attacker controlling the `sectnum` parameter could trigger an out-of-bounds read, potentially leaking sensitive memory contents or causing a crash. The fix adds a proper bounds check before the memcpy call to ensure the source offset

medium

How buffer overflow happens in C kernel driver (qcom_usbnet_main.c) and how to fix it

A series of unsafe `sprintf()` calls in the Qualcomm USB network kernel driver (`qcom_usbnet_main.c`) created buffer overflow conditions that, when combined with other memory corruption primitives in the same file, could allow an attacker with physical USB access to escalate privileges to root. The fix replaces unbounded `sprintf()` and `snprintf()` misuse with properly bounded `snprintf()` and `scnprintf()` calls that respect actual buffer sizes. This is a textbook example of how a seemingly mi

critical

How buffer overflow happens in C sprintf/strcpy and how to fix it

A critical buffer overflow vulnerability was discovered in `mupen64plus-rsp-cxd4/module.c`, where unsafe `sprintf()` and `strcpy()` calls at lines 294–298 could be exploited by a crafted N64 ROM file to corrupt memory and achieve code execution. The fix replaces these unbounded string functions with `snprintf()`, which enforces strict buffer size limits and eliminates the overflow risk. This is a textbook example of how legacy C string functions can silently introduce critical security flaws in

critical

How command injection happens in Python subprocess and how to fix it

A critical command injection vulnerability was discovered in `script/llm_semantic_analyzer.py` at line 394, where user-controlled input (API keys and model parameters) was interpolated directly into shell commands passed to `subprocess.run` with `shell=True`. An attacker who could control these parameters could inject shell metacharacters like `; rm -rf /` or `$(whoami)` to execute arbitrary commands. The fix sanitizes all user input before it reaches shell execution.