From 87091aa19c8b1a73bee85d7346b1fa67dcccadf6 Mon Sep 17 00:00:00 2001 From: jiangjianfeng Date: Thu, 18 Sep 2025 09:40:51 +0000 Subject: [PATCH] Add syscall sethostname --- book/src/kernel/linux-compatibility/README.md | 2 +- kernel/src/net/uts_ns.rs | 58 +++++++++++++++++-- kernel/src/syscall/arch/loongarch.rs | 2 + kernel/src/syscall/arch/riscv.rs | 2 + kernel/src/syscall/arch/x86.rs | 4 +- kernel/src/syscall/mod.rs | 1 + kernel/src/syscall/sethostname.rs | 10 ++++ kernel/src/syscall/uname.rs | 2 +- test/src/syscall/ltp/testcases/all.txt | 5 +- 9 files changed, 75 insertions(+), 11 deletions(-) create mode 100644 kernel/src/syscall/sethostname.rs diff --git a/book/src/kernel/linux-compatibility/README.md b/book/src/kernel/linux-compatibility/README.md index 06268218b..cfb7ad274 100644 --- a/book/src/kernel/linux-compatibility/README.md +++ b/book/src/kernel/linux-compatibility/README.md @@ -190,7 +190,7 @@ provided by Linux on x86-64 architecture. | 167 | swapon | ❌ | | | 168 | swapoff | ❌ | | | 169 | reboot | ❌ | | -| 170 | sethostname | ❌ | | +| 170 | sethostname | ✅ | | | 171 | setdomainname | ❌ | | | 172 | iopl | ❌ | | | 173 | ioperm | ❌ | | diff --git a/kernel/src/net/uts_ns.rs b/kernel/src/net/uts_ns.rs index 3a9217854..620beb577 100644 --- a/kernel/src/net/uts_ns.rs +++ b/kernel/src/net/uts_ns.rs @@ -1,5 +1,6 @@ // SPDX-License-Identifier: MPL-2.0 +use ostd::sync::RwMutexReadGuard; use spin::Once; use crate::{ @@ -10,7 +11,7 @@ use crate::{ /// The UTS namespace. pub struct UtsNamespace { - uts_name: UtsName, + uts_name: RwMutex, owner: Arc, } @@ -34,7 +35,10 @@ impl UtsNamespace { let owner = UserNamespace::get_init_singleton().clone(); - Arc::new(Self { uts_name, owner }) + Arc::new(Self { + uts_name: RwMutex::new(uts_name), + owner, + }) }) } @@ -46,7 +50,7 @@ impl UtsNamespace { ) -> Result> { owner.check_cap(CapSet::SYS_ADMIN, posix_thread)?; Ok(Arc::new(Self { - uts_name: self.uts_name, + uts_name: RwMutex::new(*self.uts_name.read()), owner, })) } @@ -56,9 +60,25 @@ impl UtsNamespace { &self.owner } - /// Returns the UTS name. - pub fn uts_name(&self) -> &UtsName { - &self.uts_name + /// Returns a read-only lock guard for accessing the UTS name. + pub fn uts_name(&self) -> RwMutexReadGuard<'_, UtsName> { + self.uts_name.read() + } + + /// Sets a new hostname for the UTS namespace. + /// + /// This method will fail with `EPERM` if the caller does not have the SYS_ADMIN capability + /// in the owner user namespace. + pub fn set_hostname(&self, addr: Vaddr, len: usize, ctx: &Context) -> Result<()> { + self.owner.check_cap(CapSet::SYS_ADMIN, ctx.posix_thread)?; + + let new_host_name = copy_uts_field_from_user(addr, len as _, ctx)?; + debug!( + "set host name: {:?}", + CStr::from_bytes_until_nul(new_host_name.as_bytes()).unwrap() + ); + self.uts_name.write().nodename = new_host_name; + Ok(()) } } @@ -74,3 +94,29 @@ pub struct UtsName { machine: [u8; UTS_FIELD_LEN], domainname: [u8; UTS_FIELD_LEN], } + +fn copy_uts_field_from_user(addr: Vaddr, len: u32, ctx: &Context) -> Result<[u8; UTS_FIELD_LEN]> { + if len.cast_signed() < 0 { + return_errno_with_message!(Errno::EINVAL, "the buffer length cannot be negative"); + } + + let user_space = ctx.user_space(); + let mut reader = user_space.reader(addr, len as usize)?; + + // UTS fields represent C strings, which must be nul-terminated. + // Therefore, the user-provided buffer length cannot exceed `UTS_FIELD_LEN - 1` + // to ensure space for the terminating nul byte. + if reader.remain() > UTS_FIELD_LEN - 1 { + return_errno_with_message!(Errno::EINVAL, "the UTS name is too long"); + } + + let mut buffer = [0u8; UTS_FIELD_LEN]; + + // Partial reads are acceptable, + // but an error is returned if no bytes can be read successfully. + if let Err((err, 0)) = reader.read_fallible(&mut VmWriter::from(buffer.as_mut_slice())) { + return Err(err.into()); + } + + Ok(buffer) +} diff --git a/kernel/src/syscall/arch/loongarch.rs b/kernel/src/syscall/arch/loongarch.rs index 195db4b34..d40d5f75b 100644 --- a/kernel/src/syscall/arch/loongarch.rs +++ b/kernel/src/syscall/arch/loongarch.rs @@ -117,6 +117,7 @@ use super::{ setfsuid::sys_setfsuid, setgid::sys_setgid, setgroups::sys_setgroups, + sethostname::sys_sethostname, setitimer::{sys_getitimer, sys_setitimer}, setns::sys_setns, setpgid::sys_setpgid, @@ -283,6 +284,7 @@ impl_syscall_nums_and_dispatch_fn! { SYS_GETGROUPS = 158 => sys_getgroups(args[..2]); SYS_SETGROUPS = 159 => sys_setgroups(args[..2]); SYS_NEWUNAME = 160 => sys_uname(args[..1]); + SYS_SETHOSTNAME = 161 => sys_sethostname(args[..2]); SYS_GETRUSAGE = 165 => sys_getrusage(args[..2]); SYS_UMASK = 166 => sys_umask(args[..1]); SYS_PRCTL = 167 => sys_prctl(args[..5]); diff --git a/kernel/src/syscall/arch/riscv.rs b/kernel/src/syscall/arch/riscv.rs index d163466fe..493e9d972 100644 --- a/kernel/src/syscall/arch/riscv.rs +++ b/kernel/src/syscall/arch/riscv.rs @@ -117,6 +117,7 @@ use super::{ setfsuid::sys_setfsuid, setgid::sys_setgid, setgroups::sys_setgroups, + sethostname::sys_sethostname, setitimer::{sys_getitimer, sys_setitimer}, setns::sys_setns, setpgid::sys_setpgid, @@ -283,6 +284,7 @@ impl_syscall_nums_and_dispatch_fn! { SYS_GETGROUPS = 158 => sys_getgroups(args[..2]); SYS_SETGROUPS = 159 => sys_setgroups(args[..2]); SYS_NEWUNAME = 160 => sys_uname(args[..1]); + SYS_SETHOSTNAME = 161 => sys_sethostname(args[..2]); SYS_GETRLIMIT = 163 => sys_getrlimit(args[..2]); SYS_SETRLIMIT = 164 => sys_setrlimit(args[..2]); SYS_GETRUSAGE = 165 => sys_getrusage(args[..2]); diff --git a/kernel/src/syscall/arch/x86.rs b/kernel/src/syscall/arch/x86.rs index 205e4dbed..c76e48368 100644 --- a/kernel/src/syscall/arch/x86.rs +++ b/kernel/src/syscall/arch/x86.rs @@ -128,6 +128,7 @@ use super::{ setfsuid::sys_setfsuid, setgid::sys_setgid, setgroups::sys_setgroups, + sethostname::sys_sethostname, setitimer::{sys_getitimer, sys_setitimer}, setns::sys_setns, setpgid::sys_setpgid, @@ -306,7 +307,8 @@ impl_syscall_nums_and_dispatch_fn! { SYS_CHROOT = 161 => sys_chroot(args[..1]); SYS_SYNC = 162 => sys_sync(args[..0]); SYS_MOUNT = 165 => sys_mount(args[..5]); - SYS_UMOUNT2 = 166 => sys_umount(args[..2]); + SYS_UMOUNT2 = 166 => sys_umount(args[..2]); + SYS_SETHOSTNAME = 170 => sys_sethostname(args[..2]); SYS_GETTID = 186 => sys_gettid(args[..0]); SYS_SETXATTR = 188 => sys_setxattr(args[..5]); SYS_LSETXATTR = 189 => sys_lsetxattr(args[..5]); diff --git a/kernel/src/syscall/mod.rs b/kernel/src/syscall/mod.rs index 7f90f3777..d54bb4516 100644 --- a/kernel/src/syscall/mod.rs +++ b/kernel/src/syscall/mod.rs @@ -144,6 +144,7 @@ mod setfsgid; mod setfsuid; mod setgid; mod setgroups; +mod sethostname; mod setitimer; mod setns; mod setpgid; diff --git a/kernel/src/syscall/sethostname.rs b/kernel/src/syscall/sethostname.rs new file mode 100644 index 000000000..43fe390c1 --- /dev/null +++ b/kernel/src/syscall/sethostname.rs @@ -0,0 +1,10 @@ +// SPDX-License-Identifier: MPL-2.0 + +use crate::{prelude::*, syscall::SyscallReturn}; + +pub fn sys_sethostname(addr: Vaddr, len: usize, ctx: &Context) -> Result { + let ns_proxy_ref = ctx.thread_local.borrow_ns_proxy(); + let ns_proxy = ns_proxy_ref.unwrap(); + ns_proxy.uts_ns().set_hostname(addr, len, ctx)?; + Ok(SyscallReturn::Return(0)) +} diff --git a/kernel/src/syscall/uname.rs b/kernel/src/syscall/uname.rs index 40f7d0437..95862fb12 100644 --- a/kernel/src/syscall/uname.rs +++ b/kernel/src/syscall/uname.rs @@ -6,6 +6,6 @@ pub fn sys_uname(old_uname_addr: Vaddr, ctx: &Context) -> Result debug!("old uname addr = 0x{:x}", old_uname_addr); let ns_proxy = ctx.thread_local.borrow_ns_proxy(); let uts_name = ns_proxy.unwrap().uts_ns().uts_name(); - ctx.user_space().write_val(old_uname_addr, uts_name)?; + ctx.user_space().write_val(old_uname_addr, &*uts_name)?; Ok(SyscallReturn::Return(0)) } diff --git a/test/src/syscall/ltp/testcases/all.txt b/test/src/syscall/ltp/testcases/all.txt index ee52c21c0..31e85d353 100644 --- a/test/src/syscall/ltp/testcases/all.txt +++ b/test/src/syscall/ltp/testcases/all.txt @@ -1379,8 +1379,9 @@ setgroups02 # setgroups03 # setgroups03_16 -# sethostname01 -# sethostname02 +sethostname01 +sethostname02 +# TODO: Drop capabilities on UID changes, so that sethostname() will fail with EPERM. # sethostname03 # setitimer01