1use core::{ops::Range, ptr::NonNull};
2
3use crate::hal::mem::PhysAddr;
4
5use crate::error::Result;
6
7struct BestFitMeta {
9 size: usize,
11 next: Option<NonNull<u8>>,
13}
14
15#[proc_macros::fmt]
20pub struct BestFitAllocator {
21 head: Option<NonNull<u8>>,
23 #[cfg(any(feature = "metrics", metrics))]
24 metrics: super::Metrics,
25}
26
27unsafe impl Send for BestFitAllocator {}
31unsafe impl Sync for BestFitAllocator {}
33
34impl BestFitAllocator {
36 pub const MIN_RANGE_SIZE: usize = size_of::<BestFitMeta>() + Self::align_up() + 1;
37
38 pub const fn new() -> Self {
42 Self {
43 head: None,
44 #[cfg(any(feature = "metrics", metrics))]
45 metrics: super::Metrics::new(),
46 }
47 }
48
49 pub unsafe fn add_range(&mut self, range: &Range<PhysAddr>) -> Result<()> {
61 let ptr = range.start;
62
63 if !ptr.is_multiple_of(align_of::<u128>()) {
65 return Err(kerr!(EINVAL));
66 }
67
68 if range.end.diff(range.start) < Self::MIN_RANGE_SIZE {
69 return Err(kerr!(EINVAL));
70 }
71
72 debug_assert!(range.end > range.start);
73 debug_assert!(range.end.diff(range.start) > size_of::<BestFitMeta>() + Self::align_up());
74 debug_assert!(range.end.as_usize() <= isize::MAX as usize);
75
76 let user_pointer = ptr + size_of::<BestFitMeta>() + Self::align_up();
78
79 let usable = range.end.diff(user_pointer);
80
81 let meta = BestFitMeta {
83 size: usable,
84 next: self.head,
85 };
86
87 unsafe { core::ptr::write(ptr.as_mut_ptr::<BestFitMeta>(), meta) };
89
90 self.head = Some(unsafe { NonNull::new_unchecked(ptr.as_mut_ptr::<u8>()) });
92
93 #[cfg(any(feature = "metrics", metrics))]
94 self.metrics
95 .record_add_range(range.end.diff(range.start), usable);
96
97 Ok(())
98 }
99
100 const fn align_up() -> usize {
104 let meta = size_of::<BestFitMeta>();
105 let align = align_of::<u128>();
106 (align - (meta % align)) % align
108 }
109
110 fn select_block(
116 &mut self,
117 size: usize,
118 requested: Option<PhysAddr>,
119 ) -> Result<(NonNull<u8>, Option<NonNull<u8>>)> {
120 let mut best_fit = Err(kerr!(ENOMEM));
121 let mut best_fit_size = usize::MAX;
122
123 let mut current = self.head;
124 let mut prev = None;
125
126 if let Some(requested) = requested {
127 while let Some(ptr) = current {
128 let meta = unsafe { ptr.cast::<BestFitMeta>().as_ref() };
130
131 if unsafe { Self::contains(meta, requested, size) } {
132 return Ok((ptr, prev));
133 }
134
135 prev = current;
137 current = meta.next;
138 }
139 }
140
141 while let Some(ptr) = current {
143 let meta = unsafe { ptr.cast::<BestFitMeta>().as_ref() };
145
146 if meta.size >= size && meta.size <= best_fit_size {
148 best_fit = Ok((ptr, prev));
149 best_fit_size = meta.size;
150 }
151
152 prev = current;
154 current = meta.next;
155 }
156
157 best_fit
158 }
159
160 unsafe fn user_ptr(ptr: NonNull<u8>) -> NonNull<u8> {
170 debug_assert!(
171 (ptr.as_ptr() as usize)
172 <= isize::MAX as usize - size_of::<BestFitMeta>() - Self::align_up()
173 );
174 unsafe { ptr.byte_add(size_of::<BestFitMeta>() + Self::align_up()) }
175 }
176
177 unsafe fn control_ptr(ptr: NonNull<u8>) -> NonNull<u8> {
187 debug_assert!((ptr.as_ptr() as usize) > size_of::<BestFitMeta>() + Self::align_up());
188 unsafe { ptr.byte_sub(size_of::<BestFitMeta>() + Self::align_up()) }
189 }
190
191 unsafe fn contains(meta: &BestFitMeta, target: PhysAddr, size: usize) -> bool {
192 let begin = unsafe {
193 Self::user_ptr(NonNull::new_unchecked(
194 meta as *const BestFitMeta as *mut u8,
195 ))
196 };
197 debug_assert!(size > 0);
198
199 if target >= begin.into() {
200 if let Some(target) = target.checked_add(size) {
201 if target > (unsafe { begin.add(meta.size) }).into() {
202 return false;
203 }
204 } else {
205 return false;
206 }
207 return true;
208 }
209 false
210 }
211}
212
213impl super::Allocator for BestFitAllocator {
215 unsafe fn malloc<T>(
226 &mut self,
227 size: usize,
228 align: usize,
229 request: Option<PhysAddr>,
230 ) -> Result<NonNull<T>> {
231 if align == 0 || align > align_of::<u128>() {
233 return Err(kerr!(EINVAL));
234 }
235
236 if let Some(request) = request {
237 if !request.is_multiple_of(align) {
238 return Err(kerr!(EINVAL));
239 }
240 }
241
242 if size == 0 {
244 return Err(kerr!(EINVAL));
245 }
246
247 #[allow(clippy::absurd_extreme_comparisons)]
249 if size >= super::MAX_ADDR {
250 return Err(kerr!(EINVAL));
251 }
252
253 let aligned_size = super::super::align_up(size);
255 debug_assert!(aligned_size >= size);
256 debug_assert!(aligned_size <= isize::MAX as usize);
257
258 #[cfg(any(feature = "metrics", metrics))]
260 let mut free_sub: usize = 0;
261 #[cfg(any(feature = "metrics", metrics))]
262 let mut blocks_sub: usize = 0;
263
264 let (split, block, prev) = match self.select_block(aligned_size, request) {
266 Ok((block, prev)) => {
267 let meta = unsafe { block.cast::<BestFitMeta>().as_mut() };
269
270 let aligned_size = if let Some(request) = request {
272 aligned_size + request.diff(unsafe { Self::user_ptr(block) }.into())
273 } else {
274 aligned_size
275 };
276
277 let min = aligned_size.saturating_add(size_of::<BestFitMeta>() + Self::align_up());
279
280 debug_assert!(
281 (block.as_ptr() as usize)
282 <= isize::MAX as usize
283 - meta.size
284 - size_of::<BestFitMeta>()
285 - Self::align_up()
286 );
287
288 debug_assert!(
289 meta.size < isize::MAX as usize - size_of::<BestFitMeta>() - Self::align_up()
290 );
291
292 if meta.size > min {
294 #[cfg(any(feature = "metrics", metrics))]
297 {
298 free_sub = min;
299 }
300
301 let remaining_meta = BestFitMeta {
303 size: meta.size - min,
304 next: meta.next,
305 };
306
307 meta.size = aligned_size;
309
310 let ptr = unsafe { Self::user_ptr(block).byte_add(aligned_size) };
312
313 unsafe {
314 ptr.cast::<BestFitMeta>().write(remaining_meta);
316 }
317
318 if let Some(prev) = prev {
320 let prev_meta = unsafe { prev.cast::<BestFitMeta>().as_mut() };
321 prev_meta.next = Some(ptr);
322 } else {
323 self.head = Some(ptr);
324 }
325
326 meta.next = None;
328
329 (true, block, prev)
330 } else {
331 #[cfg(any(feature = "metrics", metrics))]
333 {
334 free_sub = meta.size;
335 blocks_sub = 1;
336 }
337
338 (false, block, prev)
339 }
340 }
341 Err(_) => {
342 let (block, prev) = self.select_block(size, request)?;
343 #[cfg(any(feature = "metrics", metrics))]
345 {
346 let meta = unsafe { block.cast::<BestFitMeta>().as_ref() };
347 free_sub = meta.size;
348 blocks_sub = 1;
349 }
350 (false, block, prev)
351 }
352 };
353
354 if !split {
355 let meta = unsafe { block.cast::<BestFitMeta>().as_mut() };
357
358 if let Some(prev) = prev {
359 let prev_meta = unsafe { prev.cast::<BestFitMeta>().as_mut() };
360 prev_meta.next = meta.next;
362 } else {
363 self.head = meta.next;
365 }
366
367 meta.next = None;
369 }
370
371 if let Some(request) = request {
372 debug_assert!(unsafe {
373 Self::contains(block.cast::<BestFitMeta>().as_ref(), request, size)
374 });
375 }
376
377 #[cfg(any(feature = "metrics", metrics))]
378 self.metrics.record_alloc(free_sub, blocks_sub);
379
380 Ok(unsafe { Self::user_ptr(block).cast() })
382 }
383
384 unsafe fn free<T>(&mut self, ptr: NonNull<T>, size: usize) {
389 let block = unsafe { Self::control_ptr(ptr.cast()) };
390
391 let mut walk = self.head;
394 while let Some(p) = walk {
395 bug_on!(p == block, "double free");
396 walk = unsafe { p.cast::<BestFitMeta>().as_ref().next };
397 }
398
399 let meta = unsafe { block.cast::<BestFitMeta>().as_mut() };
400
401 meta.next = self.head;
403
404 bug_on!(
406 size > meta.size,
407 "allocation size {} is larger than block size {}",
408 size,
409 meta.size
410 );
411
412 self.head = Some(block);
414
415 #[cfg(any(feature = "metrics", metrics))]
416 self.metrics.record_free(meta.size);
417 }
418}
419
420#[cfg(any(feature = "metrics", metrics))]
421impl BestFitAllocator {
422 pub fn metrics(&self) -> super::Metrics {
423 self.metrics
424 }
425}
426
427#[cfg(all(test, any(feature = "metrics", metrics)))]
430mod metrics_tests {
431 use super::super::*;
432 use super::*;
433 use core::mem::size_of;
434
435 fn alloc_range(length: usize) -> std::ops::Range<crate::hal::mem::PhysAddr> {
436 use crate::hal::mem::PhysAddr;
437 let layout = std::alloc::Layout::from_size_align(length, align_of::<u128>()).unwrap();
438 let ptr = unsafe { std::alloc::alloc(layout) };
439 if ptr.is_null() {
440 std::alloc::handle_alloc_error(layout);
441 }
442 PhysAddr::new(ptr as usize)..PhysAddr::new(ptr as usize + length)
443 }
444
445 #[test]
446 fn metrics_fresh_allocator_is_zero() {
447 let allocator = BestFitAllocator::new();
448 let m = allocator.metrics();
449 assert_eq!(m.total_bytes, 0);
450 assert_eq!(m.free_bytes, 0);
451 assert_eq!(m.allocated_bytes(), 0);
452 assert_eq!(m.free_blocks, 0);
453 assert_eq!(m.alloc_count, 0);
454 assert_eq!(m.free_count, 0);
455 }
456
457 #[test]
458 fn metrics_after_add_range() {
459 let mut allocator = BestFitAllocator::new();
460 let range_len = 4096usize;
461 let range = alloc_range(range_len);
462 unsafe { allocator.add_range(&range).unwrap() };
463
464 let m = allocator.metrics();
465 assert_eq!(m.total_bytes, range_len);
466 assert_eq!(m.free_blocks, 1);
467 assert!(m.free_bytes > 0);
468 assert!(m.free_bytes < range_len, "metadata must consume some bytes");
469 assert_eq!(m.allocated_bytes(), range_len - m.free_bytes);
470 assert_eq!(m.alloc_count, 0);
471 assert_eq!(m.free_count, 0);
472 }
473
474 #[test]
475 fn metrics_alloc_increments_count_and_reduces_free() {
476 let mut allocator = BestFitAllocator::new();
477 let range = alloc_range(4096);
478 unsafe { allocator.add_range(&range).unwrap() };
479 let before = allocator.metrics();
480
481 let _ptr = unsafe { allocator.malloc::<u8>(128, 1, None).unwrap() };
482 let after = allocator.metrics();
483
484 assert_eq!(after.alloc_count, 1);
485 assert_eq!(after.free_count, 0);
486 assert!(after.free_bytes < before.free_bytes);
487 }
488
489 #[test]
490 fn metrics_free_increments_count_and_restores_free_bytes() {
491 let mut allocator = BestFitAllocator::new();
492 let range = alloc_range(4096);
493 unsafe { allocator.add_range(&range).unwrap() };
494
495 let ptr = unsafe { allocator.malloc::<u8>(128, 1, None).unwrap() };
496 let after_alloc = allocator.metrics();
497
498 unsafe { allocator.free(ptr, 128) };
499 let after_free = allocator.metrics();
500
501 assert_eq!(after_free.alloc_count, 1);
502 assert_eq!(after_free.free_count, 1);
503 assert!(after_free.free_bytes > after_alloc.free_bytes);
505 }
506
507 #[test]
508 fn metrics_free_blocks_count() {
509 let mut allocator = BestFitAllocator::new();
510 let range = alloc_range(4096);
511 unsafe { allocator.add_range(&range).unwrap() };
512
513 let p1 = unsafe { allocator.malloc::<u8>(128, 1, None).unwrap() };
514 let p2 = unsafe { allocator.malloc::<u8>(128, 1, None).unwrap() };
515 let after_two_allocs = allocator.metrics();
516
517 unsafe { allocator.free(p1, 128) };
518 let after_free1 = allocator.metrics();
519
520 unsafe { allocator.free(p2, 128) };
521 let after_free2 = allocator.metrics();
522
523 assert_eq!(after_free1.free_blocks, after_two_allocs.free_blocks + 1);
525 assert_eq!(after_free2.free_blocks, after_two_allocs.free_blocks + 2);
526 assert_eq!(after_free2.alloc_count, 2);
527 assert_eq!(after_free2.free_count, 2);
528 }
529
530 #[test]
531 fn metrics_largest_free_block_single_range() {
532 let mut allocator = BestFitAllocator::new();
533 let range = alloc_range(4096);
534 unsafe { allocator.add_range(&range).unwrap() };
535
536 let m = allocator.metrics();
537 assert_eq!(m.free_blocks, 1);
539
540 let _p = unsafe { allocator.malloc::<u8>(128, 1, None).unwrap() };
541 let m2 = allocator.metrics();
542 assert!(m2.free_bytes <= m.free_bytes);
544 }
545
546 #[test]
547 fn metrics_multiple_ranges_total_bytes() {
548 let mut allocator = BestFitAllocator::new();
549 const RANGE_LEN: usize = 1024;
550 const RANGES: usize = 3;
551
552 for _ in 0..RANGES {
553 let range = alloc_range(RANGE_LEN);
554 unsafe { allocator.add_range(&range).unwrap() };
555 }
556
557 let m = allocator.metrics();
558 assert_eq!(m.total_bytes, RANGE_LEN * RANGES);
559 assert_eq!(m.free_blocks, RANGES);
560 }
561
562 #[test]
563 fn metrics_exact_fit_no_split() {
564 let mut allocator = BestFitAllocator::new();
566 let overhead = size_of::<BestFitMeta>() + BestFitAllocator::align_up();
567 let user_size = 128usize;
568 let range = alloc_range(user_size + overhead);
569 unsafe { allocator.add_range(&range).unwrap() };
570
571 let before = allocator.metrics();
572 assert_eq!(before.free_blocks, 1);
573
574 let ptr = unsafe { allocator.malloc::<u8>(user_size, 1, None).unwrap() };
575 let after_alloc = allocator.metrics();
576 assert_eq!(after_alloc.free_blocks, 0);
578 assert_eq!(after_alloc.free_bytes, 0);
579
580 unsafe { allocator.free(ptr, user_size) };
581 let after_free = allocator.metrics();
582 assert_eq!(after_free.free_blocks, 1);
583 assert_eq!(after_free.free_bytes, before.free_bytes);
584 }
585}
586
587#[cfg(test)]
588mod tests {
589 use crate::mem::align_up;
590 use hal_api::PosixError;
591
592 use super::super::*;
593 use super::*;
594
595 fn verify_block(user_ptr: NonNull<u8>, size: usize, next: Option<NonNull<u8>>) {
596 let control_ptr = unsafe { BestFitAllocator::control_ptr(user_ptr) };
597 let meta = unsafe { control_ptr.cast::<BestFitMeta>().as_ref() };
598
599 assert!(meta.size >= size);
600 assert_eq!(meta.next, next);
601 }
602
603 fn verify_ptrs_not_overlaping(ptrs: &[(NonNull<u8>, usize)]) {
604 for (i, (ptr1, size1)) in ptrs.iter().enumerate() {
605 for (j, (ptr2, size2)) in ptrs.iter().enumerate() {
606 if i == j {
607 continue;
608 }
609
610 let begin1 = ptr1.as_ptr() as usize;
611 let end1 = begin1 + size1;
612 let begin2 = ptr2.as_ptr() as usize;
613 let end2 = begin2 + size2;
614
615 assert!(end1 <= begin2 || end2 <= begin1);
616 assert!(begin1 != begin2);
617 assert!(end1 != end2);
618 assert!(*size1 > 0);
619 assert!(*size2 > 0);
620 assert!(end1 > begin1);
621 assert!(end2 > begin2);
622 }
623 }
624 }
625
626 fn alloc_range(length: usize) -> Range<PhysAddr> {
627 let alloc_range = std::alloc::Layout::from_size_align(length, align_of::<u128>()).unwrap();
628 let ptr = unsafe { std::alloc::alloc(alloc_range) };
629 PhysAddr::new(ptr as usize)..PhysAddr::new(ptr as usize + length)
630 }
631
632 #[test]
633 fn allocate_one() {
634 let mut allocator = BestFitAllocator::new();
635
636 let range = alloc_range(4096);
637 unsafe {
638 allocator.add_range(&range).unwrap();
639 }
640
641 let ptr = unsafe { allocator.malloc(128, 1, None).unwrap() };
642
643 verify_block(ptr, 128, None);
644 }
645
646 #[test]
647 fn alloc_request() {
648 let mut allocator = BestFitAllocator::new();
649
650 let range = alloc_range(4096);
651 unsafe {
652 allocator.add_range(&range).unwrap();
653 }
654
655 let request = range.start + 128;
656 let ptr = unsafe { allocator.malloc::<u8>(128, 1, Some(request)).unwrap() };
657
658 let meta = unsafe {
660 BestFitAllocator::control_ptr(ptr)
661 .cast::<BestFitMeta>()
662 .as_ref()
663 };
664 assert!(unsafe { BestFitAllocator::contains(meta, request, 128) });
665 }
666
667 #[test]
668 fn alloc_request_to_big() {
669 let mut allocator = BestFitAllocator::new();
670
671 let range = alloc_range(4096);
672 unsafe {
673 allocator.add_range(&range).unwrap();
674 }
675
676 let request = range.start + 4096;
677 let ptr = unsafe { allocator.malloc::<u8>(128, 1, Some(request)) };
678
679 assert!(ptr.is_err_and(|e| e.kind == PosixError::ENOMEM));
680 }
681
682 #[test]
683 fn alloc_request_not_aligned() {
684 let mut allocator = BestFitAllocator::new();
685
686 let range = alloc_range(4096);
687 unsafe {
688 allocator.add_range(&range).unwrap();
689 }
690
691 let request = range.start + 127;
692 let ptr = unsafe { allocator.malloc::<u8>(128, 8, Some(request)) };
693
694 assert!(ptr.is_err_and(|e| e.kind == PosixError::EINVAL));
695 }
696
697 #[test]
698 fn alloc_request_not_available() {
699 let mut allocator = BestFitAllocator::new();
700
701 let range = alloc_range(4096);
702 unsafe {
703 allocator.add_range(&range).unwrap();
704 }
705
706 let request = range.start + 128;
707 let ptr = unsafe { allocator.malloc::<u8>(128, 1, Some(request)).unwrap() };
708 verify_block(ptr, 128, None);
709
710 let ptr = unsafe { allocator.malloc::<u8>(128, 1, Some(request)) };
711 assert!(ptr.is_err_and(|e| e.kind == PosixError::ENOMEM));
712 }
713
714 #[test]
715 fn alloc_request_out_of_range() {
716 let mut allocator = BestFitAllocator::new();
717
718 let range = alloc_range(4096);
719 unsafe {
720 allocator.add_range(&range).unwrap();
721 }
722
723 let request = range.end + 128;
724 let ptr = unsafe { allocator.malloc::<u8>(128, 1, Some(request)) };
725
726 assert!(ptr.is_err_and(|e| e.kind == PosixError::ENOMEM));
727 }
728
729 #[test]
730 fn alloc_alot() {
731 let mut allocator = BestFitAllocator::new();
732 const CNT: usize = 100;
733 const SIZE: usize = 128;
734
735 let range = alloc_range(SIZE * CNT * 2);
736 unsafe {
737 allocator.add_range(&range).unwrap();
738 }
739
740 let mut ptrs = Vec::new();
741 for _ in 0..CNT {
742 let ptr = unsafe { allocator.malloc(SIZE, 1, None).unwrap() };
743 verify_block(ptr, SIZE, None);
744 ptrs.push((ptr, SIZE));
745 }
746
747 verify_ptrs_not_overlaping(ptrs.as_slice());
748 }
749
750 #[test]
751 fn alloc_exact() {
752 let mut allocator = BestFitAllocator::new();
753 const CNT: usize = 10;
754 const SIZE: usize = 128;
755
756 let range =
757 alloc_range((SIZE + size_of::<BestFitMeta>() + BestFitAllocator::align_up()) * CNT);
758 unsafe {
759 allocator.add_range(&range).unwrap();
760 }
761
762 let mut ptrs = Vec::new();
763 for _ in 0..CNT {
764 let ptr = unsafe { allocator.malloc(SIZE, 1, None).unwrap() };
765 verify_block(ptr, SIZE, None);
766 ptrs.push((ptr, SIZE));
767 }
768
769 verify_ptrs_not_overlaping(ptrs.as_slice());
770 }
771
772 #[test]
773 fn alloc_oom() {
774 let mut allocator = BestFitAllocator::new();
775 const CNT: usize = 10;
776 const SIZE: usize = 128;
777
778 let range =
779 alloc_range((SIZE + size_of::<BestFitMeta>() + BestFitAllocator::align_up()) * CNT - 1);
780 unsafe {
781 allocator.add_range(&range).unwrap();
782 }
783
784 let mut ptrs = Vec::new();
785 for _ in 0..CNT - 1 {
786 let ptr = unsafe { allocator.malloc(SIZE, 1, None).unwrap() };
787 verify_block(ptr, SIZE, None);
788 ptrs.push(ptr);
789 }
790
791 let ptr = unsafe { allocator.malloc::<u8>(SIZE, 1, None) };
792 assert!(ptr.is_err_and(|e| e.kind == PosixError::ENOMEM));
793 }
794
795 #[test]
796 fn alloc_no_oom_through_free() {
797 let mut allocator = BestFitAllocator::new();
798 const SIZE: usize = 128;
799
800 let range = alloc_range(SIZE + size_of::<BestFitMeta>() + BestFitAllocator::align_up());
801 unsafe {
802 allocator.add_range(&range).unwrap();
803 }
804
805 let ptr = unsafe { allocator.malloc(SIZE, 1, None).unwrap() };
806 verify_block(ptr, SIZE, None);
807
808 unsafe {
809 allocator.free(ptr, SIZE);
810 }
811
812 let ptr = unsafe { allocator.malloc(SIZE, 1, None).unwrap() };
813 verify_block(ptr, SIZE, None);
814 }
815
816 #[test]
817 fn multi_range_alloc() {
818 let mut allocator = BestFitAllocator::new();
819 const CNT: usize = 10;
820 const SIZE: usize = 128;
821
822 let mut ranges = Vec::new();
823 for _ in 0..CNT {
824 let range = alloc_range(SIZE + size_of::<BestFitMeta>() + BestFitAllocator::align_up());
825 unsafe {
826 allocator.add_range(&range).unwrap();
827 }
828 ranges.push(range);
829 }
830
831 let mut ptrs = Vec::new();
832 for _ in 0..CNT {
833 let ptr = unsafe { allocator.malloc(SIZE, 1, None).unwrap() };
834 verify_block(ptr, SIZE, None);
835 ptrs.push((ptr, SIZE));
836 }
837
838 verify_ptrs_not_overlaping(ptrs.as_slice());
839 }
840
841 #[test]
842 fn multi_range_no_oom_through_free() {
843 let mut allocator = BestFitAllocator::new();
845
846 const CNT: usize = 10;
847 const SIZE: usize = 128;
848
849 let mut ranges = Vec::new();
850 for _ in 0..CNT {
851 let range = alloc_range(SIZE + size_of::<BestFitMeta>() + BestFitAllocator::align_up());
852 unsafe {
853 allocator.add_range(&range).unwrap();
854 }
855 ranges.push(range);
856 }
857
858 let mut ptrs = Vec::new();
859
860 let ptr = unsafe { allocator.malloc::<u8>(SIZE, 1, None).unwrap() };
861
862 for _ in 0..CNT - 1 {
863 let ptr = unsafe { allocator.malloc(SIZE, 1, None).unwrap() };
864 verify_block(ptr, SIZE, None);
865 ptrs.push((ptr, SIZE));
866 }
867
868 unsafe {
869 allocator.free(ptr, SIZE);
870 }
871
872 let ptr = unsafe { allocator.malloc(SIZE, 1, None).unwrap() };
873 ptrs.push((ptr, SIZE));
874
875 verify_ptrs_not_overlaping(ptrs.as_slice());
876 }
877
878 #[test]
879 fn free_corrupts_metadata() {
880 let mut allocator = BestFitAllocator::new();
881 const SIZE: usize = 17;
882 const ALIGNED: usize = 32;
883 assert!(align_up(SIZE) == ALIGNED);
884
885 let range = alloc_range(ALIGNED + size_of::<BestFitMeta>() + BestFitAllocator::align_up());
886 unsafe {
887 allocator.add_range(&range).unwrap();
888 }
889
890 let ptr1: core::ptr::NonNull<u8> = unsafe { allocator.malloc(SIZE, 1, None).unwrap() };
891
892 unsafe {
893 allocator.free(ptr1, SIZE);
894 }
895
896 let ptr2: core::ptr::NonNull<u8> = unsafe { allocator.malloc(SIZE, 1, None).unwrap() };
897
898 unsafe {
899 allocator.free(ptr2, SIZE);
900 }
901 }
902
903 #[test]
904 #[should_panic(expected = "double free")]
905 fn double_free_panics() {
906 let mut allocator = BestFitAllocator::new();
907 let range = alloc_range(4096);
908 unsafe {
909 allocator.add_range(&range).unwrap();
910 }
911
912 let ptr = unsafe { allocator.malloc::<u8>(128, 1, None).unwrap() };
913 unsafe {
914 allocator.free(ptr, 128);
915 allocator.free(ptr, 128);
918 }
919 }
920
921 #[test]
922 fn multi_range_oom() {
923 let mut allocator = BestFitAllocator::new();
925
926 const CNT: usize = 10;
927 const SIZE: usize = 128;
928
929 let mut ranges = Vec::new();
930 for _ in 0..CNT {
931 let range = alloc_range(SIZE + size_of::<BestFitMeta>() + BestFitAllocator::align_up());
932 unsafe {
933 allocator.add_range(&range).unwrap();
934 }
935 ranges.push(range);
936 }
937
938 let mut ptrs = Vec::new();
939
940 for _ in 0..CNT {
941 let ptr = unsafe { allocator.malloc(SIZE, 1, None).unwrap() };
942 verify_block(ptr, SIZE, None);
943 ptrs.push((ptr, SIZE));
944 }
945
946 let ptr = unsafe { allocator.malloc::<u8>(SIZE, 1, None) };
947 assert!(ptr.is_err_and(|e| e.kind == PosixError::ENOMEM));
948
949 verify_ptrs_not_overlaping(ptrs.as_slice());
950 }
951}
952
953#[cfg(kani)]
957mod verification {
958 use super::*;
959 use crate::mem::alloc::Allocator;
960 use crate::mem::alloc::MAX_ADDR;
961
962 fn verify_block(user_ptr: NonNull<u8>, size: usize, next: Option<NonNull<u8>>) {
963 let control_ptr = unsafe { BestFitAllocator::control_ptr(user_ptr) };
964 let meta = unsafe { control_ptr.cast::<BestFitMeta>().as_ref() };
965
966 assert!(meta.size >= size);
967 assert_eq!(meta.next, next);
968 }
969
970 fn alloc_range(length: usize) -> Option<Range<PhysAddr>> {
971 let alloc_range = std::alloc::Layout::from_size_align(length, align_of::<u128>()).unwrap();
972 let ptr = unsafe { std::alloc::alloc(alloc_range) };
973
974 if ptr.is_null() || ((ptr as usize) >= isize::MAX as usize - length) {
975 None
976 } else {
977 Some(PhysAddr::new(ptr as usize)..PhysAddr::new(ptr as usize + length))
978 }
979 }
980
981 #[kani::proof]
982 #[kani::unwind(2)]
983 fn allocate_one() {
984 let mut allocator = BestFitAllocator::new();
985
986 let size: usize = kani::any();
987 kani::assume(size < MAX_ADDR - size_of::<BestFitMeta>() - BestFitAllocator::align_up());
988 kani::assume(size > 0);
989 let larger_size: usize = kani::any_where(|&x| {
990 x > size + size_of::<BestFitMeta>() + BestFitAllocator::align_up() && x < MAX_ADDR
991 });
992
993 if let Some(range) = alloc_range(larger_size) {
994 unsafe {
995 assert_eq!(allocator.add_range(&range), Ok(()));
996 }
997
998 let ptr = unsafe { allocator.malloc(size, 1, None).unwrap() };
999
1000 verify_block(ptr, size, None);
1001 }
1002 }
1003}
1004