strcpy and strncpy C Functions – Syntax, Examples, and Security Best Practices

Igal Zeifman
Igal Zeifman

5  min read | min read | 11/05/2023

What is the strcpy() Function?

The strcpy() function is a standard library function in the C programming language, designed to copy strings from one memory location to another. It is included in the string.h header file and stands for “string copy.” The primary objective of this function is to replicate a source string into a destination buffer while ensuring both strings are null-terminated.

The strcpy() function works by taking two arguments: a pointer to the destination buffer (called dest) and a pointer to the source string (called src). The function iterates through the characters in the source string, copying each character to the destination buffer, and finally appending a null character \0 to terminate the destination string. Here’s a short code example:

#include  <stdio.h>
#include  <string.h>

int main() {
    char src[] = "Hello, World!";
    char dest[20];

    strcpy(dest, src);
    printf("Copied string: %s\n", dest);

    return 0;
}

Incorrect use of the strcpy() function can open a door to security vulnerabilities, such as memory leaks and buffer overflows when the size of the destination buffer is not properly checked before copying the source string. If the source string is larger than the destination buffer, it can overwrite adjacent memory, leading to crashes or potential execution of malicious code.

To reduce the risk of these, the strncpy function is often recommended as a safer alternative to strcpy.

Both functions have similar functions, but strncpy function takes an additional argument (n), which defines the maximum number of characters to be copied from the source string. This ensures that the destination buffer is not overwritten, even if the source string is larger than the destination buffer.

Common Use Cases For the strcpy() Function

  • Configuration parsing: When reading a configuration file and parsing key-value pairs, you might need to store the values in separate buffers for further processing. In this case, you can use strcpy to copy the values into the designated buffers.
  • Command-line argument processing: When processing command-line arguments in a C program, you can use strcpy to copy the arguments into separate buffers for further validation or manipulation.
  • Building file paths: When constructing file paths dynamically based on user input or other variables, you can use strcpy to copy the directory path, file name, and extension into a single buffer.
  • String manipulation: If you need to manipulate a string by replacing certain parts or inserting new content, you can use strcpy along with other string manipulation functions like strcat and strncpy.
  • Storing string constants: When you need to store string constants that may change in the future, you can use strcpy to copy the constant strings into separate buffers, making it easier to update the constants as needed.

How strcpy() Works: Syntax and Code Examples

Here is the basic syntax of the strcpy() function:

char *strcpy(char *dest, const char *src)

The strcpy() function copies the content of src, including the terminating null character, to the location specified by dest.

The strcpy() function works with null-terminated strings. Both string arguments should contain a null character to indicate the end of the string. Length checking is not performed. While src can be a literal string, it is recommended not to use a literal string for dest. The strcpy() function returns a pointer to the copied string (dest).

The following example demonstrates copying the content of src to dest.

// Include necessary header files
#include  <stdio.h>
#include  <string.h>


// Define the size of the strings
#define SIZE 40

// Main function
int main(void)
{
  // Declare and initialize the source and destination strings
  char source[SIZE] = "Original text";
  char destination[SIZE] = "Final text";
  
  // Declare a character pointer for the return value of strcpy()
  char *return_string;

  // Print the original destination string
  printf("Original value of destination: \"%s\"\n", destination);
  
  // Use strcpy() to copy the content of source to destination, and store the result in return_string
  return_string = strcpy(destination, source);
  
  // Print the modified destination string after copying
  printf("Current value of destination after running strcpy: \"%s\"\n", destination);

  // Return 0 to indicate successful execution
  return 0;
}

The output looks like this:

Original value of destination: "Original text"
Current value of destination after running strcpy: "Final text"

strcpy() vs. strncpy()

As mentioned, the main distinction between these two functions lies in how they manage string length during the copy process:

strcpy() transfers the source string (including its null-terminator) into the destination buffer without regard for the size of either buffer. This may result in a buffer overflow if the destination buffer is smaller than the source string. The syntax is as follows:

char* strcpy(char* destination, const char* source);

strncpy enables specifying a maximum number of characters to copy from the source string, preventing exceeding the destination buffer’s capacity. However, it might not append a null-terminator if there isn’t enough space left after copying. The syntax is as follows:

char *strncpy(char *dest, const char *src, size_t n)

memcpy() vs. strcpy()

The main difference between these two functions lies in their purpose. While both are utilized for copying data, they cater to distinct use cases. The memcpy() function transfers a specified number of bytes from the source buffer to the destination buffer, irrespective of any null-terminating characters present within the copied data.The syntax for memcpy is as follows:

void *memcpy(void *dest, const void * src, size_t n)

In contrast, strcpy() specifically targets strings by transferring all characters until a null-terminating character (\0) is encountered, stopping the copying process and appending the same character to signal the end of the new string.

In terms of performance, memcpy typically outperforms strcpy, as it does not need to check for null-terminating characters during copying. This can be particularly beneficial when copying large amounts of data or working with non-string data types.

Security Best Practices

strcpy() is very useful for copying strings in C programming. However, it can also introduce security vulnerabilities if not used properly. Here are some of the security issues associated with using this function:

  • Buffer overflow: The most common issue with the strcpy function is its ability to be manipulated for a buffer overflow. This occurs when the source string’s length surpasses the destination buffer size, potentially causing data corruption or even code execution by an attacker.
  • Faulty null-termination: If the destination buffer lacks sufficient space to accommodate both source string content and null terminator (\0), it may result in unexpected behavior as other functions might interpret incomplete strings as valid input.
  • Memory leaks: Improper usage of strcpy may lead to sensitive information stored near destination buffers being leaked as a result of overwriting beyond allocated memory space.

To address some of these security concerns, C programmers often utilize strncpy. However, it has its own set of issues, specifically:

  1. If the specified size is less than or equal to the source string’s length, strncpy does not append a null terminator to the destination buffer.
  2. The function may pad extra bytes with zeros if there is remaining space in the destination after copying all characters from the source, which can lead to performance overheads.

To minimize security risks when using these functions, use these best practices:

  • Validate input length: Always ensure input strings are within expected lengths before processing them with strcpy() or strncpy().
  • Avoid fixed-length buffers: Consider using dynamic memory allocation (e.g., malloc()) instead of fixed-length buffers whenever possible. This allows allocating precisely what’s needed for each specific case, reducing chances for overflows.
  • Use safer string functions: Consider using safer alternatives like strlcpy() or snprintf().
  • Employ compiler warnings: Enable compiler warnings to catch potential issues with strcpy() or strncpy() usage. For example, using the -Wformat-security flag in GCC will warn about format string vulnerabilities.

Deterministic Security for IoT

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 strcpy() 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.

JUMP TO SECTION

Enter data to download case study

By submitting this form, you agree to our Privacy Policy.