13

I'd like to use STL containers (plus std::basic_string) to temporarily store keys or passwords in memory, and I'd like to zero the memory when done.

I was initially planning to use STL containers parameterized on a custom allocator that zeroes memory in allocator::deallocate, but I'm presuming that containers are allowed to use memory that doesn't come from the specified allocator. For example, it seems reasonable for a std::vector or a std::string to contain a fixed-size array member meant for small allocations.

Am I rightly concerned, and should I (sigh) write my own container?

jamesdlin
  • 81,374
  • 13
  • 159
  • 204
  • What I don't get, why dont you use vector, and before letting it go out of scope, just do a memset(myVec, 0x00, myVec.size())? – SinisterMJ Aug 08 '12 at 20:59
  • For `std::vector` you'd have to jump through flaming hoops to meet the exception requirements and still store data in the vector object itself. For `std::string`, storing data in the object is not only possible, but fairly common. – Jerry Coffin Aug 08 '12 at 20:59
  • 3
    @AntonRoth: Because what happens if somebody calls `std::vector::resize` or `std::vector::push_back` and causes the `std::vector` to allocate a new block of memory and to copy the existing buffer? – jamesdlin Aug 08 '12 at 21:06
  • Yeah, I wrote in an earlier answer, which I deleted, since I wasn't too sure about the question, that you could just allocate more memory than ever necessary, and zero that. Or is the memory used that critical? – SinisterMJ Aug 08 '12 at 21:08
  • Am I missing something here? Wouldn't making the cleanup the responsibility of the stored objects rather than the container make more sense? So just make the container store a class item whose destructor cleans up after itself? When a vector gets resized, don't copy contructors and then destructors all get called...? – Roddy Aug 08 '12 at 21:40
  • Oh - Do you mean a vector of keys for example, or storing a key as a vector of chars? Remember that in a std::string, the actual characters are stored in a separately allocated piece of memory, and std::vector is basically an array of pointers to the characters (plus, length info, etc..) – Roddy Aug 08 '12 at 21:49
  • @Roddy: I don't want to go the Java route of creating some `Char` class to wrap a `char`. Ultimately the data is going to be passed to some library function that expects a C string, and converting a contiguous array of `Char` objects to an array of `char` seems annoying if the objects have padding. – jamesdlin Aug 08 '12 at 21:50
  • @Roddy: I meant using a `std::vector` to store a binary key blob, or a `std::vector`/`std::string` to store a password. And yeah, if I were using a `std::vector` of non-primitive types, I definitely would do the zeroing in the `value_type`'s destructors. – jamesdlin Aug 08 '12 at 21:56
  • Ah, in which case, Dave S's answer looks good to me. – Roddy Aug 08 '12 at 21:58

3 Answers3

8

I would use std::vector with a custom allocator that does the zero'ing out. According to the answer at May std::vector make use of small buffer optimization?, it cannot use the small buffer optimization, and hence, with a custom allocator, you should be safe.

If you take it a step further, and use that allocator to allocate the vector, and then use a smart pointer to ensure it's proper release (or do it manually), even the internal contents of the vector (such as the size) will be wiped out.

Community
  • 1
  • 1
Dave S
  • 20,507
  • 3
  • 48
  • 68
2

You can do this by allocating the string/vector using raw memory and placement new and when you're done with it, call the destructor, zero memory, and deallocate raw memory.

Daniel
  • 30,896
  • 18
  • 85
  • 139
  • 100% true and completely reasonable way! but not reusable, and somewhat bypasses STL in lieu of core C++ operators and memory hacks. Exactly all of you wrote can be nicely wrapped by writing a custom allocator, see DateS's answer – quetzalcoatl Aug 08 '12 at 21:09
  • @quetzalcoatl: it is using stl, I meant placing the string/vector object in raw memory. – Daniel Aug 08 '12 at 21:10
  • No, you have to use an allocator. Heap memory allocated by the vector/string won't be affected by you zeroing out the object's memory. Even if you explicitly zero out the values in the vector, there can still be copies left by the vector's resizing operations. – Eclipse Aug 08 '12 at 21:13
  • @Eclipse: I'm presuming that Dani meant using placement-new in conjunction with the custom allocator. – jamesdlin Aug 08 '12 at 21:17
  • 1
    @quetzalcoatl: Yes, if you use both the custom allocator and the placement new, then you can be sure you have all the memory covered. – Eclipse Aug 08 '12 at 21:28
-1

Use a custom string class that zeros the memory buffer in its destructor.

class zeroed_string : public std::string
{
public:
    ~zeroed_string()
    {
        for (int i = 0; i < size(); ++i)
            (*this)[i] = 0;
    }
// ...
};
Mark Ransom
  • 299,747
  • 42
  • 398
  • 622
  • 1
    I don't think that std::string was meant to be used as a base class, otherwise it would have virtual destructor. btw your answer is same as -4 bellow – BЈовић Aug 08 '12 at 21:43
  • 1
    As I mentioned in another (now deleted) answer, this won't work if the `std::string` is ever resized and needs to allocate a new block of memory. Other comments to that answer also pointed out that it wouldn't work with COW implementations. – jamesdlin Aug 08 '12 at 21:44
  • 2
    @BЈовић that's true if you're going to use it polymorphically but in this case you're not. I think people get too paranoid on this point. – Mark Ransom Aug 08 '12 at 21:45
  • 2
    @jamesdlin, my answer was the simplest way that might work but the advice still stands - use a custom string class that enforces the security you require. If `std::string` isn't workable, write one from scratch. – Mark Ransom Aug 08 '12 at 21:46
  • 2
    Is this tested? In principle the compiler can just optimize this loop out, as it (probably) has no observable behavior. – GManNickG Aug 08 '12 at 22:50
  • @GManNickG, I hadn't considered that. Perhaps it would be best to call an external function to clear the memory so that the compiler can't divine the possible side effects. – Mark Ransom Aug 08 '12 at 23:01