LoGin f8c5e12d70
fix: 修复slab分配器的UAF\内存越界问题 (#1111)
- 新增 `tests.rs` 模块,包含多个测试用例以验证内存分配器的正确性和性能。
- 优化 `pages.rs` 中的 `Bitfield` 实现,移除不必要的 `get_offset_for_align` 函数。
- 在 `zone.rs` 中新增 `try_reclaim_pages_in_slab` 方法,用于在特定 slab 中回收页面。
- 修复 `kernel_allocator.rs` 中的 `allocator_select_condition` 逻辑,移除对 `slab_init_state` 的依赖。
- 移除 `slab.rs` 中的 `slab_init_state` 函数,简化初始化状态检查。

Signed-off-by: longjin <longjin@DragonOS.org>
2025-03-24 23:21:22 +08:00

246 lines
8.1 KiB
Rust
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

//! A ZoneAllocator to allocate arbitrary object sizes (up to `ZoneAllocator::MAX_ALLOC_SIZE`)
//!
//! The ZoneAllocator achieves this by having many `SCAllocator`
use crate::*;
/// Creates an instance of a zone, we do this in a macro because we
/// re-use the code in const and non-const functions
///
/// We can get rid of this once the const fn feature is fully stabilized.
macro_rules! new_zone {
() => {
ZoneAllocator {
// TODO(perf): We should probably pick better classes
// rather than powers-of-two (see SuperMalloc etc.)
small_slabs: [
SCAllocator::new(1 << 3), // 8
SCAllocator::new(1 << 4), // 16
SCAllocator::new(1 << 5), // 32
SCAllocator::new(1 << 6), // 64
SCAllocator::new(1 << 7), // 128
SCAllocator::new(1 << 8), // 256
SCAllocator::new(1 << 9), // 512
SCAllocator::new(1 << 10), // 1024
SCAllocator::new(1 << 11), // 2048 ],
],
total: 0,
}
};
}
/// A zone allocator for arbitrary sized allocations.
///
/// Has a bunch of `SCAllocator` and through that can serve allocation
/// requests for many different object sizes up to (MAX_SIZE_CLASSES) by selecting
/// the right `SCAllocator` for allocation and deallocation.
///
/// The allocator provides to refill functions `refill` and `refill_large`
/// to provide the underlying `SCAllocator` with more memory in case it runs out.
pub struct ZoneAllocator<'a> {
small_slabs: [SCAllocator<'a, ObjectPage<'a>>; ZoneAllocator::MAX_BASE_SIZE_CLASSES],
total: u64,
}
impl<'a> Default for ZoneAllocator<'a> {
fn default() -> ZoneAllocator<'a> {
new_zone!()
}
}
enum Slab {
Base(usize),
Unsupported,
}
impl<'a> ZoneAllocator<'a> {
/// Maximum size which is allocated with ObjectPages (4 KiB pages).
///
/// e.g. this is 4 KiB - 80 bytes of meta-data.
pub const MAX_BASE_ALLOC_SIZE: usize = 1 << 11;
/// How many allocators of type SCAllocator<ObjectPage> we have.
pub const MAX_BASE_SIZE_CLASSES: usize = 9;
#[cfg(feature = "unstable")]
pub const fn new() -> ZoneAllocator<'a> {
new_zone!()
}
#[cfg(not(feature = "unstable"))]
pub fn new() -> ZoneAllocator<'a> {
new_zone!()
}
/// Return maximum size an object of size `current_size` can use.
///
/// Used to optimize `realloc`.
pub fn get_max_size(current_size: usize) -> Option<usize> {
match current_size {
0..=8 => Some(8),
9..=16 => Some(16),
17..=32 => Some(32),
33..=64 => Some(64),
65..=128 => Some(128),
129..=256 => Some(256),
257..=512 => Some(512),
513..=1024 => Some(1024),
1025..=2048 => Some(2048),
_ => None,
}
}
/// Figure out index into zone array to get the correct slab allocator for that size.
fn get_slab(requested_size: usize) -> Slab {
match requested_size {
0..=8 => Slab::Base(0),
9..=16 => Slab::Base(1),
17..=32 => Slab::Base(2),
33..=64 => Slab::Base(3),
65..=128 => Slab::Base(4),
129..=256 => Slab::Base(5),
257..=512 => Slab::Base(6),
513..=1024 => Slab::Base(7),
1025..=2048 => Slab::Base(8),
_ => Slab::Unsupported,
}
}
/// Reclaims empty pages by calling `dealloc` on it and removing it from the
/// empty lists in the [`SCAllocator`].
///
/// The `dealloc` function is called at most `reclaim_base_max` times for
/// base pages, and at most `reclaim_large_max` for large pages.
pub fn try_reclaim_base_pages<F>(&mut self, mut to_reclaim: usize, mut dealloc: F)
where
F: Fn(*mut ObjectPage),
{
for i in 0..ZoneAllocator::MAX_BASE_SIZE_CLASSES {
let slab = &mut self.small_slabs[i];
// reclaim的page数
let just_reclaimed = slab.try_reclaim_pages(to_reclaim, &mut dealloc);
self.total -= (just_reclaimed * OBJECT_PAGE_SIZE) as u64;
to_reclaim = to_reclaim.saturating_sub(just_reclaimed);
if to_reclaim == 0 {
break;
}
}
}
/// 获取scallocator中的还未被分配的空间
pub fn free_space(&mut self) -> u64 {
// 记录空闲空间
let mut free = 0;
// 遍历所有scallocator
for count in 0..ZoneAllocator::MAX_BASE_SIZE_CLASSES {
// 获取scallocator
let scallocator = &mut self.small_slabs[count];
// 遍历scallocator中的部分分配的page(partial_page)
for slab_page in scallocator.slabs.iter_mut() {
// 剩余可分配object数乘上page中规定的每个object的大小即空闲空间
free += slab_page.free_obj_count() * scallocator.size();
}
// 遍历scallocator中的empty_page把空页空间也加上去
free +=
scallocator.empty_slabs.elements * (scallocator.obj_per_page * scallocator.size());
}
free as u64
}
pub fn usage(&mut self) -> SlabUsage {
let free_num = self.free_space();
SlabUsage::new(self.total, free_num)
}
}
unsafe impl<'a> crate::Allocator<'a> for ZoneAllocator<'a> {
/// Allocate a pointer to a block of memory described by `layout`.
fn allocate(&mut self, layout: Layout) -> Result<NonNull<u8>, AllocationError> {
match ZoneAllocator::get_slab(layout.size()) {
Slab::Base(idx) => self.small_slabs[idx].allocate(layout),
Slab::Unsupported => Err(AllocationError::InvalidLayout),
}
}
/// Deallocates a pointer to a block of memory, which was
/// previously allocated by `allocate`.
///
/// # Arguments
/// * `ptr` - Address of the memory location to free.
/// * `layout` - Memory layout of the block pointed to by `ptr`.
/// * `slab_callback` - The callback function to free slab_page in buddy.
unsafe fn deallocate(
&mut self,
ptr: NonNull<u8>,
layout: Layout,
slab_callback: &'static dyn CallBack,
) -> Result<(), AllocationError> {
match ZoneAllocator::get_slab(layout.size()) {
Slab::Base(idx) => {
let r = self.small_slabs[idx].deallocate(ptr, layout);
if let Ok(true) = r {
self.small_slabs[idx].try_reclaim_pages(
1,
&mut |slab_page: *mut ObjectPage| {
// 将slab_page归还buddy
slab_callback
.free_slab_page(slab_page as *const _ as *mut u8, ObjectPage::SIZE);
},
);
}
r.map(|_| ())
}
Slab::Unsupported => Err(AllocationError::InvalidLayout),
}
}
/// Refills the SCAllocator for a given Layout with an ObjectPage.
///
/// # Safety
/// ObjectPage needs to be emtpy etc.
unsafe fn refill(
&mut self,
layout: Layout,
new_page: &'a mut ObjectPage<'a>,
) -> Result<(), AllocationError> {
match ZoneAllocator::get_slab(layout.size()) {
Slab::Base(idx) => {
self.small_slabs[idx].refill(new_page);
// 每refill一个page就为slab的总空间统计加上4KB
self.total += OBJECT_PAGE_SIZE as u64;
Ok(())
}
Slab::Unsupported => Err(AllocationError::InvalidLayout),
}
}
}
/// Slab内存空间使用情况
pub struct SlabUsage {
// slab总共使用的内存空间
total: u64,
// slab的空闲空间
free: u64,
}
impl SlabUsage {
/// 初始化SlabUsage
pub fn new(total: u64, free: u64) -> Self {
Self { total, free }
}
pub fn total(&self) -> u64 {
self.total
}
pub fn used(&self) -> u64 {
self.total - self.free
}
pub fn free(&self) -> u64 {
self.free
}
}