☰
  • 首页
  • 规则分类
  • 项目介绍
search
•••

禁用可变参数列表

6.4.2 ID_forbidVariadicFunction
目录 › next › previous

可变参数列表对参数的类型和数量缺乏有效的限定和控制,是公认的不安全因素。

示例:

string format(const char* fmt, ...);  // Non-compliant

假设 format 函数与 sprintf 函数功能相似,由参数 fmt 设定格式,将其他参数转为字符串后依次替换 fmt 中的占位符并返回结果。设 @ 和 $ 为占位符,分别对应字符串和整数,如调用 format("@: $", "value", 123) 则返回字符串 "value: 123"。

如果用可变参数列表实现:

string format(const char* fmt, ...) {
    va_list ap;
    stringstream res;
    va_start(ap, fmt);
    for (auto* c = fmt; *c; c++) {
        switch (*c) {
            case '@': res << va_arg(ap, char*); break;
            case '$': res << va_arg(ap, int); break;
            default:  res << *c; break;
        }
    }
    va_end(ap);
    return res.str();
}

例中 va_start、va_arg、va_end 是可变参数列表的标准支持,这种方法只能在运行时以 fmt 为依据获取后续参数,当实际参数与 fmt 不符时会造成严重问题,单纯地要求开发者小心谨慎是不可靠的,改用更安全的方法才是明智的选择。

在 C++ 代码中应使用“模板参数包”代替可变参数列表,如:

template <class T>
void get_argstrs(vector<string>& vs, const T& arg) {
    ostringstream oss;
    oss << arg;
    vs.emplace_back(oss.str());
}

template <class T, class ...Args>
void get_argstrs(vector<string>& vs, const T& arg, const Args& ...rest) {
    get_argstrs(vs, arg);
    get_argstrs(vs, rest...);  // Parameter pack expansion
}

template <class ...Args>
string format(const char* fmt, const Args& ...args) {  // Compliant
    string res;
    vector<string> vs;
    get_argstrs(vs, args...);
    auto it = vs.begin();
    for (auto* c = fmt; *c; c++) {
        if ((*c == '@' || *c == '$') && it != vs.end())
            res.append(*it++);
        else
            res.push_back(*c);
    }
    return res;
}

例中 ...args 是参数包,可以代替可变参数列表,get_argstrs 函数利用重载和递归将参数都转为 string 对象存入容器,再将 fmt 中的占位符依次替换成容器中的字符串。这种实现可以不区分 @ 和 $,参数的个数和类型可以由代码主动判断,如果参数不能转为字符串则不会通过编译,如果参数个数与占位符不符也容易作出处理。

从 C++17 开始,可利用“折叠表达式”简化 get_argstrs 函数的实现:

template <class ...Args>
void get_argstrs(vector<string>& vs, const Args& ...args) {
    (
        [&vs, &args]() {
            ostringstream oss;
            oss << args;
            vs.emplace_back(oss.str());
        }(), ...
    );  // Fold expression
}

例中 lambda 表达式和 ... 组成折叠表达式,可以免去重载和递归,化简参数包的展开。

相关

ID_badParmN ID_badVaArgType

依据

ISO/IEC 14882:2003 5.2.2(7)-undefined ISO/IEC 14882:2011 5.2.2(7)-implementation ISO/IEC 14882:2011 14.5.3 ISO/IEC 14882:2017 8.1.6

参考

C++ Core Guidelines ES.34 C++ Core Guidelines F.55 MISRA C 2004 16.1 MISRA C 2012 17.1 MISRA C++ 2008 8-4-1 SEI CERT DCL50-CPP
Copyright©2024 360 Security Technology Inc., Licensed under the Apache-2.0 license.