the most horrifying part of this blog entry:
“Unless libssl was compiled with the OPENSSL_NO_BUF_FREELISTS option (it wasn’t), libssl will maintain its own freelist, rendering any possible mitigation strategy performed by malloc useless. Yes, OpenSSL includes its own builtin exploit mitigation mitigation.”
it terrifies me that we rely so heavily on openssl.
An application doing its own memory management on top of malloc() does not seem horrifying to me. (Not that I would argue that OpenSSL is non-horrifying in general.)
the question is why it feels it’s necessary. i don’t disagree fundamentally, but if the logic in openssl is that malloc was slow on some ancient version of VMS or something, then they’ve hobbled effort at improving security with no gain in value.
(not to mention that @tedu found that the alternate codepath is clearly broken and untested. sigh.)
malloc has almost universally been slower than application-specific allocators for decades now on nearly every platform. Usually the difference isn’t big enough to be worth it, but often it is. Some current mallocs, such as my friend Jason’s jemalloc, are so fast that only very rare applications benefit from application-specific allocators, but they still aren’t in wide use. SSL implementations have been struggling to improve efficiency ever since SSL was introduced (to the point that there are multiple brands of hardware accelerators for SSL on the market) because if SSL is slow then people will switch to unencrypted HTTP.
I don’t know of anybody who’s using a modified malloc in production to improve security, although people have been using modified compilers that slow down function call and return in production for ten or fifteen years in order to improve security. @tedu’s writeup seems to show that using a modified malloc would not have prevented Heartbleed.
So, although I’m not very happy with OpenSSL, it seems to me that they made the right tradeoff in this case.
(Disclaimer: although I don’t have a personal connection with any of the members of the OpenSSL team, I did dance tango once with Kurt Roeckx, who is now no longer responsible for history’s biggest security bug, because Heartbleed is now worse.)
Theo de Raadt disagrees with me. OpenBSD uses a modified malloc in production to improve security, and has for years. I should have known that.
Thanks for the writeup, @tedu
I may be misunderstanding this but I do not see how malloc protection would prevent this attack as we are reading past the end of the input buffer, which is space that may have not yet been re-malloced.
The buffer has been malloced and therefore zero-ed, but anything after it is still in an unknown state.
If that space were previously freed then zero-ing on free would help.
The input buffer appears to be fixed size in practice. If I had been reading past the buffer, the process should have crashed, but it never did in many trials of various lengths. That’s only proof that I was unable to crash the process, but makes me think the read is not technically going past allocated memory. It’s only going past the most recently written part of that memory.
I was under the impression that heartbleed was a buffer over-read https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2014-0160
However if it is not a buffer over-read then malloc J (write garbage on malloc/free) would only protect you if the buffer was fixed size (at least 64K, to prevent an overrun) and was malloced and freed between every message and heartbeat.
Reading over the end of the input buffer would not necessarily cause a program to ‘crash’ (sigsegv), for example the memory after the buffer could still be owned by the same process performing the write (c buffers are not bounds checked).
Your example code shows packet = malloc(amt); … memcpy(buffer, packet->payload, packet->len); which is copying packet->len bytes from your amt sized buffer, thus when packet->len is > amt you are performing a read of packet->len - amt bytes after the end of the buffer.
packet = malloc(amt);
memcpy(buffer, packet->payload, packet->len);
EDIT: I’m not trying to argue that malloc J protection is useless, just that I am not sure it is a silver bullet for the heartbleed vulnerability.
It is an overrun of the initialized part of the buffer (and maybe some more beyond that.) I couldn’t get guard pages to trigger, but I’m not an evil genius.
On openbsd, guard pages would have been sufficient to prevent reading “other” memory, and junking should have been sufficient to prevent “rereading” old memory. (This all depends on having some optional featured enabled.)