原文链接:

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


std::string 下标运算符 const_reference operator[](size_type) constreference operator[](size_type) 返回指定位置的字符, pos . 当 pos >= size() 时, 返回一个值为 chartT() 类型为 charT 的对象的引用. 未检查下标操作符 (范围错误没有异常抛出), 并试图修改越界对象会导致 未定义行为, undefined behavior.

类似地, std::string::back()std::string::front() 函数也未检查, 因为它们被定义为调用operator[]() 不会抛出异常.

不要将越界值作为参数传入 std::string::operator[]() 中. 类似地, 不要对空字符串调用 std::string::back()std::string::front() . 这是 CTR50-CPP. Guarantee that container indices and iterators are within the valid range 一个具体的例子.

不合规代码示例

在这个不合规代码示例中, get_index() 调用返回的值可能比存储在 string 中元素的数目要大, 导致 未定义行为, undefined behavior.

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

extern std::size_t get_index();

void f() {
std::string s("01234567");
s[get_index()] = '1';
}

合规方案 (try/catch)

这个合规方案使用 std::basic_string::at() 函数 – 和下标 operator[] 运算符行为表现类似, 但是如果 pos >= size() 时会抛出 std::out_of_range 异常.

1
2
3
4
5
6
7
8
9
10
11
12
#include <stdexcept>
#include <string>
extern std::size_t get_index();

void f() {
std::string s("01234567");
try {
s.at(get_index()) = '1';
} catch (std::out_of_range &) {
// Handle error
}
}

合规方案 (Range Check)

这个合规方案在调用 operator[]() 之前检查了 get_index() 返回值是否在合理范围内.

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

extern std::size_t get_index();

void f() {
std::string s("01234567");
std::size_t i = get_index();
if (i < s.length()) {
s[i] = '1';
} else {
// Handle error
}
}

不合规代码示例

这个不合规代码示例试图将字符串的首字符替换为大写. 然而, 如果给定的字符串为空时, 该行为是 未定义的, undefined.

1
2
3
4
5
6
7
#include <string>
#include <locale>

void capitalize(std::string &s) {
std::locale loc;
s.front() = std::use_facet<std::ctype<char>>(loc).toupper(s.front());
}

合规方案

在这个合规方案中, 只有自负床非空时, 才会调用 std::string::front() .

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

void capitalize(std::string &s) {
if (s.empty()) {
return;
}

std::locale loc;
s.front() = std::use_facet<std::ctype<char>>(loc).toupper(s.front());
}

风险评估

未检查元素访问会导致读写越界和任意写入 (write-anywhere) 漏洞, exploits. 这些漏洞, 反过来, 会利用受攻击进程的权限造成执行任意权限代码.

Rule Severity Likelihood Remediation Cost Priority Level
STR53-CPP High Unlikely Medium P6 L2

Automated Detection

Tool Version Checker Description
Astrée 20.10 **assert_failure **
CodeSonar 6.1p0 LANG.MEM.BO LANG.MEM.BU LANG.MEM.TBA LANG.MEM.TO LANG.MEM.TU Buffer overrun Buffer underrun Tainted buffer access Type overrun Type underrun
Helix QAC 2021.2 C++3162, C++3163, C++3164, C++3165
Parasoft C/C++test 2021.1 CERT_CPP-STR53-a Guarantee that container indices are within the valid range
Polyspace Bug Finder R2021b CERT C++: STR53-CPP Checks for:Array access out of boundsArray access with tainted indexPointer dereference with tainted offsetRule partially covered.

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

SEI CERT C++ Coding Standard CTR50-CPP. Guarantee that container indices and iterators are within the valid range

Bibliography

[ISO/IEC 14882-2014] Subclause 21.4.5, “basic_string Element Access”
[Seacord 2013] Chapter 2, “Strings”

img img img