...
Code Block | ||||
---|---|---|---|---|
| ||||
class S { int C; public: S() : C(0) {} int f(int I) const { return I + C; } }; void f() { S o; int i = o.f(10); } |
Noncompliant Code Example
When moving a value from an object of standard library type, the moved-from object's value is generally left in a valid but unspecified state. The notable exception to this rule is std::unique_ptr
, which is guaranteed to represent a null pointer value when it has been moved from. In this noncompliant code example, the integer values 0
through 9
are expected to be printed to the standard output stream from a std::string
rvalue reference. However, because the object is moved and then reused under the assumption its internal state has been cleared, unexpected output may occur despite not triggering undefined behavior.
Code Block | ||||
---|---|---|---|---|
| ||||
#include <iostream>
#include <string>
void g(std::string &&v) {
std::cout << v << std::endl;
}
void f() {
std::string s;
for (unsigned i = 0; i < 10; ++i) {
s.append(1, static_cast<char>('0' + i));
g(std::move(s));
}
} |
Implementation Details
Some standard library implementations may implement the short string optimization (SSO) when implementing std::string
. In such implementations, strings under a certain length are stored in a character buffer internal to the std::string
object (avoiding an expensive heap allocation operation). However, such an implementation might not alter the original buffer value when performing a move operation. When the noncompliant code example is compiled with Clang 3.7 using libc++, the following output is produced:
Code Block |
---|
0
01
012
0123
01234
012345
0123456
01234567
012345678
0123456789 |
Compliant Solution
In this compliant solution, the std::string
object is initialized to the expected value on each iteration of the loop. This practice ensures that the object is in a valid, specified state prior to attempting to access it in g()
, resulting in the expected output:
Code Block | ||||
---|---|---|---|---|
| ||||
#include <iostream>
#include <string>
void g(std::string &&v) {
std::cout << v << std::endl;
}
void f() {
for (unsigned i = 0; i < 10; ++i) {
std::string s(1, static_cast<char>('0' + i));
g(std::move(s));
}
} |
Risk Assessment
Reading uninitialized variables is undefined behavior and can result in unexpected program behavior. In some cases, these security flaws may allow the execution of arbitrary code.
...