Safeguard
Application Security•

How do you deserialize untrusted data safely in C and C++?

In C and C++ the main risk is memory corruption from bad lengths, not gadget chains. Validate sizes first, use bounded parsers, cap resources and authenticate input.

Safeguard Security Team
5 min read

In C and C++, unsafe deserialization usually means memory corruption: a length field, count or offset in the input is trusted and a buffer is overrun or an allocation goes wrong. Parse with a bounded reader, check every size against the remaining input before use, cap total resources, and authenticate data before you parse it.

How is this different from Java or PHP?

The OWASP Deserialization Cheat Sheet covers PHP, Python, Java and .NET, where the typical problem is the library instantiating attacker-chosen types. It has no C or C++ section, and MITRE's CWE-502 entry does not mention C or C++ either. The weakness is the same in spirit, which is "the product deserializes untrusted data without sufficiently ensuring that the resulting data will be valid", but the damage looks different.

Native code has no type registry to abuse. Instead the input drives sizes and offsets. The related weaknesses on MITRE's list are CWE-787, out-of-bounds write, and CWE-190, integer overflow or wraparound, which MITRE describes as arising when a calculation assumes the result will be larger than the original value.

What does the classic bug look like?

A constructed example: a binary message has a 32 bit length followed by that many bytes. The reader allocates length plus a header size, then copies length bytes. If the attacker sends a length near the top of the 32 bit range, the addition wraps to a small number. The allocation is tiny, and the copy writes far past it. Nothing in the format was invalid on its face.

Variants include a count of elements multiplied by an element size that overflows, a length that points beyond the input buffer, an offset that points backward into earlier data, and a nesting depth that exhausts the stack.

How do you write a safer reader?

Follow a few rules without exception.

  • Track remaining bytes. Every read checks that the requested size does not exceed what is left, before reading.
  • Do arithmetic on sizes in a wide enough type, and check for overflow before the multiplication or addition, not after.
  • Reject lengths larger than a documented maximum for that field. A protocol rarely needs a four gigabyte string.
  • Limit nesting depth and total element count, so a small input cannot demand huge work.
  • Never cast raw bytes to a struct pointer and trust its fields. Copy field by field, with explicit endianness.
  • Use containers and spans that carry their size, rather than bare pointers and separate lengths.

The protobuf documentation illustrates the framing problem: the wire format is not self-delimiting, so you write each message's size first, and read that many bytes into a separate buffer before parsing from it. Its CodedInputStream class can be told to limit reads to a certain number of bytes. Frame first, bound the read, then parse.

Should you use a schema-based format?

Generally yes. A schema-based format with a maintained parser moves the boundary-checking work to code that many people test and fuzz. It is not a guarantee, because parsers have had bugs, so keep the library updated and set the size and recursion limits it offers. If you write a custom binary format, you take on all of the above yourself.

What about integrity?

CWE-502's mitigations include confirming data is untampered with an HMAC or a signature, and OWASP says to refuse any message without an authenticated signature. For network protocols between your own services, authenticate the channel so only known peers reach the parser at all. The parser then handles less hostile input, though you should still not assume peers are bug-free.

How do you test it?

Fuzz the reader with a coverage-guided fuzzer, run under address and undefined behaviour sanitizers. Seed it with valid messages and let it mutate lengths. Add regression tests for every crash, and run the fuzz target in CI for a bounded time on each change.

What to do

  1. List every place native code reads external bytes: network, files, IPC, plugins.
  2. Add explicit maximum sizes, depth and counts to each format.
  3. Replace pointer casts of raw input with bounds-checked field reads.
  4. Check size arithmetic for overflow before use.
  5. Authenticate data before parsing where the design allows.
  6. Fuzz each parser under sanitizers, and keep third-party parsers patched. Safeguard's free scan at gold.safeguard.sh/scan can check dependencies, up to 100 in the browser.

Quick check

Is CWE-502 the right label for a C buffer overflow in a parser?

Often the root cause maps better to out-of-bounds write (CWE-787) or integer overflow (CWE-190). CWE-502 describes failing to ensure deserialized data is valid, which can overlap.

What is the first check a safe reader makes?

That the requested size does not exceed the bytes remaining in the input, done before reading or allocating.

Sources

Last reviewed: 18 October 2026.

Never miss an update

Weekly insights on software supply chain security, delivered to your inbox.

Self-healing security runs on Safeguard.

Your first fix PR is minutes away.

No sales call required, even your agent can complete the purchase over MCP.