The basic_string
template class has unusual invalidation semantics. 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 non-member functions
swap()
,operator>>()
, andgetline()
- as an argument to
basic_string::swap()
- calling
data()
andc_str()
member functions calling non-const member functions, except
operator[]()
,at()
,begin()
,rbegin()
,end()
, andrend()
subsequent to any of the above uses except the forms of
insert()
anderase()
that return iterators, the first call to non-const member functionsoperator[]()
,at()
,begin()
,rbegin()
,end()
, orrend()
Non-Compliant Code Example
This non-compliant example copies the null-terminated byte string input
into the string email
, replacing ';' characters with spaces. This example is non-compliant because the iterator loc
is invalidated after the first call to insert()
. The behavior of subsequent calls to insert is undefined.
char input[] = "bogus@addr.com; cat /etc/passwd"; string email; string::iterator loc = email.begin(); // copy into string converting ";" to " " for (size_t i=0; i <= strlen(input); i++) { if (input[i] != ';') { email.insert(loc++, input[i]); } else { email.insert(loc++, ' '); } } // end string for each element in NTBS
Compliant Solution
In this compliant solution, the value of the iterator loc
is updated as a result of each call to insert so that the insert()
method is never called with an invalid iterator. The updated iterator is then incremented at the end of the loop.
char input[] = "bogus@addr.com; cat /etc/passwd"; string email; string::iterator loc = email.begin(); // copy into string converting ";" to " " for (size_t i=0; i <= strlen(input); i++) { if (input[i] != ';') { loc = email.insert(loc, input[i]); } else { loc = email.insert(loc, ' '); } ++loc; } // end string for each element in NTBS
Non-Compliant Code Example
In this non-compliant example, the string s
is initialized as "rcs" and the string iterator si
is initialized to the beginning of the string. The size of s
is three, and we'll assume the capacity is fifteen. The for
loop appends 20 characters to the end of the sting. As a result, the si
iterator is invalidated because the capacity of the string is exceeded, requiring a reallocation. As a result, the call to insert()
results in undefined behavior.
string s("rcs"); string::iterator si = s.begin(); for (size_t i=0; i<20; ++i) { s.push_back('x'); } s.insert(si, '*');
Compliant Solution
The relationship between size and capacity makes it possible to predict when a call to a non-const member function will cause a string
to perform a reallocation. This in turn makes it possible to predict when an insertion will invalidate references, pointers, and iterators (to anything other than the end of the string).
In this compliant solution, the non-compliant example is modified to only append capacity-size characters to the string s
. As a result, the call to push_back()
no longer invalidates the iterator.
string s("rcs"); string::iterator si = s.begin(); for (size_t i=0; i < 20; ++i) { if ( s.size() == s.capacity() ) { break; } s.push_back('x'); } s.insert(si, '*');
If instead of performing a push_back()
the code were to insert into an arbitrary location in the string, all references, pointers, and iterators from the insertion point to the end of the string would be invalidated.
Exceptions
The intent of these iterator invalidation rules is to give implementors greater freedom in implementation techniques. Some implementations implement method versions that do not invalidate references, pointers, and iterators in all cases. Check with the documentation for your implementation before attempting to access a (potentially) invalid iterator. Document any violation of the semantics specified by the standard for portability.
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 |
---|---|---|---|---|---|
BSC33-C |
3 (high) |
2 (probable) |
1 (high) |
P6 |
L2 |
References
[[Meyers 01]] Item 43: Prefer algorithm calls to hand-written loops.
[[ISO/IEC 14882-2003]] 21.3 Class template basic_string.
STR40-CPP. Do not use the pointer value returned by c_str() after any subsequent call to a non-const member function 07. Characters and Strings (STR) STR42-CPP. Range check element access