Warning: null destination pointer [-Wformat-overflow=] with GCC 11.2.1

Viewed 97

Here is my code:

#include <iostream>
#include <cstdio>

int main()
{
    char *str = new char[64] ;
    std::sprintf(str, "msg: %s", "hello world") ;

    std::cout << str << std::endl ;
    delete [] str ;

    return 0 ;
}

With GCC 11.2.1, using the following command:

g++ -O -fsanitize=undefined -Wformat-overflow test.cpp

I get:

test.cpp:7:17: warning: null destination pointer [-Wformat-overflow=]
    7 |     std::sprintf(str, "msg: %s", "hello world") ;
      |     ~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

I failed to understand the reason for the warning. Did I do anything wrong?

2 Answers

This seems like a bug/false-positive warning from the g++ compiler. The message is trying to warn you that using a pointer variable as the destination for the sprintf function could fail if that pointer is null (or points to a buffer of insufficient size).

It is 'trivial' to suppress this warning: simply add a check that str is not null before calling sprintf:

if (str) std::sprintf(str, "msg: %s", "hello world");

However, your code is fine as it stands, and that check is entirely superfluous, because the standard operator new [], as you have used it, cannot return a null pointer. You are using "Version (2)" of new as described on this cppreference page. Note, from that same page:

Return value

1-4) non-null pointer to suitably aligned memory of size at least size

If your new char[64] expression fails to allocate sufficient memory, then an exception will be thrown and (as your code stands) the sprintf function will not be called.

To add to Adrian's answer while you code is correct the reason GCC is giving the warning is due to the explicit call to delete[]. I found that commenting out this line removed the warning. I think GCC is seeing the delete[] call in a bare-bones context (maybe because it is outside a destructor) and it thinks the memory might be freed while in use.

The warning persists if the code is moved to a purely C context (using malloc and free) with the gcc command, meaning it might be detecting frees (using either free() or delete[]) and warning you that data might be read/written after free (even though in this case its not). Could be a error with GCC not removing the copy it has of the char arrays pointer but I'm not too sure. There might be a GCC bug report on it but I couldn't find it. Here's the code I tested.

Note: GCC version: 11.3.0, minor increment but still persistent to GCC-11. I still used the same flags/options as your example.

#include <stdlib.h>
#include <stdio.h>

int main()
{
    char* str = (char*)malloc(sizeof(char) * 64);

    sprintf(str, "msg: %s", "hello world");
    
    printf("%s\n", str);

    free(str);

    return 0;
}

When using g++ specifically though, if you use malloc and free it removes the warning for some reason. Here's the corresponding code:

#include <iostream>
#include <cstdio>

int main()
{
    char* str = (char*)std::malloc(sizeof(char) * 64);

    std::sprintf(str, "msg: %s", "hello world");
    
    std::cout << str << std::endl;

    std::free(str);

    return 0;
}

Note: I used std::cout for consistency with your given code and because the warning persists if I used std::printf.

If you're using C++ specifically, you can use std::string.

#include <iostream>
#include <string>
#include <cstdio>

int main()
{
    std::string str(64, ' ');  ///< allocate a std::string with 64 characters, each initialised to a space (' ').

    std::sprintf(&str[0], "msg: %s", "hello world");  ///< Get the pointer to the first value (C-style) by indexing the string and using the address operator (&)
    
    std::cout << str << std::endl;

    return 0;
}

The code above has no manual memory management, its done by std::string. Although I do get it if you're learning about the memory facilities in C++. Hopefully that's all clear.

Note: std::string's c_str() method wouldn't work here with std::sprintf because it expects a char* but c_str() returns a const char*.

Note: An yes, I know & can also be the reference operator or the bit-and operator but in this context it's used to obtain the address like in C.

Related