einpoklum
einpoklum

Reputation: 132320

How should I circumvent -Wformat-truncation?

Suppose I have a function taking an int *p which I know for a fact only points to values between 0 and 99. However, the compiler doesn't know that, so if I write:

char buffer[3];
snprintf(buffer, "%02d", *p);

I get a warning (at least, on GCC 8.x) - it's something like:

warning: ‘%02d’ directive output may be truncated writing between 2 and 11 bytes into a region of size 2 [-Wformat-truncation=]
   snprintf(buffer, "%02d", *p);

How should I circumvent this warning?

Upvotes: 2

Views: 1132

Answers (2)

einpoklum
einpoklum

Reputation: 132320

I can think of three ways of avoiding the warning:

  1. Local suppression using a GCC pragma:

    #if __GNUC__ >= 8
    #pragma GCC diagnostic push
    #pragma GCC diagnostic ignored "-Wformat-truncation"
    #endif
    snprintf(buffer, "%02d", *p);
    
    #if __GNUC__ >= 8
    #pragma GCC diagnostic pop
    #endif
    
  2. Uselessly clamp the printed value to make the compiler aware of the range:

    char buffer[3]; 
    int clamped_value = min(max(*p,0),99)` and print that instead of `*p`.
    snprintf(buffer, "%02d", clamped_value);
    
  3. Artificially inflate the buffer size by an extra 9 bytes;

    char buffer[3+9]; 
    snprintf(buffer, "%02d", p);
    

But I don't like any of these much. The first way is less safe (and more verbose); The second one wastes clock cycles and the third one wastes stack space.

Upvotes: 2

Paul Nowoczynski
Paul Nowoczynski

Reputation: 56

This code compiles and runs fine for me on GCC 8.3.1:

#include <stdio.h>

#define BUF_SZ 3

int main(void)
{
    int foo = 99;
    int *p = (int *)&foo;
    char buffer[BUF_SZ];

    snprintf(buffer, BUF_SZ, "%02d", *p);

    fprintf(stdout, "%s\n", buffer);

    return 0;
}
me@localhost:/tmp$ gcc -v 2>&1 | grep "gcc version"
gcc version 8.3.1 20190223 (Red Hat 8.3.1-2) (GCC)

me@localhost:/tmp$ gcc -Wall test.c && ./a.out 
99

Perhaps this version of GCC doesn't have the issue but I did notice above that you have snprintf(buffer, "%02d", *p); - which shouldn't compile at all since you're missing the size parameter to snprintf.

Also, it's worth noting that with the following diff, GCC 8.3.1 does throw the error as expected:

me@localhost:/tmp$ diff test.c test_format-truncation-warning.c
11c11
<     snprintf(buffer, BUF_SZ, "%02d", *p);
---
>     snprintf(buffer, BUF_SZ, "%03d", *p);

For others who stumble onto this page looking for a more general "workaround" for -Wformat-truncation warnings. Here's one which uses memcpy() though I doubt the authors of -Wformat-truncation intended for it to be used as an alternative to strncpy().

#if USE_STRNCPY
    /* Note that using size of 'NAME_MAX' is just to prevent recent versions
     * of GCC from throwing '-Wformat-truncation' errors.  Otherwise, a char
     * buffer of len UUID_STR_LEN would be fine.
     */
    char tmp_fname[NAME_MAX + 1] = {0};
    strncpy(tmp_fname, input_file_name, NAME_MAX);
#else
    char tmp_fname[UUID_STR_LEN] = {0};
    memcpy((void *)tmp_fname, (void *)input_file_name,
           MIN(UUID_STR_LEN - 1, strnlen(input_file_name, UUID_STR_LEN - 1));
#endif

Upvotes: 0

Related Questions