Home > front end >  How to safely zero std::array?
How to safely zero std::array?

Time:01-31

I'm trying to safely zero a std::array in a class destructor. From safely, I mean I want to be sure that compiler never optimize this zeroing. Here is what I came with:

template<size_t SZ>
struct Buf {
    ~Buf() {
        auto ptr = static_cast<volatile uint8_t*>(buf_.data());
        std::fill(ptr, ptr   buf_.size(), 0);
    }

    std::array<uint8_t, SZ> buf_{};
};

is this code working as expected? Will that volatile keyword prevent optimizing by compiler in any case?

CodePudding user response:

The C standard itself doesn't make explicit guarantees. It says:

[dcl.type.cv]

The semantics of an access through a volatile glvalue are implementation-defined. ...

[Note 5: volatile is a hint to the implementation to avoid aggressive optimization involving the object because the value of the object might be changed by means undetectable by an implementation. Furthermore, for some implementations, volatile might indicate that special hardware instructions are required to access the object. See [intro.execution] for detailed semantics. In general, the semantics of volatile are intended to be the same in C as they are in C. — end note]

Despite the lack of guarantees by the C standard, over-writing the memory through a pointer to volatile is one way that some crypto libraries clear memory - at least as a fallback when system specific function isn't available.

P.S. I recommend using const_cast instead, in order to avoid accidentally casting to a different type rather than differently qualified same type:

auto ptr = const_cast<volatile std::uint8_t*>(buf_.data());

Implicit conversion also works:

volatile std::uint8_t* ptr = buf_.data();

System specific functions for this purpose are SecureZeroMemory in windows and explicit_bzero in some BSDs and glibc.

The C11 standard has an optional function memset_s for this purpose and it may be available for you in C too but isn't of course guaranteed to be available.

There is a proposal P1315 to introduce similar function to the C standard.


Note that secure erasure is not the only consideration that has to be taken to minimise possibility of leaking sensitive data. For example, operating system may swap the memory onto permanent storage unless instructed to not do so. There's no standard way to make such instruction in C . There's mlock in POSIX and VirtualLock in windows.

CodePudding user response:

One option is to use std::atomic_ref to make relaxed atomic stores, which cannot be elided by the compiler. C 20 example:

#include <cstdint>
#include <atomic>
#include <array>

void clear(auto& array) noexcept {
    for(auto& e : array) 
        std::atomic_ref{e}.store(0, std::memory_order_relaxed);
}

void f(std::array<uint8_t, 32>& a) {
    clear(a);
}
  •  Tags:  
  • Related