将variadic模板参数转发到类似printf的函数

时间:2014-09-15 22:50:54

标签: c++ c++11 boost

我有以下日志功能:

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...);
}

此应用程序使用NDK在Android上运行,因此这是该平台的日志记录系统。问题是__android_log_print()无法编译:

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)...);
                                                                                                 ^

我不确定这意味着什么。我没有正确使用variadic模板参数吗?

1 个答案:

答案 0 :(得分:0)

printf中不受信任的输入可能是一个安全问题。使用字符串文字强制执行格式是提高安全性的一种方法

将警告转换为错误将导致构建失败,因此您不得不解决警告。

海湾合作委员会warning options有这个说法

-Werror
将所有警告变为错误。

-Wformat-security
关于使用表示可能出现的安全问题的格式函数的警告 目前,这会警告调用printf和scanf函数,其中格式字符串不是字符串文字并且没有格式参数
如果格式字符串来自不受信任的输入并包含%n,则这可能是一个安全漏洞。

通常建议在函数中创建std::string并将%s格式字符串文字传递给日志函数

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

从处理args...构建消息的位置,通常使用boost::formatstd::stringstream之类的内容。

如果要使用提供的fmt字符串和可变参数args,可以使用自定义printf样式函数解析参数,该函数生成std::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;
}