Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is object destruction reliable for cryptographic purposes?

As a follow-on to this question, I'm imagining a class which stores sensitive data, like cryptographic keys. To simplify things, assume there's no inheritance involved.

struct Credential {
  std::array<uint8_t, 32> secretStuff;
  ~Credential() { memset_s(secretStuff.data(), 32, 0, 32); }
}

I'm trying to determine if objects of this type are guaranteed to have their destructor run, or if I need to do something fancy like use an Allocator to ensure the memory is wiped. I'm interested in resiliency against compiler optimizations, so I'm looking for chapter-and-verse from the standards to assure me that I'm going to get the right behavior no matter what.

In previous questions, it's been established that objects in automatically-allocated and static storage are guaranteed to have their destructors run. I'm not interested in the static case; as far as I'm concerned it's the OS's job to make sure that the contents of previously-used memory don't leak once the program is terminated. I'm also not interested in cases where the programmer is deliberately breaking things... after all, there's nothing to say they can't just copy the data out in the first place.

Imagine you were a compiler author and wanted to break this while complying with the standard. Is there anything you could do to avoid calling the destructor (excepting program termination)? Maybe some strange exception handling behavior? And if you wouldn't be allowed to, why not, specifically?

like image 569
Reid Rankin Avatar asked Aug 15 '19 21:08

Reid Rankin


1 Answers

There are two issues involved here. One is observable effects. Destructors are allowed to have observable effects and when they do, that is a hard guarantee. A destructor can flush data to a file that would be lost if the destructor didn't run. A destructor can free objects referenced by naked pointers that would leak if the destructor didn't run. Destructors are just as important as every other function and their visible side-effects cannot magically disappear.

However, if you're concerned about non-observable effects, all bets are off. Anything a compiler can prove has no observable effects to a compliant program can be optimized away. This is why we have memset_s and unless you only use functions that define all the effects you want to rely on as observable, all bets are off.

like image 164
David Schwartz Avatar answered Nov 14 '22 23:11

David Schwartz