10

I have created a class which has a variadic template method. This method calls printf function. When passing zero arguments to the method, I get a compile warning by gcc saying:

warning: format not a string literal and no format arguments [-Wformat-security]

A simplified class example is:

class printer{
    std::map<int,std::string> str;
  public:
    printer(){
      str[0] = "null\n";
      str[1] = "%4d\n";
      str[2] = "%4d %4d\n";
      str[3] = "%4d %4d\n%4d\n";
    }
    template<typename ...Args>
    void print(Args... args){
      printf(str[sizeof...(args)].c_str(),args...);
    }
};

When using

printer p;
p.print(23);
p.print(345,23);

everything compiles smoothly, but when using

printer p;
p.print();

I get the compile warning

main.cpp: In instantiation of ‘void printer::print(Args ...) [with Args = {}]’:
main.cpp:23:11:   required from here
main.cpp:17:50: warning: format not a string literal and no format arguments [-Wformat-security]
       printf(str[sizeof...(args)].c_str(),args...);

Of course if I just call

printf("null\n");

no warning appears.

Could someone explain why this is happening?

Can I remove warning without disabling the -Wformat-security flag?

Shafik Yaghmour
  • 154,301
  • 39
  • 440
  • 740
ztik
  • 3,482
  • 16
  • 34

1 Answers1

12

This is an expected warning, if we look at the document for -Wformat-security it says:

-Wformat-security If -Wformat is specified, also warn about uses of format functions that represent possible security problems. At present, this warns about calls to printf and scanf functions where the format string is not a string literal and there are no format arguments, as in printf (foo);. This may be a security hole if the format string came from untrusted input and contains `%n'. (This is currently a subset of what -Wformat-nonliteral warns about, but in future warnings may be added to -Wformat-security that are not included in -Wformat-nonliteral.) -Wformat=2

which is the case when you pass no arguments since the result of c_str() is not a string literal.

This case:

printf("null\n");

does not warn because "null\n" is a string literal not possibly input from a user.

We can see why this is a potential security issue from this %n format specifier program giving different outputs on different compilers. Why?.

It looks like you have to turn on specific switches if you don't want all of -Wformat-secrity:

-Wformat is included in -Wall. For more control over some aspects of format checking, the options -Wformat-y2k, -Wno-format-extra-args, -Wno-format-zero-length, -Wformat-nonliteral, -Wformat-security, and -Wformat=2 are available, but are not included in -Wall.

Although this is poor option if -Wformat-secrity adds more options later on, then you need to continually keep updating.

Another alternative as AndyG mentioned would be an overload:

void print(){
  std::printf("null\n");
}
Community
  • 1
  • 1
Shafik Yaghmour
  • 154,301
  • 39
  • 440
  • 740
  • Would it be a good idea to overload OP's `print` function to one that takes no arguments (a "do nothing" function)? – AndyG Mar 30 '15 at 15:55
  • What I don't get is: Why the no-literal warning appears only the zero argument case and not in the other cases? – ztik Mar 30 '15 at 16:31
  • @ctheo see my [answer here](http://stackoverflow.com/a/29218954/1708801) for an example of why the no argument case is a problem. – Shafik Yaghmour Mar 30 '15 at 16:50
  • @AndyG yes, I meant to add that, had to step away. – Shafik Yaghmour Mar 30 '15 at 16:54
  • Thanks @ShafikYaghmour. So, avoiding warning can be done by `-Wformat-nonliteral` option. The challenge now is how to really protect the software from formating attacks. Maybe I will post an other question later. – ztik Mar 31 '15 at 07:00
  • @ctheo the overload method seems like a better method since you don't have to adjust your warning flags. – Shafik Yaghmour Mar 31 '15 at 09:18