ASVS 5.0 V1.4.1 (Level 2) requires that the application uses memory safe string handling, safer memory copy functions and careful pointer arithmetic to detect or prevent stack, buffer and heap overflows. It matters for anything written in C or C++, and for native extensions and unsafe blocks inside managed languages.
Why it matters
A buffer overflow lets an attacker overwrite memory next to a buffer: return addresses, function pointers, flags or other data. The result ranges from a crash to remote code execution. Memory errors consistently account for a large share of serious vulnerabilities in native code, and they often live in parsers that handle files, images, network packets and fonts, which is exactly where untrusted data arrives.
Languages like Rust, Go, Java, C#, Python and JavaScript check bounds for you. The requirement is mostly about two situations: native code you maintain, and the native libraries your managed code calls.
How to implement it
The first choice is to avoid unmanaged code where you can. Write new parsing code in a memory safe language, and keep C and C++ for the parts that truly need it.
Where you do write C, replace unbounded calls with bounded ones and check every size.
/* unsafe: no bounds, overflows when name is longer than 15 bytes */
char buf[16];
strcpy(buf, name);
/* safer: bounded copy that always terminates, and a visible truncation check */
char buf[16];
int n = snprintf(buf, sizeof buf, "%s", name);
if (n < 0 || (size_t)n >= sizeof buf) {
return ERR_TOO_LONG;
}
Other habits that help:
- Avoid
gets,strcpy,strcat,sprintfandscanf("%s"). Usesnprintf,memcpywith explicit checked lengths, orstrlcpywhere available. - Validate lengths before arithmetic, and check for integer overflow:
if (count > SIZE_MAX / elem_size) return ERR;. - Prefer containers with bounds checks in C++, such as
std::string,std::vectorandstd::span, and use.at()where indexing untrusted values. - Keep pointer arithmetic out of parsing code. Use a length and offset pair, and check
offset + len <= total. - Initialize memory and free it exactly once, setting the pointer to NULL after free.
Enable compiler and runtime defenses as a safety net, not as the fix: stack protectors (-fstack-protector-strong), -D_FORTIFY_SOURCE=2 (or 3), position independent executables, -Wformat-security and RELRO linking.
How to test it
Build a test binary with AddressSanitizer and UndefinedBehaviorSanitizer, then run it against your test suite and a fuzzer:
clang -g -O1 -fsanitize=address,undefined -fno-omit-frame-pointer parser.c -o parser_asan
./parser_asan < malformed_input.bin
Feed oversized fields, zero length fields, negative lengths and truncated files. A sanitizer report means a real bug. Coverage guided fuzzing (libFuzzer or AFL++) finds overflow bugs in parsers far faster than hand written cases, and a fuzz target is often just a few lines.
Static analysis flags banned functions and size mismatches across the repository, and it works well as a pull request check. The demo below shows a scan highlighting an unsafe call.
Click through it at your own pace, or open it full screen.
Do not forget the supply chain side. Image, font and compression libraries are native code too, and their memory errors become yours when you link them. Run the free in browser dependency scan at gold.safeguard.sh/scan to see which of your native dependencies carry known advisories, and use reachability analysis to learn whether your code even calls the affected function.
Common mistakes
- Replacing
strcpywithstrncpyand forgetting that it may not null terminate. - Computing buffer sizes with
sizeofon a pointer, which returns the pointer size. - Checking the length after the copy.
- Trusting a length field read from the input itself.
- Mixing signed and unsigned values in size comparisons.
- Leaving sanitizers and fuzzing out of continuous integration.
Try it yourself
Take a small C function that copies a name into a fixed buffer, compile it with sanitizers, and break it on purpose.
#include <stdio.h>
#include <string.h>
int main(int argc, char **argv) {
char buf[8];
if (argc > 1) strcpy(buf, argv[1]); /* deliberately unsafe */
printf("%s\n", buf);
return 0;
}
Compile with clang -g -fsanitize=address demo.c -o demo and run ./demo AAAAAAAAAAAAAAAAAAAA. AddressSanitizer stops the program and prints a stack buffer overflow report with the line number. Now replace the copy with the snprintf version from the article and run the same input. The report disappears, and the function rejects or truncates cleanly.
Last, write a three line fuzz target for one of your parsers and let it run for ten minutes in continuous integration. The first crash is usually a surprise, and the fix is often a missing length check.
Quick check
Is strncpy a safe replacement for strcpy?
Not by itself. It does not guarantee a null terminator when the source is too long, so you must terminate and check lengths yourself, or use snprintf.
Which tools find overflows during testing?
AddressSanitizer for runtime detection, plus fuzzers such as libFuzzer or AFL++ to generate malformed inputs that trigger them.