lobsters score: 2 · tags: compilers, security
The solution is on this Mastodon post: https://mastodon.social/@CounterPillow/117398430017955581
There is a “strub” GCC attribute: https://gcc.gnu.org/onlinedocs/gcc/Common-Attributes.html#index-strub It will wipe the value wherever it is (stack, etc.)
score 1 · canonical
Perhaps a programming language could have a compiler directive NOT to optimize away certain lines of code, such as the memset.
score 1 · canonical
memset_s serves that purpose, but this is the specific problem being discussed in the article: if the value is not in memory you don’t need to zero the memory, but by calling memset[_s] you force the compiler to spill the value onto the stack for the express purpose of zeroing it.
score 1 · canonical
Unless the compiler handles calls to
memset_s()specially so it “knows” to zero out the variable, regardless of where it lives (memory or registers). C compilers already can treatmemset()(andmemcpy(), memmove()) specially (when you include<string.h>) by inlining them or calling a function, depending upon the context.
score 1 · canonical
The compiler knows the semantics of memset, specifically that it can be treated as a dead store. memset_s (and the other _s functions) have defined semantics of guaranteeing a write, even if it’s dead - a compiler that tried lowering them such that they did not zero the memory would be nonconforming. As a result they essentially just don’t special case them at all.
score 1 · canonical
Even if normally the variable is stored only in a register,
memset_s()has to write to memory? Even if marked non-volatile?
score 1 · canonical
deleted by creator
memset_s: more or less yes, that is the core of what this article is talking about.
It sounds like memset_explicit might be the “solution”. This requires the backend be aware of these semantics, and while I don’t know about GCC, llvm’s register allocation does not carry such info and I’ve generally encountered push back against adding the data required to track such semantics (though in my case in the context of pointer authentication). IMO it would be a general win for a lot of security related purposes.
score 1 · canonical
The right answer should be
memset_explicit(). I was disappointed that this article never mentioned the Standard solution to this issue, or pre-standard versions such asexplicit_bzero(). But I don’t think that omission makes much difference because clang and gcc don’t seem to know enough aboutmemset_explicit()to avoid a spill to memory.Unfortunately
memset_s()is entangled with the Annex K portability problems and its crazy runtime constraint checking and exception handling, which makes it hard for compilers to implementmemset_s()as a sensible builtin.The special case for for
memset_s()is:Unlike memset, any call to the memset_s function shall be evaluated strictly according to the rules of the abstract machine as described in 5.1.2.4. That is, any call to the memset_s function shall assume that the memory indicated by s and n may be accessible in the future and thus contains the values indicated by c.
And for
memset_explicit():The purpose of this function is to make sensitive information stored in the object inaccessible. (footnote: The intention is that the memory store is always performed (i.e. never elided), regardless of optimizations. This is in contrast to calls to the memset function (7.26.6.1).)
score 1 · canonical
As the essay shows in spades the zeroing itself, when it does happen, can cause more harm than good by forcing stack storage or even extra copies. The problem is that the intent is not conveyed to the compiler, so you’re still playing silly bugger with it, not optimizing the wrong operation still does not do the right thing.
As Simon, Chisnall, and Anderson noted near 10 years ago in “what you get is what you C”, you can’t solve this by trying to trick the compiler for eternity, it would be a lot more sensible to tell the compiler what you need clearly.
score 1 · canonical
Yeah, the ideal fix would be some kind of “scrub this value” qualifier/attribute that gets passed to the backend
score 1 · canonical
