Skip to content

Commit ecf7749

Browse files
authored
fix(procfs): resolve race condition in namespace file creation (#1413)
- Add proper error handling for concurrent namespace file creation - Move namespace type validation earlier in the creation process - Ensure child inodes are fully initialized before being visible in children map - Handle EEXIST race condition by re-checking children map Signed-off-by: longjin <longjin@DragonOS.org>
1 parent 147241e commit ecf7749

File tree

1 file changed

+46
-25
lines changed
  • kernel/src/filesystem/procfs

1 file changed

+46
-25
lines changed

kernel/src/filesystem/procfs/mod.rs

Lines changed: 46 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -669,33 +669,40 @@ impl LockedProcFSInode {
669669
&self,
670670
ns_name: &str,
671671
) -> Result<Arc<dyn IndexNode>, SystemError> {
672-
// Convert namespace name to type
673-
let ns_type = ThreadSelfNsFileType::try_from(ns_name)?;
672+
// Convert namespace name to type,validate early.
673+
ThreadSelfNsFileType::try_from(ns_name)?;
674674

675-
// Check if file already exists without triggering recursive lookup
675+
let name = DName::from(ns_name);
676+
677+
// Fast-path: check if file already exists under the children map.
676678
{
677-
let name = DName::from(ns_name);
678679
let guard = self.0.lock();
679680
if let Some(existing) = guard.children.get(&name) {
680681
return Ok(existing.clone());
681682
}
682683
}
683684

684-
// Create the namespace file. In Linux these appear as symlinks whose
685-
// contents look like "ipc:[4026531839]". For now we model them as
686-
// regular procfs files and implement the textual target via readlink,
687-
// while `open()` returns a namespace fd.
688-
let ns_file = self.create(ns_name, FileType::File, ModeType::from_bits_truncate(0o444))?;
689-
690-
let ns_file_proc = ns_file
691-
.as_any_ref()
692-
.downcast_ref::<LockedProcFSInode>()
693-
.unwrap();
694-
695-
// Store namespace type in fd field (as u8 cast to i32)
696-
ns_file_proc.0.lock().fdata.ftype = ProcFileType::ProcThreadSelfNsChild(ns_type);
697-
698-
Ok(ns_file)
685+
// Slow-path: try to create the namespace file. In Linux these appear as
686+
// symlinks whose contents look like "ipc:[4026531839]". For now we model
687+
// them as regular procfs files and implement the textual target via
688+
// readlink, while `open()` returns a namespace fd.
689+
match self.create(ns_name, FileType::File, ModeType::from_bits_truncate(0o444)) {
690+
Ok(ns_file) => Ok(ns_file),
691+
Err(SystemError::EEXIST) => {
692+
// Lost a concurrent race: another thread created the same entry
693+
// between our initial check and create(). Look it up again and
694+
// return the existing node instead of failing.
695+
let guard = self.0.lock();
696+
if let Some(existing) = guard.children.get(&name) {
697+
Ok(existing.clone())
698+
} else {
699+
// Extremely unlikely: filesystem reported EEXIST but the
700+
// child is not present in the in-memory map. Treat as ENOENT.
701+
Err(SystemError::ENOENT)
702+
}
703+
}
704+
Err(e) => Err(e),
705+
}
699706
}
700707

701708
fn dynamical_find_fd(&self, fd: &str) -> Result<Arc<dyn IndexNode>, SystemError> {
@@ -963,9 +970,9 @@ impl IndexNode for LockedProcFSInode {
963970
if inode.metadata.file_type != FileType::Dir {
964971
return Err(SystemError::ENOTDIR);
965972
}
966-
let name = DName::from(name);
973+
let dname = DName::from(name);
967974
// 如果有重名的,则返回
968-
if inode.children.contains_key(&name) {
975+
if inode.children.contains_key(&dname) {
969976
return Err(SystemError::EEXIST);
970977
}
971978

@@ -999,14 +1006,28 @@ impl IndexNode for LockedProcFSInode {
9991006
ftype: ProcFileType::Default,
10001007
fd: -1,
10011008
},
1002-
dname: name.clone(),
1009+
dname: dname.clone(),
10031010
})));
10041011

1005-
// 初始化inode的自引用的weak指针
1006-
result.0.lock().self_ref = Arc::downgrade(&result);
1012+
{
1013+
// 初始化子 inode 自引用 weak 指针,并在必要时为
1014+
// /proc/thread-self/ns/* 节点预先设置正确的 ftype,避免并发下
1015+
// 其他线程在 children.map 可见但 ftype 仍为 Default 的中间态。
1016+
let mut child_guard = result.0.lock();
1017+
child_guard.self_ref = Arc::downgrade(&result);
1018+
1019+
// Special case: creating entries under /proc/thread-self/ns
1020+
if let ProcFileType::ProcThreadSelfNsRoot = inode.fdata.ftype {
1021+
if let FileType::File = file_type {
1022+
// 名称必须是合法的 namespace 名称,否则直接返回错误并“回滚”创建。
1023+
let ns_type = ThreadSelfNsFileType::try_from(name)?;
1024+
child_guard.fdata.ftype = ProcFileType::ProcThreadSelfNsChild(ns_type);
1025+
}
1026+
}
1027+
}
10071028

10081029
// 将子inode插入父inode的B树中
1009-
inode.children.insert(name, result.clone());
1030+
inode.children.insert(dname, result.clone());
10101031

10111032
return Ok(result);
10121033
}

0 commit comments

Comments
 (0)