Skip to content

Commit c659974

Browse files
authored
Merge pull request #225 from supabase/custom-array-buffer-allocator
feat: enforce limits on array buffer allocations
2 parents 9974dbe + 7a71c84 commit c659974

File tree

4 files changed

+119
-10
lines changed

4 files changed

+119
-10
lines changed

crates/base/src/deno_runtime.rs

Lines changed: 35 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ use event_worker::js_interceptors::sb_events_js_interceptors;
2424
use event_worker::sb_user_event_worker;
2525
use sb_core::cache::CacheSetting;
2626
use sb_core::cert::ValueRootCertStoreProvider;
27+
use sb_core::external_memory::custom_allocator;
2728
use sb_core::http_start::sb_core_http;
2829
use sb_core::net::sb_core_net;
2930
use sb_core::permissions::{sb_core_permissions, Permissions};
@@ -265,19 +266,20 @@ impl DenoRuntime {
265266
sb_core_runtime::init_ops(Some(main_module_url.clone())),
266267
];
267268

269+
let mut create_params = None;
270+
if conf.is_user_worker() {
271+
let memory_limit =
272+
mib_to_bytes(conf.as_user_worker().unwrap().memory_limit_mb) as usize;
273+
create_params = Some(
274+
deno_core::v8::CreateParams::default()
275+
.heap_limits(mib_to_bytes(0) as usize, memory_limit)
276+
.array_buffer_allocator(custom_allocator(memory_limit)),
277+
)
278+
};
268279
let runtime_options = RuntimeOptions {
269280
extensions,
270281
is_main: true,
271-
create_params: {
272-
if conf.is_user_worker() {
273-
Some(deno_core::v8::CreateParams::default().heap_limits(
274-
mib_to_bytes(0) as usize,
275-
mib_to_bytes(conf.as_user_worker().unwrap().memory_limit_mb) as usize,
276-
))
277-
} else {
278-
None
279-
}
280-
},
282+
create_params,
281283
get_error_class_fn: Some(&get_error_class_name),
282284
shared_array_buffer_store: None,
283285
compiled_wasm_module_store: Default::default(),
@@ -903,4 +905,27 @@ mod test {
903905
_ => panic!("Invalid Result"),
904906
};
905907
}
908+
909+
#[tokio::test]
910+
async fn test_array_buffer_allocation_below_limit() {
911+
let user_rt = create_basic_user_runtime("./test_cases/array_buffers", 20, 1000).await;
912+
let (_tx, unix_stream_rx) = mpsc::unbounded_channel::<UnixStream>();
913+
let result = user_rt.run(unix_stream_rx).await;
914+
assert!(result.is_ok(), "expected no errors");
915+
}
916+
917+
#[tokio::test]
918+
async fn test_array_buffer_allocation_above_limit() {
919+
let user_rt = create_basic_user_runtime("./test_cases/array_buffers", 15, 1000).await;
920+
let (_tx, unix_stream_rx) = mpsc::unbounded_channel::<UnixStream>();
921+
let result = user_rt.run(unix_stream_rx).await;
922+
match result {
923+
Err(err) => {
924+
assert!(err
925+
.to_string()
926+
.contains("RangeError: Array buffer allocation failed"));
927+
}
928+
_ => panic!("Invalid Result"),
929+
};
930+
}
906931
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
const arr = new ArrayBuffer(17 * 1024 * 1024);

crates/sb_core/external_memory.rs

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
use deno_core::v8;
2+
use deno_core::v8::UniqueRef;
3+
use std::ffi::c_void;
4+
use std::sync::atomic::{AtomicUsize, Ordering};
5+
use std::sync::Arc;
6+
7+
pub struct CustomAllocator {
8+
max: usize,
9+
count: AtomicUsize,
10+
}
11+
12+
#[allow(clippy::unnecessary_cast)]
13+
unsafe extern "C" fn allocate(allocator: &CustomAllocator, n: usize) -> *mut c_void {
14+
allocator.count.fetch_add(n, Ordering::SeqCst);
15+
let count_loaded = allocator.count.load(Ordering::SeqCst);
16+
if count_loaded > allocator.max {
17+
return std::ptr::null::<*mut [u8]>() as *mut c_void;
18+
}
19+
20+
Box::into_raw(vec![0u8; n].into_boxed_slice()) as *mut [u8] as *mut c_void
21+
}
22+
23+
#[allow(clippy::unnecessary_cast)]
24+
#[allow(clippy::uninit_vec)]
25+
unsafe extern "C" fn allocate_uninitialized(allocator: &CustomAllocator, n: usize) -> *mut c_void {
26+
allocator.count.fetch_add(n, Ordering::SeqCst);
27+
let count_loaded = allocator.count.load(Ordering::SeqCst);
28+
if count_loaded > allocator.max {
29+
return std::ptr::null::<*mut [u8]>() as *mut c_void;
30+
}
31+
32+
let mut store = Vec::with_capacity(n);
33+
store.set_len(n);
34+
Box::into_raw(store.into_boxed_slice()) as *mut [u8] as *mut c_void
35+
}
36+
37+
unsafe extern "C" fn free(allocator: &CustomAllocator, data: *mut c_void, n: usize) {
38+
allocator.count.fetch_sub(n, Ordering::SeqCst);
39+
let _ = Box::from_raw(std::slice::from_raw_parts_mut(data as *mut u8, n));
40+
}
41+
42+
#[allow(clippy::unnecessary_cast)]
43+
unsafe extern "C" fn reallocate(
44+
allocator: &CustomAllocator,
45+
prev: *mut c_void,
46+
oldlen: usize,
47+
newlen: usize,
48+
) -> *mut c_void {
49+
allocator
50+
.count
51+
.fetch_add(newlen.wrapping_sub(oldlen), Ordering::SeqCst);
52+
let count_loaded = allocator.count.load(Ordering::SeqCst);
53+
if count_loaded > allocator.max {
54+
return std::ptr::null::<*mut [u8]>() as *mut c_void;
55+
}
56+
57+
let old_store = Box::from_raw(std::slice::from_raw_parts_mut(prev as *mut u8, oldlen));
58+
let mut new_store = Vec::with_capacity(newlen);
59+
let copy_len = oldlen.min(newlen);
60+
new_store.extend_from_slice(&old_store[..copy_len]);
61+
new_store.resize(newlen, 0u8);
62+
Box::into_raw(new_store.into_boxed_slice()) as *mut [u8] as *mut c_void
63+
}
64+
65+
unsafe extern "C" fn drop(allocator: *const CustomAllocator) {
66+
Arc::from_raw(allocator);
67+
}
68+
69+
pub fn custom_allocator(max: usize) -> UniqueRef<deno_core::v8::Allocator> {
70+
let vtable: &'static v8::RustAllocatorVtable<CustomAllocator> = &v8::RustAllocatorVtable {
71+
allocate,
72+
allocate_uninitialized,
73+
free,
74+
reallocate,
75+
drop,
76+
};
77+
let allocator = Arc::new(CustomAllocator {
78+
count: AtomicUsize::new(0),
79+
max,
80+
});
81+
unsafe { v8::new_rust_allocator(Arc::into_raw(allocator), vtable) }
82+
}

crates/sb_core/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ pub mod cache;
33
pub mod cert;
44
pub mod emit;
55
pub mod errors_rt;
6+
pub mod external_memory;
67
pub mod file_fetcher;
78
pub mod http_start;
89
pub mod net;

0 commit comments

Comments
 (0)