1use crate::bump::BumpAlloc;
8use crate::config::{POLY_ARENA_SIZE, SCRATCH_ARENA_SIZE, WITNESS_ARENA_SIZE};
9use crate::sys;
10
11use std::sync::Arc;
12
13pub struct ArenaManager {
27 witness: Arc<BumpAlloc>,
28 polynomial: Arc<BumpAlloc>,
29 scratch: Arc<BumpAlloc>,
30 witness_ptr: *mut u8,
32 poly_ptr: *mut u8,
33 scratch_ptr: *mut u8,
34 witness_size: usize,
36 poly_size: usize,
37 scratch_size: usize,
38 #[cfg(feature = "guard-pages")]
40 #[allow(dead_code)]
41 has_guard_pages: bool,
42}
43
44impl ArenaManager {
45 pub fn new() -> Result<Self, crate::platform::AllocFailed> {
51 Self::with_sizes(WITNESS_ARENA_SIZE, POLY_ARENA_SIZE, SCRATCH_ARENA_SIZE)
52 }
53
54 pub fn with_sizes(
58 witness_size: usize,
59 poly_size: usize,
60 scratch_size: usize,
61 ) -> Result<Self, crate::platform::AllocFailed> {
62 let witness_ptr = sys::alloc(witness_size)?;
63 let poly_ptr = sys::alloc(poly_size)?;
64 let scratch_ptr = sys::alloc(scratch_size)?;
65
66 Ok(Self {
67 witness: Arc::new(unsafe { BumpAlloc::new(witness_ptr, witness_size) }),
68 polynomial: Arc::new(unsafe { BumpAlloc::new(poly_ptr, poly_size) }),
69 scratch: Arc::new(unsafe { BumpAlloc::new(scratch_ptr, scratch_size) }),
70 witness_ptr,
71 poly_ptr,
72 scratch_ptr,
73 witness_size,
74 poly_size,
75 scratch_size,
76 #[cfg(feature = "guard-pages")]
77 has_guard_pages: false,
78 })
79 }
80
81 #[cfg(feature = "guard-pages")]
83 pub fn with_guard_pages(
84 witness_size: usize,
85 poly_size: usize,
86 scratch_size: usize,
87 ) -> Result<Self, crate::platform::AllocFailed> {
88 let witness_guarded = sys::alloc_with_guards(witness_size)?;
89 let poly_guarded = sys::alloc_with_guards(poly_size)?;
90 let scratch_guarded = sys::alloc_with_guards(scratch_size)?;
91
92 Ok(Self {
93 witness: Arc::new(unsafe { BumpAlloc::new(witness_guarded.ptr, witness_size) }),
94 polynomial: Arc::new(unsafe { BumpAlloc::new(poly_guarded.ptr, poly_size) }),
95 scratch: Arc::new(unsafe { BumpAlloc::new(scratch_guarded.ptr, scratch_size) }),
96 witness_ptr: witness_guarded.base_ptr,
97 poly_ptr: poly_guarded.base_ptr,
98 scratch_ptr: scratch_guarded.base_ptr,
99 witness_size: witness_guarded.total_size,
100 poly_size: poly_guarded.total_size,
101 scratch_size: scratch_guarded.total_size,
102 has_guard_pages: true,
103 })
104 }
105
106 #[cfg(feature = "mlock")]
108 pub fn lock_witness(&self) -> Result<(), crate::platform::AllocFailed> {
109 sys::mlock(self.witness.base_ptr(), self.witness.capacity())
110 }
111
112 #[cfg(feature = "mlock")]
114 pub fn unlock_witness(&self) -> Result<(), crate::platform::AllocFailed> {
115 sys::munlock(self.witness.base_ptr(), self.witness.capacity())
116 }
117
118 #[inline]
120 pub fn witness(&self) -> Arc<BumpAlloc> {
121 self.witness.clone()
122 }
123
124 #[inline]
126 pub fn polynomial(&self) -> Arc<BumpAlloc> {
127 self.polynomial.clone()
128 }
129
130 #[inline]
132 pub fn scratch(&self) -> Arc<BumpAlloc> {
133 self.scratch.clone()
134 }
135
136 pub unsafe fn reset_all(&self) {
147 self.witness.secure_reset();
148 self.polynomial.reset();
149 self.scratch.reset();
150 }
151
152 pub fn stats(&self) -> ArenaStats {
154 ArenaStats {
155 witness_used: self.witness.used(),
156 witness_capacity: self.witness.capacity(),
157 polynomial_used: self.polynomial.used(),
158 polynomial_capacity: self.polynomial.capacity(),
159 scratch_used: self.scratch.used(),
160 scratch_capacity: self.scratch.capacity(),
161 #[cfg(feature = "fallback")]
162 witness_fallback_bytes: self.witness.fallback_bytes(),
163 #[cfg(feature = "fallback")]
164 polynomial_fallback_bytes: self.polynomial.fallback_bytes(),
165 #[cfg(feature = "fallback")]
166 scratch_fallback_bytes: self.scratch.fallback_bytes(),
167 }
168 }
169
170 pub fn is_sole_owner(&self) -> bool {
174 Arc::strong_count(&self.witness) == 1
175 && Arc::strong_count(&self.polynomial) == 1
176 && Arc::strong_count(&self.scratch) == 1
177 }
178
179 pub fn ref_counts(&self) -> (usize, usize, usize) {
181 (
182 Arc::strong_count(&self.witness),
183 Arc::strong_count(&self.polynomial),
184 Arc::strong_count(&self.scratch),
185 )
186 }
187
188 #[inline]
193 pub fn contains_address(&self, addr: usize) -> bool {
194 let witness_start = self.witness_ptr as usize;
196 let witness_end = witness_start + self.witness_size;
197 if addr >= witness_start && addr < witness_end {
198 return true;
199 }
200
201 let poly_start = self.poly_ptr as usize;
203 let poly_end = poly_start + self.poly_size;
204 if addr >= poly_start && addr < poly_end {
205 return true;
206 }
207
208 let scratch_start = self.scratch_ptr as usize;
210 let scratch_end = scratch_start + self.scratch_size;
211 if addr >= scratch_start && addr < scratch_end {
212 return true;
213 }
214
215 false
216 }
217}
218
219#[derive(Debug, Clone, Copy)]
221pub struct ArenaStats {
222 pub witness_used: usize,
223 pub witness_capacity: usize,
224 pub polynomial_used: usize,
225 pub polynomial_capacity: usize,
226 pub scratch_used: usize,
227 pub scratch_capacity: usize,
228 #[cfg(feature = "fallback")]
229 pub witness_fallback_bytes: usize,
230 #[cfg(feature = "fallback")]
231 pub polynomial_fallback_bytes: usize,
232 #[cfg(feature = "fallback")]
233 pub scratch_fallback_bytes: usize,
234}
235
236impl ArenaStats {
237 pub fn total_used(&self) -> usize {
239 self.witness_used + self.polynomial_used + self.scratch_used
240 }
241
242 pub fn total_capacity(&self) -> usize {
244 self.witness_capacity + self.polynomial_capacity + self.scratch_capacity
245 }
246
247 #[cfg(feature = "fallback")]
249 pub fn total_fallback_bytes(&self) -> usize {
250 self.witness_fallback_bytes + self.polynomial_fallback_bytes + self.scratch_fallback_bytes
251 }
252}
253
254impl Drop for ArenaManager {
255 fn drop(&mut self) {
256 let (witness_refs, poly_refs, scratch_refs) = self.ref_counts();
260
261 if witness_refs > 1 || poly_refs > 1 || scratch_refs > 1 {
262 eprintln!(
265 "[nalloc] WARNING: ArenaManager dropped with outstanding references! \
266 witness={}, polynomial={}, scratch={}. Memory will be leaked to prevent \
267 use-after-free. This is a bug in your code - ensure all arena handles \
268 are dropped before the ArenaManager.",
269 witness_refs - 1,
270 poly_refs - 1,
271 scratch_refs - 1
272 );
273
274 return;
276 }
277
278 unsafe {
281 self.witness.secure_reset();
282 }
283
284 if let Err(e) = sys::dealloc(self.witness_ptr, self.witness_size) {
286 eprintln!(
287 "[nalloc] Warning: Failed to deallocate witness arena: {}",
288 e
289 );
290 }
291 if let Err(e) = sys::dealloc(self.poly_ptr, self.poly_size) {
292 eprintln!(
293 "[nalloc] Warning: Failed to deallocate polynomial arena: {}",
294 e
295 );
296 }
297 if let Err(e) = sys::dealloc(self.scratch_ptr, self.scratch_size) {
298 eprintln!(
299 "[nalloc] Warning: Failed to deallocate scratch arena: {}",
300 e
301 );
302 }
303 }
304}
305
306unsafe impl Send for ArenaManager {}
308unsafe impl Sync for ArenaManager {}
309
310#[cfg(test)]
311mod tests {
312 use super::*;
313
314 #[test]
315 fn test_arena_manager_creation() {
316 let manager = ArenaManager::with_sizes(1024 * 1024, 2 * 1024 * 1024, 1024 * 1024).unwrap();
318
319 let stats = manager.stats();
320 assert_eq!(stats.witness_capacity, 1024 * 1024);
321 assert_eq!(stats.polynomial_capacity, 2 * 1024 * 1024);
322 assert_eq!(stats.scratch_capacity, 1024 * 1024);
323 assert_eq!(stats.total_used(), 0);
324 }
325
326 #[test]
327 fn test_arena_stats() {
328 let manager = ArenaManager::with_sizes(1024 * 1024, 2 * 1024 * 1024, 1024 * 1024).unwrap();
329
330 let _ = manager.witness().alloc(1024, 8);
332 let _ = manager.polynomial().alloc(2048, 64);
333 let _ = manager.scratch().alloc(512, 8);
334
335 let stats = manager.stats();
336 assert!(stats.witness_used >= 1024);
337 assert!(stats.polynomial_used >= 2048);
338 assert!(stats.scratch_used >= 512);
339 }
340
341 #[test]
342 fn test_drop_deallocates() {
343 {
345 let _manager = ArenaManager::with_sizes(1024 * 1024, 1024 * 1024, 1024 * 1024).unwrap();
346 }
348 }
350
351 #[test]
352 fn test_sole_owner_check() {
353 let manager = ArenaManager::with_sizes(1024 * 1024, 1024 * 1024, 1024 * 1024).unwrap();
354
355 assert!(manager.is_sole_owner());
357
358 let _witness_handle = manager.witness();
360
361 assert!(!manager.is_sole_owner());
363
364 drop(_witness_handle);
366
367 assert!(manager.is_sole_owner());
369 }
370
371 #[test]
372 fn test_ref_counts() {
373 let manager = ArenaManager::with_sizes(1024 * 1024, 1024 * 1024, 1024 * 1024).unwrap();
374
375 let (w, p, s) = manager.ref_counts();
376 assert_eq!((w, p, s), (1, 1, 1));
377
378 let _w1 = manager.witness();
379 let _w2 = manager.witness();
380 let _p1 = manager.polynomial();
381
382 let (w, p, s) = manager.ref_counts();
383 assert_eq!((w, p, s), (3, 2, 1));
384 }
385
386 #[test]
387 #[cfg(all(target_os = "linux", feature = "guard-pages"))]
388 fn test_guard_pages_creation() {
389 let manager =
390 ArenaManager::with_guard_pages(1024 * 1024, 1024 * 1024, 1024 * 1024).unwrap();
391
392 let ptr = manager.witness().alloc(1024, 8);
394 assert!(!ptr.is_null());
395
396 unsafe {
398 std::ptr::write_bytes(ptr, 0xAB, 1024);
399 }
400 }
401
402 #[test]
403 #[cfg(feature = "mlock")]
404 fn test_mlock() {
405 let manager = ArenaManager::with_sizes(1024 * 1024, 1024 * 1024, 1024 * 1024).unwrap();
406
407 let _ = manager.lock_witness();
410 let _ = manager.unlock_witness();
411 }
412}