strcat Function in C – Syntax, Examples, and Security Best Practices

Igal Zeifman
Igal Zeifman

6  min read | min read | 26/07/2023

What Is the strcat() C Function

strcat() is a built-in function in the C programming language found in the string.h library. It is used for string concatenation, which is the process of appending one string to the end of another string. Its name is a contraction of “string concatenate”. The function syntax is:

char *strcat(char *destination, const char *source)

The strcat() function has many common uses in the C language. For example, it can be used to build file paths dynamically, work with user input, and generate SQL queries. Each of these combines strings to create a new string.

Common Use Cases for the strcat() Function

The strcat() function is particularly useful when you need to combine two or more strings. Here are a few examples of how programmers use string concatenation:

  • Building file paths dynamically: Let’s say you have a base directory and a file name, and you need to combine them to create the full file path. Using strcat() makes this task straightforward.
  • Working with user input: For example, you might have a form where users can enter their first and last names separately. You can use the strcat() function to combine these two strings into a full name.
  • Generating SQL queries dynamically: You can start with a base query and then append different clauses based on certain conditions.
    All three of these use cases have security implications—learn more below.

How strcat() Works: Syntax and Code Example

Let’s take another look at the syntax for the strcat() function:

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

Here, destination is the string that is to be appended by the source string, source. The function returns a pointer to the destination string.
Let’s look at a simple code example:

#include <stdio.h>
#include <string.h>
int main() {
   char destination[30] = "Hello ";
   char source[10] = "World!";
   strcat(destination, source);
   printf("%s\n", destination);   
   return 0;
}

In this example, we’ve declared two strings: destination and source. We then call the strcat() function, passing in destination and source as the two arguments. After the function call, the content of destination is “Hello World!”.

It’s important to note that the destination string must be large enough to hold the combined strings. Otherwise, you risk running into a buffer overflow error. It’s also worth mentioning that strcat() does not create a new string. Instead, it modifies the destination string directly.

You should be aware that because of the possibility of buffer overflow errors, the strcat() function has inherent security risks. Buffer overflow can lead to unpredictable behavior, system crashes, and can be exploited by attackers to inject malicious code into a program. We’ll cover these risks, security best practices, and safe alternatives to strcat(), later in the article.

strcat Alternatives

strcat is an essential function, but also introduces security concerns. With this in mind, here are some alternatives that could prove to be a more secure and efficient option.

strcat vs. strncat

char *strcat(char *destination, const char *source)

strncat() is an excellent alternative to strcat(). The main advantage of using strncat() over strcat() is that it allows you to specify the maximum number of characters to be appended. This feature is very useful in preventing buffer overflow, a common security concern when using strcat().

However, it’s essential to use strncat() correctly. The third argument in strncat() is the maximum number of characters to be appended. It’s crucial to ensure that this number is not larger than the space available in the destination string. Misuse of strncat() can lead to the same buffer overflow issues encountered when using strcat().

Here is a simple code example, of how that strncat generally works:

#include <stdio.h>
#include <string.h>
int main() {
    char str1[20] = "Hello, ";
    char str2[] = "world!";
    int maxLength = 10;
    printf("Before concatenation: %s\n", str1);
    strncat(str1, str2, maxLength);
    printf("After concatenation: %s\n", str1);
    return 0;
}

strcat vs. snprintf

int snprintf(char *str, size_t size, const char *format, …);

snprintf() is another alternative to strcat(). Unlike strcat(), using snprintf() provides a safer way to concatenate strings and format output. It writes the output to the string up to a certain number of characters, avoiding buffer overflow.

Working with snprintf() for string concatenation is relatively straightforward. The first argument is the buffer to write to, the second argument is the size of the buffer, and the following arguments are the format specifier and the strings to concatenate. The advantage of snprintf() is that it automatically null-terminates the string, even if the buffer size is not large enough to hold the entire string.

Here is a code example:

#include <stdio.h>
int main() {
    char str[20];
    int value = 42;
    int maxLength = sizeof(str);
    int length = snprintf(str, maxLength, "The answer is %d", value);
    printf("Formatted string: %s\n", str);
    printf("Length of the formatted string: %d\n", length);
    return 0;
}

