CertC++-CTR52¶
Guarantee that library functions do not overflow
Required inputs: IR
Copying data into a container that is not large enough to hold that data results in a buffer overflow. To prevent such errors, data copied to the destination container must be restricted on the basis of the destination container's size, or preferably, the destination container must be guaranteed to be large enough to hold the data to be copied.
Vulnerabilities that result from copying data to an undersized buffer can also involve null-terminated strings. Consult STR50-CPP. Guarantee that storage for strings has sufficient space for character data and the null terminator for specific examples of this rule that involve strings.
Copies can be made with the
std::memcpy() function. However, the
std::memmove() and
std::memset() functions can also have the same
vulnerabilities because they overwrite a block of memory without checking that
the block is valid. Such issues are not limited to C standard library
functions; standard template library (STL) generic algorithms, such as
std::copy(),
std::fill(), and
std::transform(), also assume valid output buffer sizes [
ISO/IEC
14882-2014].
Noncompliant Code Example
STL containers can be subject to the same vulnerabilities as array data types.
The
std::copy() algorithm provides no inherent bounds checking
and can lead to a buffer overflow. In this noncompliant code example, a vector
of integers is copied from
src to
dest using
std::copy(). Because
std::copy() does nothing to expand the
dest vector, the program will overflow the buffer on copying the
first element.
#include <algorithm>
#include <vector>
void f(const std::vector<int> &src) {
std::vector<int> dest;
std::copy(src.begin(), src.end(), dest.begin());
// ...
}
This hazard applies to any algorithm that takes a destination iterator, expecting to fill it with values. Most of the STL algorithms expect the destination container to have sufficient space to hold the values provided.
Compliant Solution (Sufficient Initial Capacity)
The proper way to use
std::copy() is to ensure the destination container can hold all
the elements being copied to it. This compliant solution enlarges the capacity
of the vector prior to the copy operation.
#include <algorithm>
#include <vector>
void f(const std::vector<int> &src) {
// Initialize dest with src.size() default-inserted elements
std::vector<int> dest(src.size());
std::copy(src.begin(), src.end(), dest.begin());
// ...
}
Compliant Solution (Per-Element Growth)
An alternative approach is to supply a
std::back_insert_iterator as the destination argument. This
iterator expands the destination container by one element for each element
supplied by the algorithm, which guarantees the destination container will
become sufficiently large to hold the elements provided.
#include <algorithm>
#include <iterator>
#include <vector>
void f(const std::vector<int> &src) {
std::vector<int> dest;
std::copy(src.begin(), src.end(), std::back_inserter(dest));
// ...
}
Compliant Solution (Assignment)
The simplest solution is to construct
dest from
src directly, as in this compliant solution.
#include <vector>
void f(const std::vector<int> &src) {
std::vector<int> dest(src);
// ...
}
Noncompliant Code Example
In this noncompliant code example,
std::fill_n() is used to fill a buffer with 10 instances of
the value
0x42. However, the buffer has not allocated any space for the
elements, so this operation results in a buffer overflow.
#include <algorithm>
#include <vector>
void f() {
std::vector<int> v;
std::fill_n(v.begin(), 10, 0x42);
}
Compliant Solution (Sufficient Initial Capacity)
This compliant solution ensures the capacity of the vector is sufficient before attempting to fill the container.
#include <algorithm>
#include <vector>
void f() {
std::vector<int> v(10);
std::fill_n(v.begin(), 10, 0x42);
}
However, this compliant solution is inefficient. The constructor
will default-construct 10 elements of type
int, which are subsequently replaced by the call to
std::fill_n(), meaning that each element in the container is
initialized twice.
Compliant Solution (Fill Initialization)
This compliant solution initializes
v to 10 elements whose values are all
0x42.
#include <algorithm>
#include <vector>
void f() {
std::vector<int> v(10, 0x42);
}
Risk Assessment
Copying data to a buffer that is too small to hold the data results in a buffer overflow. Attackers can exploit this condition to execute arbitrary code.
| Rule | Severity | Likelihood | Remediation Cost | Priority | Level |
|---|---|---|---|---|---|
| CTR52-CPP | High | Likely | Medium | P18 | L1 |
Related Guidelines
| SEI CERT C++ Coding Standard | STR50-CPP. Guarantee that storage for strings has sufficient space for character data and the null terminator |
| SEI CERT C Coding Standard | ARR38-C. Guarantee that library functions do not form invalid pointers |
| MITRE CWE |
CWE 119, Failure to Constrain Operations within the Bounds
of an Allocated Memory Buffer CWE 805, Buffer Access with Incorrect Length Value |
Bibliography
| [ ISO/IEC 14882-2014] | Subclause 25.3, "Mutating Sequence Operations" |
| [ ISO/IEC TR 24772-2013] | Buffer Overflow in Heap [XYB] Buffer Overflow in Stack [XYW] Unchecked Array Indexing [XYZ] |
| [ Meyers 2001] | Item 30, "Make Sure Destination Ranges Are Big Enough" |
Possible Messages
Key |
Text |
Severity |
Disabled |
|---|---|---|---|
missing_destination_overflow_check |
destination ‘{}’ is missing an overflow check. |
None |
False |
range_larger_than_destination |
range is possible {} or more larger than destination ‘{}’. |
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
save_inserter_functions¶
save_inserter_functions : set[bauhaus.analysis.config.QualifiedName] = {'std::back_inserter', 'std::front_inserter', 'std::inserter'}
unchecked_destination_functions¶
unchecked_destination_functions
Table of functions with unchecked output iterator(s).Type: dict[bauhaus.analysis.config.QualifiedName, tuple]
Default:
{ 'std::copy': ({0, 1}, {2}), 'std::copy_backwards': ({0, 1}, {2}), 'std::copy_if': ({0, 1}, {2}), 'std::copy_n': ({0}, {2}), 'std::fill_n': (set(), {0}), 'std::move': ({0, 1}, {2}), 'std::move_backward': ({0, 1}, {2}), 'std::partition_copy': ({0, 1}, {2, 3}), 'std::remove_copy': ({0, 1}, {2}), 'std::remove_copy_if': ({0, 1}, {2}), 'std::replace_copy': ({0, 1}, {2}), 'std::replace_copy_if': ({0, 1}, {2}), 'std::rotate_copy': ({0, 2}, {3}), 'std::transform': ({0, 1}, {2}), 'std::unique_copy': ({0, 1}, {2}) }