feat(ipc): 实现IPC命名空间初步支持 (#1288)

* feat(ipc): 实现IPC命名空间第一阶段支持

- 添加IpcNamespace结构体并集成到NsProxy中
- 将全局SHM_MANAGER重构为per-ns ShmManager
- 修改shm相关系统调用以支持命名空间隔离
- 添加VMA的shm_id字段用于精确维护映射计数
- 新增IPC命名空间测试用例
- 更新相关文档

Signed-off-by: longjin <longjin@DragonOS.org>

* fmt

Signed-off-by: longjin <longjin@DragonOS.org>

---------

Signed-off-by: longjin <longjin@DragonOS.org>
This commit is contained in:
LoGin 2025-09-24 16:15:09 +08:00 committed by GitHub
parent 509ee34586
commit ef296a34e1
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
14 changed files with 452 additions and 304 deletions

View File

@ -8,3 +8,4 @@
:maxdepth: 1
signal
ipc_namespace

View File

@ -0,0 +1,56 @@
# IPC Namespace
:::{note}
Author: longjin <longjin@dragonos.org>
:::
本页描述 DragonOS 对 IPC 命名空间IPC namespace的当前支持状态与后续计划。目标是对用户暴露与 Linux 一致的语义,并在 DragonOS 现有框架上逐步完善。
## 已支持功能
- IpcNamespace 对象与 NsProxy 集成:
- 新增 `IpcNamespace` 并接入 `NsProxy`,每个任务通过 `nsproxy.ipc_ns` 访问所属 IPC 命名空间。
- 命名空间的创建/继承遵循 `clone/unshare` 语义:
- 未包含 `CLONE_NEWIPC` 时继承父命名空间;
- 包含 `CLONE_NEWIPC` 时创建独立 IPC 命名空间;
- 与 `CLONE_SYSVSEM` 互斥,行为与 Linux 一致。
- SysV SHM共享内存按命名空间隔离
- 将原全局 `SHM_MANAGER` 重构为 per-ns `ShmManager`,所有 `shmget/shmat/shmdt/shmctl` 均在 `current.nsproxy.ipc_ns` 下生效。
- `shmat`/`shmdt`VMA 记录 `ShmId`,解除映射时精确维护 `map_count``IPC_RMID` 后当 `SHM_DEST && map_count==0` 即完成物理回收。
- 基本语义与错误码对齐:`IPC_CREAT|IPC_EXCL`、`ENOENT`、拒绝 `SHM_HUGETLB` 等。
- 基础测试用例:(在 `test_ipc_ns_shm.rs` 中)
- `unshare(CLONE_NEWIPC)` 后父/子命名空间的 key 不可见;
- 跨命名空间相同 key 不冲突;
- `IPC_RMID` 后可重新创建同 key
- 输出 PASS/FAIL 与汇总结果。
## 暂未实现/计划中
- `/proc/[pid]/ns/ipc``setns`
- 暂缓,仅规划只读占位与最简 `setns` 路径;后续版本补齐权限校验与切换时序。
- SysV IPC 其它子系统:
- `msg/sem` 框架尚未纳入;`sem` 的 UNDO 列表与 `unshare/setns` 的协同需在引入时同步实现。
- POSIX mqueue
- 尚未提供 per-ns mqueuefs 内核挂载、限额与 sysctl。
- 权限与配额:
- `ipcperms()`、`ns_capable(user_ns, CAP_IPC_OWNER)`
- ucounts/RLIMIT 与 `/proc/sys/kernel/shm*` 等 per-ns sysctl。
## 兼容性与注意事项
- 当前阶段仅对 SysV SHM 提供命名空间隔离;其它 IPC 类型仍按全局语义工作。
- 代码按模块化方式演进:后续加入 `msg/sem/mqueue` 时,保持对用户侧语义的稳定与一致。
## 参考
- 代码位置:
- `kernel/src/process/namespace/ipc_namespace.rs`
- `kernel/src/process/namespace/nsproxy.rs`
- `kernel/src/ipc/syscall/` 内的 `sys_shm*`
- `kernel/src/mm/ucontext.rs`VMA 与 SHM 计数维护)

View File