strcat vs. strlcat

size_t strlcat(char * restrict dst, const char * restrict src, size_t dstsize);

strlcat() is a function that originated in BSD Unix and has been adopted in many other systems due to its robustness and efficiency. It’s specifically designed to handle string concatenation in a safer and more predictable way than strcat().

The strlcat() function takes the same arguments as strcat(), but it also includes the full size of the destination buffer as an additional argument. This allows strlcat() to ensure that no buffer overflow occurs. It’s an excellent alternative to strcat() when the size of the destination buffer is known.

Here is a code example:

#include <iostream>
#include <cstring>
extern "C" {
    #include  <bsd/string.h>
}
int main() {
    char str1[20] = "Hello, ";
    char str2[] = "world!";
    size_t size = sizeof(str1);
    size_t length = strlcat(str1, str2, size);
    std::cout << "Concatenated string: " << str1 << std::endl;
    std::cout << "Length of the concatenated string: " << length << std::endl;
    return 0;
}

Note: You will need to install additional libraries to handle a BSD string. On Ubuntu, use this command: sudo apt-get install libbsd-dev

strcat vs. memcpy

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

memcpy() is a function that copies a block of memory from one location to another. While it’s not specifically designed for string concatenation, it can be used for this purpose with some additional logic.
The advantage of using memcpy() is that it can be faster than strcat() for large strings, as it doesn’t need to scan the string to find the end. However, it’s important to remember to null-terminate the string after using memcpy() for string concatenation, as it doesn’t do this automatically.

Here is a simple code example:

#include <stdio.h>
#include <string.h>
int main() {
    char source[] = "Hello, world!";
    char destination[20];
    size_t length = strlen(source) + 1;
    memcpy(destination, source, length);
    printf("Copied string: %s\n", destination);
    return 0;
}

Security Concerns and Best Practices

As mentioned above, thestrcat() function has inherent security risks that programmers need to be aware of. The primary risk revolves around buffer overflow vulnerabilities.

Impact of buffer overflow

A buffer overflow occurs when more data is written to a block of allocated memory (the buffer) than it can hold. In the case of strcat(), this can happen if the destination string is not large enough to hold the appended source string. The overflow data then spills over into adjacent memory spaces, overwriting other data and potentially corrupting the program’s state.

This can lead to unpredictable behavior, from crashes to incorrect calculations or logic. More alarmingly, a buffer overflow can be exploited by malicious actors to inject malicious code into a program, which can then be executed to compromise a system or steal data.

Why strcat() causes buffer overflow

In the context of strcat(), a buffer overflow can occur because the function does not perform any bounds checking. It simply starts appending from the null terminator of the destination string, assuming that there is enough space to hold the source string. If this assumption is incorrect, the excess data will spill into adjacent memory, causing a buffer overflow.

This risk is further compounded by the fact that strcat() relies on null-terminated strings. If a source string is not correctly null-terminated, strcat() will continue reading and appending data until it encounters a null character, leading to undefined behavior.

Security best practices

Follow these best practices to protect your application from security vulnerabilities related to the strcat() function:

  • Consider using alternatives to strcat() when appropriate. As we’ve seen, functions can provide safer and more efficient ways to concatenate strings.
  • Avoid using strcat() when dealing with user-supplied data, as this data could contain malicious code and could be of unpredictable length. If strcat() is used to append this data to a string without checking that there is enough space, a buffer overflow and potential code injection attack can occur.
  • Always ensure that the destination buffer is large enough to hold the concatenated string. Always check the size of your buffers before using strcat().
  • Remember that strcat() always null-terminates the resulting string. This is a feature that’s often overlooked, but it’s crucial to ensuring that your strings are handled correctly.

How we

The vast majority of IoT/embedded devices use C code, and are prone to related memory and code vulnerabilities, including vulnerabilities stemming from improper use of strcat.

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

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, OpenWrtZephyr, 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.