6

It is a common pattern to use templates to enforce the compiler to initialize primitive / POD types values (https://stackoverflow.com/a/11493744/16673 or http://www.codeproject.com/Articles/825/Using-templates-for-initialization).

Does a similar pattern exist that could be used to erase the value once it goes out of scope for security reasons, to make sure the value is not left on the stack once the variable is destructed? I am afraid a naive analogous implementation might not work, as the compiler is free to ignore any assignments to a value which is going out of scope, as the value can be trivially proven not to be used any more. Is there some consistent and reasonably portable solution e.g. using volatile?

Community
  • 1
  • 1
Suma
  • 33,181
  • 16
  • 123
  • 191

2 Answers2

4

There's a function in the Windows API called SecureZeroMemory. You could look at it's implementation.

However, generally speaking, the compiler is forced to honour volatile writes. If you made the variable volatile, it should not be able to remove writes.

Puppy
  • 144,682
  • 38
  • 256
  • 465
  • What you write about volatile is probably a hint how the answer to this question would look like (the template should probably use volatile in the destructor), but it does seem to me it actually answer the question (the secured variable is not marked volatile, and I do not expected it to be marked as such, this should be handled by a template if necessary). – Suma Sep 04 '12 at 08:22
  • 1
    @Suma: It doesn't matter that the variable itself is not volatile. It's sufficient that the write is through a volatile expression, such as `*const_cast(this)`. Making the variable itself volatile ensures that `this` itself is `Foo volatile*` everywhere without casts, but you only need a volatile expression in the dtor. – MSalters Sep 04 '12 at 09:02
3

You could use some c++11 features to make this more portable, but this may suffice as a starting point:

Class

template<typename T>
class t_secure_destruct {
  static const size_t Size = sizeof(T);
  static const size_t Align = alignof(T);
public:
  t_secure_destruct() : d_memory() {
    new(this->d_memory)T;
  }

  ~t_secure_destruct() {
    reinterpret_cast<T*>(this->d_memory)->~T();
    this->scribble();
  }

  // @todo implement or delete op-assign and remaining constructors

public:
  T& get() {
    return *reinterpret_cast<T*>(this->d_memory);
  }

  const T& get() const {
    return *reinterpret_cast<const T*>(this->d_memory);
  }

private:
  void scribble() {
    for (size_t idx(0); idx < Size; ++idx) {
      this->d_memory[idx] = random();
    }
  }

private:
  __attribute__((aligned(Align))) char d_memory[Size];
};

Demo

#include <iostream>

class t_test {
public:
  t_test() : a(-1) {
    std::cout << "construct\n";
  }

  ~t_test() {
    std::cout << "destruct\n";
  }

public:
  void print() const {
    std::cout << "a = " << a << "\n";
  }

public:
  int a;
};

int main(int argc, const char* argv[]) {
  t_secure_destruct<t_test>test;
  test.get().print();
  test.get().a = 100;
  test.get().print();
  return 0;
}

Of course, you could also back that allocation with a heap allocation, if you favor. If you need to outsmart an optimizer, you may need to put the scribbler out of its reach.

justin
  • 104,054
  • 14
  • 179
  • 226
  • Sound reasonable. Wiping with randoms seems however to bring little benefit, plain zeroing should do the job. The solution does not attempt to secure against someone reading the memory chips or something like this, just against another process accessing the memory. Or is there a reason to use random data even in such case? – Suma Sep 04 '12 at 08:13
  • The solution seems to be using GCC specific features (attributes). Is there a reason to use the char for the storage at all? Why not using the original type? – Suma Sep 04 '12 at 08:15
  • @Suma it depends entirely on what you want to accomplish. zeroes would be good too -- it's just an illustration. – justin Sep 04 '12 at 08:16
  • How does this solution prevent the optimizer to optimize out the writes done in scribble, when the scribble is done in the optimizer? When using a similar code in Visual Studio, the compiler optimized it out - I assume even GCC most likely will do the same, and perhaps volatile is needed? – Suma Sep 04 '12 at 08:16
  • a) c++11 does have features for aligned storage -- i just don't remember the syntax. b) the original type should not be used because you would not have access to the memory following destruction. – justin Sep 04 '12 at 08:18
  • c) *If you need to outsmart an optimizer, you may need to put the scribbler out of its reach.* -- i added that in an update – justin Sep 04 '12 at 08:18
  • "you would not have access to the memory following destruction" Why? What is preventing me to access object memory once it is destructed? – Suma Sep 04 '12 at 08:19
  • @Suma simple - you can't access the object's memory after its destructor is called. it's out of scope, and may be reused. it's UB. only when you own the memory (e.g. using a container like the one above), will you be free to overwrite it. performing this operation might look more familiar if a heap allocation were used, but you said stack in your OP, so i wrote it for a stack allocation. – justin Sep 04 '12 at 08:30
  • If you have a member object in a class, I think it is safe to assume you can access its memory even after the destructor of the member object is called. – Suma Sep 04 '12 at 10:43
  • I see the problem now: if I overwrite the memory, I can no longer let the default destructor to be called on it, which a compiler would do on a member object. – Suma Sep 04 '12 at 10:48
  • Still, volatile is needed for scribble, otherwise the compiler can optimize it out. – Suma Sep 04 '12 at 10:49
  • @Suma yes - the destructor should be called exactly once. as far as scribble or clearing: yes, there are multiple ways that you can approach it -- i just illustrated the event sequence and went on to suggest using an clear/scribble operation which was not visible to or accessible by whichever optimizers you are using. DeadMG's suggestion `SecureZeroMemory` is such a function -- or `volatile` should also do the trick. – justin Sep 04 '12 at 11:09
  • One problem I see with this solution is using it with types like `std::string` that contain a pointer to data stored elsewhere in memory. `t_secure_destruct`, `t_secure_destruct>`, etc would only scribble over the pointer to the data, not the actual data itself. For a `std::string`, this would work OK only for small string values when SSO is implemented, but it would not work for longer values. – Remy Lebeau Mar 27 '20 at 18:00