diff --git a/os/StarryOS/kernel/src/syscall/mod.rs b/os/StarryOS/kernel/src/syscall/mod.rs index 9321b6a562..b0033a7052 100644 --- a/os/StarryOS/kernel/src/syscall/mod.rs +++ b/os/StarryOS/kernel/src/syscall/mod.rs @@ -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 _), @@ -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 _), diff --git a/os/StarryOS/kernel/src/syscall/sys.rs b/os/StarryOS/kernel/src/syscall/sys.rs index 6e4183f7a3..233d6dcbc7 100644 --- a/os/StarryOS/kernel/src/syscall/sys.rs +++ b/os/StarryOS/kernel/src/syscall/sys.rs @@ -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; @@ -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). @@ -539,27 +538,62 @@ pub fn sys_setgroups(size: usize, list: *const u32) -> AxResult { 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 { + 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 { + 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> = vec![MaybeUninit::uninit(); len]; + vm_read_slice(name.cast::(), &mut buf)?; + let bytes: Vec = 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::(), 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 { - name.vm_write(UTSNAME)?; +pub fn sys_setdomainname(name: *const c_char, len: usize) -> AxResult { + 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> = vec![MaybeUninit::uninit(); len]; + vm_read_slice(name.cast::(), &mut buf)?; + // SAFETY: vm_read_slice filled buf with valid data from user space. + let bytes: Vec = 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::(), + domainname.as_mut_ptr(), + len, + ); + } + let proc_data = &curr.as_thread().proc_data; + proc_data.uts_ns.lock().lock().domainname = domainname; Ok(0) } diff --git a/os/StarryOS/kernel/src/syscall/task/clone.rs b/os/StarryOS/kernel/src/syscall/task/clone.rs index 8637b15474..0ea8eb6468 100644 --- a/os/StarryOS/kernel/src/syscall/task/clone.rs +++ b/os/StarryOS/kernel/src/syscall/task/clone.rs @@ -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(()) @@ -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) { diff --git a/os/StarryOS/kernel/src/syscall/task/mod.rs b/os/StarryOS/kernel/src/syscall/task/mod.rs index 2143a0e822..20af681957 100644 --- a/os/StarryOS/kernel/src/syscall/task/mod.rs +++ b/os/StarryOS/kernel/src/syscall/task/mod.rs @@ -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::*, }; diff --git a/os/StarryOS/kernel/src/syscall/task/namespace.rs b/os/StarryOS/kernel/src/syscall/task/namespace.rs new file mode 100644 index 0000000000..c3df89d66f --- /dev/null +++ b/os/StarryOS/kernel/src/syscall/task/namespace.rs @@ -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 { + 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) +} diff --git a/os/StarryOS/kernel/src/task/mod.rs b/os/StarryOS/kernel/src/task/mod.rs index 336cef3d57..e605436969 100644 --- a/os/StarryOS/kernel/src/task/mod.rs +++ b/os/StarryOS/kernel/src/task/mod.rs @@ -2,6 +2,7 @@ mod cred; pub mod futex; +mod namespace; mod ops; pub mod posix_timer; mod resources; @@ -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; @@ -511,6 +512,11 @@ pub struct ProcessData { aspace: SpinNoIrq>>, /// The resource scope pub scope: RwLock, + /// 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>>, /// The user heap top heap_top: AtomicUsize, @@ -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), diff --git a/os/StarryOS/kernel/src/task/namespace.rs b/os/StarryOS/kernel/src/task/namespace.rs new file mode 100644 index 0000000000..ccb80f2fbb --- /dev/null +++ b/os/StarryOS/kernel/src/task/namespace.rs @@ -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>> = 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, + } +} diff --git a/test-suit/starryos/normal/qemu-smp1/bugfix/bug-unshare-uts/c/CMakeLists.txt b/test-suit/starryos/normal/qemu-smp1/bugfix/bug-unshare-uts/c/CMakeLists.txt new file mode 100644 index 0000000000..d8ace1d5c7 --- /dev/null +++ b/test-suit/starryos/normal/qemu-smp1/bugfix/bug-unshare-uts/c/CMakeLists.txt @@ -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) diff --git a/test-suit/starryos/normal/qemu-smp1/bugfix/bug-unshare-uts/c/src/main.c b/test-suit/starryos/normal/qemu-smp1/bugfix/bug-unshare-uts/c/src/main.c new file mode 100644 index 0000000000..464bf1e956 --- /dev/null +++ b/test-suit/starryos/normal/qemu-smp1/bugfix/bug-unshare-uts/c/src/main.c @@ -0,0 +1,347 @@ +#define _GNU_SOURCE +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* ------------------------------------------------------------------ */ +/* Fallback definitions for older / minimal libc headers */ +/* ------------------------------------------------------------------ */ +#ifndef SYS_unshare +#define SYS_unshare 97 +#endif +#ifndef SYS_sethostname +#define SYS_sethostname 161 +#endif +#ifndef SYS_setdomainname +#define SYS_setdomainname 162 +#endif +#ifndef CLONE_NEWUTS +#define CLONE_NEWUTS 0x04000000 +#endif +#ifndef CLONE_NEWNS +#define CLONE_NEWNS 0x00020000 +#endif + +/* ------------------------------------------------------------------ */ +/* Test helpers */ +/* ------------------------------------------------------------------ */ +static int passed; +static int failed; + +static void note_pass(const char *name) +{ + printf(" PASS: %s\n", name); + passed++; +} + +static void note_fail(const char *name, const char *detail) +{ + printf(" FAIL: %s: %s\n", name, detail); + failed++; +} + +static int unshare_raw(int flags) +{ + return (int)syscall(SYS_unshare, flags); +} + +static int sethostname_raw(const char *name, size_t len) +{ + return (int)syscall(SYS_sethostname, name, len); +} + +static int setdomainname_raw(const char *name, size_t len) +{ + return (int)syscall(SYS_setdomainname, name, len); +} + +/* ------------------------------------------------------------------ */ +/* 1. unshare(CLONE_NEWUTS) succeeds */ +/* ------------------------------------------------------------------ */ +static void test_unshare_newuts(void) +{ + errno = 0; + int ret = unshare_raw(CLONE_NEWUTS); + if (ret == 0) { + note_pass("unshare(CLONE_NEWUTS) returns 0"); + } else { + char buf[128]; + snprintf(buf, sizeof(buf), "ret=%d errno=%d (%s)", ret, errno, strerror(errno)); + note_fail("unshare(CLONE_NEWUTS)", buf); + } +} + +/* ------------------------------------------------------------------ */ +/* 2. unshare rejects unsupported namespace flags */ +/* ------------------------------------------------------------------ */ +static void test_unshare_reject_newpid(void) +{ + errno = 0; + int ret = unshare_raw(0x20000000); /* CLONE_NEWPID */ + if (ret == -1 && errno == EINVAL) { + note_pass("unshare(NEWPID) returns EINVAL"); + } else { + char buf[128]; + snprintf(buf, sizeof(buf), "ret=%d errno=%d (%s), expected -1/EINVAL", + ret, errno, strerror(errno)); + note_fail("unshare(NEWPID) EINVAL", buf); + } +} + +/* ------------------------------------------------------------------ */ +/* 3. sethostname + uname round-trip in unshared namespace */ +/* ------------------------------------------------------------------ */ +static void test_sethostname_uname(void) +{ + /* Enter a fresh UTS namespace */ + if (unshare_raw(CLONE_NEWUTS) != 0) { + note_fail("sethostname+uname", "unshare failed, cannot run sub-test"); + return; + } + + /* Set hostname to a known value */ + const char *hn = "test-hostname-42"; + errno = 0; + if (sethostname_raw(hn, strlen(hn)) != 0) { + char buf[128]; + snprintf(buf, sizeof(buf), "sethostname errno=%d (%s)", errno, strerror(errno)); + note_fail("sethostname", buf); + return; + } + note_pass("sethostname succeeds"); + + /* Read back via uname */ + struct utsname uts; + errno = 0; + if (uname(&uts) != 0) { + char buf[128]; + snprintf(buf, sizeof(buf), "uname errno=%d (%s)", errno, strerror(errno)); + note_fail("uname after sethostname", buf); + return; + } + + if (strcmp(uts.nodename, hn) == 0) { + note_pass("uname nodename matches sethostname value"); + } else { + char buf[256]; + snprintf(buf, sizeof(buf), "nodename=\"%s\" expected=\"%s\"", + uts.nodename, hn); + note_fail("uname nodename", buf); + } +} + +/* ------------------------------------------------------------------ */ +/* 4. setdomainname + uname round-trip */ +/* ------------------------------------------------------------------ */ +static void test_setdomainname_uname(void) +{ + if (unshare_raw(CLONE_NEWUTS) != 0) { + note_fail("setdomainname+uname", "unshare failed"); + return; + } + + const char *dn = "test-domain-7"; + errno = 0; + if (setdomainname_raw(dn, strlen(dn)) != 0) { + char buf[128]; + snprintf(buf, sizeof(buf), "setdomainname errno=%d (%s)", errno, strerror(errno)); + note_fail("setdomainname", buf); + return; + } + note_pass("setdomainname succeeds"); + + struct utsname uts; + if (uname(&uts) != 0) { + note_fail("uname after setdomainname", "uname syscall failed"); + return; + } + + if (strcmp(uts.domainname, dn) == 0) { + note_pass("uname domainname matches setdomainname value"); + } else { + char buf[256]; + snprintf(buf, sizeof(buf), "domainname=\"%s\" expected=\"%s\"", + uts.domainname, dn); + note_fail("uname domainname", buf); + } +} + +/* ------------------------------------------------------------------ */ +/* 5. After fork, child inherits parent's hostname (clone_ns) */ +/* ------------------------------------------------------------------ */ +static void test_fork_inherits_hostname(void) +{ + if (unshare_raw(CLONE_NEWUTS) != 0) { + note_fail("fork-inherits", "unshare failed"); + return; + } + + const char *hn = "parent-hn"; + if (sethostname_raw(hn, strlen(hn)) != 0) { + note_fail("fork-inherits setup", "sethostname failed"); + return; + } + + int pipefd[2]; + if (pipe(pipefd) != 0) { + note_fail("fork-inherits", "pipe failed"); + return; + } + + pid_t pid = fork(); + if (pid < 0) { + note_fail("fork-inherits", "fork failed"); + close(pipefd[0]); + close(pipefd[1]); + return; + } + + if (pid == 0) { + /* Child: send nodename back to parent via pipe */ + close(pipefd[0]); + struct utsname uts; + int ok = (uname(&uts) == 0 && strcmp(uts.nodename, hn) == 0); + const char *name = ok ? hn : (uname(&uts) == 0 ? uts.nodename : "ERROR"); + write(pipefd[1], name, strlen(name) + 1); + close(pipefd[1]); + _exit(0); + } + + /* Parent: read child's nodename */ + close(pipefd[1]); + char child_name[65]; + ssize_t n = read(pipefd[0], child_name, sizeof(child_name)); + close(pipefd[0]); + + int status; + waitpid(pid, &status, 0); + + if (n > 0 && strcmp(child_name, hn) == 0) { + note_pass("fork child inherits parent hostname"); + } else { + char buf[256]; + snprintf(buf, sizeof(buf), "child nodename=\"%s\" expected=\"%s\" n=%zd", + n > 0 ? child_name : "(none)", hn, n); + note_fail("fork-inherits", buf); + } +} + +/* ------------------------------------------------------------------ */ +/* 6. Shared namespace: child hostname change visible to parent */ +/* ------------------------------------------------------------------ */ +static void test_shared_namespace_propagation(void) +{ + if (unshare_raw(CLONE_NEWUTS) != 0) { + note_fail("shared-ns", "unshare failed"); + return; + } + + const char *parent_hn = "parent-shared"; + if (sethostname_raw(parent_hn, strlen(parent_hn)) != 0) { + note_fail("shared-ns setup", "parent sethostname failed"); + return; + } + + int pipefd[2]; + if (pipe(pipefd) != 0) { + note_fail("shared-ns", "pipe failed"); + return; + } + + pid_t pid = fork(); + if (pid < 0) { + note_fail("shared-ns", "fork failed"); + close(pipefd[0]); + close(pipefd[1]); + return; + } + + if (pid == 0) { + close(pipefd[0]); + + /* Child: change to its own hostname in the shared namespace */ + const char *child_hn = "child-shared"; + if (sethostname_raw(child_hn, strlen(child_hn)) != 0) { + write(pipefd[1], "SETFAIL", 8); + close(pipefd[1]); + _exit(1); + } + + /* Verify child sees its own hostname */ + struct utsname uts; + if (uname(&uts) != 0) { + write(pipefd[1], "UNAMEFAIL", 10); + close(pipefd[1]); + _exit(1); + } + + write(pipefd[1], uts.nodename, strlen(uts.nodename) + 1); + close(pipefd[1]); + _exit(0); + } + + /* Parent */ + close(pipefd[1]); + char child_name[65]; + ssize_t n = read(pipefd[0], child_name, sizeof(child_name)); + close(pipefd[0]); + + int status; + waitpid(pid, &status, 0); + + if (n <= 0) { + note_fail("shared-ns", "failed to read child nodename"); + return; + } + + /* Parent should see the child's change because they share the + * same UTS namespace via Arc. */ + struct utsname uts; + if (uname(&uts) != 0) { + note_fail("shared-ns parent uname", "uname failed"); + return; + } + + const char *child_expected = "child-shared"; + int child_ok = (strcmp(child_name, child_expected) == 0); + int parent_ok = (strcmp(uts.nodename, child_expected) == 0); + + if (child_ok && parent_ok) { + note_pass("shared namespace: parent sees child hostname change"); + } else { + char buf[256]; + snprintf(buf, sizeof(buf), + "child=\"%s\"(exp=%s) parent=\"%s\"(exp=%s)", + child_name, child_expected, uts.nodename, child_expected); + note_fail("shared-ns", buf); + } +} + +/* ------------------------------------------------------------------ */ +/* main */ +/* ------------------------------------------------------------------ */ +int main(void) +{ + printf("=== bug-unshare-uts ===\n"); + + test_unshare_newuts(); + test_unshare_reject_newpid(); + test_sethostname_uname(); + test_setdomainname_uname(); + test_fork_inherits_hostname(); + test_shared_namespace_propagation(); + + printf("=== Results: %d passed, %d failed ===\n", passed, failed); + if (failed == 0) { + printf("ALL TESTS PASSED\n"); + return 0; + } + printf("SOME TESTS FAILED\n"); + return 1; +} diff --git a/test-suit/starryos/normal/qemu-smp1/bugfix/qemu-aarch64.toml b/test-suit/starryos/normal/qemu-smp1/bugfix/qemu-aarch64.toml index 30836bfb3b..b1db4dc20d 100644 --- a/test-suit/starryos/normal/qemu-smp1/bugfix/qemu-aarch64.toml +++ b/test-suit/starryos/normal/qemu-smp1/bugfix/qemu-aarch64.toml @@ -93,6 +93,7 @@ test_commands = [ "/usr/bin/bug-open-fifo-wronly-no-reader-no-enxio", "/usr/bin/bug-prctl-set-vma-anon-name", "/usr/bin/bug-waitid-basic", + "/usr/bin/bug-unshare-uts", ] success_regex = ["(?m)^STARRY_GROUPED_TESTS_PASSED\\s*$"] fail_regex = ['(?i)\bpanic(?:ked)?\b', '(?m)^STARRY_GROUPED_TEST_FAILED:'] diff --git a/test-suit/starryos/normal/qemu-smp1/bugfix/qemu-loongarch64.toml b/test-suit/starryos/normal/qemu-smp1/bugfix/qemu-loongarch64.toml index 037fd24a91..0b97f4b0c7 100644 --- a/test-suit/starryos/normal/qemu-smp1/bugfix/qemu-loongarch64.toml +++ b/test-suit/starryos/normal/qemu-smp1/bugfix/qemu-loongarch64.toml @@ -94,6 +94,7 @@ test_commands = [ "/usr/bin/bug-open-fifo-wronly-no-reader-no-enxio", "/usr/bin/bug-prctl-set-vma-anon-name", "/usr/bin/bug-waitid-basic", + "/usr/bin/bug-unshare-uts", ] success_regex = ["(?m)^STARRY_GROUPED_TESTS_PASSED\\s*$"] fail_regex = ['(?i)\bpanic(?:ked)?\b', '(?m)^STARRY_GROUPED_TEST_FAILED:'] diff --git a/test-suit/starryos/normal/qemu-smp1/bugfix/qemu-riscv64.toml b/test-suit/starryos/normal/qemu-smp1/bugfix/qemu-riscv64.toml index 6ffb6e8bc3..b005358603 100644 --- a/test-suit/starryos/normal/qemu-smp1/bugfix/qemu-riscv64.toml +++ b/test-suit/starryos/normal/qemu-smp1/bugfix/qemu-riscv64.toml @@ -102,6 +102,7 @@ test_commands = [ "/usr/bin/bug-open-fifo-wronly-no-reader-no-enxio", "/usr/bin/bug-prctl-set-vma-anon-name", "/usr/bin/bug-waitid-basic", + "/usr/bin/bug-unshare-uts", ] success_regex = ["(?m)^STARRY_GROUPED_TESTS_PASSED\\s*$"] fail_regex = ['(?i)\bpanic(?:ked)?\b', '(?m)^STARRY_GROUPED_TEST_FAILED:'] diff --git a/test-suit/starryos/normal/qemu-smp1/bugfix/qemu-x86_64.toml b/test-suit/starryos/normal/qemu-smp1/bugfix/qemu-x86_64.toml index 7c6c35f3e8..cef7c82c12 100644 --- a/test-suit/starryos/normal/qemu-smp1/bugfix/qemu-x86_64.toml +++ b/test-suit/starryos/normal/qemu-smp1/bugfix/qemu-x86_64.toml @@ -99,6 +99,7 @@ test_commands = [ "/usr/bin/bug-open-fifo-wronly-no-reader-no-enxio", "/usr/bin/bug-prctl-set-vma-anon-name", "/usr/bin/bug-waitid-basic", + "/usr/bin/bug-unshare-uts", ] success_regex = ["(?m)^STARRY_GROUPED_TESTS_PASSED\\s*$"] fail_regex = ['(?i)\bpanic(?:ked)?\b', '(?m)^STARRY_GROUPED_TEST_FAILED:']