5 ms·
Wasn't the heartbleed issue that you could trick it into reading past the memory it had allocated? That's different to explicitly reusing memory you've allocate
by ajanuary 12y ago
Wasn't the heartbleed issue that you could trick it into reading past the memory it had allocated? That's different to explicitly reusing memory you've allocated without clearing it in between.
The original claim was that rust would prevent the class of errors that caused Heartbleed. No one claimed rust would prevent you from writing a program with a different bug that just happens to exhibit similar behavior.
Buffer overruns are tricker to spot than explicitly reusing a buffer.
[Edit]
An example of an actual buffer overrun, with no changes to pingback.
C:
$:/tmp # cat bleed.c
#include <fcntl.h>
#include <unistd.h>
#include <assert.h>
void
pingback(char *path, char *outpath, unsigned char *buffer)
{
int fd;
if ((fd = open(path, O_RDONLY)) == -1)
assert(!"open");
if (read(fd, buffer, 256) < 1)
assert(!"read");
close(fd);
size_t len = buffer[0];
if ((fd = creat(outpath, 0644)) == -1)
assert(!"creat");
if (write(fd, buffer, len) != len)
assert(!"write");
close(fd);
}
int
main(int argc, char **argv)
{
unsigned char buffer2[10];
unsigned char buffer1[10];
pingback("yourping", "yourecho", buffer1);
pingback("myping", "myecho", buffer2);
}
$:/tmp # gcc bleed.c && ./a.out && cat yourecho myecho
#i have many secrets. this is one.
#i know your
one.
Æ+x-core:/tmp #
Rust:
C:\Users\ajanuary\Desktop>cat hearbleed.rs
use std::old_io::File;
fn pingback(path : Path, outpath : Path, buffer : &mut[u8]) {
let mut fd = File::open(&path);
match fd.read(buffer) {
Err(what) => panic!("say {}", what),
Ok(x) => if x < 1 { return; }
}
let len = buffer[0] as usize;
let mut outfd = File::create(&outpath);
match outfd.write_all(&buffer[0 .. len]) {
Err(what) => panic!("say {}", what),
Ok(_) => ()
}
}
fn main() {
let buffer2 = &mut[0u8; 10];
let buffer1 = &mut[0u8; 10];
pingback(Path::new("yourping"), Path::new("yourecho"), buffer1);
pingback(Path::new("myping"), Path::new("myecho"), buffer2);
}
C:\Users\ajanuary\Desktop>hearbleed.exe
thread '<main>' panicked at 'assertion failed: index.end <= self.len()', C:\bot\slave\nightly-dist-rustc-win-64\build\src\libcore\slice.rs:524
- ksherlock 12y agoOpenssl uses their own memory allocator (since malloc is slow on big-endian x86 xenix or something) so they _do_ reuse memory without clearing it in between. Had they used the system malloc, it wouldn't have been vulnerable (on OpenBSD and probably elsewhere). Can rust prevent you from implementing your own (buggy) memory allocator?
- steveklabnik 12y agoYes, because we don't support custom allocators yet ;)
- Jweb_Guru 12y agoNo (well, there is still work to be done on custom allocators, but it's planned). But given that Rust uses jemalloc by default, not the system allocator, it is substantially less likely that you will want to replace it with your own. You certainly can't do it without copious amounts of unsafe code (which is really rather unfortunate).
- eddyb 12y agoYou would have to use unsafe code and isolate that as much as you can, behind a safe interface. Memory reuse without clearing would be considered safe, though, as none of the other abstractions actually let you read that memory (doing so would easily be UB anyways in LLVM).
- IshKebab 12y agoStandard `malloc` doesn't zero memory either. The problem was not caused by their custom allocator. It was exacerbated by it because it allocated everything really close together.
- masklinn 12y agoWell it's not really that it allocated everything really close together in memory-space, but since it would try to reuse existing memory from the freelist before asking the OS for more memory, you were more or less certain to get memory openssl had previously used as scratch space to, say, store a private key for temporary operations.