diff --git a/kernel/src/filesystem/fat/entry.rs b/kernel/src/filesystem/fat/entry.rs index b5ee37869..2860c2064 100644 --- a/kernel/src/filesystem/fat/entry.rs +++ b/kernel/src/filesystem/fat/entry.rs @@ -16,7 +16,7 @@ use log::{debug, warn}; use system_error::SystemError; use super::{ - fs::{Cluster, FATFileSystem, MAX_FILE_SIZE}, + fs::{Cluster, FATFileSystem, MAX_FILE_SIZE, ZERO_BUF_SIZE}, utils::decode_u8_ascii, }; @@ -197,10 +197,13 @@ impl FATFile { // 计算本次写入位置在分区上的偏移量 let offset = fs.cluster_bytes_offset(current_cluster) + in_cluster_bytes_offset; - // 写入磁盘 + // 写入磁盘(防御性检查:若设备返回0字节写入视为IO错误) let w = fs .gendisk .write_at_bytes(&buf[start..start + end_len], offset as usize)?; + if w == 0 { + return Err(SystemError::EIO); + } // 更新偏移量数据 write_ok += w; @@ -322,8 +325,22 @@ impl FATFile { return Ok(()); } - let zeroes: Vec = vec![0u8; (range_end - range_start) as usize]; - fs.gendisk.write_at_bytes(&zeroes, range_start as usize)?; + // 限制每次写入的缓冲区大小,避免大文件扩展时分配过大内存 + let zeroes: Vec = vec![0u8; ZERO_BUF_SIZE]; + let mut offset = range_start; + let mut remain = (range_end - range_start) as usize; + + while remain > 0 { + let write_size = core::cmp::min(remain, ZERO_BUF_SIZE); + let w = fs + .gendisk + .write_at_bytes(&zeroes[..write_size], offset as usize)?; + if w == 0 { + return Err(SystemError::EIO); + } + offset += write_size as u64; + remain -= write_size; + } return Ok(()); } diff --git a/kernel/src/filesystem/fat/fs.rs b/kernel/src/filesystem/fat/fs.rs index d98c886d8..c180ada1b 100644 --- a/kernel/src/filesystem/fat/fs.rs +++ b/kernel/src/filesystem/fat/fs.rs @@ -1,4 +1,7 @@ +use crate::arch::MMArch; use crate::filesystem::vfs::syscall::RenameFlags; +use crate::mm::truncate::truncate_inode_pages; +use crate::mm::MemoryManagementArch; use alloc::string::ToString; use alloc::{ string::String, @@ -48,6 +51,9 @@ const FAT_MAX_NAMELEN: u64 = 255; /// FAT32文件系统的最大的文件大小 pub const MAX_FILE_SIZE: u64 = 0xffff_ffff; +/// 每次清零写入时的缓冲区上限(避免分配过大内存) +pub const ZERO_BUF_SIZE: usize = 512 * 1024; // 512KB + /// @brief 表示当前簇和上一个簇的关系的结构体 /// 定义这样一个结构体的原因是,FAT文件系统的文件中,前后两个簇具有关联关系。 #[allow(dead_code)] @@ -1749,8 +1755,14 @@ impl IndexNode for LockedFATInode { Ok(()) } fn resize(&self, len: usize) -> Result<(), SystemError> { - // 先调整页缓存大小,但不要提前返回;后续仍需同步到底层文件并更新元数据 + //检查是否超过fat支持的最大容量 + if (len as u64) > MAX_FILE_SIZE { + return Err(SystemError::EFBIG); + } + // 先调整页缓存:清除被截断区间的缓存页,再缩容缓存大小 if let Some(page_cache) = self.page_cache() { + let start_page = (len + MMArch::PAGE_SIZE - 1) >> MMArch::PAGE_SHIFT; + truncate_inode_pages(page_cache.clone(), start_page); page_cache.lock_irqsave().resize(len)?; } @@ -1767,16 +1779,13 @@ impl IndexNode for LockedFATInode { } Ordering::Greater => { // 如果新的长度比旧的长度大,那么就在文件末尾添加空白 - let mut buf: Vec = Vec::new(); - let mut remain_size = len - old_size; - let buf_size = remain_size; - // let buf_size = core::cmp::min(remain_size, 512 * 1024); - buf.resize(buf_size, 0); + let buf: Vec = vec![0u8; ZERO_BUF_SIZE]; + let mut remain_size = len - old_size; let mut offset = old_size; while remain_size > 0 { - let write_size = core::cmp::min(remain_size, buf_size); - file.write(fs, &buf[0..write_size], offset as u64)?; + let write_size = core::cmp::min(remain_size, ZERO_BUF_SIZE); + file.write(fs, &buf[..write_size], offset as u64)?; remain_size -= write_size; offset += write_size; } diff --git a/kernel/src/filesystem/vfs/file.rs b/kernel/src/filesystem/vfs/file.rs index 14a185344..25e106b8f 100644 --- a/kernel/src/filesystem/vfs/file.rs +++ b/kernel/src/filesystem/vfs/file.rs @@ -8,6 +8,7 @@ use log::error; use system_error::SystemError; use super::{FileType, IndexNode, InodeId, Metadata, SpecialNodeData}; +use crate::{arch::ipc::signal::Signal, filesystem::vfs::InodeFlags, process::pid::PidPrivateData}; use crate::{ arch::MMArch, driver::{ @@ -37,7 +38,6 @@ use crate::{ ProcessControlBlock, ProcessManager, RawPid, }, }; -use crate::{filesystem::vfs::InodeFlags, process::pid::PidPrivateData}; const MAX_LFS_FILESIZE: i64 = i64::MAX; /// Namespace fd backing data, typically created from /proc/thread-self/ns/* files. @@ -988,8 +988,30 @@ impl File { /// @return 成功:Ok() /// 失败:Err(错误码) pub fn ftruncate(&self, len: usize) -> Result<(), SystemError> { - // 如果文件不可写,返回错误 - self.writeable()?; + // 类型必须是普通文件,否则 EINVAL + let md = self.inode.metadata()?; + if md.file_type != FileType::File { + return Err(SystemError::EINVAL); + } + + // O_PATH 直接返回 EBADF,保持与 open 时的行为一致 + let mode = *self.mode.read(); + if mode.contains(FileMode::FMODE_PATH) { + return Err(SystemError::EBADF); + } + + // 非可写打开返回 EINVAL(对齐 gVisor 预期) + if !mode.contains(FileMode::FMODE_WRITE) || !mode.can_write() { + return Err(SystemError::EINVAL); + } + + // RLIMIT_FSIZE 检查 + let current_pcb = ProcessManager::current_pcb(); + let fsize_limit = current_pcb.get_rlimit(RLimitID::Fsize); + if fsize_limit.rlim_cur != u64::MAX && len as u64 > fsize_limit.rlim_cur { + let _ = send_signal_to_pid(current_pcb.raw_pid(), Signal::SIGXFSZ); + return Err(SystemError::EFBIG); + } // 统一通过 VFS 封装,复用类型/只读检查 crate::filesystem::vfs::vcore::vfs_truncate(self.inode(), len)?; diff --git a/kernel/src/filesystem/vfs/syscall/sys_ftruncate.rs b/kernel/src/filesystem/vfs/syscall/sys_ftruncate.rs index 7d8f73acb..5f6d84c1c 100644 --- a/kernel/src/filesystem/vfs/syscall/sys_ftruncate.rs +++ b/kernel/src/filesystem/vfs/syscall/sys_ftruncate.rs @@ -31,7 +31,7 @@ impl Syscall for SysFtruncateHandle { } fn handle(&self, args: &[usize], _frame: &mut TrapFrame) -> Result { let fd = Self::fd(args); - let len = Self::len(args); + let len = Self::len(args)?; let binding = ProcessManager::current_pcb().fd_table(); let fd_table_guard = binding.read(); @@ -49,7 +49,7 @@ impl Syscall for SysFtruncateHandle { fn entry_format(&self, args: &[usize]) -> Vec { vec![ FormattedSyscallParam::new("fd", format!("{:#x}", Self::fd(args))), - FormattedSyscallParam::new("len", format!("{:#x}", Self::len(args))), + FormattedSyscallParam::new("len", format!("{:#x}", Self::len(args).unwrap_or(0))), ] } } @@ -59,8 +59,12 @@ impl SysFtruncateHandle { args[0] as i32 } - fn len(args: &[usize]) -> usize { - args[1] + fn len(args: &[usize]) -> Result { + let len = args[1]; + if len > isize::MAX as usize { + return Err(SystemError::EINVAL); + } + Ok(len) } } diff --git a/kernel/src/filesystem/vfs/syscall/sys_truncate.rs b/kernel/src/filesystem/vfs/syscall/sys_truncate.rs index 0e503a17e..f632af118 100644 --- a/kernel/src/filesystem/vfs/syscall/sys_truncate.rs +++ b/kernel/src/filesystem/vfs/syscall/sys_truncate.rs @@ -1,10 +1,12 @@ -use crate::arch::syscall::nr::SYS_TRUNCATE; +use crate::arch::{ipc::signal::Signal, syscall::nr::SYS_TRUNCATE}; use crate::{ arch::interrupt::TrapFrame, filesystem::vfs::{ - fcntl::AtFlags, utils::user_path_at, IndexNode, MAX_PATHLEN, VFS_MAX_FOLLOW_SYMLINK_TIMES, + fcntl::AtFlags, permission::PermissionMask, utils::user_path_at, FileType, IndexNode, + MAX_PATHLEN, VFS_MAX_FOLLOW_SYMLINK_TIMES, }, - process::ProcessManager, + ipc::kill::send_signal_to_pid, + process::{resource::RLimitID, ProcessManager}, syscall::{ table::{FormattedSyscallParam, Syscall}, user_access::check_and_clone_cstr, @@ -33,8 +35,7 @@ impl Syscall for SysTruncateHandle { fn handle(&self, args: &[usize], _frame: &mut TrapFrame) -> Result { let path_ptr = args[0] as *const u8; - let length = args[1]; - + let length = Self::len(args)?; // 复制并校验用户态路径 let path = check_and_clone_cstr(path_ptr, Some(MAX_PATHLEN))?; let path = path.to_str().map_err(|_| SystemError::EINVAL)?; @@ -50,6 +51,21 @@ impl Syscall for SysTruncateHandle { let target: Arc = begin_inode .lookup_follow_symlink(remain_path.as_str(), VFS_MAX_FOLLOW_SYMLINK_TIMES)?; + let md = target.metadata()?; + // DAC write permission check + let cred = ProcessManager::current_pcb().cred(); + cred.inode_permission(&md, PermissionMask::MAY_WRITE.bits())?; + + // RLIMIT_FSIZE enforcement for regular files + if md.file_type == FileType::File { + let fsize_limit = ProcessManager::current_pcb().get_rlimit(RLimitID::Fsize); + if fsize_limit.rlim_cur != u64::MAX && length as u64 > fsize_limit.rlim_cur { + let _ = + send_signal_to_pid(ProcessManager::current_pcb().raw_pid(), Signal::SIGXFSZ); + return Err(SystemError::EFBIG); + } + } + vfs_truncate(target, length)?; Ok(0) } @@ -62,4 +78,14 @@ impl Syscall for SysTruncateHandle { } } +impl SysTruncateHandle { + fn len(args: &[usize]) -> Result { + let len = args[1]; + if len > isize::MAX as usize { + return Err(SystemError::EINVAL); + } + Ok(len) + } +} + syscall_table_macros::declare_syscall!(SYS_TRUNCATE, SysTruncateHandle); diff --git a/kernel/src/filesystem/vfs/vcore.rs b/kernel/src/filesystem/vfs/vcore.rs index b5842fbef..d64af2de8 100644 --- a/kernel/src/filesystem/vfs/vcore.rs +++ b/kernel/src/filesystem/vfs/vcore.rs @@ -372,6 +372,11 @@ pub(super) fn do_file_lookup_at( pub fn vfs_truncate(inode: Arc, len: usize) -> Result<(), SystemError> { let md = inode.metadata()?; + // 防御性检查:统一拒绝超出 isize::MAX 的长度,避免后续类型转换溢出 + if len > isize::MAX as usize { + return Err(SystemError::EINVAL); + } + if md.file_type == FileType::Dir { return Err(SystemError::EISDIR); } diff --git a/user/apps/tests/syscall/gvisor/blocklists/truncate_test b/user/apps/tests/syscall/gvisor/blocklists/truncate_test new file mode 100644 index 000000000..36f093ed0 --- /dev/null +++ b/user/apps/tests/syscall/gvisor/blocklists/truncate_test @@ -0,0 +1 @@ +TruncateTest.FtruncateVirtualTmp diff --git a/user/apps/tests/syscall/gvisor/whitelist.txt b/user/apps/tests/syscall/gvisor/whitelist.txt index 4af4fc66c..bda2cfd62 100644 --- a/user/apps/tests/syscall/gvisor/whitelist.txt +++ b/user/apps/tests/syscall/gvisor/whitelist.txt @@ -33,6 +33,7 @@ preadv_test pwrite64_test pwritev2_test utimes_test +truncate_test fadvise_test open_test