Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.
Comment: Split and updated the last CS, removing duplicate code for brevity

...

Code Block
bgColor#FFcccc
langcpp
#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
bgColor#ccccff
langcpp
//...
  // 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
bgColor#ccccff
langcpp
//...
  // 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.

...