In Rust, you cannot ever read uninitialized memory (including allocated memory) without using unsafe code (as can be seen in the original code sample, the Rust buffer, unlike the C buffer, is initially zeroed out). So in safe Rust, what you are describing indeed could not happen. The unsafety would have to be explicit at the caller end: the unsafe within the allocator implementation isn't enough.
> So in safe Rust, what you are describing indeed could not happen.
Read the last paragraph. OpenSSL has a buffer reuse system via a freelist (and the non-freelist code had bitrotted), it didn't release buffers to the system's allocator after use, buffers were initialised across calls.
Otherwise while heartbleed would still have existed to a large extent, it would also have been mitigable by e.g. malloc.conf or shimming in a zeroing malloc.
An allocator like that would require unsafe code to write and could not expose a safe interface unless it couldn't be used to read uninitialized memory.
`Vec::with_capacity` would still not let you read the data even if you had a custom allocator.
You could write a custom "allocator" that created a pool of zeroed buffers initially, gave out buffers from that pool and accepted buffers back into that pool, and just didn't zero them in between. That would be perfectly "safe" as far as the language was concerned, and would allow you to reproduce the problem.
You would have to go out of your way to do so and handing out buffers that allow reading them without initialization would be a huge warning sign IMO.
If you want performance, you don't have to worry about zeroing anything, just call `.clear()` on a `Vec`.
Nobody is saying you can't reproduce Heartbleed's effect in Rust, you just have to actually design for it, be it maliciously or out of misunderstanding of the language or a library construct.
The real question is how much harder Rust makes that which is often frustratingly trivial to sneak into C code.
In C, it's impossible to hand out buffers that have to be initialized before they're read. You have to go out of your way to initialize them first.
In Rust, it's the other way around. You cannot hand out arbitrarily-sized buffers that allow for reading uninitialized memory without going out of your way to do so.
Both languages can allow for writing bad code, but in C it's trivially easy to get the bad code by accident, and in Rust, you pretty much have to do it by design.
There are mallocs that indeed do initialize memory before they're read. jemalloc, used in FreeBSD doesn't do it by default, but it's easy to set an option in /etc/malloc.conf so it does so, and ottomalloc in OpenBSD zeroes malloced memory because it uses mmap much more heavily. So yes, it is more than possible to have pre-initialized buffers in C, it's just that certain OSes use terrible memory allocation algorithms, with no way of even tuning them to be safe by default.
You would have to go out of your way to write your own custom allocator instead of just calling malloc and free, but that's what the OpenSSL folks did.
I'd be interested to see an actual implementation of such an allocator in Rust that exposed a safe interface. You could do it in the specific case of chunks of predefined sizes, and maybe even for all byte arrays, but to allow arbitrary types in the allocator I do not think you could expose a safe interface without requiring initialization.
Again: I'm quite confident you could reproduce this specific vulnerability. You would just have to go out of your way to do it and the benefits of managing a free list yourself aren't really there (jemalloc is quite good at large allocations).
I believe if it went through any of Rust's planned custom allocator system (i.e. worked through `box`) it would still not work, though.
You could certainly rewrite exactly the same system from C, of course (though as I noted elsewhere, it seems less likely that OpenSSL would want to replace jemalloc than the often-slow system allocator), but I think it would be quite difficult to write a safe implementation that worked with Rust's borrow checker. The closest thing to a custom allocator that works in Rust right now is an arena with a free list, which requires you to explicitly initialize any newly allocated elements. As another commentor noted, you would really have to go quite far out of your way to reproduce the issue.