3

I have below piece of code

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

void fn(char *status, size_t maxLen)
{
    strcat(status, "1234567890");
}

int main()
{
    char status[5] = { 0 };
    size_t statusMaxLen = sizeof(status) / sizeof(status[0]);
    printf("%s%zu", "size of a status string ", statusMaxLen);
    fn(status, statusMaxLen);
    return 0;
}

I am getting

run-time check failure #2 - Stack around the variable was corrupted

erro when using strcat. If I replace strcat with strcat_s() like this

strcat_s(status, maxLen, "1234567890");

getting

Debug assertion failed. Buffer is too small

So only type of error is different but still my program crashes. The solution to avoid crash is to check the size before concatenating. In this case why I need strcat_s. strcat also will work fine when size check done. So what will be the real use of strcat_s here.

Whether I use strcat or strcat_s, I need to check the size before copying / concatenating. If I do size check, then why should I prefer strcat_s over strcat?

if((strlen("1234567890") + strlen(status)) < maxLen){ 
    strcat(status, "1234567890");}`
anastaciu
  • 23,467
  • 7
  • 28
  • 53
Arun
  • 2,247
  • 3
  • 28
  • 51
  • `strcat()` overflows the buffer because it does not check to ensure the parameters you pass are proper as there's no way in C to know the size of an array passed to a function. `strcat_s()` avoids buffer overflows the same way the portable, standard C function `strncat()` does: by having you pass a maximum length to copy to the function so you don't overwrite the buffer. `strcat_s()` provides no advantages over `strncat()`. – Andrew Henle Feb 25 '20 at 11:33
  • @AndrewHenle Whether I use strcat or strcat_s, I need to check the size before copying / concatenating. If I do size check, then why should I prefer strcat_s over strcat? – Arun Feb 25 '20 at 11:38
  • You're probably running into the default parameter validation failure. See https://learn.microsoft.com/en-us/cpp/c-runtime-library/parameter-validation?view=vs-2019 I never use the `*_s()` functions because they are entirely non-portable so I'm not intimately familiar with how parameter validation works or how it fails. – Andrew Henle Feb 25 '20 at 11:43
  • 2
    You don't always get the first crash. When you don't get the crash, hackers can take over your computer. But you always get the second crash. – user253751 Feb 25 '20 at 11:55
  • 1
    Since nobody answered your question, you are correct. As long as ypu perform the check correctly there is no advantage to strcat_s. – stark Feb 25 '20 at 12:04
  • 1
    It is not safe in the sense that it *avoids* your program from crashing. In fact it is safe by *intentionally* crashing your program. Buffer overflows are highly exploitable, they help an attacker turn data into code. The RTC check is only enabled in the Debug build, it is too slow for optimized code. – Hans Passant Feb 25 '20 at 12:07

5 Answers5

4

You should always check buffer sizes, regardless of which string concatenation function you use. That said, the code with strcat_s is better in two ways:

  • This is no longer a buffer overflow vulnerability.

    A buffer overflow vulnerability occurs when an attacker is able to supply a piece of data that is too long, and that data then overwrites something outside of the buffer, in some cases allowing the attacker to take control of the process by tricking it to run malicious code or misbehave in some other way.

    Since strcat_s crashes instead of allowing this to happen, the behaviour is now predictable and cannot be exploited by an attacker.

  • You can choose a more useful behaviour than crashing the process.

    You can set the constraint handler that gets invoked when strcat_s detects that the destination buffer is too small.

There isn't really any way around checking the buffer size. Even if you use strncat, you have the issue that strncat doesn't null-terminate the destination buffer if it is too small, which can cause all kinds of problems. Never mind, I mixed up strncpy and strncat. Note that the size argument to strncat probably doesn't do what you expect.

legoscia
  • 39,593
  • 22
  • 116
  • 167
  • 1
    But checking for the end of the input string and allowing it to be max "x" characters long would have prevented the program from crashing entirely. Thus `strcat_s` being "safer" is mostly MS BS. – Lundin Feb 25 '20 at 12:04
  • @Lundin I'm not sure that argument is correct. It is true that in an ideal world where every programmer checks buffer lengths correctly 100% of the time there is no advantage to `strcat_s`, but in the real world, wouldn't making programmers use `strcat_s` make a fair number of buffer overflow vulnerabilities less exploitable? – legoscia Feb 25 '20 at 12:16
  • 3
    Yes there are lots of incompetent programmers. The solution to that problem is to educate them, not to treat them like lost causes who can't even be trusted with basic string handling. It's not a _programming language_ problem, it's a "why is the industry full of quacks" problem. If one doesn't even understand how `strcat` and null terminated strings work, one shouldn't be writing production code in C, but go back to school. – Lundin Feb 25 '20 at 12:19
  • 1
    The `strncpy()` doesn't always null-terminate strings. The `strncat()` function does. What’s odd (or one of the things that are odd) about about `strncat()` is that if you have `char dst[32] = ””;`, then `strncat(dst, src, sizeof(dst);` is a buffer overflow if `src` is too long. – Jonathan Leffler Feb 25 '20 at 14:39
4

Whether I use strcat or strcat_s, I need to check the size before copying / concatenating. If I do size check, then why should I prefer strcat_s over strcat?

You shouldn't. If you check the size in advance, strcat is faster and more portable than strcat_s.

The _s functions are dangerou_s in general, because they are poorly standardized and lack compiler support.

Buffer overruns are bad, but having your program crashing instead isn't an "advantage". Instead make sure that it doesn't crash at all.

The worst thing that could happen to your last example is that strlen goes on reading out of bounds when handed a too long or non-null terminated string, which would result in array-out-of-bounds access and potentially a seg fault crash.

As a rule of thumb, first sanitize your program's input, then apply the fastest possible function on the verified data.

Lundin
  • 195,001
  • 40
  • 254
  • 396
0

strcat() operates on strings. So the buffer must be big enough to hold the terminating \0too. Or make sure that the string is shorter than the size of the buffer. Also, make sure that there is a \0 at the end of the string.

Also, if you read the documentation for strcat() you will understand that the buffer you provide is too small to hold the resulting string. strcat() will not allocate memory in order to accommodate longer strings.


I cannot test now, but you should probably have:

size_t statusMaxLen = sizeof(status) / sizeof(status[0]) - 1;

making statusMaxLen smaller with 1 (needed for \0).

virolino
  • 2,073
  • 5
  • 21
  • Microsoft's implementation of the optional Annex K functions from the C standard, such as `strcat_s()`, actually **add** uncertainty because you can not depend on how they will respond in any detected error condition - they can abort the entire process. And since the response depends on a global process state, there's no way to set the state without introducing race conditions to mulithreaded processes. Nevermind using Microsoft's `*_s()` functions makes your code non-portable without really making it safer... – Andrew Henle Feb 25 '20 at 11:41
  • Oh, that is unexpected. I do not use MS compilers at all, sorry for the trouble. I will delete the offeding line. – virolino Feb 25 '20 at 11:46
  • @AndrewHenle Annex K actually specifies that some run-time constraint handler registred with `set_constraint_handler_s` should be called. What good such a handler does, I don't know. And if MS actually follows the standard is another story. – Lundin Feb 25 '20 at 12:29
  • @Lundin [MS doesn't follow the standard](http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1967.htm): "For example, it doesn't provide the `set_constraint_handler_s` function but instead ..." – Andrew Henle Feb 25 '20 at 14:16
0

Advantage of strcat_s() is that this error is guaranteed to be detected. With strcat(), if there is an overflow the behavior is undefined.

See the documentation:

the following errors are detected at runtime and call the currently installed constraint handler function:

  • src or dest is a null pointer
  • destsz is zero or greater than RSIZE_MAX
  • there is no null terminator in the first destsz bytes of dest
  • truncation would occur (the available space at the end of dest would not fit every character, including the null terminator, of src)
  • overlap would occur between the source and the destination strings
VLL
  • 9,634
  • 1
  • 29
  • 54
0

The definition of strcat() clearly hints to why your program crashes:

It takes two arguments, i.e, two strings or character arrays, and stores the resultant concatenated string in the first string specified in the argument.

Your first argument status is not large enough to hold the concatenated string.

strcat will cause stack smashing, strcat_s will cause segfault. These are two different things as explained in:

Isnt Segmentation fault the same as the smashing the stack?

the difference between segment fault and Stack smashing detected

anastaciu
  • 23,467
  • 7
  • 28
  • 53
  • Whether I use strcat or strcat_s, I need to check the size before copying / concatenating. If I do size check, then why should I prefer strcat_s over strcat? if((strlen("1234567890") + strlen(status)) < maxLen){ strcat(status, "1234567890");} – Arun Feb 25 '20 at 11:42
  • The program is obviously meant to crash, the question is why it also crashes with `strcat_s()`. – VLL Feb 25 '20 at 11:42
  • @Arun It is being terminated in a more decisive way with `strcat_s`, avoiding the unpredictable effects of buffer overflow that might go unnoticed for a while. – Ian Abbott Feb 25 '20 at 11:46
  • 2
    No, the idea of `strncat` is to copy fixed width strings in ancient Unix systems. So unless that's what you are doing, you are doing it wrong - strncat was never intended to be some "safe" version of strcat. – Lundin Feb 25 '20 at 12:24
  • 1
    https://stackoverflow.com/questions/2114896/why-are-strlcpy-and-strlcat-considered-insecure – Lundin Feb 25 '20 at 12:24
  • Btw the OP never mentioned `strncat` so I'm not sure why you brought it up. – Lundin Feb 25 '20 at 12:26