Reputation: 1064
I'm getting the following gcc format-truncation warning:
test.c:8:33: warning: ‘/input’ directive output may be truncated writing 6 bytes into a region of size between 1 and 20 [-Wformat-truncation=]
snprintf(dst, sizeof(dst), "%s-more", src);
^~~~~~
test.c:8:3: note: ‘snprintf’ output between 7 and 26 bytes into a destination of size 20
snprintf(dst, sizeof(dst), "%s-more", src);
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
on code like this:
char dst[20];
char src[20];
scanf("%s", src);
snprintf(dst, sizeof(dst), "%s-more", src);
printf("%s\n", dst);
I'm aware that it might be truncated - but this is exactly the reason why I'm using snprintf in the first place. Is there a way how to make it clear to the compiler that this is intended (without using a pragma or -Wno-format-truncation)?
Upvotes: 70
Views: 65801
Reputation: 1250
Note when you put the "src" into a separate function the warning disappears:
#include <stdio.h>
char *getsrc();
int main()
{
char dst[20];
snprintf(dst, sizeof(dst), "%s-more", getsrc());
printf("%s\n", dst);
return 0;
}
char *getsrc()
{
static char src[20];
scanf("%s", src);
return src;
}
Upvotes: 0
Reputation: 11
it is possible without define
//GCC 8.4 check
snprintf(str, (volatile size_t){sizeof(str)}, "42")
//as an option
#define WARN_SUPPRESS_VOLATILE(val) (volatile typeof(val)){(val)}
Upvotes: 1
Reputation: 1994
Also, Introducing a volatile
temporary variable for the destination size is also a workaround here.
char dst[20];
char src[20];
volatile int dst_size = sizeof(dst);
snprintf(dst, dst_size, "%s-more", src);
As suggested by Martin Sebor, you may also use this snprintf_trunc
macro;
#define snprintf_trunc(dst, size, ...) \
do { \
volatile size_t n = size; \
snprintf (dst, n, __VA_ARGS__); \
} while (0)
Upvotes: 4
Reputation: 140990
Level 1 of -Wformat-truncation [...] warns only about calls to bounded functions whose return value is unused and that will most likely result in output truncation.
Unhandled output truncation is typically a bug in the program. [...]
In cases when truncation is expected the caller typically checks the return value from the function and handles it somehow (e.g., by branching on it). In those cases, the warning is not issued. The source line printed by the warning suggests that this is not one of those cases. The warning is doing what it was designed to do.
#include <stdio.h>
#include <stdlib.h>
int main() {
char dst[2], src[2] = "a";
// snprintf(dst, sizeof(dst), "%s!", src); // warns
int ret = snprintf(dst, sizeof(dst), "%s!", src);
if (ret < 0) {
abort();
}
// But don't we love confusing one liners?
for (int ret = snprintf(dst, sizeof(dst), "%s!", src); ret < 0;) exit(ret);
// Can we do better?
snprintf(dst, sizeof(dst), "%s!", src) < 0 ? abort() : (void)0;
// Don't we love obfuscation?
#define snprintf_nowarn(...) (snprintf(__VA_ARGS__) < 0 ? abort() : (void)0)
snprintf_nowarn(dst, sizeof(dst), "%s!", src);
}
Tested on https://godbolt.org/ with gcc7.1 gcc7.2 gcc7.3 gcc8.1 with -O{0,1,2,3} -Wall -Wextra -pedantic
. Gives no warning. gcc8.1 optimizes/removes the call to abort()
with optimization greater than -O1
.
Oddly enough, when compiling as a C++ source file, the warning is still there even when we check the return value. All is fine in C. In C++ prefer std::format_to
anyway. So:
#include <stdio.h>
#include <stdlib.h>
int main() {
char dst[2];
char src[2] = "a";
// does not warn in C
// warns in C++ with g++ newer than 10.1 with optimization -O2
int ret = snprintf(dst, sizeof(dst), "%s!", src);
if (ret < 0) {
abort();
}
// does not warn in C
// still warns in C++
ret = snprintf(dst, sizeof(dst), "%s!", "a");
if (ret < 0) {
abort();
}
// use compiler specific pragmas to disable the warning
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wformat-truncation"
snprintf(dst, sizeof(dst), "%s!", "a");
#pragma GCC diagnostic pop
// wrapper macro with compiler specific pragmas
// works for any gcc
// works from g++ 10.1
#ifndef __GNUC__
#define snprintf_nowarn snprintf
#else
#define snprintf_nowarn(...) __extension__({ \
_Pragma("GCC diagnostic push"); \
_Pragma("GCC diagnostic ignored \"-Wformat-truncation\""); \
const int _snprintf_nowarn = snprintf(__VA_ARGS__); \
_Pragma("GCC diagnostic pop"); \
_snprintf_nowarn; \
})
#endif
snprintf_nowarn(dst, sizeof(dst), "%s!", "a");
}
Upvotes: 61
Reputation: 2253
This error is only triggered when length-limited *printf
functions are called (e.g. snprintf
, vsnprintf
). In other words, it is not an indication that you may be overflowing a buffer, as may happen with sprintf; it only notifies you that you aren't checking whether snprintf
is doing its job and truncating. (Side note: snprintf
always null-terminates, so this can't result in a non-terminated string.)
Knowing that, I'm much more sanguine about disabling it globally using -Wno-format-truncation
, rather than trying to coax gcc
into ignoring a specific instance.
Upvotes: 18
Reputation: 403
This page was useful to me:
https://www.fluentcpp.com/2019/08/30/how-to-disable-a-warning-in-cpp/
You could resolve the issue for a gcc/clang compiler by doing this:
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wformat-truncation"
snprintf(dst, sizeof(dst), "%s-more", src);
#pragma GCC diagnostic pop
The webpage above also has a solution for Visual Studio compiler warnings.
Upvotes: 9