What is memcpy()
memcpy()
is a standard function used in the C programming language to copy blocks of memory from one place to another. Its prototype is defined in the string.h
header file as follows:
void *memcpy(void *dest, const void *src, size_t n);
The memcpy()
function copies the contents of a source buffer to a destination buffer, starting from the memory location pointed to by src
, and continuing for n
bytes. The areas of memory should not overlap, and the behavior is undefined if they do.
The function is commonly used in C programming for tasks such as copying the contents of one array to another, or for copying structures or other data types that are not handled by simple assignment. Using memcpy()
is often preferred over a loop-based approach to copying memory, as it is generally faster and more efficient.
Common Use Cases for the memcpy() Function
The versatility of the memcpy()
function makes it a go-to option for a variety of programming tasks involving strings, arrays, and pointers in the C programming language, for instance:
- Strings:
memcpy()
is often used for tasks such as concatenation or copying substrings. For example, you could use memcpy to copy a portion of a source string to adest
buffer or to concatenate two strings together into a new buffer. - Arrays:
memcpy()
can be used to copy the contents of one array to another. This is often useful when working with large arrays that may be time-consuming to copy using a loop-based approach.memcpy
can also be used to manipulate arrays by copying subsets of elements from one location to another. - Pointers: The
memcpy()
function enables the copying of the contents of one memory location to different places, even when the source and destination are not contiguous blocks of memory. This is useful when working with complex data structures or when copying data between non-contiguous memory spaces.
How memcpy() Works: Syntax and Code Example
The memcpy()
function takes three arguments:
- dest: a pointer to the destination buffer location where the data is to be copied.
- src: a pointer to the source buffer from where the data is to be copied.
- n: the number of copied bytes.
Here is a simple example of how it all comes together. The below code copies the content of src
(the source buffer) to dest
(the destination buffer). Note that this code uses the sizeof
operator instead of n
(the number of bytes to be copied), which allows the program to copy all of the bytes from the source buffer.
#include <stdio.h> #include <string.h> int main() { //the source buffer char src[20] = "Hello"; //dst buffer char dst[20] = {1}; //copy the source buffer int dst memcpy(dst,src,sizeof(src)); //print the dst buffer printf("dst = %s\n", dst); return 1; }
The output looks like this:
memcpy Alternatives
Of course, memcpy()
is not the only way to move memory blocks around in C. Here is a quick rundown of some alternatives:
memcpy vs. memmove
memcpy()
and memmove()
are both standard C-language library functions used to copy blocks of memory between different locations. The main difference between memcpy()
and memmove()
is in how they handle overlapping memory regions. When copying memory, memcpy
assumes that the source and destination memory regions do not overlap, and will produce undefined behavior if they do. In contrast, memmove()
is designed to handle overlapping memory regions and will copy the memory correctly regardless of whether the regions overlap or not.
In the example below the memcpy()
function enables the copying of the substring “world!” from the str
array to the buffer
array. However, since the source and destination regions overlap, the function’s behavior is not defined, and the program may produce unexpected results.
The memmove()
function does exactly the same, copying the substring to the buffer array, but it will be correctly handle the overlapping regions.
#include <stdio.h> #include <string.h> int main() { char str[] = "Hello, world!"; char buffer[20]; // Use memcpy to copy the string to the buffer memcpy(buffer, str + 7, 6); printf("Using memcpy: %s\n", buffer); // Use memmove to copy the string to the buffer memmove(buffer, str + 7, 6); printf("Using memmove: %s\n", buffer); return 0; }
memcpy vs. strcpy
strcpy()
is another standard C-language library function and (you guessed it) it’s also used to copy memory between locations. The main difference between it and memcpy()
is the type of data they are designed to handle. Specifically, memcpy()
is a low-level function that can copy any type of data, including non-character data types such as integers, floating-point numbers, and structures. In contrast, strcpy()
is specifically designed to copy null-terminated character strings, and will produce undefined behavior if used with non-string data.
Another important difference between memcpy()
and strcpy()
is the way they handle the null terminator character. memcpy()
does not add a null terminator to the copied data, so it is up to the programmer to ensure that the destination buffer is properly terminated. In contrast, strcpy()
automatically adds a null terminator to the copied string, so the programmer does not need to explicitly add one.
The simple example below illustrates the difference between the two functions. In this example, memcpy
is used to copy the contents of the source array to the buffer array. Since it does not add a null terminator, the programmer must manually add one to ensure that the copied string is properly terminated.
In contrast, the strcpy
function is used to copy the same string to the buffer array, and automatically adds a null terminator to the end of the copied string.
#include <stdio.h> #include <string.h> int main() { char source[] = "Hello, world!"; char buffer[20]; // Use memcpy to copy the string to the buffer memcpy(buffer, source, strlen(source)); buffer[strlen(source)] = '\0'; printf("Using memcpy: %s\n", buffer); // Use strcpy to copy the string to the buffer strcpy(buffer, source); printf("Using strcpy: %s\n", buffer); return 0; }
Security Best Practices
memcpy()
can be used securely if it is used with care. However, since it is a low-level function that allows direct memory access, it can be dangerous if used incorrectly.
There are two primary security concerns with memcpy()
:
- Buffer overflow vulnerabilities: If the destination buffer is too small to hold the copied data, the function can overwrite adjacent memory regions and cause undefined behavior, which could potentially be exploited by an attacker. To avoid this, it is essential to ensure that the dest buffer is sufficiently large to hold the copied data and to check that the size of the copied data does not exceed the size of the buffer.
- Memory leaks: If the function is used to copy sensitive information, such as passwords or cryptographic keys, and the memory is not properly cleared after use, an attacker may be able to access this information by reading the memory.
To avoid these security concerns, it is important to follow these best practices when using memcpy()
:
- Ensuring that the destination buffer has an adequate size to hold the copied data.
- Checking the amount of data being copied to ensure it does not exceed the capacity of the buffer.
- Avoiding the use of
strcpy
with non-string data types, since it can result in undefined behavior and potential security vulnerabilities. - Clearing the memory after use, especially if the data being copied is sensitive.
- Using standard library functions like
strncpy
andmemset
that are designed to handle strings and memory operations safely, rather than manually writing code that accesses memory directly.
Deterministic Security for IoT with Sternum
The memcpy()
function is a commonplace culprit behind many vulnerabilities in IoT/embedded devices.
Sternum’s patented EIV™ (embedded integrity verification) technology protects from these with runtime (RASP-like) protection that deterministically prevents all memory and code manipulation attempts, offering blanket protection from a broad range of software weaknesses (CWEs), including the ones related to improper use of memcpy()
and other exploitable functions.
Embedding itself directly in the firmware code, EIV™ is agentless and connection agnostic. Operating at the bytecode level, it is also universally compatible with any IoT device or operating system (RTOS, Linux, OpenWrt, Zephyr, Micirum, FreeRTOS, etc.) and has a low overhead of only 1-3%, even on legacy devices.
The runtime protection features are also augmented by (XDR-like) threat detection capabilities of Sternum’s Cloud platform, its AI-powered anomaly detection, and extended monitoring capabilities.
To learn more, check out these case studies of how this technology was used to:
– Help a Fortune 500 company catch memory leaks in pre-production (Zephyr device)
– Uncover buffer overflow vulnerabilities in 80,000 NAS devices
– Uncover buffer overflow vulnerability in a very popular smart plug device
Also check out the video below to see Sternum EIV™ in action, as it provides out-of-the-box mitigation of Ripple20 malware, used for memory corruption attacks.