...
Code Block | ||||
---|---|---|---|---|
| ||||
#include <algorithm>
#include <cstddef>
#include <memory>
#include <type_traits>
class S {
int i;
public:
S() : i(0) {}
S(int i) : i(i) {}
S(const S&) = default;
S& operator=(const S&) = default;
};
template <typename Iter>
void f(Iter i, Iter e) {
static_assert(std::is_same<typename std::iterator_traits<Iter>::value_type, S>::value,
"Expecting iterators over type S");
ptrdiff_t count = std::distance(i, e);
if (!count) {
return;
}
// Get some temporary memory.
auto p = std::get_temporary_buffer<S>(count);
if (p.second < count) {
// Handle error; memory wasn't allocated, or insufficient memory was allocated.
return;
}
S *vals = p.first;
// Copy the values into the memory.
std::copy(i, e, vals);
// ...
// Return the temporary memory.
std::return_temporary_buffer(vals);
} |
Implementation Details
A reasonable implementation of std::get_temporary_buffer()
and std::copy()
can result in code that behaves like (error-checking elided):
...
The act of dereferencing result
is undefined behavior because the memory pointed to is not an object of type S
within its lifetime.
Compliant Solution (std::uninitialized_copy()
)
In this compliant solution, std::uninitialized_copy()
is used to perform the copy, instead of std::copy()
, ensuring that the objects are initialized using placement new
instead of dereferencing uninitialized memory. The compliant solution also shows an alternate solution using a std::raw_storage_iterator
, with the same well-defined resultsIdentical code from the noncompliant code example has been elided for brevity.
Code Block | ||||
---|---|---|---|---|
| ||||
//...
// Copy the values into the memory.
std::uninitialized_copy(i, e, vals);
// ... |
Compliant Solution (std::raw_storage_iterator
)
This compliant solution uses std::copy()
with a std::raw_storage_iterator
as the destination iterator, with the same well-defined results as using std::uninitialized_copy()
. As with the previous compliant solution, identical code from the noncompliant code example has been elided for brevity.
Code Block | ||||
---|---|---|---|---|
| ||||
//... // Copy the values into the memory. #include <algorithm> #include <cstddef> #include <memory> #include <type_traits> class S { int i; public: S() : i(0) {} S(int i) : i(i) {} S(const S&) = default; S& operator=(const S&) = default; }; template <typename Iter> void f(Iter i, Iter e) { static_assert(std::is_same<typename std::iterator_traits<Iter>::value_type, S>::value, "Expecting iterators over type S"); ptrdiff_t count = std::distance(i, e); if (!count) { return; } // Get some temporary memory. auto p = std::get_temporary_buffer<S>(count); if (p.second < count) { // Handle error; memory wasn't allocated, or insufficient memory was allocated. return; } S *vals = p.first; // Copy the values into the memory. std::uninitialized_copy(i, e, vals); // It is also acceptable to use a raw_storage_iterator in situations where an iterator // over uninitialized objects is required. // std::copy(i, e, std::raw_storage_iterator<S*, S>(vals)); // ... // Return the temporary memory. std::return_temporary_buffer(vals); } |
Risk Assessment
Referencing an object outside of its lifetime can result in an attacker being able to run arbitrary code.
...