Skip to content

Commit 0f2c7c4

Browse files
fix(vm): Guard builtins against non-heap args, oversized allocations and fuzz.yml script initialization.
2 parents 2a983f2 + fd6296d commit 0f2c7c4

6 files changed

Lines changed: 19 additions & 8 deletions

File tree

.github/workflows/fuzzer.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ jobs:
3939
- name: Seeds and instrumented build
4040
working-directory: compiler/fuzz-afl
4141
run: |
42-
./seeds.sh
42+
bash ./seeds.sh
4343
cargo afl build
4444
4545
- name: Fuzz on all cores

compiler/src/modules/vm/builtins/bytes_helpers.rs

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -35,12 +35,12 @@ impl<'a> VM<'a> {
3535
pub fn call_int_from_bytes(&mut self) -> Result<(), VmErr> {
3636
let order = self.pop()?;
3737
let v = self.pop()?;
38-
let buf = match self.heap.get(v) {
39-
HeapObj::Bytes(b) => b.clone(),
38+
let buf = match self.heap.try_get(v) {
39+
Some(HeapObj::Bytes(b)) => b.clone(),
4040
_ => return Err(cold_type("int_from_bytes() first arg must be bytes")),
4141
};
42-
let order_s = match self.heap.get(order) {
43-
HeapObj::Str(s) => s.clone(),
42+
let order_s = match self.heap.try_get(order) {
43+
Some(HeapObj::Str(s)) => s.clone(),
4444
_ => return Err(cold_type("int_from_bytes() byteorder must be 'big' or 'little'")),
4545
};
4646
if buf.len() > 8 { return Err(cold_overflow()); }
@@ -71,8 +71,8 @@ impl<'a> VM<'a> {
7171
let length = length.as_int() as usize;
7272
if length > 8 { return Err(cold_value("int_to_bytes() length must be <= 8")); }
7373
if n_i128 < 0 { return Err(cold_value("int_to_bytes() requires a non-negative int")); }
74-
let order_s = match self.heap.get(order) {
75-
HeapObj::Str(s) => s.clone(),
74+
let order_s = match self.heap.try_get(order) {
75+
Some(HeapObj::Str(s)) => s.clone(),
7676
_ => return Err(cold_type("int_to_bytes() byteorder must be 'big' or 'little'")),
7777
};
7878
let big = match order_s.as_str() {

compiler/src/modules/vm/builtins/container.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -149,6 +149,8 @@ impl<'a> VM<'a> {
149149
if a.is_int() {
150150
let n = a.as_int();
151151
if n < 0 { return Err(cold_value("negative count")); }
152+
// Length is user-controlled; cap it against the heap budget so a huge count errors instead of aborting in the allocator.
153+
if n as usize > self.heap.limit() { return Err(cold_heap()); }
152154
alloc::vec![0u8; n as usize]
153155
} else if a.is_heap() {
154156
if let HeapObj::Bytes(b) = self.heap.get(a) {

compiler/src/modules/vm/handlers/builtin_methods/prelude.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,5 +8,5 @@ pub(super) use super::super::methods_helpers::{
88
list_clone, list_mut, dict_entries, dict_mut, set_clone, set_mut,
99
iter_to_vec, capitalize_first, title_case,
1010
};
11-
pub(super) use crate::modules::vm::types::{cold_type, cold_value, eq_vals_with_heap};
11+
pub(super) use crate::modules::vm::types::{cold_type, cold_value, cold_heap, eq_vals_with_heap};
1212
pub(super) use alloc::{string::{String, ToString}, vec, vec::Vec};

compiler/src/modules/vm/handlers/builtin_methods/string.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -236,6 +236,8 @@ pub fn center(vm: &mut VM, recv: Val, pos: &[Val]) -> Result<(), VmErr> {
236236
let s = recv_str(vm, recv)?;
237237
if !pos[0].is_int() { return Err(cold_type("center() width must be an integer")); }
238238
let width = pos[0].as_int().max(0) as usize;
239+
// User-controlled width drives the output size; cap it so a huge value errors instead of aborting in the allocator.
240+
if width > vm.heap.limit() { return Err(cold_heap()); }
239241
let fill = if pos.len() > 1 {
240242
val_to_str(vm, pos[1])?.chars().next().unwrap_or(' ')
241243
} else { ' ' };
@@ -252,6 +254,8 @@ pub fn zfill(vm: &mut VM, recv: Val, pos: &[Val]) -> Result<(), VmErr> {
252254
if !pos[0].is_int() { return Err(cold_type("zfill() requires an integer argument")); }
253255
let s = recv_str(vm, recv)?;
254256
let width = pos[0].as_int().max(0) as usize;
257+
// User-controlled width drives the output size; cap it so a huge value errors instead of aborting in the allocator.
258+
if width > vm.heap.limit() { return Err(cold_heap()); }
255259
let nchars = s.chars().count();
256260
let out = if nchars >= width {
257261
s

compiler/tests/cases/vm.json

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -600,6 +600,8 @@
600600
{"src": "print(int_to_bytes(258, 2, 'big'))", "output": ["b'\\x01\\x02'"]},
601601
{"src": "print(int_to_bytes(513, 2, 'little'))", "output": ["b'\\x01\\x02'"]},
602602
{"src": "n = 0xCAFEBABE\nprint(int_from_bytes(int_to_bytes(n, 4, 'big'), 'big'))", "output": ["3405691582"]},
603+
{"src": "print(int_from_bytes(5, 'big'))", "output": [], "error": "int_from_bytes() first arg must be bytes"},
604+
{"src": "print(int_to_bytes(508, 4, -1))", "output": [], "error": "int_to_bytes() byteorder must be 'big' or 'little'"},
603605
{"src": "print(frozenset())", "output": ["frozenset()"]},
604606
{"src": "print(sorted(frozenset([1,2,3,2,1])))", "output": ["[1, 2, 3]"]},
605607
{"src": "fs = frozenset([1,2,3])\nprint(2 in fs)\nprint(5 in fs)", "output": ["True", "False"]},
@@ -1803,6 +1805,9 @@
18031805
{"src": "print('a' * 20000000)", "output": [], "error": "MemoryError"},
18041806
{"src": "print([0] * 20000000)", "output": [], "error": "MemoryError"},
18051807
{"src": "print(list(range(20000000)))", "output": [], "error": "MemoryError"},
1808+
{"src": "print(bytes(999999999995))", "output": [], "error": "MemoryError"},
1809+
{"src": "print('a'.center(999999999995))", "output": [], "error": "MemoryError"},
1810+
{"src": "print('a'.zfill(999999999995))", "output": [], "error": "MemoryError"},
18061811
{"src": "print(format(5, '20000001d'))", "output": [], "error": "format width exceeds limit"},
18071812
{"src": "print(format(5, '.20000001f'))", "output": [], "error": "format precision exceeds limit"},
18081813
{"src": "def f():\n sleep(0)\n return f()\nprint(f())", "output": [], "error": "RecursionError"},

0 commit comments

Comments
 (0)