My heart is ok, but my eyes are bleeding
blog.leafsr.com
blog.leafsr.com
Testing is utterly hard. It means that you need creativity, something that many lack or are to lazy to. Many programmers are to lazy to think about testing, I fear. That is the reason, there exist some very limited number of really good software testers in the world.
When you want to find that bug in testing (taken, you even have the super-malloc), you first have to come up with the idea, to send a falsified message length for exactly this message type. Without this idea, even the super-hero-malloc would have no chance at all to give any indication of a bug.
AND when you have such an idea, you would find the bug, even without any super-hero-malloc ... any malloc or any freelist would suffice, because the result would show you, that something bad is going on.
So the focus on the self-written allocator seems to get a little biased in this discussion.
Yes, when writing security relevant libraries, you should be really, really careful and you should take any chance you get to make it more save. And it might have in some cases increased the chance of finding the bug in production, when just using the standard malloc (with security mitigation active) -- but I really doubt, that it would have increased the chance in testing.
I also must say, that really many successful software products use their own specialized allocators -- the reason is simple: Because malloc is so unspecific, it is not the fastest beast around -- and many specialized allocators have their own security mitigation build in.
A last point: Google also wants (or wanted) to switch to OpenSSL. The reason: Speed. So to blame the OpenSSL guys that they want to make their library really fast, has a little bad taste. Everybody wants speed -- even those that claim other after something has happened. My thought for you: What would be the benefit, if OpenSSL would fail because of bad speed, nobody would use it but an other product -- and exactly that product would have the trouble we found now in OpenSSL (or worse?) .....
Computer Industry can sometimes be unforgiving. How you do it, it is wrong.
Sometimes a default generic allocator will have design properties about it that make exploiting certain bug classes difficult (e.g. WebKits RenderArena and Use-After-Free). There are exceptions of course, see Chromes Partition Alloc for one example.
Heap hardening/exploit mitigations are hard to get right. Ptmalloc has done a decent job save for 1 or 2 techniques lost to time. As a result modern exploits target application specific data (C++ object vtables, function pointers, etc) and not heap meta data.
[1] I wrote this blog post and I audit code for a living
I of course have not your expertise. I am also not totally up to date with the newest mitigation techniques. I think, it depends largely on the focus of the allocator. Most have only speed on focus and that is what is implemented.
I feel like I'm missing something here. Where are the self written allocators? This article states that the OPENSSL_Malloc is simply malloc by default.
I wrote this sentence, because there is a big discussion ungoing, with some people blaming the self written allocator of OpenSSL. See the link, I gave. My point is, that even when it is so, having the standard allocator does not guarantee that you find this bug /in testing/ -- what is claimed in that article.
Correction: When you read the article of this topic exactly, the freelist implementation (allocator) is used for recvd data. That is what started the discussion.
To clarify, are we calling the freelist implementation, which the heartbeat code does not use, an allocator?
Update: I agree with the author that it is wrong to point the blame at the freelist implementation. If every C application that manages the reuse of commonly used data structures is doing wrong, then pretty much every modern server application will have to be re-written -- for instance Apache [1].
C is fast and portable and binds to just about any language which is why OPENSSL is in such wide use. Maybe it would be better to use more 'secure' languages like Go, but if OPENSSL was written in Go, how many applications would use it? I'd say almost none.
[1] https://apr.apache.org/docs/apr/1.5/group__apr__pools.html
The code in question does not use the freelist implementation. It goes directly to OPENSSL_Malloc which is basically malloc.
What happened was the exploit allowed remote clients to read beyond the size of the buffer allocated by OPENSSL_Malloc. There just happened to be data from the freelist sitting next to it.
Even if the freelist implementation wasn't used, that wouldn't have prevented this exploit. There would just be something else sitting there in memory, but we can't predict what that would have been.
If OpenSSL had just written a malloc(3) implementation that sat in a separate shared-object--the way that, for example, jemalloc does--then switching it out would be a simple and obvious linker argument. Any developer who had used a project that relies on an alternate malloc (e.g. redis) would know that "test while linked against system malloc" is a crucial step.
Instead, you have to learn that there's a specific OpenSSL debugging flag that disables the "caching behavior" of the malloc wrapper, causing it to become a passthrough to regular malloc. None of the tests use this flag.
But where are they called from?
Update: To answer my own question the freelist is used in
ssl3_setup_read_buffer()
ssl3_release_read_buffer()
ssl3_setup_writer_buffer()
ssl3_release_write_buffer()
This is not the same as universally replacing the allocator.To be clear, this is NOT TRUE. The freelist is used for some allocations, but not in the patch in point. Here is the patch that addresses the bug: https://github.com/openssl/openssl/commit/731f431497f463f3a2...
It clearly uses OPENSSL_Malloc which is basically the equivalent of calling malloc.
https://github.com/openssl/openssl/commit/731f431497f463f3a2...
Since they used the freelist/malloc wrappers, they avoided this trap.
Plus, IMO, if you're implementing a freelist, you're implementing 80% of what a malloc is anyways. It's _mostly_ freelist handling, the only way to add memory to a process aside from mmap(2) is setbrk(2). There's no way to give memory back, so they only thing left for a malloc implementation to do is create a freelist to reissue memory previously freed.
You can say you're still backed by malloc(3) if you want, but you're only getting the setbrk(2) handling and throwing away everything else it's trying to provide you.
Edit: although that is also false if you are using a quickcheck-like testing framework.
Edit2: the article may have been claiming that, but Ted did not claim that, nor did Theo.
The point of self implemented allocators is not, that they are all better than malloc or that the implementor is more wise or that there is some magical additional stuff or even that some code is saved (the oposite is true) ...
The point is only, that malloc implements the most general case (what it must, since it is the magical "I can do it all" tool (at least in memory management)). When you go away from the general case you can find several specific cases, that can be implemented specialized for this case -- and those specific cases could be implemented very much more efficient (concerning speed, not coding!).
That is the whole point. Also there are some specific allocators that also implement some safeguides for common memory problems. But that seems to be more of the exception, when I take into account the feedback.
If would have been better if they had gone whole-hog with a malloc library and built it from the ground up for their specific purposes instead of half-assed "freelist speedhacks."
[1] https://news.ycombinator.com/item?id=7565064
[2] https://github.com/openssl/openssl/blob/a898936218bc279b5d7c...
Please don't mistake a poor attempt at humor for ego.