Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions os/StarryOS/kernel/src/syscall/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -547,6 +547,7 @@ pub fn handle_syscall(uctx: &mut UserContext) {
Sysno::fork => sys_fork(uctx),
#[cfg(target_arch = "x86_64")]
Sysno::vfork => sys_vfork(uctx),
Sysno::unshare => sys_unshare(uctx.arg0() as _),
Sysno::exit => sys_exit(uctx.arg0() as _),
Sysno::exit_group => sys_exit_group(uctx.arg0() as _),
Sysno::wait4 => sys_waitpid(uctx.arg0() as _, uctx.arg1() as _, uctx.arg2() as _),
Expand Down Expand Up @@ -629,6 +630,8 @@ pub fn handle_syscall(uctx: &mut UserContext) {
Sysno::setgroups => sys_setgroups(uctx.arg0() as _, uctx.arg1() as _),
Sysno::setfsuid => sys_setfsuid(uctx.arg0() as _),
Sysno::setfsgid => sys_setfsgid(uctx.arg0() as _),
Sysno::sethostname => sys_sethostname(uctx.arg0() as _, uctx.arg1() as _),
Sysno::setdomainname => sys_setdomainname(uctx.arg0() as _, uctx.arg1() as _),
Sysno::uname => sys_uname(uctx.arg0() as _),
Sysno::sysinfo => sys_sysinfo(uctx.arg0() as _),
Sysno::syslog => sys_syslog(uctx.arg0() as _, uctx.arg1() as _, uctx.arg2() as _),
Expand Down
72 changes: 53 additions & 19 deletions os/StarryOS/kernel/src/syscall/sys.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
use alloc::{sync::Arc, vec, vec::Vec};
use core::{ffi::c_char, mem::MaybeUninit};

use ax_config::ARCH;
use ax_errno::{AxError, AxResult};
use ax_fs::FS_CONTEXT;
use ax_sync::Mutex;
Expand All @@ -18,7 +17,7 @@ use starry_vm::{VmMutPtr, vm_read_slice, vm_write_slice};

#[cfg(target_arch = "riscv64")]
use crate::mm::UserPtr;
use crate::task::{AsThread, processes};
use crate::task::{AsThread, build_utsname, processes};

/// Sentinel value meaning "don't change this ID" (userspace passes -1 as signed,
/// which becomes `u32::MAX` after the `as u32` cast in the dispatch table).
Expand Down Expand Up @@ -539,27 +538,62 @@ pub fn sys_setgroups(size: usize, list: *const u32) -> AxResult<isize> {
Ok(0)
}

const fn pad_str(info: &str) -> [c_char; 65] {
let mut data: [c_char; 65] = [0; 65];
// this needs #![feature(const_copy_from_slice)]
// data[..info.len()].copy_from_slice(info.as_bytes());
pub fn sys_uname(name: *mut new_utsname) -> AxResult<isize> {
let curr = current();
// Build the utsname inside a block so the SpinNoIrq guard is dropped
// before we touch user memory via vm_write (access_user_memory requires
// IRQs enabled, but SpinNoIrq disables them).
let uts = {
let ns_arc = curr.as_thread().proc_data.uts_ns.lock();
let ns = ns_arc.lock();
build_utsname(&ns)
};
name.vm_write(uts)?;
Ok(0)
}

pub fn sys_sethostname(name: *const c_char, len: usize) -> AxResult<isize> {
if len > 64 {
return Err(AxError::InvalidInput);
}
let curr = current();
if curr.as_thread().cred().euid != 0 {
return Err(AxError::OperationNotPermitted);
}
let mut buf: Vec<MaybeUninit<u8>> = vec![MaybeUninit::uninit(); len];
vm_read_slice(name.cast::<u8>(), &mut buf)?;
let bytes: Vec<u8> = unsafe { buf.into_iter().map(|v| v.assume_init()).collect() };
let mut nodename: [c_char; 65] = [0; 65];
unsafe {
core::ptr::copy_nonoverlapping(info.as_ptr().cast(), data.as_mut_ptr(), info.len());
core::ptr::copy_nonoverlapping(bytes.as_ptr().cast::<c_char>(), nodename.as_mut_ptr(), len);
}
data
let proc_data = &curr.as_thread().proc_data;
proc_data.uts_ns.lock().lock().nodename = nodename;
Ok(0)
}

const UTSNAME: new_utsname = new_utsname {
sysname: pad_str("Linux"),
nodename: pad_str("starry"),
release: pad_str("10.0.0"),
version: pad_str("10.0.0"),
machine: pad_str(ARCH),
domainname: pad_str("https://github.com/Starry-OS/StarryOS"),
};

pub fn sys_uname(name: *mut new_utsname) -> AxResult<isize> {
name.vm_write(UTSNAME)?;
pub fn sys_setdomainname(name: *const c_char, len: usize) -> AxResult<isize> {
if len > 64 {
return Err(AxError::InvalidInput);
}
let curr = current();
if curr.as_thread().cred().euid != 0 {
return Err(AxError::OperationNotPermitted);
}
let mut buf: Vec<MaybeUninit<u8>> = vec![MaybeUninit::uninit(); len];
vm_read_slice(name.cast::<u8>(), &mut buf)?;
// SAFETY: vm_read_slice filled buf with valid data from user space.
let bytes: Vec<u8> = unsafe { buf.into_iter().map(|v| v.assume_init()).collect() };
let mut domainname: [c_char; 65] = [0; 65];
unsafe {
core::ptr::copy_nonoverlapping(
bytes.as_ptr().cast::<c_char>(),
domainname.as_mut_ptr(),
len,
);
}
let proc_data = &curr.as_thread().proc_data;
proc_data.uts_ns.lock().lock().domainname = domainname;
Ok(0)
}

Expand Down
17 changes: 15 additions & 2 deletions os/StarryOS/kernel/src/syscall/task/clone.rs
Original file line number Diff line number Diff line change
Expand Up @@ -121,11 +121,13 @@ impl CloneArgs {
| CloneFlags::NEWNET
| CloneFlags::NEWPID
| CloneFlags::NEWUSER
| CloneFlags::NEWUTS
| CloneFlags::NEWCGROUP;

// CLONE_NEWUTS is supported; other namespace flags are not yet
// implemented and we reject them.
if flags.intersects(namespace_flags) {
warn!("sys_clone/sys_clone3: namespace flags detected, stub support only");
error!("sys_clone/sys_clone3: unsupported namespace flags {flags:?}");
return Err(AxError::InvalidInput);
}

Ok(())
Expand Down Expand Up @@ -242,6 +244,17 @@ impl CloneArgs {
// fork child PR_GET_DUMPABLE returns 0.
proc_data.set_dumpable(old_proc_data.dumpable());

// Inherit the parent's UTS namespace. With the Arc model,
// the default path shares the same Arc so that changes by
// one process are visible to all processes in the namespace.
// CLONE_NEWUTS creates a fresh private namespace instead.
if flags.contains(CloneFlags::NEWUTS) {
let new_inner = old_proc_data.uts_ns.lock().lock().clone_ns();
*proc_data.uts_ns.lock() = alloc::sync::Arc::new(ax_kspin::SpinNoIrq::new(new_inner));
} else {
*proc_data.uts_ns.lock() = old_proc_data.uts_ns.lock().clone();
}

{
let mut scope = proc_data.scope.write();
if flags.contains(CloneFlags::FILES) {
Expand Down
4 changes: 3 additions & 1 deletion os/StarryOS/kernel/src/syscall/task/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,12 @@ mod ctl;
mod execve;
mod exit;
mod job;
mod namespace;
mod schedule;
mod thread;
mod wait;

pub use self::{
clone::*, clone3::*, ctl::*, execve::*, exit::*, job::*, schedule::*, thread::*, wait::*,
clone::*, clone3::*, ctl::*, execve::*, exit::*, job::*, namespace::*, schedule::*, thread::*,
wait::*,
};
28 changes: 28 additions & 0 deletions os/StarryOS/kernel/src/syscall/task/namespace.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
use alloc::sync::Arc;
use ax_errno::{AxError, AxResult};
use ax_kspin::SpinNoIrq;
use ax_task::current;
use linux_raw_sys::general::CLONE_NEWUTS;

use crate::task::AsThread;

/// unshare(2) — disassociate parts of the process execution context.
///
/// Currently only `CLONE_NEWUTS` is supported; other namespace flags return
/// `EINVAL`.
pub fn sys_unshare(flags: u32) -> AxResult<isize> {
if flags & !CLONE_NEWUTS != 0 {
debug!("sys_unshare: unsupported flags {:#x}", flags);
return Err(AxError::InvalidInput);
}

if flags & CLONE_NEWUTS != 0 {
let curr = current();
let proc_data = &curr.as_thread().proc_data;
let mut guard = proc_data.uts_ns.lock();
let new_inner = guard.lock().clone_ns();
*guard = Arc::new(SpinNoIrq::new(new_inner));
}

Ok(0)
}
12 changes: 10 additions & 2 deletions os/StarryOS/kernel/src/task/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

mod cred;
pub mod futex;
mod namespace;
mod ops;
pub mod posix_timer;
mod resources;
Expand Down Expand Up @@ -31,8 +32,8 @@ use starry_signal::{
};

pub use self::{
cred::*, futex::*, ops::*, posix_timer::PosixTimerTable, resources::*, signal::*, stat::*,
timer::*, user::*,
cred::*, futex::*, namespace::*, ops::*, posix_timer::PosixTimerTable, resources::*, signal::*,
stat::*, timer::*, user::*,
};
#[cfg(feature = "kcov")]
use crate::kcov::KcovThreadState;
Expand Down Expand Up @@ -511,6 +512,11 @@ pub struct ProcessData {
aspace: SpinNoIrq<Arc<Mutex<AddrSpace>>>,
/// The resource scope
pub scope: RwLock<Scope>,
/// The UTS namespace (hostname, domainname) of this process.
/// Wrapped in `Arc` so that processes in the same namespace share
/// the same `UtNamespace`; changes made by one process are visible
/// to all processes in the namespace.
pub uts_ns: SpinNoIrq<Arc<SpinNoIrq<UtNamespace>>>,
/// The user heap top
heap_top: AtomicUsize,

Expand Down Expand Up @@ -621,6 +627,8 @@ impl ProcessData {

futex_table: Arc::new(FutexTable::new()),

uts_ns: SpinNoIrq::new(ROOT_UTS_NS.clone()),

vfork_done: SpinNoIrq::new(None),

umask: AtomicU32::new(0o022),
Expand Down
62 changes: 62 additions & 0 deletions os/StarryOS/kernel/src/task/namespace.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
use alloc::sync::Arc;
use core::ffi::c_char;

use ax_config::ARCH;
use ax_kspin::SpinNoIrq;

/// The initial root UTS namespace, shared by all processes until
/// they call `unshare(CLONE_NEWUTS)`.
pub static ROOT_UTS_NS: spin::Lazy<Arc<SpinNoIrq<UtNamespace>>> = spin::Lazy::new(|| {
Arc::new(SpinNoIrq::new(UtNamespace::new_root()))
});

const fn pad_str(info: &str) -> [c_char; 65] {
let mut data: [c_char; 65] = [0; 65];
unsafe {
core::ptr::copy_nonoverlapping(info.as_ptr().cast(), data.as_mut_ptr(), info.len());
}
data
}

/// Per-process UTS namespace, containing the hostname and domain name
/// visible to `uname(2)`. When a process calls `unshare(CLONE_NEWUTS)` or
/// `clone(CLONE_NEWUTS)`, it receives a fresh copy of the parent namespace
/// so that subsequent `sethostname(2)` / `setdomainname(2)` do not affect
/// the original namespace.
pub struct UtNamespace {
pub nodename: [c_char; 65],
pub domainname: [c_char; 65],
}

impl UtNamespace {
/// Create the initial root UTS namespace with default values.
pub fn new_root() -> Self {
Self {
nodename: pad_str("starry"),
domainname: pad_str("https://github.com/Starry-OS/StarryOS"),
}
}

/// Clone the namespace (shallow copy of nodename/domainname).
pub fn clone_ns(&self) -> Self {
Self {
nodename: self.nodename,
domainname: self.domainname,
}
}
}

/// Build a `new_utsname` from the calling process's UTS namespace.
/// The `sysname`, `release`, `version`, and `machine` fields are
/// system-wide constants; only `nodename` and `domainname` are
/// per-namespace.
pub fn build_utsname(ns: &UtNamespace) -> linux_raw_sys::system::new_utsname {
linux_raw_sys::system::new_utsname {
sysname: pad_str("Linux"),
nodename: ns.nodename,
release: pad_str("10.0.0"),
version: pad_str("10.0.0"),
machine: pad_str(ARCH),
domainname: ns.domainname,
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
cmake_minimum_required(VERSION 3.20)
project(bug-unshare-uts C)

set(CMAKE_C_STANDARD 11)
set(CMAKE_C_STANDARD_REQUIRED ON)
set(CMAKE_C_EXTENSIONS ON)

add_executable(bug-unshare-uts src/main.c)
target_compile_options(bug-unshare-uts PRIVATE -Wall -Wextra -Werror)

install(TARGETS bug-unshare-uts RUNTIME DESTINATION usr/bin)
Loading
Loading