Skip to content

Instantly share code, notes, and snippets.

@rexim
Last active April 3, 2024 19:40
Show Gist options
  • Star 36 You must be signed in to star a gist
  • Fork 3 You must be signed in to fork a gist
  • Save rexim/38c176fe4669ef83db69aca9909d7b7f to your computer and use it in GitHub Desktop.
Save rexim/38c176fe4669ef83db69aca9909d7b7f to your computer and use it in GitHub Desktop.
The Most Memory Safe Buffer Overflow in Rust!
// 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!")
}
}
@TinusgragLin
Copy link

I see there are two problems, but for one of them, instead of 'buffer overflow', I think it's more like not having a length field in your LinkedList class whose get_length is supposed to be O(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.

@TinusgragLin
Copy link

My roommate coming from C thought list[index] might actually cause SegFault. So for anyone not familiar with Rust, list[index] is simply list.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();
}

@rexim
Copy link
Author

rexim commented Nov 10, 2023

I see there are two problems, but for one of them, instead of 'buffer overflow', I think it's more like not having a length field in your LinkedList class whose get_length is supposed to be O(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.

@rexim
Copy link
Author

rexim commented Nov 10, 2023

My roommate coming from C thought list[index] might actually cause SegFault. So for anyone not familiar with Rust, list[index] is simply list.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 ;)

@TinusgragLin
Copy link

I see there are two problems, but for one of them, instead of 'buffer overflow', I think it's more like not having a length field in your LinkedList class whose get_length is supposed to be O(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.

@ElectronicRU
Copy link

@TinusgragLin yes, Index and IndexMut do in fact provide &T and &mut T.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment