Out of bound read and write can lead to code execution
Closed this issue · 5 comments
Hi, I know that this project is unactive and that it's useless writing an issue but here I am.
Pretty much every instructions using an offset to write/read data are vulnerable to an out of bound vulnerabillity. For example, here is the code for the LOAD instruction:
case LOAD:
offset = code[ip++];
stack[++sp] = stack[fp+offset];
break;
The offset is a user controlled variable inside the VM and can be up to 8 bytes long (0xffffffff bytes) when the stack allocated for the VM is only 0x1f40 bytes long.
You (i don't really know if someone is reading this) should implement a security check to ensure that it's not bigger that the size of the stack.
PS: i actually wrote an article 🥸 about exploiting this specific vm in order to spawn a shell https://www.numb3rs.re/posts/popping_shells_1/
Wow, I'm honored that you researched this. I really enjoyed your blog post.
Yes, this is inactive, but you're right that it should be fixed.
The origin of this project was to prove to Terence Parr that C would be significantly faster than his Java implementation. It was faster, but obviously at the expense of memory safety, as you have demonstrated.
Hi @Numb3rsProprety - Thanks again for filing this.
I added a SAFE_MODE
switch which checks sp
and ip
before all memory access.
I was curious how that would affect runtime performance. I changed the "counter" demo in vmtest.c
to count to 2 billion.
On my machine:
SAFE_MODE
enabled = 2.58 secondsSAFE_MODE
disabled = 2.28 seconds- (2.58 - 2.28) / 2.28 = 13% slowdown for the safety
The "counter" demo executes 1,800,000,008 instructions.
SAFE_MODE
enabled = 2.58 seconds => 698 million instructions per secondSAFE_MODE
disabled = 2.28 seconds => 789 million instructions per second
Quick comment on performance of VM vs JIT techniques: I was playing with cranelift-jit-demo, and ported the "counter" demo to the demo's toy language:
fn counter() -> (r) {
n = 200000000
r = 0
while r < n {
r = r + 1
}
}
The full executable consistently runs in 0.1 seconds on my machine (same machine as above).
$ time ./target/release/toy.exe
the answer is: 200000000
real 0m0.108s
user 0m0.000s
sys 0m0.015s
That is a 20x+ speedup, with more functionality (AST parsing, Rust type safety, etc).
Hi @codyebberson, I'm really glad you enjoyed the blog post, and kudos for fixing the vulnerability! I never would have guessed that this security check could slow down the program like that—it's crazy