The std::basic_string
type uses the traits design pattern to handle implementation details of the various string types, resulting in a series of string-like classes with a common, underlying implementation. Specifically, the std::basic_string
class is paired with std::char_traits
to create the std::string
, std::wstring
, std::u16string
, and std::u32string
classes. The std::char_traits
class is explicitly specialized to provide policy-based implementation details to the std::basic_string
type. One such implementation detail is the std::char_traits::length
function, which is frequently used when dealing with null-terminated string data such as const char *
or const wchar_t *
values. According to the C++ Standard, [char.traits.require], Table 62 [ISO/IEC 14882-2014], passing a null pointer to this function results in undefined behavior because it would result in dereferencing a null pointer.
The following std::basic_string
member functions result in a call to traits::length
:
basic_string::basic_string(const charT *, const Allocator &)
basic_string &basic_string::append(const charT *)
basic_string &basic_string::assign(const charT *)
basic_string &basic_string::insert(size_type, const charT *)
basic_string &basic_string::replace(size_type, size_type, const charT *)
basic_string &basic_string::replace(const_iterator, const_iterator, const charT *)
size_type basic_string::find(const charT *, size_type)
size_type basic_string::rfind(const charT *, size_type)
size_type basic_string::find_first_of(const charT *, size_type)
size_type basic_string::find_last_of(const charT *, size_type)
size_type basic_string::find_first_not_of(const charT *, size_type)
size_type basic_string::find_last_not_of(const charT *, size_type)
int basic_string::compare(const charT *)
int basic_string::compare(size_type, size_type, const charT *)
basic_string &basic_string::operator=(const charT *)
basic_string &basic_string::operator+=(const charT *)
The following std::basic_string
nonmember functions result in a call to to traits::length
:
basic_string operator+(const charT *, const basic_string&)
basic_string operator+(const charT *, basic_string &&)
basic_string operator+(const basic_string &, const charT *)
basic_string operator+(basic_string &&, const charT *)
bool operator==(const charT *, const basic_string &)
bool operator==(const basic_string &, const charT *)
bool operator!=(const charT *, const basic_string &)
bool operator!=(const basic_string &, const charT *)
bool operator<(const charT *, const basic_string &)
bool operator<(const basic_string &, const charT *)
bool operator>(const charT *, const basic_string &)
bool operator>(const basic_string &, const charT *)
bool operator<=(const charT *, const basic_string &)
bool operator<=(const basic_string &, const charT *)
bool operator>=(const charT *, const basic_string &)
bool operator>=(const basic_string &, const charT *)
Do not call any of the above functions with a null pointer as the const charT *
argument. This rule is a specific instance of EXP34-C. Do not dereference null pointers.
Implementation Details
Some standard library vendors, such as libstdc++, will throw a std::logic_error
when a null pointer is used in these function calls. However, this is not a requirement from the standard, and not all vendors implement this behavior, such as libc++ and the Microsoft Visual Studio STL. For portability, you should not rely on this behavior.
Noncompliant Code Example
In this noncompliant code example, a std::string
object is created from the results of a call to std::getenv()
. However, since std::getenv()
returns a null pointer on failure, this can lead to undefined behavior when the environment variable does not exist (or some other error occurs).
#include <cstdlib> #include <string> void f() { std::string tmp(std::getenv("TMP")); if (!tmp.empty()) { // ... } }
Compliant Solution
In this compliant solution, the results from the call to std::getenv()
are checked for null prior to constructing the std::string
object:
#include <cstdlib> #include <string> void f() { const char *tmpPtrVal = std::getenv("TMP"); std::string tmp(tmpPtrVal ? tmpPtrVal : ""); if (!tmp.empty()) { // ... } }
Risk Assessment
Dereferencing a null pointer is undefined behavior, typically abnormal program termination. In some situations, however, dereferencing a null pointer can lead to the execution of arbitrary code [Jack 2007], [van Sprundel 2006]. The indicated severity is for this more severe case; on platforms where it is not possible to exploit a null pointer dereference to execute arbitrary code, the actual severity is low.
Rule | Severity | Likelihood | Remediation Cost | Priority | Level |
---|---|---|---|---|---|
STR51-CPP | High | Likely | Medium | P18 | L1 |
Automated Detection
Tool | Version | Checker | Description |
---|---|---|---|
Related Vulnerabilities
Search for vulnerabilities resulting from the violation of this rule on the CERT website.
Related Guidelines
Bibliography
[ISO/IEC 14882-2014] | 21.2.1, "Character Trait Requirements" |
[ISO/IEC 9899:2011] | Section 7.20.3, "Memory management functions" |
[Jack 2007] | |
[van Sprundel 2006] |