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) {
270 let (witness_refs, poly_refs, scratch_refs) = self.ref_counts();
274
275 if witness_refs > 1 || poly_refs > 1 || scratch_refs > 1 {
276 eprintln!(
279 "[nalloc] WARNING: ArenaManager dropped with outstanding references! \
280 witness={}, polynomial={}, scratch={}. Memory will be leaked to prevent \
281 use-after-free. This is a bug in your code - ensure all arena handles \
282 are dropped before the ArenaManager.",
283 witness_refs - 1,
284 poly_refs - 1,
285 scratch_refs - 1
286 );
287
288 return;
290 }
291
292 unsafe {
295 self.witness.secure_reset();
296 }
297
298 if let Err(e) = sys::dealloc(self.witness_ptr, self.witness_size) {
300 eprintln!(
301 "[nalloc] Warning: Failed to deallocate witness arena: {}",
302 e
303 );
304 }
305 if let Err(e) = sys::dealloc(self.poly_ptr, self.poly_size) {
306 eprintln!(
307 "[nalloc] Warning: Failed to deallocate polynomial arena: {}",
308 e
309 );
310 }
311 if let Err(e) = sys::dealloc(self.scratch_ptr, self.scratch_size) {
312 eprintln!(
313 "[nalloc] Warning: Failed to deallocate scratch arena: {}",
314 e
315 );
316 }
317 }
318}
319
320unsafe impl Send for ArenaManager {}
322unsafe impl Sync for ArenaManager {}
323
324#[cfg(test)]
325mod tests {
326 use super::*;
327
328 #[test]
329 fn test_arena_manager_creation() {
330 let manager = ArenaManager::with_sizes(1024 * 1024, 2 * 1024 * 1024, 1024 * 1024).unwrap();
332
333 let stats = manager.stats();
334 assert_eq!(stats.witness_capacity, 1024 * 1024);
335 assert_eq!(stats.polynomial_capacity, 2 * 1024 * 1024);
336 assert_eq!(stats.scratch_capacity, 1024 * 1024);
337 assert_eq!(stats.total_used(), 0);
338 }
339
340 #[test]
341 fn test_arena_stats() {
342 let manager = ArenaManager::with_sizes(1024 * 1024, 2 * 1024 * 1024, 1024 * 1024).unwrap();
343
344 let _ = manager.witness().alloc(1024, 8);
346 let _ = manager.polynomial().alloc(2048, 64);
347 let _ = manager.scratch().alloc(512, 8);
348
349 let stats = manager.stats();
350 assert!(stats.witness_used >= 1024);
351 assert!(stats.polynomial_used >= 2048);
352 assert!(stats.scratch_used >= 512);
353 }
354
355 #[test]
356 fn test_drop_deallocates() {
357 {
359 let _manager = ArenaManager::with_sizes(1024 * 1024, 1024 * 1024, 1024 * 1024).unwrap();
360 }
362 }
364
365 #[test]
366 fn test_sole_owner_check() {
367 let manager = ArenaManager::with_sizes(1024 * 1024, 1024 * 1024, 1024 * 1024).unwrap();
368
369 assert!(manager.is_sole_owner());
371
372 let _witness_handle = manager.witness();
374
375 assert!(!manager.is_sole_owner());
377
378 drop(_witness_handle);
380
381 assert!(manager.is_sole_owner());
383 }
384
385 #[test]
386 fn test_ref_counts() {
387 let manager = ArenaManager::with_sizes(1024 * 1024, 1024 * 1024, 1024 * 1024).unwrap();
388
389 let (w, p, s) = manager.ref_counts();
390 assert_eq!((w, p, s), (1, 1, 1));
391
392 let _w1 = manager.witness();
393 let _w2 = manager.witness();
394 let _p1 = manager.polynomial();
395
396 let (w, p, s) = manager.ref_counts();
397 assert_eq!((w, p, s), (3, 2, 1));
398 }
399
400 #[test]
401 #[cfg(all(target_os = "linux", feature = "guard-pages"))]
402 fn test_guard_pages_creation() {
403 let manager =
404 ArenaManager::with_guard_pages(1024 * 1024, 1024 * 1024, 1024 * 1024).unwrap();
405
406 let ptr = manager.witness().alloc(1024, 8);
408 assert!(!ptr.is_null());
409
410 unsafe {
412 std::ptr::write_bytes(ptr, 0xAB, 1024);
413 }
414 }
415
416 #[test]
417 #[cfg(feature = "mlock")]
418 fn test_mlock() {
419 let manager = ArenaManager::with_sizes(1024 * 1024, 1024 * 1024, 1024 * 1024).unwrap();
420
421 let _ = manager.lock_witness();
424 let _ = manager.unlock_witness();
425 }
426}