45

GCC 8 added a -Wstringop-truncation warning. From https://gcc.gnu.org/bugzilla/show_bug.cgi?id=82944 :

The -Wstringop-truncation warning added in GCC 8.0 via r254630 for bug 81117 is specifically intended to highlight likely unintended uses of the strncpy function that truncate the terminating NUL charcter from the source string. An example of such a misuse given in the request is the following:

char buf[2];

void test (const char* str)
{
  strncpy (buf, str, strlen (str));
}

I get the same warning with this code.

strncpy(this->name, name, 32);

warning: 'char* strncpy(char*, const char*, size_t)' specified bound 32 equals destination size [-Wstringop-truncation`]

Considering that this->name is char name[32] and name is a char* with a length potentially greater than 32. I would like to copy name into this->name and truncate it if it is greater than 32. Should size_t be 31 instead of 32? I'm confused. It is not mandatory for this->name to be NUL-terminated.

JRR
  • 3,024
  • 2
  • 13
  • 37
  • If `name` is `"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"`, do you want `this->name` to hold `"ABCDEFGHIJKLMNOPQRSTUVWXYZabcde"` (a string) or `{'A','B',...,'e','f'}` (a sequence of characters, without a null character to terminate it)? –  May 06 '18 at 10:24
  • 6
    A sequence of characters, without a null character to terminate it. – JRR May 06 '18 at 10:26
  • 1
    If you are copying sequence of characters, it may be better choice `memcpy`. – Sany Apr 14 '23 at 11:55

9 Answers9

41

This message is trying to warn you that you're doing exactly what you're doing. A lot of the time, that's not what the programmer intended. If it is what you intended (meaning, your code will correctly handle the case where the character array will not end up containing any null character), turn off the warning.

If you do not want to or cannot turn it off globally, you can turn it off locally as pointed out by @doron:

#include <string.h>
char d[32];
void f(const char *s) {
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wstringop-truncation"
    strncpy(d, s, 32);
#pragma GCC diagnostic pop
}
  • Thank you. My issue is that I release this code in a place on which I have few control on the compilation options (on the CRAN, the place that hold packages of the R programming language). Can I do the same with `memcpy` to skip the warning without changing the compilation options? – JRR May 06 '18 at 10:39
  • 2
    You can use #pragma to turn off warnings locally. – doron May 06 '18 at 11:06
  • My code must compile without warning at least with gcc 5,6,7,8 and clang 3,4. This pragma statement raise warnings. Is there a way to target gcc 8 specifically? – JRR May 06 '18 at 11:57
  • 1
    @JRR If you need to you can check `__GNUC__`, but I would recommend checking behaviour rather than checking versions whenever possible. You can instead ignore `-Wpragmas`, `-Wunknown-warning-option` and `-Wstringop-truncation` (in that order). No warnings on GCC 4.6 and newer, and clang 3.5 and newer. –  May 06 '18 at 12:28
  • This is too verbose and causes warnings on other compilers. See my solution https://stackoverflow.com/a/56402365/33708 – Mehrdad Afshari May 31 '19 at 22:32
  • use `-Wno-stringop-truncation` to disable this warning and still have `-Wall` as can be seen in [this stack overflow answer](https://stackoverflow.com/a/935667/1502121) – m4l490n Feb 12 '20 at 18:45
  • If `strlen(s) >= 32` this code invokes UB if you try to print `d`, for example. – ivaigult Feb 10 '21 at 16:02
  • Don't turn off the warning, fix the problem and the warning goes away - https://stackoverflow.com/a/66140407/2351246 – BitByteDog Jul 03 '22 at 18:26
6

This new GCC warning renders strncpy() mostly unusable in many projects: Code review will not accept code, that produces warnings. But if strncpy() is used only with strings short enough, so that it can write the terminating zero byte, then zeroing out the destination buffer in the beginning and then plain strcpy() would achieve the same job.

Actually, strncpy() is one of the functions, that they had better not put into the C library. There are legitimate use cases for it, sure. But library designers forgot to put fixed size string aware counterparts to strncpy() into the standard, too. The most important such functions, strnlen() and strndup(), were only included 2008 into POSIX.1, decades after strncpy() was created! And there is still no function, that copies a strncpy() generated fixed-length string into a preallocated buffer with correct C semantics, i.e. always writing the 0-termination byte. One such function could be:

// Copy string "in" with at most "insz" chars to buffer "out", which
// is "outsz" bytes long. The output is always 0-terminated. Unlike
// strncpy(), strncpy_t() does not zero fill remaining space in the
// output buffer:
char* strncpy_t(char* out, size_t outsz, const char* in, size_t insz){
    assert(outsz > 0);
    while(--outsz > 0 && insz > 0 && *in) { *out++ = *in++; insz--; }
    *out = 0;
    return out;
}

I recommend to use two length inputs for strncpy_t(), to avoid confusion: If there was only a single size argument, it would be unclear, if it is the size of the output buffer or the maximum length of the input string (which is usually one less).

Kai Petzke
  • 2,150
  • 21
  • 29
4

TL;DR: handle the truncation case and the warning will dissappear.


This warning happened to be really useful for me, as it uncovered an issue in my code. Consider this listing:

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

int main() {
    const char long_string[] = "It is a very long string";
    char short_string[8];
    
    strncpy(short_string, long_string, sizeof(short_string));

    /* This line is extremely important, it handles string truncation */
    short_string[7] = '\0';

    printf("short_string = \"%s\"\n", short_string);

    return 0;
}

demo

As the comment says short_string[7] = '\0'; is necessary here. From the strncpy man:

Warning: If there is no null byte among the first n bytes of src, the string placed in dest will not be null-terminated.

If we remove this line, it invokes UB. For example, for me, the program starts printing:

short_string = "It is a It is a very long string"

Basically, GCC wants you to fix the UB. I added such handling to my code and the warning is gone.

ivaigult
  • 6,198
  • 5
  • 38
  • 66
  • This is the correct approach to solving this warning. The warning represents a real problem in the code that needs resolving. Just turning the warning off as presented by the accepted answers is not a solution. – BitByteDog Jul 03 '22 at 18:20
3

There are very little justified case for using strncpy. This is a quite dangerous function. If the source string length (without the null character) is equal to the destination buffer size, then strncpy will not add the null character at the end of the destination buffer. So the destination buffer will not be null terminated.

We should write this kind of code on Linux:

lenSrc = strnlen(pSrc, destSize)
if (lenSrc < destSize)
    memcpy(pDest, pSrc, lenSrc + 1);
else {
    /* Handle error... */
}

In your case, if you want to truncate the source on copy, but still want a null terminated destination buffer, then you could write this kind of code:

destSize = 32

sizeCp = strnlen(pSrc, destSize - 1);
memcpy(pDest, pSrc, sizeCp);
pDest[sizeCp] = '\0';

Edit: Oh... If this not mandatory to be NULL terminated, strncpy is the right function to use. And yes you need to call it with 32 and not 31. I think you need to ignore this warning by disabling it... Honestly I do not have a good answer for that...

Edit2: In order to mimic the strncpy function, you could write this code:

destSize = 32

sizeCp = strnlen(pSrc, destSize - 1);
memcpy(pDest, pSrc, sizeCp + 1);
benjarobin
  • 4,410
  • 27
  • 21
  • 8
    There is still a good use for it: when you need to copy a string into a fixed-size buffer and set all the "unused" bytes to zero. This comes up from time to time, but the point is that `strncpy()` does not produce C-style null-terminated strings--it does something different. – John Zwinck May 06 '18 at 09:57
  • I'm not sure to understand you point with the unjustified usage of strncopy. I slightly edit my question to explain my intention. Basically what I want it to truncate to 32 char the source into the destination. – JRR May 06 '18 at 10:20
  • @JRR do you want `destination` to be still NUL-terminated? Because in this case, `strncpy` is the wrong function. – Matteo Italia May 06 '18 at 10:25
  • 1
    @JRR I edited my answer, but you should edit your question, this was unclear. – benjarobin May 06 '18 at 10:32
  • What is the role of -1 +1? I guess it is used to exclude the nul byte – JRR May 06 '18 at 10:57
2

The responses from others led me to just write a simple version of strncpy.

    #include<string.h>

    char* mystrncpy(char* dest, const char*src, size_t n) {
        memset(dest, 0, n);
        memcpy(dest, src, strnlen(src, n-1));
        return dest;
     }

It avoids the warnings and guarantees dest is null terminated. I'm using the g++ compiler and wanted to avoid pragma entries.

1

I found the best way to suppress the warning is to put the expression in parentheses like this gRPC patch:

(strncpy(req->initial_request.name, lb_service_name,
         GRPC_GRPCLB_SERVICE_NAME_MAX_LENGTH));

The problem with #pragma diagnostics suppression solution is that the #pragma itself will cause a warning when the compiler does not recognize either the pragma or the particular warning; also it is too verbose.

Mehrdad Afshari
  • 414,610
  • 91
  • 852
  • 789
1

I found this while looking for a near-perfect solution to this problem. Since most of the answers here describing the possibility and ways about how to handle without suppressing the warning. The accepted answer suggests the use of the following wrapper which results in another set of warnings and is frustrating and not desirable.

#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wstringop-truncation"
    ...
#pragma GCC diagnostic pop

Instead, I found this working solution, can't say if there are any pitfalls, but it does the work nicely.

_Pragma("GCC diagnostic push")
_Pragma("GCC diagnostic ignored \"-Wstringop-truncation\"")
    strncpy(d, s, 32);
_Pragma("GCC diagnostic pop")

See full article here.

Avinal
  • 184
  • 4
  • 11
0

What it say is that we can only use len - 1 characters because last one should be '\0', so use seems to clean the warning we only can copy len - 1 ...

by the examples:

strncpy(this->name, name, 31);

or

#include <string.h>
char d[32];
void f(const char *s) {
    strncpy(d, s, 31);
}
d[31] = '\0';
Sérgio
  • 6,966
  • 1
  • 48
  • 53
0

Just disable the pragma warning while you are at it. Then the warning disabling locally works.

#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wpragmas"
#pragma GCC diagnostic ignored "-Wstringop-truncation"
    ...
#pragma GCC diagnostic pop