Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.
Comment: Adding an NCCE/CS pair for reuse of moved-from objects

...

Code Block
bgColor#ccccff
langcpp
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, since 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
bgColor#FFcccc
langcpp
#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 (saving 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 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
bgColor#ccccff
langcpp
#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.

...