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!")
}
}
@rexim
Copy link
Author

rexim commented Nov 9, 2023

@psprojectC They are just lyke pointers, but they are not literally the same thing. Did the gist trigger you to the point where you feel like you need to argue over the definitions of such basic words? (:

@psprojectC
Copy link

@rexim what thing from this gist would i be triggered over? im not sure i understand, could u please explain?

@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