When memcpy Betrays You: A Lesson in Overlapping Buffers and Ancient Toolchains
A maddening memory bug on an old 32-bit toolchain reminded me: memcpy and overlapping buffers don’t mix. Here's how memmove saved the day—and why even seasoned C devs still trip over this classic pitfall.

You think you know a standard library until it turns on you.
I've written more C code than I care to admit — moved more bytes around than a smuggler in the Matrix. And yet, this week, I got bit hard by memcpy()
. Not on a bleeding-edge platform, but deep in the weeds: cross-compiling for an old 32-bit system using a legacy toolchain held together by tape, prayers, and an ancient glibc.
The bug? A maddening memory corruption that only appeared on the target. Debug logs were clean. Stack was good. But data? Twisted just enough to break things in the weirdest ways.
The culprit: overlapping buffers.
See, memcpy()
is not guaranteed to behave correctly when source and destination overlap. In fact, it’s explicitly undefined behavior. I knew that — in the same way you “know” not to cross the streams, but you do it anyway because it’s always worked fine. Until it doesn’t.
The Trap
Modern implementations of memcpy()
are smart — they detect overlap and internally call memmove()
, or they implement memory-safe back-to-front copies using SIMD and cache prefetch tricks. In many cases, memmove()
is even faster, especially when optimized with SSE and AVX registers.
"Memmove is implemented as SSE optimized assembler code, copying from back to front. It uses hardware prefetch to load the data into the cache, and copies 128 bytes to XMM registers, then stores them at the destination."
— StackOverflow
So in modern systems? Sure. You might get away with it. But when you’re building for legacy silicon with an old C runtime — you get the real memcpy()
. The bare-metal, standard-following, data-corrupting variety.
And it will not help you.
The Fix
Swapping in memmove()
fixed it instantly. The weird corruption vanished. The data was clean. I bought myself a forehead-shaped dent in the workbench.
The Lesson
💡 If you’re copying memory and the buffers might overlap — don’t be clever. Just use memmove()
.
It’s not about performance. It’s about surviving long enough to care.
Especially when you're dusting off toolchains older than your junior devs.