Couldn't you just compile these functions with optimization turned off, in a separate binary or something?
How to zero a buffer
81–90 of 216 posts
Re: How to zero a buffer
#82Earlier quoted context omitted.
I'm gonna go ahead and say it: Perhaps in retrospect C is an inappropriate choice of language for these kinds of applications. This "Performance at all costs, including safety and predictability" thing may be appropriate in video games, but for security-critical applications that philosophy is downright negligent.
I'm not aware of any language that would be better. Most languages don't even let you touch memory to try to zero it.
Just like how a language that doesn't do array bounds checking and permits pointer math (read: a language where buffer overflows are a damn feature) isn't appropriate for security critical tasks.
Just like how a language that must be actively fought using clever hacks in order to prevent it from undermining your attempts to guard against common exploit vectors isn't appropriate for security critical tasks.
Re: How to zero a buffer
#83Earlier quoted context omitted.
I'm gonna go ahead and say it: Perhaps in retrospect C is an inappropriate choice of language for these kinds of applications. This "Performance at all costs, including safety and predictability" thing may be appropriate in video games, but for security-critical applications that philosophy is downright negligent.
I'm not aware of any language that would be better. Most languages don't even let you touch memory to try to zero it.
Re: How to zero a buffer
#84 /* implemented in another translation unit */
void zero_for_sure(void *data, size_t size);
void func(void)
{
char securedata[42];
/* ... */
zero_for_sure(securedata, sizeof securedata);
}
The key here is that our zero_for_sure is an external function in a separately translated file. In the absence of a stunningly advanced global optimization that peeks into other previously compiled units, the compiler has no idea what zero_for_sure does, and so it has to earnestly pass it the given piece of memory.In turn, zero_for_sure is just this:
void zero_for_sure(void *ptr, size_t size)
{
memset(ptr, 0, size);
}
The compiler has no idea where ptr might come from since this is an external function, and so it cannot optimize away the memset.Only if the compiler could consider the whole program together could it still optimize this.
In fact, you don't even need this function, just a dummy external function:
void zero_for_sure(void *ptr, size_t size)
{
char securedata[42];
/* ... */
memset(securedata, 0, sizeof securedata);
commit(securedata);
}
Of course, commit is a noop which just returns. But the compiler doesn't know that because commit is in another translation unit.The only optimization card that the compiler could pull here is since securedata is going away (so that it is illegal for commit to stash a pointer to it), it's okay to call commit with a pointer to some other block which contains zeros, and not actually securedata.
With any trick like this, you should inspect the object code to make sure it's doing what you think it's doing.
Oh, and sizeof doesn't require parentheses when the operand is an expression; they are required when a type name is used as an operand.
Re: How to zero a buffer
#85The article the completely obvious: /* implemented in another translation unit */ void zero_for_sure(void *data, size_t size); void func(void) { char securedata[42]; /* ... */ zero_for_sure(securedata, sizeof securedata); } The key here is that our zero_for_sure is an external function in a separately translated file. In the absence of a stunningly advanced global optimization that peeks into other previously compile…
You'll be surprised, but this stuff exists since late last century, known as Link-Time Code Generation (LTCG):
Re: How to zero a buffer
#86The article the completely obvious: /* implemented in another translation unit */ void zero_for_sure(void *data, size_t size); void func(void) { char securedata[42]; /* ... */ zero_for_sure(securedata, sizeof securedata); } The key here is that our zero_for_sure is an external function in a separately translated file. In the absence of a stunningly advanced global optimization that peeks into other previously compile…
Re: How to zero a buffer
#87The article the completely obvious: /* implemented in another translation unit */ void zero_for_sure(void *data, size_t size); void func(void) { char securedata[42]; /* ... */ zero_for_sure(securedata, sizeof securedata); } The key here is that our zero_for_sure is an external function in a separately translated file. In the absence of a stunningly advanced global optimization that peeks into other previously compile…
Re: How to zero a buffer
#88Re: How to zero a buffer
#89Nice teaser at the end there. Does it have something to do with the fact that the OS may have paged the memory containing the sensitive data to disk?
I figured the article would be about how you have to write random data to the buffer to truly "zero" it, otherwise the ghost of the data can still be read using some trick.
Re: How to zero a buffer
#90Earlier quoted context omitted.
The first comment there (by Anonymous) claims that the final technique can also be optimized: (memset_ptr)(p, 0, len); > can be replaced by: if (memset_ptr == memset) { memset(p, 0, len); } else { memset_ptr(p, 0, len); } > Which in turn can be optimized using the other tricks noticed above into: if (memset_ptr != memset) { memset_ptr(p, 0, len); } I'm no expert, but this seems like a believable defeat of the techniq…
That's not quite right since it's now reading memset_ptr twice, but the concept does seem to be right -- the volatile pointer must be read but the standard doesn't require that the function is invoked.
Of course, using `dlsym()` isn't exactly portable...