Passing a variational template parameter to a printf-like function

I have the following log function:

template<typename... Arguments>
void Log(const char* file, const int line, int level, const char* fmt, Arguments... args)
{
   std::string formattedFile;

   if (file)
   {
      boost::filesystem::path p(file);
      formattedFile = p.filename().string();
   }

   std::string message{boost::str(boost::format("%1%:%2% [%3%] - %s") % formattedFile % line % m_uxid % fmt)};
   __android_log_print(level, m_tag.c_str(), message.c_str(), args...);
}

      

This app runs on Android using the NDK, so it is a logging system for that platform. The problem is that it __android_log_print()

won't compile with:

error: format not a string literal and no format arguments [-Werror=format-security]
          __android_log_print(level, m_tag.c_str(), message.c_str(), std::forward<Arguments>(args)...);
                                                                                                 ^

      

I'm not sure what that means. Am I not using the variational pattern argument correctly?

+3


source to share


1 answer


Invalid input printf

can be a security issue. Providing formatting with a string literal is one way to improve security

Turning warnings into errors will cause the build to fail, so you'll have to refer to the warning.

GCC warning options have this to say

-Werror

:
Prevent all warnings.

-Wformat-security

:
Warn about using format functions that present potential security problems.
This currently warns about calls to printf and scanf where the format string is not a string literal and there are format arguments
It could be a security hole if the format string comes from untrusted input and contains %n

.



It is generally a std::string

good idea to create in your function and pass this using a format string literal %s

to your logging function

__android_log_print(level, m_tag.c_str(), "%s", message.c_str());

      

If the message is generated from processing args...

, something like boost::format

or is usually used std::stringstream

.

If you want to use the provided string fmt

and variable arguments, you can parse the arguments using a custom style function printf

that createsstd::string

std::string va_string_printf(const char* format, va_list ap)
{
    char stack_buf[256];

    char* buf = stack_buf;
    int buf_size = sizeof(stack_buf);

    std::string out_str;

    while(true)
    {
        va_list ap1;
        va_copy(ap1, ap);
        int min_buf_size = vsnprintf(buf, buf_size, format, ap1) + 1;
        va_end(ap1);

        if (min_buf_size > buf_size)
        {
            if (buf != stack_buf) // allocate a bigger buffer
                delete[] buf;

            buf = new char[min_buf_size];
            buf_size = min_buf_size;
            continue;
        }

        out_str = buf;
        break;
    }

    if (buf != stack_buf)
        delete[] buf;

    return out_str;
}

std::string string_printf(const char* format, ...)
{
    va_list ap;
    va_start(ap, format);
    std::string str = va_string_printf(format, ap);
    va_end(ap);

    return str;
}

      

0


source







All Articles