CertC++-STR52¶
Use valid references, pointers, and iterators to reference elements of a basic_string
Required inputs: IR
Since
std::basic_string is a container of characters, this rule is
a specific instance of
CTR51-CPP.
Use valid references, pointers, and iterators to reference elements of a
container. As a container, it supports iterators just like other containers
in the Standard Template Library. However, the
std::basic_string template class has
unusual invalidation semantics. The C++ Standard, [string.require],
paragraph 5 [
ISO/IEC
14882-2014], states the following:
References, pointers, and iterators referring to the elements of a
basic_stringsequence may be invalidated by the following uses of thatbasic_stringobject:
- As an argument to any standard library function taking a reference to non-const
basic_stringas an argument.- Calling non-const member functions, except
operator[],at,front,back,begin,rbegin,end, andrend.
Examples of standard library functions taking a reference to non-
const
std::basic_string are
std::swap(),
::operator>>(basic_istream &, string &), and
std::getline().
Do not use an invalidated reference, pointer, or iterator because doing so results in undefined behavior.
Noncompliant Code Example
This noncompliant code example copies
input into a
std::string, replacing semicolon (
;) characters with spaces. This example is noncompliant
because the iterator
loc is invalidated after the first call to
insert(). The behavior of subsequent calls to
insert() is undefined.
#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 : ' ');
}
}
Compliant Solution (
std::string::insert())
In this compliant solution, the value of the iterator
loc is updated as a result of each call to
insert() so that the invalidated iterator is never accessed.
The updated iterator is then incremented at the end of the loop.
#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 : ' ');
}
}
Compliant Solution (
std::replace())
This compliant solution uses a standard algorithm to perform the replacement. When possible, using a generic algorithm is preferable to inventing your own solution.
#include <algorithm>
#include <string>
void f(const std::string &input) {
std::string email{input};
std::replace(email.begin(), email.end(), ';', ' ');
}
Noncompliant Code Example
In this noncompliant code example,
data is invalidated after the call to
replace(), and so its use in
g() is undefined behavior.
#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);
}
Compliant Solution
In this compliant solution, the pointer to
exampleString's internal buffer is not generated until after the
modification from
replace() has completed.
#include <iostream>
#include <string>
extern void g(const char *);
void f(std::string &exampleString) {
// ...
exampleString.replace(0, 2, "bb");
// ...
g(exampleString.data());
}
Risk Assessment
Using an invalid reference, pointer, or iterator to a string object could allow an attacker to run arbitrary code.
| Rule | Severity | Likelihood | Remediation Cost | Priority | Level |
|---|---|---|---|---|---|
| STR52-CPP | High | Probable | High | P6 | L2 |
Related Guidelines
| 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" |
Possible Messages
Key |
Text |
Severity |
Disabled |
|---|---|---|---|
invalidated_object |
The referenced object “{}” of {} “{}” could be invalid. |
None |
False |
Options¶
This rule shares the following common options: exclude_in_macros, exclude_messages_in_system_headers, excludes, extend_exclude_to_macro_invocations, includes, justification_checker, languages, post_processing, provider, report_at, severity
The following places define options that affect this rule: Stylechecks, Analysis-GlobalOptions
invalidating_function_map¶
invalidating_function_map
Invalidating (unqualified) methods for the given (qualified) container types. (Some method names may come from super classes of the container type.) If an empty set of invalidating functions is given for a certain type (i.e. 'std::basic_string'), all methods not explicitly given in the option 'non_invalidating_functions' are considered as invalidating.Type: dict[bauhaus.analysis.config.QualifiedName, set[bauhaus.analysis.config.FunctionName]]
Default:
{ 'std::basic_string': set() }
invalidating_only_argument_map¶
invalidating_only_argument_map
For functions like "erase" only taking a single iterator argument only this argument iterator is invalided (possibly missing aliases). This avoids false positives for a common pattern where elements are erased in a loop.Type: dict[bauhaus.analysis.config.QualifiedName, set[bauhaus.analysis.config.FunctionName]]
Default:
{ 'std::list': {'erase'}, 'std::map': {'erase'}, 'std::multimap': {'erase'}, 'std::multiset': {'erase'}, 'std::set': {'erase'}, 'std::unordered_map': {'erase'}, 'std::unordered_multimap': {'erase'}, 'std::unordered_multiset': {'erase'}, 'std::unordered_set': {'erase'} }
non_invalidating_functions¶
non_invalidating_functions : set[bauhaus.analysis.config.FunctionName] = {'at', 'back', 'begin', 'end', 'front', 'operator[]', 'rbegin', 'rend'}