-
-
Save rexim/38c176fe4669ef83db69aca9909d7b7f to your computer and use it in GitHub Desktop.
| // The Most Memory Safe Buffer Overflow in Rust! | |
| // | |
| // Consider all the code below under Public Domain | |
| // | |
| // How to build: | |
| // $ rustc main.rs | |
| // | |
| // Wrong password: | |
| // $ printf "hello\n" | ./main | |
| // | |
| // Right password: | |
| // $ printf "password\n" | ./main | |
| // | |
| // Universal password: | |
| // $ printf "aaaaaaaaaaaaaa\0aaaaaaaaaaaaaa\0" | ./main | |
| // | |
| // Support Rust Recovery Foundation: https://rustrecoveryfoundation.neocities.org/ | |
| use std::io::{BufRead, Write}; | |
| const BUF_CAP: usize = 15; | |
| type Ptr = usize; | |
| fn alloc_buffer(mem: &mut Vec::<char>, size: usize) -> Ptr { | |
| let result = mem.len(); | |
| for _ in 0..size { | |
| mem.push(' ') | |
| } | |
| result | |
| } | |
| fn alloc_str(mem: &mut Vec<char>, s: &str) -> Ptr { | |
| let result = mem.len(); | |
| for c in s.chars() { | |
| mem.push(c) | |
| } | |
| mem.push('\0'); | |
| result | |
| } | |
| fn read_line_into_buffer(input: &mut impl BufRead, mem: &mut Vec<char>, buf: Ptr) { | |
| let mut s = String::new(); | |
| let n = input.read_line(&mut s).unwrap(); | |
| for (i, c) in s.chars().enumerate() { | |
| mem[buf + i] = c; | |
| } | |
| if mem[buf + n - 1] == '\n' { | |
| mem[buf + n - 1] = '\0' | |
| } else { | |
| mem[buf + n] = '\0'; | |
| } | |
| } | |
| fn streq(mem: &Vec<char>, mut s1: Ptr, mut s2: Ptr) -> bool { | |
| while mem[s1] != '\0' && mem[s2] != '\0' { | |
| if mem[s1] != mem[s2] { | |
| return false; | |
| } | |
| s1 += 1; | |
| s2 += 1; | |
| } | |
| mem[s1] == '\0' && mem[s2] == '\0' | |
| } | |
| fn main() { | |
| let mut mem = Vec::<char>::new(); | |
| let buffer = alloc_buffer(&mut mem, BUF_CAP); | |
| let password = alloc_str(&mut mem, "password"); | |
| alloc_buffer(&mut mem, BUF_CAP); | |
| print!("Password: "); | |
| std::io::stdout().flush().unwrap(); | |
| read_line_into_buffer(&mut std::io::stdin().lock(), &mut mem, buffer); | |
| if streq(&mem, buffer, password) { | |
| println!("Access Granted!") | |
| } else { | |
| println!("Access Denied!") | |
| } | |
| } |
My roommate coming from C thought
list[index]might actually causeSegFault. So for anyone not familiar with Rust,list[index]is simplylist.get(index).unwrap(), which basically is:if index < list.length() { return list.get_without_check(index); // the real get_without_check is actually marked unsafe. } else { WTFYouAreDoingYourIndexIsCriminalYouAssHole(); }
Here is the thing about C... Depending on the situation list[index] may NOT segfault even when index >= list.length(), which is way worse ;)
I see there are two problems, but for one of them, instead of 'buffer overflow', I think it's more like not having a
lengthfield in yourLinkedListclass whoseget_lengthis supposed to beO(1). Here is my summary of what might go wrong in this example:let n = // some number not known in compile time; // We define `buf_a` to be the first 3 bytes of `v`. Other bytes are considered something else. let v: Vec<u8> = vec![0, 0, 0, 20, 20, 20]; // No! This is not actually an element of `buf_a`, we just accessed something outside of `buf_a`! let element_of_buf_a = v[3]; // No! We might get a index out of bound error! let element_of_v = v[n]; // But the compiler is fine with this code and does not even warn us!@rexim Do you think this minifies this example? I am new to Rust, so I might be wrong.
Indices in Rust specifically don't have a concept of ownership attached to them with all the corresponding consequences which you should always keep in mind.
Hmm... I am not sure what you mean, are you saying that you can not move an element out of Vec if it is not Copy and semantically vec[index] actually mean copy the element out? Or that the traits backing the [] syntax(i.e. Index IndexMut) only gives &T and &mut T but not T? Or something about index type itself (usize, Range, ...)?
I will be very grateful if you can elaborate since I am still learning Rust.
@TinusgragLin yes, Index and IndexMut do in fact provide &T and &mut T.
Indices in Rust specifically don't have a concept of ownership attached to them with all the corresponding consequences which you should always keep in mind.