@ -1,10 +1,7 @@
use crate::{
arch::mm::LockedFrameAllocator,
filesystem::vfs::syscall::ModeType,
libs::{
align::page_align_up,
spinlock::{SpinLock, SpinLockGuard},
},
libs::align::page_align_up,
mm::{
allocator::page_frame::{FrameAllocator, PageFrameCount, PhysPageFrame},
page::{page_manager_lock_irqsave, PageFlags, PageType},
@ -15,33 +12,14 @@ use crate::{
time::PosixTimeSpec,
};
use core::fmt;
use core::sync::atomic::{compiler_fence, Ordering};
use hashbrown::HashMap;
use ida::IdAllocator;
use log::info;
use num::ToPrimitive;
use system_error::SystemError;
pub static mut SHM_MANAGER: Option<SpinLock<ShmManager>> = None;
/// 用于创建新的私有IPC对象
pub const IPC_PRIVATE: ShmKey = ShmKey::new(0);
/// 初始化SHM_MANAGER
pub fn shm_manager_init() {
info!("shm_manager_init");
let shm_manager = SpinLock::new(ShmManager::new());
compiler_fence(Ordering::SeqCst);
unsafe { SHM_MANAGER = Some(shm_manager) };
compiler_fence(Ordering::SeqCst);
info!("shm_manager_init done");
}
pub fn shm_manager_lock() -> SpinLockGuard<'static, ShmManager> {
unsafe { SHM_MANAGER.as_ref().unwrap().lock() }
}
int_like!(ShmId, usize);
int_like!(ShmKey, usize);
@ -138,6 +116,12 @@ pub struct ShmManager {
key2id: HashMap<ShmKey, ShmId>,
}
impl Default for ShmManager {
fn default() -> Self {
Self::new()
}
}
impl ShmManager {
pub fn new() -> Self {
ShmManager {
@ -178,7 +162,7 @@ impl ShmManager {
// 创建共享内存page并添加到PAGE_MANAGER中
let mut page_manager_guard = page_manager_lock_irqsave();
let (paddr, _page) = page_manager_guard.create_pages(
PageType::Shm(shm_id),
PageType::Shm,
PageFlags::PG_UNEVICTABLE,
&mut LockedFrameAllocator,
page_count,

View File

@ -4,15 +4,16 @@ use crate::syscall::table::FormattedSyscallParam;
use crate::{
arch::syscall::nr::SYS_SHMAT,
arch::MMArch,
ipc::shm::{shm_manager_lock, ShmFlags, ShmId},
ipc::shm::{ShmFlags, ShmId},
libs::align::page_align_up,
mm::{
allocator::page_frame::{PageFrameCount, PhysPageFrame, VirtPageFrame},
page::{page_manager_lock_irqsave, EntryFlags, PageFlushAll},
syscall::ProtFlags,
ucontext::{AddressSpace, VMA},
ucontext::{AddressSpace, PhysmapParams, VMA},
VirtAddr, VmFlags,
},
process::ProcessManager,
syscall::{table::Syscall, user_access::UserBufferReader},
};
use syscall_table_macros::declare_syscall;
@ -36,7 +37,8 @@ pub(super) fn do_kernel_shmat(
vaddr: VirtAddr,
shmflg: ShmFlags,
) -> Result<usize, SystemError> {
let mut shm_manager_guard = shm_manager_lock();
let ipcns = ProcessManager::current_ipcns();
let mut shm_manager_guard = ipcns.shm.lock();
let current_address_space = AddressSpace::current()?;
let mut address_write_guard = current_address_space.write();
@ -59,15 +61,15 @@ pub(super) fn do_kernel_shmat(
let flusher: PageFlushAll<MMArch> = PageFlushAll::new();
// 将共享内存映射到对应虚拟区域
let vma = VMA::physmap(
let params = PhysmapParams {
phys,
destination,
count,
vm_flags,
page_flags,
&mut address_write_guard.user_mapper.utable,
flusher,
)?;
flags: page_flags,
shm_id: Some(id),
};
let vma = VMA::physmap(params, &mut address_write_guard.user_mapper.utable, flusher)?;
// 将VMA加入到当前进程的VMA列表中
address_write_guard.mappings.insert_vma(vma);
@ -125,7 +127,10 @@ pub(super) fn do_kernel_shmat(
}
// 更新vma的映射状态
vma.lock_irqsave().set_mapped(true);
let mut vma_guard = vma.lock_irqsave();
vma_guard.set_mapped(true);
vma_guard.set_shm_id(Some(id));
drop(vma_guard);
vaddr.data()
}

View File

@ -2,7 +2,8 @@ use crate::alloc::vec::Vec;
use crate::arch::interrupt::TrapFrame;
use crate::{
arch::syscall::nr::SYS_SHMCTL,
ipc::shm::{shm_manager_lock, ShmCtlCmd, ShmId},
ipc::shm::{ShmCtlCmd, ShmId},
process::ProcessManager,
syscall::table::{FormattedSyscallParam, Syscall},
};
use syscall_table_macros::declare_syscall;
@ -28,7 +29,9 @@ pub(super) fn do_kernel_shmctl(
user_buf: *const u8,
from_user: bool,
) -> Result<usize, SystemError> {
let mut shm_manager_guard = shm_manager_lock();
// per-ns 管理器
let ipcns = ProcessManager::current_ipcns();
let mut shm_manager_guard = ipcns.shm.lock();
match cmd {
// 查看共享内存元信息

View File

@ -3,7 +3,8 @@ use crate::arch::interrupt::TrapFrame;
use crate::syscall::table::FormattedSyscallParam;
use crate::{
arch::syscall::nr::SYS_SHMGET,
ipc::shm::{shm_manager_lock, ShmFlags, ShmKey, IPC_PRIVATE},
ipc::shm::{ShmFlags, ShmKey, IPC_PRIVATE},
process::ProcessManager,
syscall::table::Syscall,
};
use log::error;
@ -34,7 +35,9 @@ pub(super) fn do_kernel_shmget(
return Err(SystemError::ENOSYS);
}
let mut shm_manager_guard = shm_manager_lock();
// 从当前进程的 IPC 命名空间获取 per-ns SHM 管理器
let ipcns = ProcessManager::current_ipcns();
let mut shm_manager_guard = ipcns.shm.lock();
match key {
// 创建共享内存段
IPC_PRIVATE => shm_manager_guard.add(key, size, shmflg),

View File

@ -6,7 +6,6 @@ use crate::{
arch::MMArch,
driver::serial::serial8250::send_to_default_serial8250_port,
filesystem::procfs::kmsg::kmsg_init,
ipc::shm::shm_manager_init,
libs::printk::PrintkWriter,
mm::{
allocator::slab::slab_init,
@ -59,8 +58,6 @@ pub unsafe fn mm_init() {
kmsg_init();
// enable PAGE_MANAGER
page_manager_init();
// enable SHM_MANAGER
shm_manager_init();
// enable PAGE_RECLAIMER
page_reclaimer_init();

View File

@ -19,7 +19,6 @@ use crate::{
exception::ipi::{IpiKind, IpiTarget},
filesystem::{page_cache::PageCache, vfs::FilePrivateData},
init::initcall::INITCALL_CORE,
ipc::shm::ShmId,
libs::{
rwlock::{RwLock, RwLockReadGuard, RwLockWriteGuard},
spinlock::{SpinLock, SpinLockGuard},
@ -672,7 +671,7 @@ pub enum PageType {
/// 文件映射页,含文件映射相关信息
File(FileMapInfo),
/// 共享内存页记录ShmId
Shm(ShmId),
Shm,
}
#[derive(Debug, Clone)]

View File

@ -21,7 +21,7 @@ use crate::{
arch::{mm::PageMapper, CurrentIrqArch, MMArch},
exception::InterruptArch,
filesystem::vfs::file::File,
ipc::shm::{shm_manager_lock, ShmFlags},
ipc::shm::{ShmFlags, ShmId},
libs::{
align::page_align_up,
rwlock::RwLock,
@ -36,7 +36,7 @@ use super::{
allocator::page_frame::{
deallocate_page_frames, PageFrameCount, PhysPageFrame, VirtPageFrame, VirtPageFrameIter,
},
page::{EntryFlags, Flusher, InactiveFlusher, PageFlushAll, PageType},
page::{EntryFlags, Flusher, InactiveFlusher, PageFlushAll},
syscall::{MadvFlags, MapFlags, MremapFlags, ProtFlags},
MemoryManagementArch, PageTableKind, VirtAddr, VirtRegion, VmFlags,
};
@ -1202,10 +1202,11 @@ impl LockedVMA {
if let Some((paddr, _flags)) = mapper.translate(guard.region().start()) {
// 如果是共享页,执行释放操作
let page = page_manager_guard.get(&paddr).unwrap();
let page_guard = page.read_irqsave();
if let PageType::Shm(shm_id) = page_guard.page_type() {
let mut shm_manager_guard = shm_manager_lock();
if let Some(kernel_shm) = shm_manager_guard.get_mut(shm_id) {
let _page_guard = page.read_irqsave();
if let Some(shm_id) = guard.shm_id {
let ipcns = ProcessManager::current_ipcns();
let mut shm_manager_guard = ipcns.shm.lock();
if let Some(kernel_shm) = shm_manager_guard.get_mut(&shm_id) {
// 更新最后一次断开连接时间
kernel_shm.update_dtim();
@ -1215,7 +1216,7 @@ impl LockedVMA {
// 释放shm_id
if kernel_shm.map_count() == 0 && kernel_shm.mode().contains(ShmFlags::SHM_DEST)
{
shm_manager_guard.free_id(shm_id);
shm_manager_guard.free_id(&shm_id);
}
}
}
@ -1405,6 +1406,17 @@ impl VMASplitResult {
}
}
/// Parameters for physmap operation
#[derive(Debug)]
pub struct PhysmapParams {
pub phys: PhysPageFrame,
pub destination: VirtPageFrame,
pub count: PageFrameCount,
pub vm_flags: VmFlags,
pub flags: EntryFlags<MMArch>,
pub shm_id: Option<ShmId>,
}
/// @brief 虚拟内存区域
#[derive(Debug)]
pub struct VMA {
@ -1425,6 +1437,8 @@ pub struct VMA {
file_pgoff: Option<usize>,
provider: Provider,
/// 关联的 SysV SHM 标识(当此 VMA 来自 shmat 时设置)
shm_id: Option<ShmId>,
}
impl core::hash::Hash for VMA {
@ -1461,6 +1475,7 @@ impl VMA {
provider: Provider::Allocated,
vm_file: file,
file_pgoff: pgoff,
shm_id: None,
}
}
@ -1496,6 +1511,11 @@ impl VMA {
self.flags = MMArch::vm_get_page_prot(self.vm_flags);
}
#[inline(always)]
pub fn set_shm_id(&mut self, shm: Option<ShmId>) {
self.shm_id = shm;
}
/// # 拷贝当前VMA的内容
///
/// ### 安全性
@ -1512,6 +1532,7 @@ impl VMA {
provider: Provider::Allocated,
file_pgoff: self.file_pgoff,
vm_file: self.vm_file.clone(),
shm_id: self.shm_id,
};
}
@ -1526,6 +1547,7 @@ impl VMA {
provider: Provider::Allocated,
file_pgoff: self.file_pgoff,
vm_file: self.vm_file.clone(),
shm_id: self.shm_id,
};
}
@ -1589,31 +1611,29 @@ impl VMA {
/// 把物理地址映射到虚拟地址
///
/// @param phys 要映射的物理地址
/// @param destination 要映射到的虚拟地址
/// @param count 要映射的页帧数量
/// @param flags 页面标志位
/// @param params 物理映射参数
/// @param mapper 页表映射器
/// @param flusher 页表项刷新器
///
/// @return 返回映射后的虚拟内存区域
pub fn physmap(
phys: PhysPageFrame,
destination: VirtPageFrame,
count: PageFrameCount,
vm_flags: VmFlags,
flags: EntryFlags<MMArch>,
params: PhysmapParams,
mapper: &mut PageMapper,
mut flusher: impl Flusher<MMArch>,
) -> Result<Arc<LockedVMA>, SystemError> {
let mut cur_phy = phys;
let mut cur_dest = destination;
let mut cur_phy = params.phys;
let mut cur_dest = params.destination;
for _ in 0..count.data() {
for _ in 0..params.count.data() {
// 将物理页帧映射到虚拟页帧
let r =
unsafe { mapper.map_phys(cur_dest.virt_address(), cur_phy.phys_address(), flags) }
.expect("Failed to map phys, may be OOM error");
let r = unsafe {
mapper.map_phys(
cur_dest.virt_address(),
cur_phy.phys_address(),
params.flags,
)
}
.expect("Failed to map phys, may be OOM error");
// todo: 增加OOM处理
@ -1625,18 +1645,24 @@ impl VMA {
}
let r: Arc<LockedVMA> = LockedVMA::new(VMA::new(
VirtRegion::new(destination.virt_address(), count.data() * MMArch::PAGE_SIZE),
vm_flags,
flags,
VirtRegion::new(
params.destination.virt_address(),
params.count.data() * MMArch::PAGE_SIZE,
),
params.vm_flags,
params.flags,
None,
None,
true,
));
if let Some(id) = params.shm_id {
r.lock_irqsave().set_shm_id(Some(id));
}
// 将VMA加入到anon_vma中
let mut page_manager_guard = page_manager_lock_irqsave();
cur_phy = phys;
for _ in 0..count.data() {
cur_phy = params.phys;
for _ in 0..params.count.data() {
let paddr = cur_phy.phys_address();
let page = page_manager_guard.get_unwrap(&paddr);
page.write_irqsave().insert_vma(r.clone());

View File

@ -0,0 +1,70 @@
use alloc::sync::{Arc, Weak};
use crate::ipc::shm::ShmManager;
use crate::libs::spinlock::SpinLock;
use crate::process::namespace::{
nsproxy::NsCommon, user_namespace::UserNamespace, NamespaceOps, NamespaceType,
};
use crate::process::ProcessManager;
// 根 IPC 命名空间
lazy_static::lazy_static! {
pub static ref INIT_IPC_NAMESPACE: Arc<IpcNamespace> = IpcNamespace::new_root();
}
/// DragonOS 的 IPC 命名空间
pub struct IpcNamespace {
ns_common: NsCommon,
self_ref: Weak<IpcNamespace>,
/// 关联的 user namespace (权限判断使用)
pub user_ns: Arc<UserNamespace>,
/// SysV SHM 管理器(阶段一:仅支持 per-ns shm
pub shm: SpinLock<ShmManager>,
}
impl NamespaceOps for IpcNamespace {
fn ns_common(&self) -> &NsCommon {
&self.ns_common
}
}
impl IpcNamespace {
fn new_root() -> Arc<Self> {
Arc::new_cyclic(|weak_self| Self {
ns_common: NsCommon::new(0, NamespaceType::Ipc),
self_ref: weak_self.clone(),
user_ns: crate::process::namespace::user_namespace::INIT_USER_NAMESPACE.clone(),
shm: SpinLock::new(ShmManager::new()),
})
}
/// 复制/创建 IPC 命名空间
pub fn copy_ipc_ns(
&self,
clone_flags: &crate::process::fork::CloneFlags,
user_ns: Arc<UserNamespace>,
) -> Arc<IpcNamespace> {
use crate::process::fork::CloneFlags;
if !clone_flags.contains(CloneFlags::CLONE_NEWIPC) {
return self.self_ref.upgrade().unwrap();
}
// 创建新的 IPC 命名空间SHM 空间独立
Arc::new_cyclic(|weak_self| IpcNamespace {
ns_common: NsCommon::new(self.ns_common.level + 1, NamespaceType::Ipc),
self_ref: weak_self.clone(),
user_ns,
shm: SpinLock::new(ShmManager::new()),
})
}
}
impl ProcessManager {
pub fn current_ipcns() -> Arc<IpcNamespace> {
if Self::initialized() {
ProcessManager::current_pcb().nsproxy.read().ipc_ns.clone()
} else {
INIT_IPC_NAMESPACE.clone()
}
}
}

View File

@ -1,3 +1,4 @@
pub mod ipc_namespace;
pub mod mnt;
pub mod nsproxy;
pub mod pid_namespace;

View File

@ -11,6 +11,7 @@ use crate::process::{
};
use core::{fmt::Debug, intrinsics::likely};
use super::ipc_namespace::{IpcNamespace, INIT_IPC_NAMESPACE};
use super::{pid_namespace::PidNamespace, user_namespace::UserNamespace, NamespaceType};
/// A structure containing references to all per-process namespaces (filesystem/mount, UTS, network, etc.).
@ -28,9 +29,10 @@ pub struct NsProxy {
/// mount namespace挂载命名空间
pub mnt_ns: Arc<MntNamespace>,
pub uts_ns: Arc<UtsNamespace>,
/// ipc namespaceSysV IPC、POSIX mqueue 等)
pub ipc_ns: Arc<IpcNamespace>,
// 其他namespace为未来扩展预留
// pub net_ns: Option<Arc<NetNamespace>>,
// pub ipc_ns: Option<Arc<IpcNamespace>>,
// pub cgroup_ns: Option<Arc<CgroupNamespace>>,
// pub time_ns: Option<Arc<TimeNamespace>>,
}
@ -47,10 +49,12 @@ impl NsProxy {
let root_pid_ns = super::pid_namespace::INIT_PID_NAMESPACE.clone();
let root_mnt_ns = root_mnt_namespace();
let root_uts_ns = INIT_UTS_NAMESPACE.clone();
let root_ipc_ns = INIT_IPC_NAMESPACE.clone();
Arc::new(Self {
pid_ns_for_children: root_pid_ns,
mnt_ns: root_mnt_ns,
uts_ns: root_uts_ns,
ipc_ns: root_ipc_ns,
})
}
@ -69,6 +73,7 @@ impl NsProxy {
pid_ns_for_children: self.pid_ns_for_children.clone(),
mnt_ns: self.mnt_ns.clone(),
uts_ns: self.uts_ns.clone(),
ipc_ns: self.ipc_ns.clone(),
}
}
}
@ -149,10 +154,12 @@ pub(super) fn create_new_namespaces(
let mnt_ns = nsproxy.mnt_ns.copy_mnt_ns(clone_flags, user_ns.clone())?;
let uts_ns = nsproxy.uts_ns.copy_uts_ns(clone_flags, user_ns.clone())?;
let ipc_ns = nsproxy.ipc_ns.copy_ipc_ns(clone_flags, user_ns.clone());
let result = NsProxy {
pid_ns_for_children,
mnt_ns,
uts_ns,
ipc_ns,
};
let result = Arc::new(result);

View File

@ -14,7 +14,7 @@ dependencies = [
"futures-sink",
"log",
"pin-project 0.4.30",
"tokio 0.2.25",
"tokio",
"tokio-util",
]
@ -117,7 +117,7 @@ dependencies = [
"futures-channel",
"futures-util",
"smallvec",
"tokio 0.2.25",
"tokio",
]
[[package]]
@ -133,11 +133,11 @@ dependencies = [
"futures-channel",
"futures-util",
"log",
"mio 0.6.23",
"mio",
"mio-uds",
"num_cpus",
"slab",
"socket2 0.3.19",
"socket2",
]
[[package]]
@ -161,7 +161,7 @@ dependencies = [
"actix-server",
"actix-service",
"log",
"socket2 0.3.19",
"socket2",
]
[[package]]
@ -245,7 +245,7 @@ dependencies = [
"serde",
"serde_json",
"serde_urlencoded",
"socket2 0.3.19",
"socket2",
"time",
"tinyvec",
"url",
@ -262,21 +262,6 @@ dependencies = [
"syn 1.0.109",
]
[[package]]
name = "addr2line"
version = "0.24.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dfbe277e56a376000877090da837660b4427aad530e3028d44e0bffe4f89a1c1"
dependencies = [
"gimli",
]
[[package]]
name = "adler2"
version = "2.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa"
[[package]]
name = "aho-corasick"
version = "1.1.3"
@ -327,21 +312,6 @@ dependencies = [
"serde_urlencoded",
]
[[package]]
name = "backtrace"
version = "0.3.75"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6806a6321ec58106fea15becdad98371e28d92ccbc7c8f1b3b6dd724fe8f1002"
dependencies = [
"addr2line",
"cfg-if 1.0.1",
"libc",
"miniz_oxide",
"object",
"rustc-demangle",
"windows-targets 0.52.6",
]
[[package]]
name = "base-x"
version = "0.2.11"
@ -552,7 +522,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "778e2ac28f6c47af28e4907f13ffd1e1ddbd400980a9abd7c8df189bf578a5ad"
dependencies = [
"libc",
"windows-sys 0.60.2",
"windows-sys",
]
[[package]]
@ -690,15 +660,9 @@ checksum = "8fc3cb4d91f53b50155bdcfd23f6a4c39ae1969c2ae85982b135750cccaf5fce"
dependencies = [
"cfg-if 1.0.1",
"libc",
"wasi 0.9.0+wasi-snapshot-preview1",
"wasi",
]
[[package]]
name = "gimli"
version = "0.31.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f"
[[package]]
name = "h2"
version = "0.2.7"
@ -713,7 +677,7 @@ dependencies = [
"http",
"indexmap",
"slab",
"tokio 0.2.25",
"tokio",
"tokio-util",
"tracing",
"tracing-futures",
@ -891,17 +855,6 @@ dependencies = [
"cfg-if 1.0.1",
]
[[package]]
name = "io-uring"
version = "0.7.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b86e202f00093dcba4275d4636b93ef9dd75d025ae560d2521b45ea28ab49013"
dependencies = [
"bitflags 2.9.1",
"cfg-if 1.0.1",
"libc",
]
[[package]]
name = "iovec"
version = "0.1.4"
@ -917,7 +870,7 @@ version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f7e2f18aece9709094573a9f24f483c4f65caa4298e2f7ae1b71cc65d853fad7"
dependencies = [
"socket2 0.3.19",
"socket2",
"widestring",
"winapi 0.3.9",
"winreg",
@ -1028,15 +981,6 @@ version = "0.3.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a"
[[package]]
name = "miniz_oxide"
version = "0.8.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316"
dependencies = [
"adler2",
]
[[package]]
name = "mio"
version = "0.6.23"
@ -1056,17 +1000,6 @@ dependencies = [
"winapi 0.2.8",
]
[[package]]
name = "mio"
version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "78bed444cc8a2160f01cbcf811ef18cac863ad68ae8ca62092e8db51d51c761c"
dependencies = [
"libc",
"wasi 0.11.1+wasi-snapshot-preview1",
"windows-sys 0.59.0",
]
[[package]]
name = "mio-uds"
version = "0.6.8"
@ -1075,7 +1008,7 @@ checksum = "afcb699eb26d4332647cc848492bbc15eafb26f08d0304550d5aa1f612e066f0"
dependencies = [
"iovec",
"libc",
"mio 0.6.23",
"mio",
]
[[package]]
@ -1123,15 +1056,6 @@ dependencies = [
"libc",
]
[[package]]
name = "object"
version = "0.36.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "62948e14d923ea95ea2c7c86c71013138b66525b86bdc08d2dcc262bdb497b87"
dependencies = [
"memchr",
]
[[package]]
name = "once_cell"
version = "1.21.3"
@ -1285,7 +1209,6 @@ dependencies = [
"nix",
"sc",
"syscalls",
"tokio 1.46.1",
]
[[package]]
@ -1388,12 +1311,6 @@ dependencies = [
"winapi 0.3.9",
]
[[package]]
name = "rustc-demangle"
version = "0.1.25"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "989e6739f80c4ad5b13e0fd7fe89531180375b18520cc8c82080e4dc4035b84f"
[[package]]
name = "rustc_version"
version = "0.2.3"
@ -1601,16 +1518,6 @@ dependencies = [
"winapi 0.3.9",
]
[[package]]
name = "socket2"
version = "0.5.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e22376abed350d73dd1cd119b57ffccad95b4e585a7cda43e286245ce23c0678"
dependencies = [
"libc",
"windows-sys 0.52.0",
]
[[package]]
name = "spin"
version = "0.5.2"
@ -1828,7 +1735,7 @@ dependencies = [
"lazy_static",
"libc",
"memchr",
"mio 0.6.23",
"mio",
"mio-uds",
"pin-project-lite 0.1.12",
"signal-hook-registry",
@ -1836,35 +1743,6 @@ dependencies = [
"winapi 0.3.9",
]
[[package]]
name = "tokio"
version = "1.46.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0cc3a2344dafbe23a245241fe8b09735b521110d30fcefbbd5feb1797ca35d17"
dependencies = [
"backtrace",
"io-uring",
"libc",
"mio 1.0.4",
"pin-project-lite 0.2.16",
"signal-hook-registry",
"slab",
"socket2 0.5.10",
"tokio-macros",
"windows-sys 0.52.0",
]
[[package]]
name = "tokio-macros"
version = "2.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6e06d43f1345a3bcd39f6a56dbb7dcab2ba47e68e8ac134855e7e2bdbaf8cab8"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.104",
]
[[package]]
name = "tokio-util"
version = "0.3.1"
@ -1876,7 +1754,7 @@ dependencies = [
"futures-sink",
"log",
"pin-project-lite 0.1.12",
"tokio 0.2.25",
"tokio",
]
[[package]]
@ -1925,7 +1803,7 @@ dependencies = [
"rand",
"smallvec",
"thiserror",
"tokio 0.2.25",
"tokio",
"url",
]
@ -1944,7 +1822,7 @@ dependencies = [
"resolv-conf",
"smallvec",
"thiserror",
"tokio 0.2.25",
"tokio",
"trust-dns-proto",
]
@ -2010,12 +1888,6 @@ version = "0.9.0+wasi-snapshot-preview1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519"
[[package]]
name = "wasi"
version = "0.11.1+wasi-snapshot-preview1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b"
[[package]]
name = "wasm-bindgen"
version = "0.2.100"
@ -2134,47 +2006,13 @@ version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
[[package]]
name = "windows-sys"
version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d"
dependencies = [
"windows-targets 0.52.6",
]
[[package]]
name = "windows-sys"
version = "0.59.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b"
dependencies = [
"windows-targets 0.52.6",
]
[[package]]
name = "windows-sys"
version = "0.60.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb"
dependencies = [
"windows-targets 0.53.2",
]
[[package]]
name = "windows-targets"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973"
dependencies = [
"windows_aarch64_gnullvm 0.52.6",
"windows_aarch64_msvc 0.52.6",
"windows_i686_gnu 0.52.6",
"windows_i686_gnullvm 0.52.6",
"windows_i686_msvc 0.52.6",
"windows_x86_64_gnu 0.52.6",
"windows_x86_64_gnullvm 0.52.6",
"windows_x86_64_msvc 0.52.6",
"windows-targets",
]
[[package]]
@ -2183,106 +2021,58 @@ version = "0.53.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c66f69fcc9ce11da9966ddb31a40968cad001c5bedeb5c2b82ede4253ab48aef"
dependencies = [
"windows_aarch64_gnullvm 0.53.0",
"windows_aarch64_msvc 0.53.0",
"windows_i686_gnu 0.53.0",
"windows_i686_gnullvm 0.53.0",
"windows_i686_msvc 0.53.0",
"windows_x86_64_gnu 0.53.0",
"windows_x86_64_gnullvm 0.53.0",
"windows_x86_64_msvc 0.53.0",
"windows_aarch64_gnullvm",
"windows_aarch64_msvc",
"windows_i686_gnu",
"windows_i686_gnullvm",
"windows_i686_msvc",
"windows_x86_64_gnu",
"windows_x86_64_gnullvm",
"windows_x86_64_msvc",
]
[[package]]
name = "windows_aarch64_gnullvm"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3"
[[package]]
name = "windows_aarch64_gnullvm"
version = "0.53.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "86b8d5f90ddd19cb4a147a5fa63ca848db3df085e25fee3cc10b39b6eebae764"
[[package]]
name = "windows_aarch64_msvc"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469"
[[package]]
name = "windows_aarch64_msvc"
version = "0.53.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c7651a1f62a11b8cbd5e0d42526e55f2c99886c77e007179efff86c2b137e66c"
[[package]]
name = "windows_i686_gnu"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b"
[[package]]
name = "windows_i686_gnu"
version = "0.53.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c1dc67659d35f387f5f6c479dc4e28f1d4bb90ddd1a5d3da2e5d97b42d6272c3"
[[package]]
name = "windows_i686_gnullvm"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66"
[[package]]
name = "windows_i686_gnullvm"
version = "0.53.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9ce6ccbdedbf6d6354471319e781c0dfef054c81fbc7cf83f338a4296c0cae11"
[[package]]
name = "windows_i686_msvc"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66"
[[package]]
name = "windows_i686_msvc"
version = "0.53.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "581fee95406bb13382d2f65cd4a908ca7b1e4c2f1917f143ba16efe98a589b5d"
[[package]]
name = "windows_x86_64_gnu"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78"
[[package]]
name = "windows_x86_64_gnu"
version = "0.53.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2e55b5ac9ea33f2fc1716d1742db15574fd6fc8dadc51caab1c16a3d3b4190ba"
[[package]]
name = "windows_x86_64_gnullvm"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d"
[[package]]
name = "windows_x86_64_gnullvm"
version = "0.53.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0a6e035dd0599267ce1ee132e51c27dd29437f63325753051e71dd9e42406c57"
[[package]]
name = "windows_x86_64_msvc"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
[[package]]
name = "windows_x86_64_msvc"
version = "0.53.0"

View File

@ -0,0 +1,206 @@
extern crate nix;
use nix::errno::Errno;
use nix::sched::{self, CloneFlags};
use nix::sys::wait::{waitpid, WaitStatus};
use nix::unistd::{fork, getpid, pipe, read, write, ForkResult};
const IPC_CREAT: i32 = 0o1000;
fn shmget(key: i32, size: usize, flags: i32) -> Result<i32, i32> {
let r = unsafe { libc::shmget(key as libc::key_t, size, flags as libc::c_int) };
if r < 0 {
return Err(Errno::last_raw());
}
Ok(r)
}
fn shmctl(id: i32, cmd: i32) -> Result<i32, i32> {
let r = unsafe { libc::shmctl(id, cmd, std::ptr::null_mut()) };
if r < 0 {
return Err(Errno::last_raw());
}
Ok(r)
}
fn shmat(id: i32) -> Result<*mut libc::c_void, i32> {
let r = unsafe { libc::shmat(id, std::ptr::null(), 0) };
if (r as isize) == -1 {
return Err(Errno::last_raw());
}
Ok(r)
}
fn shmdt(addr: *mut libc::c_void) -> Result<i32, i32> {
let r = unsafe { libc::shmdt(addr) };
if r < 0 {
return Err(Errno::last_raw());
}
Ok(r)
}
struct Suite {
passed: usize,
failed: usize,
}
impl Suite {
fn new() -> Self {
Self {
passed: 0,
failed: 0,
}
}
fn ok(&mut self, name: &str) {
self.passed += 1;
println!("[PASS] {}", name);
}
fn fail(&mut self, name: &str, msg: &str) {
self.failed += 1;
println!("[FAIL] {}: {}", name, msg);
}
fn finish(&self) -> i32 {
println!("Summary: passed={}, failed={}", self.passed, self.failed);
if self.failed == 0 {
0
} else {
1
}
}
}
fn main() {
let key_parent: i32 = 0x12345; // 仅父命名空间使用
let key_child: i32 = 0x23456; // 仅子命名空间使用
let size: usize = 4096;
let mut suite = Suite::new();
println!("[parent:{}] create shm in parent ns", getpid());
let id_parent = shmget(key_parent, size, IPC_CREAT | 0o600).expect("parent shmget failed");
let (pr, pw) = pipe().expect("pipe");
unsafe {
match fork() {
Ok(ForkResult::Parent { child }) => {
// case2: ensure child ns's shm is not visible to parent while child alive
// wait child signals via pipe
let mut buf = [0u8; 1];
use std::os::fd::AsRawFd;
let _ = read(pr.as_raw_fd(), &mut buf).expect("read pipe");
match shmget(key_child, size, 0o600) {
Ok(_) => suite.fail(
"parent cannot see child's shm by key_child",
"unexpected shm visible",
),
Err(e) => {
if e == libc::ENOENT {
suite.ok("parent cannot see child's shm by key_child");
} else {
suite.fail(
"parent cannot see child's shm by key_child",
&format!("errno={}", e),
);
}
}
}
match waitpid(child, None) {
Ok(WaitStatus::Exited(_, status)) => {
if status == 0 {
suite.ok("child exited 0");
} else {
suite.fail("child exited 0", &format!("status={}", status));
}
}
Ok(other) => suite.fail("child wait status", &format!("{:?}", other)),
Err(e) => suite.fail("waitpid", &format!("{:?}", e)),
}
// parent can still attach/detach its own segment and then remove
match shmat(id_parent) {
Ok(addr) => {
let _ = shmdt(addr)
.map_err(|e| suite.fail("parent shmdt", &format!("errno={}", e)));
let _ = shmctl(id_parent, libc::IPC_RMID)
.map_err(|e| suite.fail("parent IPC_RMID", &format!("errno={}", e)));
suite.ok("parent attach/detach and remove");
}
Err(e) => suite.fail("parent shmat", &format!("errno={}", e)),
}
std::process::exit(suite.finish());
}
Ok(ForkResult::Child) => {
println!("[child:{}] unshare CLONE_NEWIPC", getpid());
if let Err(e) = sched::unshare(CloneFlags::CLONE_NEWIPC) {
eprintln!("unshare failed: {:?}", e);
std::process::exit(2);
}
// case1: child can't see parent's shm by key
match shmget(key_parent, size, 0o600) {
Ok(_) => {
println!("[FAIL] child cannot see parent's shm by key");
std::process::exit(3);
}
Err(e) => {
if e == libc::ENOENT {
println!("[PASS] child cannot see parent's shm by key");
} else {
println!("[FAIL] child cannot see parent's shm by key: errno={}", e);
std::process::exit(3);
}
}
}
// create its own shm with a different key in new ipc ns
let id_child = match shmget(key_child, size, IPC_CREAT | 0o600) {
Ok(x) => x,
Err(e) => {
eprintln!("child shmget(key_child) failed errno={}", e);
std::process::exit(4);
}
};
// also create same key as parent in child ns to ensure no conflict across ns
let _id_child_same_key = match shmget(key_parent, size, IPC_CREAT | 0o600) {
Ok(x) => x,
Err(e) => {
eprintln!("child shmget(key_parent in child ns) failed errno={}", e);
std::process::exit(4);
}
};
// signal parent
let _ = write(pw, &[1u8]).expect("write pipe");
// shmat + write, then mark IPC_RMID and ensure we can create new shm with the same key
let addr = match shmat(id_child) {
Ok(p) => p,
Err(e) => {
eprintln!("child shmat failed errno={}", e);
std::process::exit(5);
}
};
unsafe {
*(addr as *mut u8) = 0xAB;
}
// mark to be removed
let _ = shmctl(id_child, libc::IPC_RMID).expect("IPC_RMID failed");
// now shmget with same key and EXCL should succeed with a new id
let id2 = shmget(key_child, size, IPC_CREAT | 0o600 | libc::IPC_EXCL)
.expect("child shmget recreate failed");
if id2 == id_child {
eprintln!("id2 should differ from id_child");
std::process::exit(6);
}
// detach old mapping
let _ = shmdt(addr).expect("child shmdt failed");
// cleanup new one
let addr2 = shmat(id2).expect("child shmat id2 failed");
let _ = shmdt(addr2).expect("child shmdt id2 failed");
let _ = shmctl(id2, libc::IPC_RMID).expect("child IPC_RMID id2 failed");
std::process::exit(0);
}
Err(err) => panic!("fork failed: {:?}", err),
}
}
}