3

I have some security critical code, and I'd like to make sure that sensitive buffers are always erased before being freed. I can implement a buffer class which calls memset_s in its destructor, but perhaps there is a more convenient way.

If I replace std::allocator with a variant that calls memset_s in deallocate(), will that force std::vector not to allocate T objects anywhere else except as temporaries?

Thanks.

  • Probably of interest: http://stackoverflow.com/a/5735744/16287 – Drew Dormann Apr 02 '15 at 21:33
  • Yes, for reasons best explained [here](http://stackoverflow.com/questions/8190950/may-stdvector-make-use-of-small-buffer-optimization). – MSalters Apr 02 '15 at 21:42
  • How about `allocate_shared`, or some variant of [`allocate_unique`](http://stackoverflow.com/a/23132307/596781)? – Kerrek SB Apr 02 '15 at 21:55
  • What about an adapter class that erases memory in its destructor? `class my_unsafe_type {/*...*}; template class safe_erase_adapter {/*...*/}; std::vector> my_vector;`? – dyp Apr 03 '15 at 01:00
  • 1
    Note that erasing the buffer memory might not be sufficient. For example, if the stored objects have remote parts. – dyp Apr 03 '15 at 01:01
  • your way calling memet_s in dtor is the right way, i think. initialization and uninitialization is the duty of ctor and dtor, not the duty of allocator and deallocator – BruceAdi Apr 03 '15 at 06:54

2 Answers2

2

There are two reasons why you could have such T objects: either as elements of the vector, or for other reasons.

They can't be elements of the vector as that would violate contiguity, as well as violate the no-throw guarantee for swap. If you have elements for other reasons, they'd have to be constructed with observable complexity. Additionally T::T() may not be available (DefaultConstructable isn't required) or it might throw which would be an observable effect as well.

So, in general vector cannot have "hidden" elements.

MSalters
  • 173,980
  • 10
  • 155
  • 350
  • `vector` can create local variables of the `value_type`. This is done e.g. in single-element `insert` and `emplace` functions, probably to allow inserting an element when the function parameter(s) alias the current buffer, and the buffer needs to be resized. – dyp Apr 03 '15 at 00:57
  • @dyp: True, they may exist during those calls. Once the function return, they have to be gone. This can work, because `T` has to be CopyConstructable. Perhaps not literally a temporary, but sufficiently close to one. – MSalters Apr 03 '15 at 10:40
2

The allocator is a template argument, if you decide to implement one for your particular use case it will be active only in those objects for which you explicitly opt into this allocator:

std::vector<T,SecureAllocator> v; // this uses the memset_s under the hood
std::vector<T>                 n; // this doesn't

Now, the allocator modifies the type of the object, which means that if you have functions that take std::vector<T> as arguments you will not be able to pass a std::vector<T,SecureAllocator>.

Alternatively, you could implement a polymorphic allocator in which the source of the memory can be controlled at runtime. That is supported in BSL (an implementation of the C++03 standard library available in github), in which case the vectors are of the same type even if they allocate from different sources:

bsl::vector<T> v(bslma::Default::allocator()); 
     // bslma::Default::allocator() is new/delete
bsl::vector<T> n(secureAllocator());
David Rodríguez - dribeas
  • 204,818
  • 23
  • 294
  • 489