diff --git a/crates/napi/src/bindgen_runtime/js_values/buffer.rs b/crates/napi/src/bindgen_runtime/js_values/buffer.rs index 4c3fde3c..0565aaad 100644 --- a/crates/napi/src/bindgen_runtime/js_values/buffer.rs +++ b/crates/napi/src/bindgen_runtime/js_values/buffer.rs @@ -1,10 +1,19 @@ +#[cfg(debug_assertions)] +use std::collections::HashSet; use std::mem; use std::ops::{Deref, DerefMut}; use std::ptr; use std::slice; +#[cfg(debug_assertions)] +use std::sync::Mutex; use crate::{bindgen_prelude::*, check_status, sys, Result, ValueType}; +#[cfg(debug_assertions)] +thread_local! { + pub (crate) static BUFFER_DATA: Mutex> = Default::default(); +} + /// Zero copy u8 vector shared between rust and napi. /// Auto reference the raw JavaScript value, and release it when dropped. /// So it is safe to use it in `async fn`, the `&[u8]` under the hood will not be dropped until the `drop` called. @@ -53,6 +62,16 @@ impl Buffer { impl From> for Buffer { fn from(mut data: Vec) -> Self { let inner_ptr = data.as_mut_ptr(); + #[cfg(debug_assertions)] + { + let is_existed = BUFFER_DATA.with(|buffer_data| { + let buffer = buffer_data.lock().expect("Unlock buffer data failed"); + buffer.contains(&inner_ptr) + }); + if is_existed { + panic!("Share the same data between different buffers is not allowed, see: https://github.com/nodejs/node/issues/32463#issuecomment-631974747"); + } + } let len = data.len(); let capacity = data.capacity(); mem::forget(data); diff --git a/crates/napi/src/bindgen_runtime/mod.rs b/crates/napi/src/bindgen_runtime/mod.rs index 7b116eb6..13b69bf7 100644 --- a/crates/napi/src/bindgen_runtime/mod.rs +++ b/crates/napi/src/bindgen_runtime/mod.rs @@ -52,6 +52,13 @@ pub unsafe extern "C" fn drop_buffer( finalize_hint: *mut c_void, ) { let length_ptr = finalize_hint as *mut (usize, usize); + #[cfg(debug_assertions)] + { + js_values::BUFFER_DATA.with(|buffer_data| { + let mut buffer = buffer_data.lock().expect("Unlock Buffer data failed"); + buffer.remove(&(finalize_data as *mut u8)); + }); + } unsafe { let (length, cap) = *Box::from_raw(length_ptr); mem::drop(Vec::from_raw_parts(finalize_data as *mut u8, length, cap));