Live data from Hacker News

How to zero a buffer

daemonology.net

141–150 of 216 posts

Re: How to zero a buffer

#141
post #111

Why would you want to zero a buffer ? Because it may contain sensitive information, I presume. If you don't have additional properties w.r.t allocated memory, what prevent a system with high load to temporarily put the given memory block on swap, leaking the information on disk ? Security is hard...

Nothing's perfect, but we do what we can. Swap encryption is cool.

Re: How to zero a buffer

#142
post #118
post #111

Why would you want to zero a buffer ? Because it may contain sensitive information, I presume. If you don't have additional properties w.r.t allocated memory, what prevent a system with high load to temporarily put the given memory block on swap, leaking the information on disk ? Security is hard...

>Why would you want to zero a buffer ? Because it may contain sensitive information, I presume. You don't zero sensitive buffers. You randomize them, then free() them.

Why do you randomize them?

Re: How to zero a buffer

#143
post #70

In GNU C one can add the statement asm ("" : : "m" (&key)); just before or after the memset, effectively telling the compiler that the address of "key" escapes the scope of the function.

GCC also has an `optimize' function attribute which might make sense to use here. This can set optimizations for the function to -O0. But I haven't tried it.

That's not enough, since even -O0 applies a few optimizations, and which optimizations it applies could change in the future.

The best answer is really GCC's __asm__("" : : "m" (&key)), or perhaps something like __asm__("" : : "r" (key) : "memory"), after the memset. It generates no extra code, just ensures that the memset won't be removed.

For other compilers (in practice only MSVC, since clang is gcc-compatible), you could pass the pointer to a dummy assembly function instead of using inline assembly; even link-time optimization can't know what happens within a function written in assembly. Or, for better performance, create in assembly a "safer_memset" which is a single instruction: a jump to the real memset function.

Re: How to zero a buffer

#144
post #136

Is the proposed solution really the best approach? It seems complicated to me and relies on obscure parts of the language. Maybe the problem (compiler optimizes away function call because the result is no longer needed) could be solved like this: memset(key, 0, sizeof(key)); if (key[0]) // we are using key, so you can't skip memset() dropDead(); Unless the compilers "understand" memset and still optimize away the las…

> Unless the compilers "understand" memset and still optimize away the last two lines

They do. That's the whole "problem" - the compiler knows what memset is and what it does, since it's specified in the standard.

Re: How to zero a buffer

#145

Why wouldn't you make key volatile? Shouldn't that solve all the problems? Or is it because it would be to slow because the compiler can't do that many optimizations in the rest of the function any more?

Yes, making key volatile would force the zeroing to happen; and yes, you don't want to do that because it would absolutely kill your code performance.

Can you play the game the other way and "fail safe"?

i.e. declare the storage volatile but running your crypto code on a non-volatile ptr to it (obtained via cast) to get your performance back?

If the compiler then generates enough smarts to work out that the non-volatile ptr you've passed into your crypto code is referring to volatile storage, then you keep security but get a (noticeable in testing?) performance hit.

I guess that's not as good as your solution though.

Re: How to zero a buffer

#146

You should have test cases to verify the zeroing behavior in the object code. Even if the standard says a compiler must do something does not mean that it does.

The difficult thing is that any way to verify the zeroing behavior would change the compiler's decision about whether it could elide the call to memset. So it's possible (well, almost guaranteed) that the test would succeed even though the memory wouldn't actually be zeroed in production.

I was thinking about that. I think the way to do the test would be to put in a known key value, then call the platform-specific equivalent of "abort and write a core file". The test would then grovel through the core looking for the known key sequence.

Re: How to zero a buffer

#147
post #95

Does GCC include any flags to prevent this sort of detrimental optimization?

There's a bunch listed (https://gcc.gnu.org/onlinedocs/gcc-3.1/gcc/Optimize-Options....), including

-O0 // Do Not Optimise

This sounds like it should do the trick (but I've not done C coding for quite some time, so I don't know if there's a nuance as to why it wouldn't), but would also presumably kill any other optimisation.

A better option may be to combine that with pragmas (https://gcc.gnu.org/onlinedocs/gcc/Function-Specific-Option-...) to switch optimisation levels within the code.

Anyone who's played with GCC recently know whether this would work or not?

Re: How to zero a buffer

#148
post #121

I had no idea that such things are possible in C. The things about I've read recently (the "friendly" C suggestion) and this seem like violations of the spirit of the language. And for what, really? The language looses its signature predictability, which to me seemed like a great feature of C. If you write crappy code and expect the compiler to fix it for you, you should maybe consider another language. I can only im…

> If you write crappy code and expect the compiler to fix it for you, you should maybe consider another language. This seems rather to be a case of writing good code and having the compiler break it for you.

Yes it is. You misunderstood my point, which was about the purpose of existence for such optimizations - they are meant to improve code, which presumably needs improvement. But if your code needs improvement, then why not go for a higher level language?

BTW, I gave you an upvote by accident :D

Re: How to zero a buffer

#149
post #44

When I allocate the key on the heap, the memset is carried (heavily optimized and inlined). When I allocate key on the stack, it disappears. Using gcc -03: #include void doSecure(void) { /*char key[32];*/ char *key = (char*) malloc(sizeof(char)*32); memset(key,sizeof(char),32); } int main(void) { doSecure(); return 0; } -- key on stack main: .LFB13: .cfi_startproc xorl %eax, %eax ret .cfi_endproc -- key on heap main:…

    memset(key,sizeof(char),32);
Note that you are not clearing 32 bytes with NUL (byte value 0). You are clearing 32 bytes with byte value 1 == (int)(sizeof(char).

That's why memset that works 8 bytes at a time fills memory with 72340172838076673 == 0x0101010101010101

http://linux.die.net/man/3/memset

Re: How to zero a buffer

#150
post #148

Earlier quoted context omitted.

> If you write crappy code and expect the compiler to fix it for you, you should maybe consider another language. This seems rather to be a case of writing good code and having the compiler break it for you.

Yes it is. You misunderstood my point, which was about the purpose of existence for such optimizations - they are meant to improve code, which presumably needs improvement. But if your code needs improvement, then why not go for a higher level language? BTW, I gave you an upvote by accident :D

They are meant to make the code run faster. If your code needs optimization, going for higher level languages is seldom a good idea.

You could try do low level optimizations in your C or assembly, but for most programs this will eventually backfire. So letting the compiler do its job is actually a good thing.

Post reply on HN