Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Passing zero argument pack to printf

Tags:

c++

gcc

variadic

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?

like image 933
ztik Avatar asked Mar 30 '15 15:03

ztik


1 Answers

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");
}
like image 130
Shafik Yaghmour Avatar answered Sep 17 '22 03:09

Shafik Yaghmour