原文链接:

STR52-CPP. Use valid references, pointers, and iterators to reference elements of a basic_string


既然 std::basic_string 是字符的容器, 这条规则也是 CTR51-CPP. Use valid references, pointers, and iterators to reference elements of a container 的一个具体例子. 作为容器, 这和其他标准模板库里的其他容器一样支持迭代器. 然而, std::basic_string 模板类具有不一样的失效语义. The C++ Standard, [string.require], paragraph 5 [ISO/IEC 14882-2014], 陈述如下:

参考自 basic_string 序列中元素的引用, 指针和迭代器在下述使用 时可能失效: std::basic_string 对象

  • 作为那些引用非-const basic_string 为参数的标准库函数的实参.
  • 调用除 operator[], at, front, back, begin, rbegin, end, 和 rend 之外的非-const 成员函数.

References, pointers, and iterators referring to the elements of a basic_string sequence may be invalidated by the following uses of that basic_string object:

  • As an argument to any standard library function taking a reference to non-const basic_string as an argument.
  • Calling non-const member functions, except operator[], at, front, back, begin, rbegin, end, and rend.

引用非-const std::basic_string 的标准库函数的例子有 std::swap(), ::operator>>(basic_istream &, string &), 和 std::getline().

不要使用非法的引用, 指针或者迭代器, 因为这会导致 未定义行为, undefined behavior.

不合规代码示例

这个不合规代码示例将 input 拷贝到 std::string 中, 用空格来替换分号 (;). 这个例子时不合规的, 因为迭代器 loc 在第一次调用 insert() 后已失效. 后续 insert() 的调用是未定义的.

1
2
3
4
5
6
7
8
9
10
11
#include <string>

void f(const std::string &input) {
std::string email;

// Copy input into email converting ";" to " "
std::string::iterator loc = email.begin();
for (auto i = input.begin(), e = input.end(); i != e; ++i, ++loc) {
email.insert(loc, *i != ';' ? *i : ' ');
}
}

合规方案 (std::string::insert())

在这个合规方案中, 迭代器 loc 的值在每次 insert() 调用后被更新了, 使得失效的迭代器从未被访问. 之后被更新的迭代器在循环结束是被递增了.

1
2
3
4
5
6
7
8
9
10
11
#include <string>

void f(const std::string &input) {
std::string email;

// Copy input into email converting ";" to " "
std::string::iterator loc = email.begin();
for (auto i = input.begin(), e = input.end(); i != e; ++i, ++loc) {
loc = email.insert(loc, *i != ';' ? *i : ' ');
}
}

合规方案 (std::replace())

这个合规方案使用标准算法来执行替换. 当可能时, 在你的方案中使用通用算法是一种更恰当的方式.

1
2
3
4
5
6
7
8
9
10
11
12
#include <iostream>
#include <string>

extern void g(const char *);

void f(std::string &exampleString) {
const char *data = exampleString.data();
// ...
exampleString.replace(0, 2, "bb");
// ...
g(data);
}

不合规代码示例

在这个不合规代码示例中, 在调用 replace() 之后, data 是失效的, 所以在 g() 中它的使用是未定义行为.

1
2
3
4
5
6
7
8
9
10
11
12
13

#include <iostream>
#include <string>

extern void g(const char *);

void f(std::string &exampleString) {
const char *data = exampleString.data();
// ...
exampleString.replace(0, 2, "bb");
// ...
g(data);
}

合规方案

在这个合规方案中, 指向 exampleString 的内部缓冲区的指针直到 replace() 完成修改后才被生成.

1
2
3
4
5
6
7
8
9
10
11
#include <iostream>
#include <string>

extern void g(const char *);

void f(std::string &exampleString) {
// ...
exampleString.replace(0, 2, "bb");
// ...
g(exampleString.data());
}

风险预估

使用非法的指向 string 对象的引用, 指针或者迭代器将允许攻击者运行任意代码.

Rule Severity Likelihood Remediation Cost Priority Level
STR52-CPP High Probable High P6 L2

Automated Detection

Tool Version Checker Description
Tool Version Checker Description
Helix QAC 2021.2 C++4746, C++4747, C++4748, C++4749
Parasoft C/C++test 2021.1 CERT_CPP-STR52-a Use valid references, pointers, and iterators to reference elements of a basic_string

Search for vulnerabilities resulting from the violation of this rule on the CERT website.

SEI CERT C++ Coding Standard CTR51-CPP. Use valid references, pointers, and iterators to reference elements of a container

Bibliography

[ISO/IEC 14882-2014] Subclause 21.4.1, “basic_string General Requirements”
[Meyers 2001] Item 43, “Prefer Algorithm Calls to Hand-written Loops”

img img img