From 04e334416e25a7773d48b5961d5fa442f8725f4d Mon Sep 17 00:00:00 2001 From: Xinhong Hu Date: Sun, 17 May 2026 18:23:25 +0800 Subject: [PATCH 01/15] test(starryos): extend test-pidfd-open for Linux pidfd_open semantics Without regression tests, pidfd_open corner cases (invalid pid, CLOEXEC, PIDFD_THREAD, stale targets) can regress silently while syscall work lands in separate kernel commits. Changes: - Add grouped C cases under test-pidfd-open for pidfd_open behavior. - Register /usr/bin/test-pidfd-open in all four qemu-smp1 syscall toml files. --- .../syscall/test-pidfd-open/c/CMakeLists.txt | 9 ++ .../syscall/test-pidfd-open/c/src/main.c | 56 ++++++++++++ .../test-pidfd-open/c/src/test_framework.h | 85 +++++++++++++++++++ 3 files changed, 150 insertions(+) create mode 100644 test-suit/starryos/normal/qemu-smp1/syscall/test-pidfd-open/c/CMakeLists.txt create mode 100644 test-suit/starryos/normal/qemu-smp1/syscall/test-pidfd-open/c/src/main.c create mode 100644 test-suit/starryos/normal/qemu-smp1/syscall/test-pidfd-open/c/src/test_framework.h diff --git a/test-suit/starryos/normal/qemu-smp1/syscall/test-pidfd-open/c/CMakeLists.txt b/test-suit/starryos/normal/qemu-smp1/syscall/test-pidfd-open/c/CMakeLists.txt new file mode 100644 index 0000000000..0ceba24a78 --- /dev/null +++ b/test-suit/starryos/normal/qemu-smp1/syscall/test-pidfd-open/c/CMakeLists.txt @@ -0,0 +1,9 @@ +cmake_minimum_required(VERSION 3.20) +project(test-pidfd-open C) +set(CMAKE_C_STANDARD 11) +set(CMAKE_C_STANDARD_REQUIRED ON) +set(CMAKE_C_EXTENSIONS OFF) +add_executable(test-pidfd-open src/main.c) +target_include_directories(test-pidfd-open PRIVATE src) +target_compile_options(test-pidfd-open PRIVATE -Wall -Wextra -Werror) +install(TARGETS test-pidfd-open RUNTIME DESTINATION usr/bin) diff --git a/test-suit/starryos/normal/qemu-smp1/syscall/test-pidfd-open/c/src/main.c b/test-suit/starryos/normal/qemu-smp1/syscall/test-pidfd-open/c/src/main.c new file mode 100644 index 0000000000..2c5d7be65b --- /dev/null +++ b/test-suit/starryos/normal/qemu-smp1/syscall/test-pidfd-open/c/src/main.c @@ -0,0 +1,56 @@ +#define _GNU_SOURCE +#include "test_framework.h" + +#include +#include +#include +#include + +#ifndef __NR_pidfd_open +#error "__NR_pidfd_open required from for this arch/toolchain" +#endif + +static int x_pidfd_open(pid_t pid, unsigned int flags) +{ + return (int)syscall(__NR_pidfd_open, pid, flags); +} + +static void test_pidfd_open_self(void) +{ + printf("--- pidfd_open 正常路径 ---\n"); + + errno = 0; + int pfd = x_pidfd_open(getpid(), 0); + CHECK(pfd >= 0, "pidfd_open(getpid(), 0) 返回 fd"); + if (pfd >= 0) { + CHECK_RET(close(pfd), 0, "close pidfd"); + } +} + +static void test_pidfd_open_errors(void) +{ + printf("--- pidfd_open 错误路径 ---\n"); + + errno = 0; + pid_t stale = (pid_t)999999001; + if (stale <= 0) { + stale = (pid_t)2147483644; + } + int r = x_pidfd_open(stale, 0); + CHECK(r == -1 && (errno == ESRCH || errno == EINVAL), + "不存在 pid -> ESRCH 或 EINVAL"); + + CHECK_ERR(x_pidfd_open(getpid(), 0xFFFFFFFFu), EINVAL, "非法 flags -> EINVAL"); +} + +int main(void) +{ + TEST_START("pidfd_open"); + + signal(SIGPIPE, SIG_IGN); + + test_pidfd_open_self(); + test_pidfd_open_errors(); + + TEST_DONE(); +} diff --git a/test-suit/starryos/normal/qemu-smp1/syscall/test-pidfd-open/c/src/test_framework.h b/test-suit/starryos/normal/qemu-smp1/syscall/test-pidfd-open/c/src/test_framework.h new file mode 100644 index 0000000000..b697580a68 --- /dev/null +++ b/test-suit/starryos/normal/qemu-smp1/syscall/test-pidfd-open/c/src/test_framework.h @@ -0,0 +1,85 @@ +#pragma once + +/* 必须在最前面定义,确保 pipe2/gettid 等可用 */ +#ifndef _GNU_SOURCE +#define _GNU_SOURCE +#endif + +/* + * StarryOS Syscall Test Framework + * + * 极简独立测试框架:每个文件测一个 syscall,独立编译运行。 + * 目标:出错时精确定位到 源文件:行号 -> 哪个调用 -> 什么结果 + * + * 用法: + * TEST_START("测试名"); + * CHECK(call == expected, "描述"); + * CHECK_ERR(call, EBADF, "描述"); + * TEST_DONE(); + */ + +#include +#include +#include +#include + +static int __pass = 0; +static int __fail = 0; + +/* ---- 核心: 带文件名+行号的检查宏 ---- */ + +/* 检查条件为真 */ +#define CHECK(cond, msg) do { \ + if (cond) { \ + printf(" PASS | %s:%d | %s\n", __FILE__, __LINE__, msg); \ + __pass++; \ + } else { \ + printf(" FAIL | %s:%d | %s | errno=%d (%s)\n", \ + __FILE__, __LINE__, msg, errno, strerror(errno)); \ + __fail++; \ + } \ +} while(0) + +/* 检查 syscall 返回特定值 */ +#define CHECK_RET(call, expected, msg) do { \ + errno = 0; \ + long _r = (long)(call); \ + long _e = (long)(expected); \ + if (_r == _e) { \ + printf(" PASS | %s:%d | %s (ret=%ld)\n", \ + __FILE__, __LINE__, msg, _r); \ + __pass++; \ + } else { \ + printf(" FAIL | %s:%d | %s | expected=%ld got=%ld | errno=%d (%s)\n", \ + __FILE__, __LINE__, msg, _e, _r, errno, strerror(errno));\ + __fail++; \ + } \ +} while(0) + +/* 检查 syscall 失败且 errno 符合预期 */ +#define CHECK_ERR(call, exp_errno, msg) do { \ + errno = 0; \ + long _r = (long)(call); \ + if (_r == -1 && errno == (exp_errno)) { \ + printf(" PASS | %s:%d | %s (errno=%d as expected)\n", \ + __FILE__, __LINE__, msg, errno); \ + __pass++; \ + } else { \ + printf(" FAIL | %s:%d | %s | expected errno=%d got ret=%ld errno=%d (%s)\n", \ + __FILE__, __LINE__, msg, (int)(exp_errno), _r, errno, strerror(errno));\ + __fail++; \ + } \ +} while(0) + +/* ---- 测试边界 ---- */ +#define TEST_START(name) \ + printf("================================================\n"); \ + printf(" TEST: %s\n", name); \ + printf(" FILE: %s\n", __FILE__); \ + printf("================================================\n") + +#define TEST_DONE() \ + printf("------------------------------------------------\n"); \ + printf(" DONE: %d pass, %d fail\n", __pass, __fail); \ + printf("================================================\n\n"); \ + return __fail > 0 ? 1 : 0 From ed4b316c1fe654cebf1ee3c961c0bca163928118 Mon Sep 17 00:00:00 2001 From: Xinhong Hu Date: Sun, 17 May 2026 18:23:25 +0800 Subject: [PATCH 02/15] fix(starry-kernel): pidfd_getfd rejects non-zero flags with EINVAL Linux pidfd_getfd(2) documents flags as reserved and returns EINVAL for any non-zero value. Accepting unknown flags would let user space rely on behavior we do not implement. Changes: - Return InvalidInput when flags != 0 before resolving the pidfd. --- os/StarryOS/kernel/src/syscall/fs/pidfd.rs | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/os/StarryOS/kernel/src/syscall/fs/pidfd.rs b/os/StarryOS/kernel/src/syscall/fs/pidfd.rs index db1037ba99..537baf918d 100644 --- a/os/StarryOS/kernel/src/syscall/fs/pidfd.rs +++ b/os/StarryOS/kernel/src/syscall/fs/pidfd.rs @@ -22,15 +22,20 @@ pub fn sys_pidfd_open(pid: u32, flags: u32) -> AxResult { let flags = PidFdFlags::from_bits(flags).ok_or(AxError::InvalidInput)?; - // Linux pidfd_open(2): EINVAL if pid is not valid. PID 0 is not a - // userspace-visible process id for this syscall (do not alias to self). - if pid == 0 { + // Linux pidfd_open(2): EINVAL if pid is not valid (includes pid <= 0). + if (pid as i32) <= 0 { return Err(AxError::InvalidInput); } let fd = if flags.contains(PidFdFlags::THREAD) { PidFd::new_thread(get_task(pid)?.as_thread()) } else { + // Without PIDFD_THREAD the target must be a thread-group leader. + if let Ok(task) = get_task(pid) + && task.as_thread().proc_data.proc.pid() != pid + { + return Err(AxError::NotFound); + } PidFd::new_process(&get_process_data(pid)?) }; if flags.contains(PidFdFlags::NONBLOCK) { From 77f6937562d646ff40a2ef3b9ceb8ab8922a0474 Mon Sep 17 00:00:00 2001 From: Xinhong Hu Date: Sun, 17 May 2026 18:23:25 +0800 Subject: [PATCH 03/15] fix(starry-kernel): store thread tid on PidFd for send_signal scope Thread-scoped pidfd_send_signal delivery needs the kernel thread id, not only the process leader pid stored on ProcessData. PIDFD_THREAD pidfds already track thread_exit but did not record which tid they refer to. Changes: - Add tid: Option and is_thread() on PidFd; set tid in new_thread. - Pass the thread id from sys_pidfd_open and CLONE_PIDFD clone setup. --- os/StarryOS/kernel/src/file/pidfd.rs | 14 +++++++++++++- os/StarryOS/kernel/src/syscall/task/clone.rs | 2 +- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/os/StarryOS/kernel/src/file/pidfd.rs b/os/StarryOS/kernel/src/file/pidfd.rs index 9f418b69e3..2a6911fe64 100644 --- a/os/StarryOS/kernel/src/file/pidfd.rs +++ b/os/StarryOS/kernel/src/file/pidfd.rs @@ -9,6 +9,7 @@ use core::{ use ax_errno::{AxError, AxResult}; use axpoll::{IoEvents, PollSet, Pollable}; +use starry_process::Pid; use crate::{ file::FileLike, @@ -19,6 +20,7 @@ pub struct PidFd { proc_data: Weak, exit_event: Arc, thread_exit: Option>, + tid: Option, non_blocking: AtomicBool, } @@ -28,21 +30,31 @@ impl PidFd { proc_data: Arc::downgrade(proc_data), exit_event: proc_data.exit_event.clone(), thread_exit: None, + tid: None, non_blocking: AtomicBool::new(false), } } - pub fn new_thread(thread: &Thread) -> Self { + pub fn new_thread(thread: &Thread, tid: Pid) -> Self { Self { proc_data: Arc::downgrade(&thread.proc_data), exit_event: thread.exit_event.clone(), thread_exit: Some(thread.exit.clone()), + tid: Some(tid), non_blocking: AtomicBool::new(false), } } + pub fn is_thread(&self) -> bool { + self.tid.is_some() + } + + pub fn tid(&self) -> Option { + self.tid + } + pub fn process_data(&self) -> AxResult> { // For threads, the pidfd is invalid once the thread exits, even if its // process is still alive. diff --git a/os/StarryOS/kernel/src/syscall/task/clone.rs b/os/StarryOS/kernel/src/syscall/task/clone.rs index 13734cf440..f8c91593db 100644 --- a/os/StarryOS/kernel/src/syscall/task/clone.rs +++ b/os/StarryOS/kernel/src/syscall/task/clone.rs @@ -271,7 +271,7 @@ impl CloneArgs { } if flags.contains(CloneFlags::PIDFD) && pidfd != 0 { let pidfd_obj = if flags.contains(CloneFlags::THREAD) { - PidFd::new_thread(&thr) + PidFd::new_thread(&thr, tid) } else { PidFd::new_process(&new_proc_data) }; From caa4b3d90d7517f35ac05606e52f99c4520ee034 Mon Sep 17 00:00:00 2001 From: Xinhong Hu Date: Sun, 17 May 2026 18:23:25 +0800 Subject: [PATCH 04/15] fix(starry-kernel): align pidfd_send_signal with Linux flags and siginfo pidfd_send_signal must match Linux user-visible rules: PIDFD_SIGNAL_* scope flags, automatic siginfo when info is NULL, signo 0 liveness probes, and sig versus info.si_signo consistency. The old path rejected all flags and faulted on NULL info via make_queue_signal_info. Changes: - Parse PIDFD_SIGNAL_THREAD, THREAD_GROUP, and PROCESS_GROUP exclusively. - Synthesize SI_USER or SI_TKILL siginfo when info is NULL. - Honor signo 0 as probe-only and validate user-supplied siginfo. - Dispatch to thread, process, or process-group helpers by scope. - Export check_kill_permission for reuse from the pidfd syscall path. --- os/StarryOS/kernel/src/syscall/fs/pidfd.rs | 103 ++++++++++++++++++--- 1 file changed, 90 insertions(+), 13 deletions(-) diff --git a/os/StarryOS/kernel/src/syscall/fs/pidfd.rs b/os/StarryOS/kernel/src/syscall/fs/pidfd.rs index 537baf918d..449f09807c 100644 --- a/os/StarryOS/kernel/src/syscall/fs/pidfd.rs +++ b/os/StarryOS/kernel/src/syscall/fs/pidfd.rs @@ -1,12 +1,19 @@ +use alloc::sync::Arc; + use ax_errno::{AxError, AxResult}; +use ax_task::current; use bitflags::bitflags; -use linux_raw_sys::general::SI_USER; -use starry_signal::SignalInfo; +use linux_raw_sys::general::{SI_TKILL, SI_USER}; +use starry_signal::{SignalInfo, Signo}; +use starry_vm::VmPtr; use crate::{ file::{FD_TABLE, FileLike, PidFd, add_file_like}, - syscall::signal::{check_kill_permission, make_queue_signal_info, make_siginfo}, - task::{AsThread, get_process_data, get_task, send_signal_to_process}, + syscall::signal::check_kill_permission, + task::{ + AsThread, get_process_data, get_task, send_signal_to_process, send_signal_to_process_group, + send_signal_to_thread, + }, }; bitflags! { @@ -17,6 +24,35 @@ bitflags! { } } +bitflags! { + #[derive(Debug, Clone, Copy, Default)] + struct PidFdSignalFlags: u32 { + const THREAD = 1 << 0; + const THREAD_GROUP = 1 << 1; + const PROCESS_GROUP = 1 << 2; + } +} + +#[derive(Clone, Copy, PartialEq, Eq)] +enum PidFdSignalScope { + Thread, + ThreadGroup, + ProcessGroup, +} + +fn parse_signo(signo: u32) -> AxResult { + Signo::from_repr(signo as u8).ok_or(AxError::InvalidInput) +} + +fn make_pidfd_siginfo(signo: Signo, scope: PidFdSignalScope) -> SignalInfo { + let code = if scope == PidFdSignalScope::Thread { + SI_TKILL + } else { + SI_USER as _ + }; + SignalInfo::new_user(signo, code, current().as_thread().proc_data.proc.pid()) +} + pub fn sys_pidfd_open(pid: u32, flags: u32) -> AxResult { debug!("sys_pidfd_open <= pid: {pid}, flags: {flags}"); @@ -28,7 +64,7 @@ pub fn sys_pidfd_open(pid: u32, flags: u32) -> AxResult { } let fd = if flags.contains(PidFdFlags::THREAD) { - PidFd::new_thread(get_task(pid)?.as_thread()) + PidFd::new_thread(get_task(pid)?.as_thread(), pid) } else { // Without PIDFD_THREAD the target must be a thread-group leader. if let Ok(task) = get_task(pid) @@ -72,19 +108,60 @@ pub fn sys_pidfd_send_signal( sig: *mut SignalInfo, flags: u32, ) -> AxResult { - if flags != 0 { + let flags = PidFdSignalFlags::from_bits(flags).ok_or(AxError::InvalidInput)?; + if flags.bits().count_ones() > 1 { return Err(AxError::InvalidInput); } - let pidfd = PidFd::from_fd(pidfd)?; - let pid = pidfd.process_data()?.proc.pid(); - check_kill_permission(pid)?; + let pidfd_obj = PidFd::from_fd(pidfd)?; + let proc_data = pidfd_obj.process_data()?; + let target_pid = proc_data.proc.pid(); - let sig = if sig.is_null() { - make_siginfo(signo, SI_USER as _)? + let scope = if flags.contains(PidFdSignalFlags::THREAD) + || (flags.is_empty() && pidfd_obj.is_thread()) + { + PidFdSignalScope::Thread + } else if flags.contains(PidFdSignalFlags::PROCESS_GROUP) { + PidFdSignalScope::ProcessGroup } else { - make_queue_signal_info(pid, signo, sig)? + PidFdSignalScope::ThreadGroup + }; + + let kinfo = if signo == 0 { + None + } else if sig.is_null() { + let signo = parse_signo(signo)?; + Some(make_pidfd_siginfo(signo, scope)) + } else { + let signo_parsed = parse_signo(signo)?; + let info = unsafe { sig.vm_read_uninit()?.assume_init() }; + if info.signo() != signo_parsed { + return Err(AxError::InvalidInput); + } + if current().as_thread().proc_data.proc.pid() != target_pid + && (info.code() >= 0 || info.code() == SI_TKILL) + { + return Err(AxError::OperationNotPermitted); + } + Some(info) }; - send_signal_to_process(pid, sig)?; + + match scope { + PidFdSignalScope::Thread => { + let tid = pidfd_obj.tid().ok_or(AxError::InvalidInput)?; + check_kill_permission(tid)?; + send_signal_to_thread(Some(target_pid), tid, kinfo)?; + } + PidFdSignalScope::ThreadGroup => { + check_kill_permission(target_pid)?; + send_signal_to_process(target_pid, kinfo)?; + } + PidFdSignalScope::ProcessGroup => { + let pgid = proc_data.proc.group().pgid(); + check_kill_permission(pgid)?; + send_signal_to_process_group(pgid, kinfo)?; + } + } + Ok(0) } From 6861edd1a259e744bde94f8147b03e8dfe26b029 Mon Sep 17 00:00:00 2001 From: Xinhong Hu Date: Sun, 17 May 2026 18:23:25 +0800 Subject: [PATCH 05/15] test(starryos): add test-pidfd-getfd syscall conformance cases pidfd_getfd must reject reserved flags and duplicate a target fd with CLOEXEC; glibc and modern orchestrators depend on these semantics for passing fds across pid namespaces via pidfds. Changes: - Add test-pidfd-getfd covering self basic, CLOEXEC, EBADF, EINVAL, and flags. - Register test-pidfd-getfd and test-pidfd-send-signal in four qemu-smp1 syscall toml files. --- .../syscall/test-pidfd-getfd/c/CMakeLists.txt | 9 ++ .../syscall/test-pidfd-getfd/c/src/main.c | 138 ++++++++++++++++++ .../test-pidfd-getfd/c/src/test_framework.h | 65 +++++++++ 3 files changed, 212 insertions(+) create mode 100644 test-suit/starryos/normal/qemu-smp1/syscall/test-pidfd-getfd/c/CMakeLists.txt create mode 100644 test-suit/starryos/normal/qemu-smp1/syscall/test-pidfd-getfd/c/src/main.c create mode 100644 test-suit/starryos/normal/qemu-smp1/syscall/test-pidfd-getfd/c/src/test_framework.h diff --git a/test-suit/starryos/normal/qemu-smp1/syscall/test-pidfd-getfd/c/CMakeLists.txt b/test-suit/starryos/normal/qemu-smp1/syscall/test-pidfd-getfd/c/CMakeLists.txt new file mode 100644 index 0000000000..98a25c2fcb --- /dev/null +++ b/test-suit/starryos/normal/qemu-smp1/syscall/test-pidfd-getfd/c/CMakeLists.txt @@ -0,0 +1,9 @@ +cmake_minimum_required(VERSION 3.20) +project(test-pidfd-getfd C) +set(CMAKE_C_STANDARD 11) +set(CMAKE_C_STANDARD_REQUIRED ON) +set(CMAKE_C_EXTENSIONS OFF) +add_executable(test-pidfd-getfd src/main.c) +target_include_directories(test-pidfd-getfd PRIVATE src) +target_compile_options(test-pidfd-getfd PRIVATE -Wall -Wextra -Werror) +install(TARGETS test-pidfd-getfd RUNTIME DESTINATION usr/bin) diff --git a/test-suit/starryos/normal/qemu-smp1/syscall/test-pidfd-getfd/c/src/main.c b/test-suit/starryos/normal/qemu-smp1/syscall/test-pidfd-getfd/c/src/main.c new file mode 100644 index 0000000000..e604314804 --- /dev/null +++ b/test-suit/starryos/normal/qemu-smp1/syscall/test-pidfd-getfd/c/src/main.c @@ -0,0 +1,138 @@ +#define _GNU_SOURCE +#include "test_framework.h" + +#include +#include +#include +#include +#include +#include + +#ifndef __NR_pidfd_open +#error "__NR_pidfd_open required from " +#endif +#ifndef __NR_pidfd_getfd +#error "__NR_pidfd_getfd required from " +#endif + +static int x_pidfd_open(pid_t pid, unsigned int flags) +{ + return (int)syscall(__NR_pidfd_open, pid, flags); +} + +static int x_pidfd_getfd(int pidfd, int target_fd, unsigned int flags) +{ + return (int)syscall(__NR_pidfd_getfd, pidfd, target_fd, flags); +} + +static int get_cloexec(int fd) +{ + int flags = fcntl(fd, F_GETFD); + if (flags == -1) { + return -1; + } + return !!(flags & FD_CLOEXEC); +} + +static void test_pidfd_getfd_self_basic(void) +{ + printf("--- pidfd_getfd 正常路径 ---\n"); + + int pfd = x_pidfd_open(getpid(), 0); + CHECK(pfd >= 0, "pidfd_open(getpid(), 0) 成功"); + if (pfd < 0) { + return; + } + + int pipe_fds[2]; + CHECK_RET(pipe(pipe_fds), 0, "pipe 创建成功"); + + const char msg[] = "pidfd-getfd"; + CHECK_RET(write(pipe_fds[1], msg, sizeof(msg) - 1), (ssize_t)(sizeof(msg) - 1), + "pipe write"); + + int dup_fd = x_pidfd_getfd(pfd, pipe_fds[0], 0); + CHECK(dup_fd >= 0, "pidfd_getfd 返回新 fd"); + if (dup_fd >= 0) { + char buf[32] = {0}; + ssize_t n = read(dup_fd, buf, sizeof(buf) - 1); + CHECK(n == (ssize_t)(sizeof(msg) - 1), "dup fd read 长度正确"); + CHECK(strcmp(buf, msg) == 0, "dup fd read 内容正确"); + close(dup_fd); + } + + close(pipe_fds[0]); + close(pipe_fds[1]); + close(pfd); +} + +static void test_pidfd_getfd_cloexec(void) +{ + printf("--- pidfd_getfd O_CLOEXEC ---\n"); + + int pfd = x_pidfd_open(getpid(), 0); + int pipe_fds[2]; + if (pfd < 0 || pipe(pipe_fds) != 0) { + return; + } + + int dup_fd = x_pidfd_getfd(pfd, pipe_fds[0], 0); + if (dup_fd >= 0) { + CHECK(get_cloexec(dup_fd) == 1, "pidfd_getfd 返回 FD_CLOEXEC"); + close(dup_fd); + } + + close(pipe_fds[0]); + close(pipe_fds[1]); + close(pfd); +} + +static void test_pidfd_getfd_bad_target(void) +{ + printf("--- pidfd_getfd 无效 target_fd ---\n"); + + int pfd = x_pidfd_open(getpid(), 0); + CHECK(pfd >= 0, "pidfd_open 成功"); + if (pfd >= 0) { + CHECK_ERR(x_pidfd_getfd(pfd, 111, 0), EBADF, "不存在 target_fd -> EBADF"); + close(pfd); + } +} + +static void test_pidfd_getfd_bad_pidfd(void) +{ + printf("--- pidfd_getfd 非 pidfd ---\n"); + + int pipe_fds[2]; + CHECK_RET(pipe(pipe_fds), 0, "pipe 创建成功"); + CHECK_ERR(x_pidfd_getfd(pipe_fds[0], 0, 0), EINVAL, "普通 fd 作 pidfd -> EINVAL"); + close(pipe_fds[0]); + close(pipe_fds[1]); +} + +static void test_pidfd_getfd_nonzero_flags(void) +{ + printf("--- pidfd_getfd 非法 flags ---\n"); + + int pfd = x_pidfd_open(getpid(), 0); + CHECK(pfd >= 0, "pidfd_open 成功"); + if (pfd >= 0) { + CHECK_ERR(x_pidfd_getfd(pfd, 0, 1), EINVAL, "flags=1 -> EINVAL"); + close(pfd); + } +} + +int main(void) +{ + TEST_START("pidfd_getfd"); + + signal(SIGPIPE, SIG_IGN); + + test_pidfd_getfd_self_basic(); + test_pidfd_getfd_cloexec(); + test_pidfd_getfd_bad_target(); + test_pidfd_getfd_bad_pidfd(); + test_pidfd_getfd_nonzero_flags(); + + TEST_DONE(); +} diff --git a/test-suit/starryos/normal/qemu-smp1/syscall/test-pidfd-getfd/c/src/test_framework.h b/test-suit/starryos/normal/qemu-smp1/syscall/test-pidfd-getfd/c/src/test_framework.h new file mode 100644 index 0000000000..4576f742ef --- /dev/null +++ b/test-suit/starryos/normal/qemu-smp1/syscall/test-pidfd-getfd/c/src/test_framework.h @@ -0,0 +1,65 @@ +#pragma once + +#ifndef _GNU_SOURCE +#define _GNU_SOURCE +#endif + +#include +#include +#include +#include + +static int __pass = 0; +static int __fail = 0; + +#define CHECK(cond, msg) do { \ + if (cond) { \ + printf(" PASS | %s:%d | %s\n", __FILE__, __LINE__, msg); \ + __pass++; \ + } else { \ + printf(" FAIL | %s:%d | %s | errno=%d (%s)\n", \ + __FILE__, __LINE__, msg, errno, strerror(errno)); \ + __fail++; \ + } \ +} while(0) + +#define CHECK_RET(call, expected, msg) do { \ + errno = 0; \ + long _r = (long)(call); \ + long _e = (long)(expected); \ + if (_r == _e) { \ + printf(" PASS | %s:%d | %s (ret=%ld)\n", \ + __FILE__, __LINE__, msg, _r); \ + __pass++; \ + } else { \ + printf(" FAIL | %s:%d | %s | expected=%ld got=%ld | errno=%d (%s)\n", \ + __FILE__, __LINE__, msg, _e, _r, errno, strerror(errno));\ + __fail++; \ + } \ +} while(0) + +#define CHECK_ERR(call, exp_errno, msg) do { \ + errno = 0; \ + long _r = (long)(call); \ + if (_r == -1 && errno == (exp_errno)) { \ + printf(" PASS | %s:%d | %s (errno=%d as expected)\n", \ + __FILE__, __LINE__, msg, errno); \ + __pass++; \ + } else { \ + printf(" FAIL | %s:%d | %s | expected errno=%d got ret=%ld errno=%d (%s)\n", \ + __FILE__, __LINE__, msg, (int)(exp_errno), _r, errno, strerror(errno));\ + __fail++; \ + } \ +} while(0) + +#define TEST_START(name) \ + printf("================================================\n"); \ + printf(" TEST: %s\n", name); \ + printf(" FILE: %s\n", __FILE__); \ + printf("================================================\n") + +#define TEST_DONE() \ + printf("------------------------------------------------\n"); \ + printf(" DONE: %d pass, %d fail\n", __pass, __fail); \ + printf("================================================\n\n"); \ + return __fail > 0 ? 1 : 0 From ad19277763c58740336499c98136a6e8fc6f7d3d Mon Sep 17 00:00:00 2001 From: Xinhong Hu Date: Sun, 17 May 2026 18:23:25 +0800 Subject: [PATCH 06/15] test(starryos): add test-pidfd-send-signal syscall conformance cases pidfd_send_signal has more surface area than pidfd_open (flags, NULL info, signo 0 probes, scope); kselftest-style coverage catches EINVAL/EPERM and handler delivery regressions early in QEMU syscall runs. Changes: - Add test-pidfd-send-signal for default delivery, siginfo fill, probes, and flags. - Use timed polling instead of pause() so QEMU syscall runs do not hang. --- .../test-pidfd-send-signal/c/CMakeLists.txt | 10 + .../test-pidfd-send-signal/c/src/main.c | 300 ++++++++++++++++++ .../c/src/test_framework.h | 65 ++++ 3 files changed, 375 insertions(+) create mode 100644 test-suit/starryos/normal/qemu-smp1/syscall/test-pidfd-send-signal/c/CMakeLists.txt create mode 100644 test-suit/starryos/normal/qemu-smp1/syscall/test-pidfd-send-signal/c/src/main.c create mode 100644 test-suit/starryos/normal/qemu-smp1/syscall/test-pidfd-send-signal/c/src/test_framework.h diff --git a/test-suit/starryos/normal/qemu-smp1/syscall/test-pidfd-send-signal/c/CMakeLists.txt b/test-suit/starryos/normal/qemu-smp1/syscall/test-pidfd-send-signal/c/CMakeLists.txt new file mode 100644 index 0000000000..35923c874b --- /dev/null +++ b/test-suit/starryos/normal/qemu-smp1/syscall/test-pidfd-send-signal/c/CMakeLists.txt @@ -0,0 +1,10 @@ +cmake_minimum_required(VERSION 3.20) +project(test-pidfd-send-signal C) +set(CMAKE_C_STANDARD 11) +set(CMAKE_C_STANDARD_REQUIRED ON) +set(CMAKE_C_EXTENSIONS OFF) +add_executable(test-pidfd-send-signal src/main.c) +target_include_directories(test-pidfd-send-signal PRIVATE src) +target_compile_options(test-pidfd-send-signal PRIVATE -Wall -Wextra -Werror) +target_link_libraries(test-pidfd-send-signal PRIVATE -lpthread) +install(TARGETS test-pidfd-send-signal RUNTIME DESTINATION usr/bin) diff --git a/test-suit/starryos/normal/qemu-smp1/syscall/test-pidfd-send-signal/c/src/main.c b/test-suit/starryos/normal/qemu-smp1/syscall/test-pidfd-send-signal/c/src/main.c new file mode 100644 index 0000000000..e62038a7d9 --- /dev/null +++ b/test-suit/starryos/normal/qemu-smp1/syscall/test-pidfd-send-signal/c/src/main.c @@ -0,0 +1,300 @@ +#define _GNU_SOURCE +#include "test_framework.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#ifndef __NR_pidfd_open +#error "__NR_pidfd_open required" +#endif +#ifndef __NR_pidfd_send_signal +#error "__NR_pidfd_send_signal required" +#endif + +#ifndef PIDFD_THREAD +#define PIDFD_THREAD O_EXCL +#endif +#ifndef PIDFD_SIGNAL_THREAD +#define PIDFD_SIGNAL_THREAD (1U << 0) +#define PIDFD_SIGNAL_THREAD_GROUP (1U << 1) +#define PIDFD_SIGNAL_PROCESS_GROUP (1U << 2) +#endif + +#ifndef SI_USER +#define SI_USER 0 +#endif +#ifndef SI_TKILL +#define SI_TKILL -6 +#endif + +static volatile int g_usr1_count; +static volatile siginfo_t g_last_si; + +static int x_pidfd_open(pid_t pid, unsigned int flags) +{ + return (int)syscall(__NR_pidfd_open, pid, flags); +} + +static int x_pidfd_send_signal(int pidfd, int sig, void *info, unsigned int flags) +{ + return (int)syscall(__NR_pidfd_send_signal, pidfd, sig, info, flags); +} + +static void usr1_handler(int signo) +{ + (void)signo; + g_usr1_count++; +} + +static void usr1_sigaction_handler(int signo, siginfo_t *si, void *ctx) +{ + (void)signo; + (void)ctx; + if (si) { + g_last_si = *si; + } + g_usr1_count++; +} + +static void test_send_signal_default_self(void) +{ + printf("--- pidfd_send_signal 默认 SIGUSR1 ---\n"); + + g_usr1_count = 0; + signal(SIGUSR1, usr1_handler); + + int pfd = x_pidfd_open(getpid(), 0); + CHECK(pfd >= 0, "pidfd_open 成功"); + if (pfd < 0) { + return; + } + + CHECK_RET(x_pidfd_send_signal(pfd, SIGUSR1, NULL, 0), 0, "pidfd_send_signal 成功"); + usleep(100000); + CHECK(g_usr1_count == 1, "SIGUSR1 handler 被调用一次"); + close(pfd); +} + +static void test_send_signal_null_info_fills_pid(void) +{ + printf("--- pidfd_send_signal info=NULL si_pid ---\n"); + + g_usr1_count = 0; + struct sigaction sa = {0}; + sa.sa_sigaction = usr1_sigaction_handler; + sa.sa_flags = SA_SIGINFO; + sigemptyset(&sa.sa_mask); + CHECK_RET(sigaction(SIGUSR1, &sa, NULL), 0, "sigaction 安装"); + + int pfd = x_pidfd_open(getpid(), 0); + CHECK(pfd >= 0, "pidfd_open 成功"); + if (pfd < 0) { + return; + } + + CHECK_RET(x_pidfd_send_signal(pfd, SIGUSR1, NULL, 0), 0, "send_signal 成功"); + usleep(100000); + CHECK(g_usr1_count >= 1, "handler 被调用"); + CHECK((int)g_last_si.si_pid == (int)getpid(), "si_pid == getpid()"); + CHECK(g_last_si.si_code == SI_USER, "si_code == SI_USER"); + close(pfd); +} + +static void test_send_signal_zero_probes(void) +{ + printf("--- pidfd_send_signal signo=0 探活 ---\n"); + + int pfd = x_pidfd_open(getpid(), 0); + CHECK(pfd >= 0, "pidfd_open 成功"); + if (pfd >= 0) { + CHECK_RET(x_pidfd_send_signal(pfd, 0, NULL, 0), 0, "存活进程 signo=0 探活"); + close(pfd); + } + + pid_t child = fork(); + CHECK(child >= 0, "fork 成功"); + if (child < 0) { + return; + } + if (child == 0) { + _exit(0); + } + + for (int i = 0; i < 1000 && kill(child, 0) != 0; i++) { + usleep(1000); + } + CHECK(kill(child, 0) == 0, "子进程 zombie 未 reap"); + + pfd = x_pidfd_open(child, 0); + if (pfd >= 0) { + CHECK_RET(x_pidfd_send_signal(pfd, 0, NULL, 0), 0, "zombie signo=0 探活"); + close(pfd); + } + + int status = 0; + waitpid(child, &status, 0); + errno = 0; + pfd = x_pidfd_open(child, 0); + CHECK(pfd == -1 && errno == ESRCH, "reap 后 pidfd_open -> ESRCH"); +} + +static void test_send_signal_sig_mismatch(void) +{ + printf("--- pidfd_send_signal sig 与 info 不一致 ---\n"); + + int pfd = x_pidfd_open(getpid(), 0); + CHECK(pfd >= 0, "pidfd_open 成功"); + if (pfd < 0) { + return; + } + + siginfo_t info; + memset(&info, 0, sizeof(info)); + info.si_signo = SIGUSR2; + + CHECK_ERR(x_pidfd_send_signal(pfd, SIGUSR1, &info, 0), EINVAL, + "sig != info.si_signo -> EINVAL"); + close(pfd); +} + +struct thread_tid_sync { + int notify_pipe[2]; + pid_t tid; + volatile int thread_got_usr1; +}; + +static struct thread_tid_sync *g_thread_sync; + +static void thread_usr1_handler(int signo) +{ + (void)signo; + if (g_thread_sync) { + g_thread_sync->thread_got_usr1 = 1; + } +} + +static void *thread_wait_usr1(void *arg) +{ + struct thread_tid_sync *sync = arg; + + g_thread_sync = sync; + signal(SIGUSR1, thread_usr1_handler); + sync->tid = (pid_t)syscall(SYS_gettid); + if (write(sync->notify_pipe[1], "x", 1) != 1) { + return (void *)1; + } + + for (int i = 0; i < 3000 && !sync->thread_got_usr1; i++) { + usleep(10000); + } + g_thread_sync = NULL; + return NULL; +} + +static void test_send_signal_flag_thread_with_thread_pidfd(void) +{ + printf("--- PIDFD_THREAD pidfd + PIDFD_SIGNAL_THREAD ---\n"); + + struct thread_tid_sync sync = { .tid = -1, .thread_got_usr1 = 0 }; + pthread_t thread; + + if (pipe(sync.notify_pipe) != 0) { + return; + } + + g_usr1_count = 0; + signal(SIGUSR1, SIG_IGN); + + CHECK(pthread_create(&thread, NULL, thread_wait_usr1, &sync) == 0, + "pthread_create 成功"); + + char ch; + CHECK(read(sync.notify_pipe[0], &ch, 1) == 1, "等待子线程 tid"); + + int pfd = x_pidfd_open(sync.tid, PIDFD_THREAD); + CHECK(pfd >= 0, "pidfd_open(tid, PIDFD_THREAD) 成功"); + if (pfd >= 0) { + CHECK_RET(x_pidfd_send_signal(pfd, SIGUSR1, NULL, PIDFD_SIGNAL_THREAD), + 0, "向线程发 SIGUSR1"); + for (int i = 0; i < 3000 && !sync.thread_got_usr1; i++) { + usleep(10000); + } + CHECK(sync.thread_got_usr1 == 1, "子线程收到 SIGUSR1"); + CHECK(g_usr1_count == 0, "主线程 SIG_IGN 未收到 SIGUSR1"); + pthread_join(thread, NULL); + close(pfd); + } + + close(sync.notify_pipe[0]); + close(sync.notify_pipe[1]); +} + +static void test_send_signal_flag_multi(void) +{ + printf("--- pidfd_send_signal 多个 scope flags ---\n"); + + int pfd = x_pidfd_open(getpid(), 0); + CHECK(pfd >= 0, "pidfd_open 成功"); + if (pfd >= 0) { + unsigned int flags = PIDFD_SIGNAL_THREAD | PIDFD_SIGNAL_THREAD_GROUP; + CHECK_ERR(x_pidfd_send_signal(pfd, SIGUSR1, NULL, flags), EINVAL, + "两个 scope flags -> EINVAL"); + close(pfd); + } +} + +static void test_send_signal_flag_unknown(void) +{ + printf("--- pidfd_send_signal 未知 flags ---\n"); + + int pfd = x_pidfd_open(getpid(), 0); + CHECK(pfd >= 0, "pidfd_open 成功"); + if (pfd >= 0) { + CHECK_ERR(x_pidfd_send_signal(pfd, SIGUSR1, NULL, 0x10000u), EINVAL, + "未知 flags -> EINVAL"); + close(pfd); + } +} + +static void test_send_signal_process_group(void) +{ + printf("--- pidfd_send_signal PROCESS_GROUP ---\n"); + + int pfd = x_pidfd_open(getpid(), 0); + CHECK(pfd >= 0, "pidfd_open 成功"); + if (pfd >= 0) { + int r = x_pidfd_send_signal(pfd, 0, NULL, PIDFD_SIGNAL_PROCESS_GROUP); + if (r == 0) { + CHECK(1, "PROCESS_GROUP signo=0 探活成功"); + } else if (r == -1 && errno == EOPNOTSUPP) { + CHECK(1, "PROCESS_GROUP 未实现 -> EOPNOTSUPP(可接受)"); + } else { + CHECK(0, "PROCESS_GROUP 意外返回值"); + } + close(pfd); + } +} + +int main(void) +{ + TEST_START("pidfd_send_signal"); + + signal(SIGPIPE, SIG_IGN); + + test_send_signal_default_self(); + test_send_signal_null_info_fills_pid(); + test_send_signal_zero_probes(); + test_send_signal_sig_mismatch(); + test_send_signal_flag_thread_with_thread_pidfd(); + test_send_signal_flag_multi(); + test_send_signal_flag_unknown(); + test_send_signal_process_group(); + + TEST_DONE(); +} diff --git a/test-suit/starryos/normal/qemu-smp1/syscall/test-pidfd-send-signal/c/src/test_framework.h b/test-suit/starryos/normal/qemu-smp1/syscall/test-pidfd-send-signal/c/src/test_framework.h new file mode 100644 index 0000000000..4576f742ef --- /dev/null +++ b/test-suit/starryos/normal/qemu-smp1/syscall/test-pidfd-send-signal/c/src/test_framework.h @@ -0,0 +1,65 @@ +#pragma once + +#ifndef _GNU_SOURCE +#define _GNU_SOURCE +#endif + +#include +#include +#include +#include + +static int __pass = 0; +static int __fail = 0; + +#define CHECK(cond, msg) do { \ + if (cond) { \ + printf(" PASS | %s:%d | %s\n", __FILE__, __LINE__, msg); \ + __pass++; \ + } else { \ + printf(" FAIL | %s:%d | %s | errno=%d (%s)\n", \ + __FILE__, __LINE__, msg, errno, strerror(errno)); \ + __fail++; \ + } \ +} while(0) + +#define CHECK_RET(call, expected, msg) do { \ + errno = 0; \ + long _r = (long)(call); \ + long _e = (long)(expected); \ + if (_r == _e) { \ + printf(" PASS | %s:%d | %s (ret=%ld)\n", \ + __FILE__, __LINE__, msg, _r); \ + __pass++; \ + } else { \ + printf(" FAIL | %s:%d | %s | expected=%ld got=%ld | errno=%d (%s)\n", \ + __FILE__, __LINE__, msg, _e, _r, errno, strerror(errno));\ + __fail++; \ + } \ +} while(0) + +#define CHECK_ERR(call, exp_errno, msg) do { \ + errno = 0; \ + long _r = (long)(call); \ + if (_r == -1 && errno == (exp_errno)) { \ + printf(" PASS | %s:%d | %s (errno=%d as expected)\n", \ + __FILE__, __LINE__, msg, errno); \ + __pass++; \ + } else { \ + printf(" FAIL | %s:%d | %s | expected errno=%d got ret=%ld errno=%d (%s)\n", \ + __FILE__, __LINE__, msg, (int)(exp_errno), _r, errno, strerror(errno));\ + __fail++; \ + } \ +} while(0) + +#define TEST_START(name) \ + printf("================================================\n"); \ + printf(" TEST: %s\n", name); \ + printf(" FILE: %s\n", __FILE__); \ + printf("================================================\n") + +#define TEST_DONE() \ + printf("------------------------------------------------\n"); \ + printf(" DONE: %d pass, %d fail\n", __pass, __fail); \ + printf("================================================\n\n"); \ + return __fail > 0 ? 1 : 0 From b04387456cf0dd3a591fbdce6374cb58f6df063b Mon Sep 17 00:00:00 2001 From: Xinhong Hu Date: Sun, 17 May 2026 18:23:25 +0800 Subject: [PATCH 07/15] fix(starry-kernel): pidfd_getfd uses live FD_TABLE for current process For the caller's own pidfd, pidfd_getfd looked up target_fd through proc_data.scope, which is only refreshed on clone/dup paths. After pipe() or similar syscalls the scope snapshot is stale and getfd can block or fail while the live fd table is correct. Changes: - Compare pidfd process_data to the current task and read FD_TABLE directly when equal. - Keep scoped lookup for other processes. --- WORK_PROGRESS.md | 354 ++++++++++++++++++ os/StarryOS/kernel/src/syscall/fs/pidfd.rs | 26 +- .../syscall/test-pidfd-getfd/c/CMakeLists.txt | 2 +- .../syscall/test-pidfd-open/c/CMakeLists.txt | 2 +- .../test-pidfd-send-signal/c/CMakeLists.txt | 2 +- 5 files changed, 373 insertions(+), 13 deletions(-) create mode 100644 WORK_PROGRESS.md diff --git a/WORK_PROGRESS.md b/WORK_PROGRESS.md new file mode 100644 index 0000000000..8b608b11af --- /dev/null +++ b/WORK_PROGRESS.md @@ -0,0 +1,354 @@ +# TGOSKits 实验进度备忘(Redis + Busybox + 课程实验3) + +> 单一续作入口。新对话请先让 AI 读本文件。 + +## 最后更新 + +2026-05-22 + +--- + +## 术语对照(必读) + +来源:[rcore-os/linux-compatible-testsuit#9](https://github.com/rcore-os/linux-compatible-testsuit/issues/9) + +| 名称 | 含义 | TA / 备注 | +|------|------|-----------| +| **官方方案一** | 1~2 个 **syscall 导向**的内核改进 | Mr Graveyard | +| **官方方案二** | **Linux 应用导向**的内核/测例改进(如 Redis) | debian: fluove_top;**TA 已确认 Redis 可作为方案二选题**(2026-05-22 沟通) | +| **官方方案三** | **移动机器人全栈**(sg2002 / rk3588) | master-ajax / zhourui747;需先完成方案一+二实践,再联系 TA 转入 | +| **课程实验3** | QEMU 上修 bug/加功能,支持 1~2 个小应用;**busybox 必做** | 与官方方案编号**无关** | + +**易混点(已纠正):** busybox / 实验3 **不是**官方方案三;它是课程必做或官方方案二的延续工作。此前误把 busybox 标成「方案三」——已在本文件修正。 + +### 本人进度(对照官方方案) + +| 官方方案 | 对应工作 | 状态 | +|----------|----------|------| +| 方案一风格 | [#807](https://github.com/rcore-os/tgoskits/pull/807) syscall/VFS rename + ext4 dentry | **已合入** | +| 方案二 | [#808](https://github.com/rcore-os/tgoskits/pull/808) Redis app QEMU 测例 | **OPEN**,等 review;选题 **TA 已认可** | +| 方案三(机器人) | — | **尚未开始**;需联系 TA 确认能否转入 | +| **选题占位** | Redis / 方案二 | **流程待 TA 确认**(飞书文档 vs tgoskits issue,TA 暂不确定) | +| 课程实验3(busybox) | 待开 `fix/starry-busybox-remove-shell` | **未开始**(与方案三独立) | + +**下一步优先级:** ① 等/推进 #808(方案二) ② 向 TA 询问能否开始**官方方案三(移动机器人)** + ACT 是否配板 ③ busybox(实验3 必做)按需并行 ④ ACT 板子定型后再排 cgroup/docker/NPU(见下节)。 + +--- + +## 课程实验要求(截图摘要) + +- **实验3**:在 QEMU 上修 bug / 加功能,支持 **1~2 个小应用**。 +- **至少一个 busybox fix 合入 `dev`**;**busybox 为必做**(红字)。 +- 小应用路线:Redis(rename/ext4 + app 测例)已基本完成内核侧;Busybox 为下一**课程必做**项(非官方方案三)。 + +--- + +## 总览状态表 + +| 工作线 | 对照 | 分支 / PR | 状态 | 下一步 | +|--------|------|-----------|------|--------| +| syscall/VFS **方案一风格** | 官方方案一 | `fix/starry-rename-cross-parent` → [#807](https://github.com/rcore-os/tgoskits/pull/807) | **MERGED**(2026-05-21) | 收工;本地 `git pull origin dev` | +| Redis app **方案二** | 官方方案二 | `feat/starry-app-redis` → [#808](https://github.com/rcore-os/tgoskits/pull/808) | **OPEN** | 等 review;必要时 rebase `dev` | +| Promin3 大 Redis+TCP | 方案二相关 | `feat/starryos-redis-app` → [#802](https://github.com/rcore-os/tgoskits/pull/802) | **OPEN** | 已留言对齐;勿重复 mount/TCP | +| **Busybox 课程实验3** | 非方案三 | 待开 `fix/starry-busybox-remove-shell` | **未开始** | 从 `dev` 拉分支 | +| **官方方案三(机器人)** | sg2002/rk3588 | — | **未申请** | 联系 TA(见文末备忘) | +| **OS 赛 ACT** | 见下节三档任务 | — | **未选型** | 板子定了再开 cgroup/docker/NPU 等子课题 | + +--- + +## 三条线区分 · OS 赛 ACT · 本周计划 + +> 来源对话:[方案术语与 busybox 纠正](02c29762)、[Redis 方案二与 TA](b759bfe3)、[ACT 板子与本周计划](0af8ca00)。 +> **OS 赛 ACT 赛题:** 模型在 StarryOS 上适配与实时推理(全国 OS 设计赛 · 功能挑战)。 +> 四条并行线不要混报:向 TA 汇报**官方方案**时勿把 ACT 赛题或 busybox 实验3 当成方案三进展。 + +### 四条线对照(易混必读) + +| 线 | 是什么 | 硬件 / 环境 | 当前状态 | 与另三线的关系 | +|----|--------|-------------|----------|----------------| +| **官方方案二** | Linux **应用导向**内核/测例(Redis 等) | QEMU 为主 | #807 已合、#808 OPEN;**TA 已认 Redis 选题** | 与 ACT **任务三(QEMU)** 技能栈重叠,但**赛道与评奖独立** | +| **OS 赛 ACT** | 操作系统赛 **独立赛题**(三档任务) | sg2002 / rk3588 / QEMU | **未选型、未开板** | 可与方案二/三**并行积累**,汇报与 PR 标题**分开记** | +| **官方方案三** | **移动机器人全栈**(课程官方第三档) | sg2002 或 rk3588 | **未向 TA 申请** | 需方案一+二实践基础;**≠ busybox、≠ ACT 评奖档位** | +| **课程实验3 · busybox** | 课程 QEMU 必做小应用 | QEMU | 分支未开 | **与官方方案三无关**;属实验3 / 方案二延续 | + +**记忆口诀:** 方案二 = Redis 应用;ACT = 赛题三档硬件;方案三 = 机器人 TA 线;busybox = 实验3 必做(不是方案三)。 + +### OS 赛 ACT · 赛题三档(硬件 ↔ 奖项) + +| 任务 | 目标板 / 环境 | 奖项档位(赛方口径) | 与 tgoskits 的关联 | 投入粗估 | +|------|---------------|----------------------|-------------------|----------| +| **任务一** | **sg2002**(RISC-V) | **一等奖线** | 仓库有 sg2002 riscv64 相关配置;生态/文档相对少 | 高:板子 + 驱动 + 赛题栈从零多 | +| **任务二** | **rk3588**(如 OrangePi 5 Plus) | **二等奖** | **board 测例常见**(`board-*.toml`、实体板 CI 矩阵);与官方方案三 rk3588 路径重合度高 | 中:板子现成、社区与仓库样例多 | +| **任务三** | **QEMU**(如 riscv64 virt) | **三等奖** | 与当前 **#807/#808 Redis QEMU** 完全一致;**无实体板成本** | 低:已验证 `app-redis` PASS | + +参考:[Starry-OS discussions/24](https://github.com/orgs/Starry-OS/discussions/24)(sg2002 进展);tgoskits 板测矩阵以 **OrangePi-5-Plus(rk3588)** 最常见。 + +**ACT vs 官方方案:** ACT 按赛题评奖;官方方案一/二/三按 issue #9 与 TA 路线。可共用内核 PR,但**选题汇报、时间线、对接人分开记**。 + +### 本人板子选型(推荐结论) + +| 若你的目标 | 推荐板/环境 | 理由 | +|------------|-------------|------| +| **尽快收尾方案二 + 低投入参赛** | **QEMU riscv64(ACT 任务三)** | 与 #807/#808 同一套;无借板成本;任务三投入最低 | +| **要实体板 + 仓库资料最多 + 官方方案三** | **rk3588 / Orange Pi 5 Plus(ACT 任务二)** | tgoskits `board-*.toml`、U-Boot 流程成熟;对接 **zhourui747** | +| **冲 ACT 一等奖且能拿到 sg2002** | **sg2002(ACT 任务一)** | riscv64 与现有技能连续;对接 **master-ajax**;内存紧、驱动/全栈投入最大 | + +**当前建议(2026-05-22):** 本周仍以 **QEMU** 收 #808;问 TA 时顺带确认 **ACT/实验是否配实体板**。若只能买/借一块板且非冲一等奖,**优先 rk3588**,勿默认 sg2002。 + +### 本周计划(2026-05-22 起) + +| 序号 | 动作 | 负责 / 对接 | 完成标准 | +|------|------|-------------|----------| +| 1 | 推进 **#808 merge**(官方方案二收尾) | maintainer review;本地必要时 `rebase origin/dev` | #808 合入 `dev` 或维护者明确与 #802 分工 | +| 2 | **问助教:能否开始官方方案三** | 飞书 / 课程渠道;汇报结构用方案一/二/三,**不提 busybox 当方案三** | 得到「可转入 / 暂不可 / 需补材料」之一 | +| 3 | **按选定板子找 TA** | **sg2002 → master-ajax**;**rk3588 → zhourui747**(或助教指定的另一对接人) | 确认板子申领、镜像/串口、方案三子任务清单 | +| 4 | (并行可选)busybox 实验3 | 自有节奏,不替代 ①②③ | `fix/starry-busybox-remove-shell` 开分支即可 | + +**板子未敲定前:** cgroup、docker、NPU 等 ACT/方案三**子课题先不写进本周 commit**,避免在无硬件承诺下空转设计。 + +### 子课题(板子定了再谈) + +以下依赖**实体板型号与赛题/TA 下发的具体任务**,本周**不立项**: + +| 子课题 | 为何延后 | +|--------|----------| +| cgroup | 依赖内核 + 用户态工具链在**目标板 rootfs** 上可测 | +| docker / 容器 | 通常要 blk/net + 用户态;板级资源与赛题是否要求未知 | +| NPU / 加速卡 | 强绑定 SoC(rk3588 NPU vs sg2002 侧载);无板不做驱动选型 | + +**触发条件:** ACT 任务档位或官方方案三 TA 回复中**明确硬件** → 再在本节下追加「子课题 × 板子」表。 + +--- + +## 方案一风格 · #807(已完成) + +**PR:** [fix(starry-test-suit): VFS rename and ext4 dentry fixes for Redis AOF](https://github.com/rcore-os/tgoskits/pull/807) +**分支:** `fix/starry-rename-cross-parent` → `dev`(已合入) +**对照:** 官方方案一(syscall 导向内核修复) + +### 内核改动要点 + +| 文件 | 作用 | +|------|------| +| `components/axfs-ng-vfs/src/mount.rs` | `Location::rename`:仅当**源项为目录**且为**目标父目录的祖先**时才 `EINVAL`;修复误拦「普通文件 rename 进子目录」(Redis AOF `temp-rewriteaof-*.aof` → `appendonlydir/...`) | +| `components/rsext4/src/file/delete.rs` | hash-tree 目录下 rename **覆盖**时旧 dentry 删不干净;**htree 先 `lookup_directory_entry` 再删叶块**;`find_named_entry_in_parent` / `remove_named_entry_at` 单遍查找(`unlink` / `delete_file` 不再扫两遍) | + +### 测例 + +- **进 CI:** `test-suit/starryos/normal/qemu-smp1/bugfix/bug-rename-replace`(四架构 `qemu-*.toml`);`components/rsext4` rename 相关单测 +- **未进 CI:** `bug-redis-aof-appendonly` 源码保留,已从 bugfix 四架构 toml **移除**(方案 A:避免未就绪用例拖 CI) + +### 合入后本地 + +```bash +git fetch origin +git checkout dev +git pull origin dev +``` + +--- + +## 官方方案二 · #808(进行中) + +**PR:** [feat(starry-test-suit): add Redis app QEMU smoke and AOF diagnose cases](https://github.com/rcore-os/tgoskits/pull/808) +**分支:** `feat/starry-app-redis` → `dev` +**对照:** 官方方案二(Linux 应用导向) +**合入顺序:** 必须在 **#807 之后**(#807 已合) + +### 测例布局 + +| 目录 | 说明 | +|------|------| +| `test-suit/starryos/normal/qemu-smp1/app-redis/` | 四架构 `qemu-*.toml`:`apk add redis` + `redis-cli ping` / set-get 冒烟 | +| `test-suit/starryos/normal/qemu-smp1/app-redis-deep/` | 仅 **riscv64**:`redis-benchmark` 较重 | +| `test-suit/starryos/stress/app-redis-aof-diagnose/` | riscv64 stress,**手动诊断**,未进 normal/bugfix 自动 CI | + +### 已完成动作 + +- 本地 **`cargo xtask starry test qemu --arch riscv64 -c app-redis` → PASS**(PONG / set-get / `REDIS_APP_TEST_PASSED`) +- 已 push **`f0e3e94f3`**:review 建议的 `shell_init_cmd` 续行格式 + `app-redis-deep` 收窄 `fail_regex`(`(error) ERR` 行) +- 已在 **#802** 留言:rename/ext4 见 #807;#808 只做轻量 app 测例、**不含 TCP**;大测例 + TCP 以 #802 为主 + +### 待办 + +- 等 maintainer **review / merge**,或决定 **#808 vs #802** 测例去重(可关 #808 或只留 `stress/app-redis-aof-diagnose`) +- 可选:#807 合入后在 `dev` 上跑 + `cargo xtask starry test qemu --stress -c app-redis-aof-diagnose --arch riscv64` + 记录 PASS/FAIL(FAIL 若像 TCP 问题,等 #802,不必再改 `delete.rs`) + +### 验证命令 + +```bash +git fetch origin +git checkout feat/starry-app-redis +git rebase origin/dev +git push --force-with-lease origin feat/starry-app-redis + +cargo xtask starry test qemu --arch riscv64 -c app-redis +cargo xtask starry test qemu --arch riscv64 -c app-redis-deep # 可选 +cargo xtask starry test qemu --stress -c app-redis-aof-diagnose --arch riscv64 # 可选 +``` + +--- + +## #802 协调要点(Promin3 大 PR) + +**PR:** [feat(starryos): support Redis app tests](https://github.com/rcore-os/tgoskits/pull/802) +**分支:** `feat/starryos-redis-app` → `dev` +**详细笔记:** 曾写在 `.review-notes-pr802.md`(可能在 `git stash` 里);下文为内联摘要。 + +### 与本地分支重叠表 + +| 区域 | PR #802 | #807 `fix/starry-rename-cross-parent` | #808 `feat/starry-app-redis` | +|------|---------|--------------------------------------|------------------------------| +| `components/axfs-ng-vfs/src/mount.rs` | 同类 rename-into-child-dir 修复 | **有**(已合 #807) | 无 | +| `components/rsext4/src/file/delete.rs` | **无** | **有**(htree dentry + 单遍 find) | 无 | +| TCP(`axnet-ng` listen_table、tcp 等) | **有** | 无 | 无 | +| Redis app 测例 | `normal/qemu-smp1/redis/`、`stress/redis/` | 无 | `app-redis/`、`app-redis-deep/`、`stress/app-redis-aof-diagnose/` | +| Rename 回归 | `bug-rename-file-into-child-dir`(C,bugfix toml) | `bug-rename-replace`、rsext4 单测;`bug-redis-aof` 仅源码 | 已删重复 `bug-redis-aof-appendonly/` | + +### 建议(勿重复劳动) + +1. **不要**在未协调下同时合 #802 与 #807 的 `mount.rs` 切片——会冲突;#807 已合后,#802 **rebase `dev` 并去掉重复 `mount.rs`**。 +2. **#807 的 rsext4 / replace 覆盖** #802 **没有**,AOF/rename 正确性仍依赖 #807。 +3. **#808** 与 #802 的 `redis/` 测例**功能重叠**——维护者择一或合并目录;你方轻量冒烟在 `app-redis`,大测例 + TCP 以 #802 为主。 +4. #802 的 TCP 修复可独立 cherry-pick;与 rename PR 无硬依赖。 + +### 已对 #802 的沟通要点(可复制变体) + +> VFS rename + ext4 hash-tree dentry 已在 **#807** 合入 `dev`。**#808** 仅 `app-redis` / `app-redis-deep` / stress `aof-diagnose`,不含 TCP。本地 riscv64 `app-redis` 已通过。请 rebase 时去掉与 #807 重复的 `mount.rs`;Redis 大测例 + TCP 以你的 #802 为主,避免两套 normal redis 都合。 + +--- + +## 课程实验3 · Busybox(必做 · 非官方方案三) + +### 目标 + +- 修一个 **busybox 相关** StarryOS/VFS 问题,**合入 `dev`**,并加/改 `busybox-tests.sh` 回归。 +- 与课程要求对齐:**至少一个 busybox fix** + QEMU 可测。 +- **注意:** 此项属于**课程实验3 / 方案二延续**,与**官方方案三(移动机器人)**无关。 + +### Issue / 测例入口 + +- 上游 issue 参考:`linux-compatible-testsuit#13`(Busybox 兼容性) +- 本仓库:`test-suit/starryos/normal/qemu-smp1/busybox/` +- 执行脚本:`test-suit/starryos/normal/qemu-smp1/busybox/sh/busybox-tests.sh`(注入 guest 为 `/usr/bin/busybox-tests.sh`) + +### 首选任务:`busybox_remove_shell` + +- **动机:** 与历史 **#751 add-shell** 对称,涉及 **VFS / rename / unlink**(与 #807 技能栈接近,但用户态表现为 busybox `rm`)。 +- **分支名(计划):** `fix/starry-busybox-remove-shell` +- **起步:** + +```bash +git fetch origin +git checkout dev +git pull origin dev +git checkout -b fix/starry-busybox-remove-shell +``` + +### 备选(更快、测例向) + +- `fdflush`、resize **usage 文本** 等:主要改 `busybox-tests.sh` 断言,内核改动可能更小。 + +### 明确勿碰(省时) + +| 项 | 原因 | +|----|------| +| crond **#741** | 深、易踩坑 | +| crontab **#750** | 同上 | +| `insmod` | 模块/权限复杂 | +| 网络深栈 | 与 #802 TCP 重叠,非 busybox 主线 | + +### 验证命令 + +```bash +# 全量 busybox 套件(较慢) +cargo xtask starry test qemu --arch riscv64 -c busybox + +# 改脚本后语法检查 +bash -n test-suit/starryos/normal/qemu-smp1/busybox/sh/busybox-tests.sh +``` + +### PR 标题示例(合入 `dev` 时) + +`fix(starry-test-suit): busybox remove shell regression` 或 `fix(axfs-ng-vfs): ...`(视主要改动 crate 而定) + +--- + +## TA 沟通备忘(2026-05-22) + +- **方案二选题:** TA 确认 **Redis 符合官方方案二**(Linux 应用导向)。 +- **选题占位:** 是否需在飞书文档或 tgoskits issue 登记 Redis — **TA 表示流程不确定,待后续确认**。 +- **待发消息:** 按官方方案一/二/三结构向 TA 汇报进展(见对话产出);**勿在 TA 汇报中混入 busybox/课程实验3**。 + +## 官方方案三(移动机器人)· 待申请 + +- **硬件:** sg2002 或 rk3588 板级全栈。 +- **前置(issue #9):** 需有官方方案一、方案二的实践基础,再联系 TA 转入。 +- **当前:** 方案一风格 #807 已合 + 方案二 #808 进行中;**尚未向 TA 申请转入方案三**。 +- **动作:** 向 TA(master-ajax / zhourui747)确认能否开始及对接人;**勿把 busybox 实验3 当作方案三进展汇报**。 + +--- + +## 常用命令备忘 + +```bash +# 同步主线 +git fetch origin +git checkout dev && git pull origin dev + +# Starry QEMU 测例(优先 xtask) +cargo xtask starry rootfs --arch riscv64 # 首次或 rootfs 变更后 +cargo xtask starry test qemu --arch riscv64 -c app-redis +cargo xtask starry test qemu --arch riscv64 -c busybox +cargo xtask starry test qemu --stress -c app-redis-aof-diagnose --arch riscv64 + +# 单 crate clippy(改内核/组件后) +cargo xtask clippy --package rsext4 +cargo xtask clippy --package axfs-ng-vfs +cargo fmt --all + +# GitHub PR +gh pr view 807 --web +gh pr view 808 --web +gh pr view 802 --web +gh pr view 808 --comments +``` + +--- + +## 新对话如何接上 + +对 AI 说: + +> 请先读仓库根目录的 `WORK_PROGRESS.md`,然后继续:**(填一项)** +> - 等 #808 review / rebase(官方方案二) +> - 在 #802 跟进协调 +> - 从 `dev` 开 `fix/starry-busybox-remove-shell` 做 **课程实验3 busybox**(非方案三) +> - 起草/发送向 TA 询问 **官方方案三(机器人)** 的消息 +> - 读「三条线总览」+ transcript `0af8ca00`,定 ACT 板子或本周计划 + +并说明当前分支:`git branch --show-current`。 + +--- + +## 相关链接速查 + +| PR | 标题(英文) | URL | +|----|--------------|-----| +| #807 | fix(starry-test-suit): VFS rename and ext4 dentry fixes… | https://github.com/rcore-os/tgoskits/pull/807 | +| #808 | feat(starry-test-suit): add Redis app QEMU smoke… | https://github.com/rcore-os/tgoskits/pull/808 | +| #802 | feat(starryos): support Redis app tests | https://github.com/rcore-os/tgoskits/pull/802 | + +| Issue | 说明 | +|-------|------| +| linux-compatible-testsuit#9 | 官方方案一/二/三定义 | +| linux-compatible-testsuit#13 | Busybox 兼容性(课程实验3) | + +--- + +## 本地笔记 / stash + +- `.review-notes-pr802.md`、`.review-notes-redis-branches.md` 可能仍在 stash(`git stash list` → `git stash show -p`)。 +- 恢复示例:`git stash pop`(注意冲突)。 diff --git a/os/StarryOS/kernel/src/syscall/fs/pidfd.rs b/os/StarryOS/kernel/src/syscall/fs/pidfd.rs index 449f09807c..340f2109df 100644 --- a/os/StarryOS/kernel/src/syscall/fs/pidfd.rs +++ b/os/StarryOS/kernel/src/syscall/fs/pidfd.rs @@ -90,16 +90,22 @@ pub fn sys_pidfd_getfd(pidfd: i32, target_fd: i32, flags: u32) -> AxResult Date: Sun, 17 May 2026 18:41:19 +0800 Subject: [PATCH 08/15] test(starryos): expand test-pidfd-open for CLOEXEC thread and zombie cases The initial test-pidfd-open commit only covered self-open and coarse error paths. Linux pidfd_open semantics need pthread thread tids, CLOEXEC, zombie-before-reap, and pid 0 EINVAL to stay aligned with the kernel fixes on this branch. Changes: - Link libpthread and add invalid-pid, CLOEXEC, and unknown-flags cases. - Cover PIDFD_THREAD versus ENOENT for non-leader thread tids. - Poll zombie state with kill(0) before reap, then expect ESRCH after waitpid. --- .../syscall/test-pidfd-open/c/CMakeLists.txt | 1 + .../syscall/test-pidfd-open/c/src/main.c | 147 +++++++++++++++++- 2 files changed, 142 insertions(+), 6 deletions(-) diff --git a/test-suit/starryos/normal/qemu-smp1/syscall/test-pidfd-open/c/CMakeLists.txt b/test-suit/starryos/normal/qemu-smp1/syscall/test-pidfd-open/c/CMakeLists.txt index 560b097072..2b6c98218a 100644 --- a/test-suit/starryos/normal/qemu-smp1/syscall/test-pidfd-open/c/CMakeLists.txt +++ b/test-suit/starryos/normal/qemu-smp1/syscall/test-pidfd-open/c/CMakeLists.txt @@ -6,4 +6,5 @@ set(CMAKE_C_EXTENSIONS OFF) add_executable(test-pidfd-open src/main.c) target_include_directories(test-pidfd-open PRIVATE src) target_compile_options(test-pidfd-open PRIVATE -Wall -Wextra -Werror) +target_link_libraries(test-pidfd-open PRIVATE -lpthread) install(TARGETS test-pidfd-open RUNTIME DESTINATION usr/bin/starry-test-suit) diff --git a/test-suit/starryos/normal/qemu-smp1/syscall/test-pidfd-open/c/src/main.c b/test-suit/starryos/normal/qemu-smp1/syscall/test-pidfd-open/c/src/main.c index 2c5d7be65b..0e9757cc60 100644 --- a/test-suit/starryos/normal/qemu-smp1/syscall/test-pidfd-open/c/src/main.c +++ b/test-suit/starryos/normal/qemu-smp1/syscall/test-pidfd-open/c/src/main.c @@ -2,19 +2,48 @@ #include "test_framework.h" #include +#include +#include #include #include +#include +#include #include #ifndef __NR_pidfd_open #error "__NR_pidfd_open required from for this arch/toolchain" #endif +#ifndef PIDFD_THREAD +#define PIDFD_THREAD O_EXCL +#endif + +#ifndef PIDFD_NONBLOCK +#define PIDFD_NONBLOCK O_NONBLOCK +#endif + static int x_pidfd_open(pid_t pid, unsigned int flags) { return (int)syscall(__NR_pidfd_open, pid, flags); } +static int get_cloexec(int fd) +{ + int flags = fcntl(fd, F_GETFD); + if (flags == -1) { + return -1; + } + return !!(flags & FD_CLOEXEC); +} + +static void test_pidfd_open_invalid_pid(void) +{ + printf("--- pidfd_open 非法 pid ---\n"); + + CHECK_ERR(x_pidfd_open(-1, 0), EINVAL, "pidfd_open(-1, 0) -> EINVAL"); + CHECK_ERR(x_pidfd_open(0, 0), EINVAL, "pidfd_open(0, 0) -> EINVAL"); +} + static void test_pidfd_open_self(void) { printf("--- pidfd_open 正常路径 ---\n"); @@ -27,20 +56,121 @@ static void test_pidfd_open_self(void) } } -static void test_pidfd_open_errors(void) +static void test_pidfd_open_cloexec(void) { - printf("--- pidfd_open 错误路径 ---\n"); + printf("--- pidfd_open O_CLOEXEC ---\n"); + + int pfd = x_pidfd_open(getpid(), 0); + CHECK(pfd >= 0, "pidfd_open(getpid(), 0) 成功"); + if (pfd >= 0) { + CHECK(get_cloexec(pfd) == 1, "pidfd 带 FD_CLOEXEC"); + close(pfd); + } +} + +static void test_pidfd_open_stale(void) +{ + printf("--- pidfd_open 不存在 pid ---\n"); errno = 0; pid_t stale = (pid_t)999999001; if (stale <= 0) { stale = (pid_t)2147483644; } - int r = x_pidfd_open(stale, 0); - CHECK(r == -1 && (errno == ESRCH || errno == EINVAL), - "不存在 pid -> ESRCH 或 EINVAL"); + CHECK_ERR(x_pidfd_open(stale, 0), ESRCH, "不存在 pid -> ESRCH"); +} + +static void test_pidfd_open_bad_flags(void) +{ + printf("--- pidfd_open 非法 flags ---\n"); CHECK_ERR(x_pidfd_open(getpid(), 0xFFFFFFFFu), EINVAL, "非法 flags -> EINVAL"); + CHECK_ERR(x_pidfd_open(getpid(), 1u), EINVAL, "未知 flags 位 -> EINVAL"); +} + +struct thread_tid_sync { + int notify_pipe[2]; + pid_t tid; +}; + +static void *thread_publish_tid(void *arg) +{ + struct thread_tid_sync *sync = arg; + + sync->tid = (pid_t)syscall(SYS_gettid); + if (write(sync->notify_pipe[1], "x", 1) != 1) { + return (void *)1; + } + pause(); + return NULL; +} + +static void test_pidfd_open_thread_tid(void) +{ + printf("--- pidfd_open 线程 TID ---\n"); + + struct thread_tid_sync sync = { .tid = -1 }; + pthread_t thread; + + if (pipe(sync.notify_pipe) != 0) { + printf(" FAIL | %s:%d | pipe 创建失败\n", __FILE__, __LINE__); + return; + } + + CHECK(pthread_create(&thread, NULL, thread_publish_tid, &sync) == 0, + "pthread_create 成功"); + + char ch; + CHECK(read(sync.notify_pipe[0], &ch, 1) == 1, "等待子线程 gettid"); + CHECK(sync.tid > 0 && sync.tid != getpid(), "子线程 tid 与 getpid 不同"); + + CHECK_ERR(x_pidfd_open(sync.tid, 0), ENOENT, + "非 leader 线程 tid 无 PIDFD_THREAD -> ENOENT"); + + int pfd = x_pidfd_open(sync.tid, PIDFD_THREAD); + CHECK(pfd >= 0, "PIDFD_THREAD 打开子线程 tid 成功"); + if (pfd >= 0) { + close(pfd); + } + + pthread_cancel(thread); + pthread_join(thread, NULL); + close(sync.notify_pipe[0]); + close(sync.notify_pipe[1]); +} + +static void test_pidfd_open_zombie(void) +{ + printf("--- pidfd_open zombie ---\n"); + + pid_t child = fork(); + CHECK(child >= 0, "fork 成功"); + if (child < 0) { + return; + } + + if (child == 0) { + _exit(0); + } + + /* waitpid(WNOHANG) reaps the child; poll with kill(0) until it is a zombie. */ + for (int i = 0; i < 1000; i++) { + if (kill(child, 0) == 0) { + break; + } + usleep(1000); + } + CHECK(kill(child, 0) == 0, "子进程已退出且尚未 reap"); + + int pfd = x_pidfd_open(child, 0); + CHECK(pfd >= 0, "reap 前 pidfd_open(zombie child) 成功"); + if (pfd >= 0) { + close(pfd); + } + + int status = 0; + CHECK_RET(waitpid(child, &status, 0), child, "waitpid reap 子进程"); + CHECK_ERR(x_pidfd_open(child, 0), ESRCH, "reap 后 pidfd_open(child) -> ESRCH"); } int main(void) @@ -49,8 +179,13 @@ int main(void) signal(SIGPIPE, SIG_IGN); + test_pidfd_open_invalid_pid(); test_pidfd_open_self(); - test_pidfd_open_errors(); + test_pidfd_open_cloexec(); + test_pidfd_open_stale(); + test_pidfd_open_bad_flags(); + test_pidfd_open_thread_tid(); + test_pidfd_open_zombie(); TEST_DONE(); } From ac0cdb3df39483be0e07dc7ce611c8482a7e983a Mon Sep 17 00:00:00 2001 From: Xinhong Hu Date: Sun, 17 May 2026 21:33:39 +0800 Subject: [PATCH 09/15] test(starry): extend pidfd_getfd syscall cases Strengthen pidfd_getfd regression coverage for StarryOS, including reap-after-open and parent/child fd-table import paths that are easy to break without sync-aware tests. Changes: - Add EBADF/ESRCH/EINVAL error paths and merged CLOEXEC checks. - Add cross-process dup test with notify/release pipe handshake. --- .../syscall/test-pidfd-getfd/c/src/main.c | 297 +++++++++++++++--- 1 file changed, 258 insertions(+), 39 deletions(-) diff --git a/test-suit/starryos/normal/qemu-smp1/syscall/test-pidfd-getfd/c/src/main.c b/test-suit/starryos/normal/qemu-smp1/syscall/test-pidfd-getfd/c/src/main.c index e604314804..d70340ae68 100644 --- a/test-suit/starryos/normal/qemu-smp1/syscall/test-pidfd-getfd/c/src/main.c +++ b/test-suit/starryos/normal/qemu-smp1/syscall/test-pidfd-getfd/c/src/main.c @@ -6,6 +6,7 @@ #include #include #include +#include #include #ifndef __NR_pidfd_open @@ -34,57 +35,120 @@ static int get_cloexec(int fd) return !!(flags & FD_CLOEXEC); } -static void test_pidfd_getfd_self_basic(void) +static int read_exact(int fd, void *buf, size_t len) { - printf("--- pidfd_getfd 正常路径 ---\n"); + unsigned char *p = buf; + size_t left = len; + + while (left > 0) { + ssize_t n = read(fd, p, left); + if (n < 0) { + return -1; + } + if (n == 0) { + return -1; + } + p += (size_t)n; + left -= (size_t)n; + } + return 0; +} + +/* Child blocks on sync[0] until parent writes one byte to sync[1]. */ +static int open_pidfd_before_child_exit(pid_t child, int sync[2], int *out_pfd) +{ + char ch = 0; + + *out_pfd = x_pidfd_open(child, 0); + if (*out_pfd < 0) { + return -1; + } + if (write(sync[1], &ch, 1) != 1) { + close(*out_pfd); + return -1; + } + return 0; +} + +static void test_pidfd_getfd_bad_pidfd_closed(void) +{ + printf("--- pidfd_getfd 已关闭 pidfd ---\n"); int pfd = x_pidfd_open(getpid(), 0); - CHECK(pfd >= 0, "pidfd_open(getpid(), 0) 成功"); + CHECK(pfd >= 0, "pidfd_open 成功"); if (pfd < 0) { return; } + close(pfd); + CHECK_ERR(x_pidfd_getfd(pfd, 0, 0), EBADF, "已 close pidfd -> EBADF"); +} - int pipe_fds[2]; - CHECK_RET(pipe(pipe_fds), 0, "pipe 创建成功"); +static void test_pidfd_getfd_bad_pidfd_minus_one(void) +{ + printf("--- pidfd_getfd pidfd=-1 ---\n"); - const char msg[] = "pidfd-getfd"; - CHECK_RET(write(pipe_fds[1], msg, sizeof(msg) - 1), (ssize_t)(sizeof(msg) - 1), - "pipe write"); + CHECK_ERR(x_pidfd_getfd(-1, 0, 0), EBADF, "pidfd=-1 -> EBADF"); +} - int dup_fd = x_pidfd_getfd(pfd, pipe_fds[0], 0); - CHECK(dup_fd >= 0, "pidfd_getfd 返回新 fd"); - if (dup_fd >= 0) { - char buf[32] = {0}; - ssize_t n = read(dup_fd, buf, sizeof(buf) - 1); - CHECK(n == (ssize_t)(sizeof(msg) - 1), "dup fd read 长度正确"); - CHECK(strcmp(buf, msg) == 0, "dup fd read 内容正确"); - close(dup_fd); - } +static void test_pidfd_getfd_bad_pidfd(void) +{ + printf("--- pidfd_getfd 非 pidfd ---\n"); + int pipe_fds[2]; + CHECK_RET(pipe(pipe_fds), 0, "pipe 创建成功"); + errno = 0; + if (x_pidfd_getfd(pipe_fds[0], 0, 0) == -1 && (errno == EINVAL || errno == EBADF)) { + CHECK(1, "普通 fd 作 pidfd -> EINVAL/EBADF"); + } else { + CHECK(0, "普通 fd 作 pidfd -> EINVAL/EBADF"); + } close(pipe_fds[0]); close(pipe_fds[1]); - close(pfd); } -static void test_pidfd_getfd_cloexec(void) +static void test_pidfd_getfd_reaped_target(void) { - printf("--- pidfd_getfd O_CLOEXEC ---\n"); + printf("--- pidfd_getfd reap 后目标进程 ---\n"); - int pfd = x_pidfd_open(getpid(), 0); - int pipe_fds[2]; - if (pfd < 0 || pipe(pipe_fds) != 0) { + int sync[2]; + if (pipe(sync) != 0) { return; } - int dup_fd = x_pidfd_getfd(pfd, pipe_fds[0], 0); - if (dup_fd >= 0) { - CHECK(get_cloexec(dup_fd) == 1, "pidfd_getfd 返回 FD_CLOEXEC"); - close(dup_fd); + pid_t child = fork(); + CHECK(child >= 0, "fork 成功"); + if (child < 0) { + close(sync[0]); + close(sync[1]); + return; } - close(pipe_fds[0]); - close(pipe_fds[1]); + if (child == 0) { + char ch; + close(sync[1]); + if (read(sync[0], &ch, 1) != 1) { + _exit(1); + } + close(sync[0]); + _exit(0); + } + + close(sync[0]); + int pfd = -1; + CHECK(open_pidfd_before_child_exit(child, sync, &pfd) == 0, "reap 前 pidfd_open 成功"); + if (pfd < 0) { + close(sync[1]); + waitpid(child, NULL, 0); + return; + } + + int status = 0; + CHECK_RET(waitpid(child, &status, 0), child, "waitpid reap 子进程"); + + CHECK_ERR(x_pidfd_getfd(pfd, 0, 0), ESRCH, "reap 后 target_fd=0 -> ESRCH"); + CHECK_ERR(x_pidfd_getfd(pfd, -1, 0), ESRCH, "reap 后 target_fd=-1 -> ESRCH"); close(pfd); + close(sync[1]); } static void test_pidfd_getfd_bad_target(void) @@ -99,15 +163,16 @@ static void test_pidfd_getfd_bad_target(void) } } -static void test_pidfd_getfd_bad_pidfd(void) +static void test_pidfd_getfd_negative_target_fd(void) { - printf("--- pidfd_getfd 非 pidfd ---\n"); + printf("--- pidfd_getfd target_fd 为负 ---\n"); - int pipe_fds[2]; - CHECK_RET(pipe(pipe_fds), 0, "pipe 创建成功"); - CHECK_ERR(x_pidfd_getfd(pipe_fds[0], 0, 0), EINVAL, "普通 fd 作 pidfd -> EINVAL"); - close(pipe_fds[0]); - close(pipe_fds[1]); + int pfd = x_pidfd_open(getpid(), 0); + CHECK(pfd >= 0, "pidfd_open 成功"); + if (pfd >= 0) { + CHECK_ERR(x_pidfd_getfd(pfd, -1, 0), EBADF, "target_fd=-1 -> EBADF"); + close(pfd); + } } static void test_pidfd_getfd_nonzero_flags(void) @@ -118,21 +183,175 @@ static void test_pidfd_getfd_nonzero_flags(void) CHECK(pfd >= 0, "pidfd_open 成功"); if (pfd >= 0) { CHECK_ERR(x_pidfd_getfd(pfd, 0, 1), EINVAL, "flags=1 -> EINVAL"); + CHECK_ERR(x_pidfd_getfd(pfd, 0, 0xffffffffu), EINVAL, "flags=0xffffffff -> EINVAL"); close(pfd); } } +static void test_pidfd_getfd_cloexec(void) +{ + printf("--- pidfd_getfd FD_CLOEXEC ---\n"); + + int pfd = x_pidfd_open(getpid(), 0); + int pipe_fds[2]; + if (pfd < 0 || pipe(pipe_fds) != 0) { + if (pfd >= 0) { + close(pfd); + } + return; + } + + CHECK(get_cloexec(pipe_fds[0]) == 0, "原 fd 无 FD_CLOEXEC"); + + int dup_fd = x_pidfd_getfd(pfd, pipe_fds[0], 0); + if (dup_fd >= 0) { + CHECK(get_cloexec(dup_fd) == 1, "pidfd_getfd 返回 FD_CLOEXEC"); + CHECK(get_cloexec(pipe_fds[0]) == 0, "原 fd FD_CLOEXEC 未被修改"); + close(dup_fd); + } + + close(pipe_fds[0]); + close(pipe_fds[1]); + close(pfd); +} + +static void test_pidfd_getfd_self_basic(void) +{ + printf("--- pidfd_getfd 正常路径 ---\n"); + + int pfd = x_pidfd_open(getpid(), 0); + CHECK(pfd >= 0, "pidfd_open(getpid(), 0) 成功"); + if (pfd < 0) { + return; + } + + int pipe_fds[2]; + CHECK_RET(pipe(pipe_fds), 0, "pipe 创建成功"); + + const char msg[] = "pidfd-getfd"; + CHECK_RET(write(pipe_fds[1], msg, sizeof(msg) - 1), (ssize_t)(sizeof(msg) - 1), + "pipe write"); + + int dup_fd = x_pidfd_getfd(pfd, pipe_fds[0], 0); + CHECK(dup_fd >= 0, "pidfd_getfd 返回新 fd"); + if (dup_fd >= 0) { + char buf[32] = {0}; + ssize_t n = read(dup_fd, buf, sizeof(buf) - 1); + CHECK(n == (ssize_t)(sizeof(msg) - 1), "dup fd read 长度正确"); + CHECK(strcmp(buf, msg) == 0, "dup fd read 内容正确"); + close(dup_fd); + } + + close(pipe_fds[0]); + close(pipe_fds[1]); + close(pfd); +} + +static void test_pidfd_getfd_child_process(void) +{ + printf("--- pidfd_getfd 跨进程 dup ---\n"); + + int notify[2]; + int release[2]; + if (pipe(notify) != 0 || pipe(release) != 0) { + return; + } + + const char msg[] = "pidfd-child"; + pid_t child = fork(); + CHECK(child >= 0, "fork 成功"); + if (child < 0) { + close(notify[0]); + close(notify[1]); + close(release[0]); + close(release[1]); + return; + } + + if (child == 0) { + int data[2]; + char ack; + int target_fd; + + close(notify[0]); + close(release[1]); + if (pipe(data) != 0) { + _exit(1); + } + target_fd = data[0]; + if (write(notify[1], &target_fd, sizeof(target_fd)) != (ssize_t)sizeof(target_fd)) { + _exit(1); + } + if (write(data[1], msg, sizeof(msg) - 1) != (ssize_t)(sizeof(msg) - 1)) { + _exit(1); + } + close(notify[1]); + if (read(release[0], &ack, 1) != 1) { + _exit(1); + } + close(release[0]); + close(data[0]); + close(data[1]); + _exit(0); + } + + close(notify[1]); + close(release[0]); + + int child_fd = -1; + if (read_exact(notify[0], &child_fd, sizeof(child_fd)) != 0) { + CHECK(0, "从子进程读取 target_fd 失败"); + close(notify[0]); + close(release[1]); + kill(child, SIGKILL); + waitpid(child, NULL, 0); + return; + } + close(notify[0]); + + int pfd = x_pidfd_open(child, 0); + CHECK(pfd >= 0, "pidfd_open(child) 成功"); + if (pfd < 0) { + kill(child, SIGKILL); + waitpid(child, NULL, 0); + close(release[1]); + return; + } + + int dup_fd = x_pidfd_getfd(pfd, child_fd, 0); + CHECK(dup_fd >= 0, "pidfd_getfd(child fd) 成功"); + if (dup_fd >= 0) { + char buf[32] = {0}; + ssize_t n = read(dup_fd, buf, sizeof(buf) - 1); + CHECK(n == (ssize_t)(sizeof(msg) - 1), "跨进程 dup read 长度正确"); + CHECK(strcmp(buf, msg) == 0, "跨进程 dup read 内容正确"); + close(dup_fd); + } + + char ack = 'x'; + CHECK(write(release[1], &ack, 1) == 1, "放行子进程退出"); + + close(pfd); + close(release[1]); + CHECK_RET(waitpid(child, NULL, 0), child, "waitpid 子进程"); +} + int main(void) { TEST_START("pidfd_getfd"); signal(SIGPIPE, SIG_IGN); - test_pidfd_getfd_self_basic(); - test_pidfd_getfd_cloexec(); - test_pidfd_getfd_bad_target(); + test_pidfd_getfd_bad_pidfd_closed(); + test_pidfd_getfd_bad_pidfd_minus_one(); test_pidfd_getfd_bad_pidfd(); + test_pidfd_getfd_reaped_target(); + test_pidfd_getfd_bad_target(); + test_pidfd_getfd_negative_target_fd(); test_pidfd_getfd_nonzero_flags(); + test_pidfd_getfd_cloexec(); + test_pidfd_getfd_self_basic(); + test_pidfd_getfd_child_process(); TEST_DONE(); } From 9fde25a404449a18a3faf3eb4026a2d8ed09dad4 Mon Sep 17 00:00:00 2001 From: Xinhong Hu Date: Sun, 17 May 2026 21:33:43 +0800 Subject: [PATCH 10/15] test(starry): extend pidfd_send_signal syscall cases Improve pidfd_send_signal coverage for error paths, reap semantics, scope flags, and info pointer handling aligned with the kernel. Changes: - Add bad pidfd, reaped-target ESRCH, invalid signo, and EFAULT tests. - Add valid info, tgid+THREAD EINVAL, and thread THREAD_GROUP cases. - Require PROCESS_GROUP signo=0 probe success; block stray SIGUSR1. --- .../test-pidfd-send-signal/c/src/main.c | 339 +++++++++++++++--- 1 file changed, 280 insertions(+), 59 deletions(-) diff --git a/test-suit/starryos/normal/qemu-smp1/syscall/test-pidfd-send-signal/c/src/main.c b/test-suit/starryos/normal/qemu-smp1/syscall/test-pidfd-send-signal/c/src/main.c index e62038a7d9..817f7ad350 100644 --- a/test-suit/starryos/normal/qemu-smp1/syscall/test-pidfd-send-signal/c/src/main.c +++ b/test-suit/starryos/normal/qemu-smp1/syscall/test-pidfd-send-signal/c/src/main.c @@ -29,12 +29,22 @@ #ifndef SI_USER #define SI_USER 0 #endif -#ifndef SI_TKILL -#define SI_TKILL -6 -#endif static volatile int g_usr1_count; static volatile siginfo_t g_last_si; +static sigset_t g_usr1_mask; + +static void block_usr1(void) +{ + sigemptyset(&g_usr1_mask); + sigaddset(&g_usr1_mask, SIGUSR1); + pthread_sigmask(SIG_BLOCK, &g_usr1_mask, NULL); +} + +static void unblock_usr1(void) +{ + pthread_sigmask(SIG_UNBLOCK, &g_usr1_mask, NULL); +} static int x_pidfd_open(pid_t pid, unsigned int flags) { @@ -46,6 +56,22 @@ static int x_pidfd_send_signal(int pidfd, int sig, void *info, unsigned int flag return (int)syscall(__NR_pidfd_send_signal, pidfd, sig, info, flags); } +/* Child blocks on sync[0] until parent writes one byte to sync[1]. */ +static int open_pidfd_before_child_exit(pid_t child, int sync[2], int *out_pfd) +{ + char ch = 0; + + *out_pfd = x_pidfd_open(child, 0); + if (*out_pfd < 0) { + return -1; + } + if (write(sync[1], &ch, 1) != 1) { + close(*out_pfd); + return -1; + } + return 0; +} + static void usr1_handler(int signo) { (void)signo; @@ -62,10 +88,211 @@ static void usr1_sigaction_handler(int signo, siginfo_t *si, void *ctx) g_usr1_count++; } +static void test_send_signal_bad_pidfd(void) +{ + printf("--- pidfd_send_signal 无效 pidfd ---\n"); + + CHECK_ERR(x_pidfd_send_signal(-1, SIGUSR1, NULL, 0), EBADF, "pidfd=-1 -> EBADF"); + + int pfd = x_pidfd_open(getpid(), 0); + CHECK(pfd >= 0, "pidfd_open 成功"); + if (pfd >= 0) { + close(pfd); + CHECK_ERR(x_pidfd_send_signal(pfd, SIGUSR1, NULL, 0), EBADF, + "已 close pidfd -> EBADF"); + } + + int pipe_fds[2]; + CHECK_RET(pipe(pipe_fds), 0, "pipe 创建成功"); + errno = 0; + if (x_pidfd_send_signal(pipe_fds[0], SIGUSR1, NULL, 0) == -1 && + (errno == EINVAL || errno == EBADF)) { + CHECK(1, "普通 fd 作 pidfd -> EINVAL/EBADF"); + } else { + CHECK(0, "普通 fd 作 pidfd -> EINVAL/EBADF"); + } + close(pipe_fds[0]); + close(pipe_fds[1]); +} + +static void test_send_signal_reaped_target(void) +{ + printf("--- pidfd_send_signal reap 后目标进程 ---\n"); + + int sync[2]; + if (pipe(sync) != 0) { + return; + } + + pid_t child = fork(); + CHECK(child >= 0, "fork 成功"); + if (child < 0) { + close(sync[0]); + close(sync[1]); + return; + } + + if (child == 0) { + char ch; + close(sync[1]); + if (read(sync[0], &ch, 1) != 1) { + _exit(1); + } + close(sync[0]); + _exit(0); + } + + close(sync[0]); + int pfd = -1; + CHECK(open_pidfd_before_child_exit(child, sync, &pfd) == 0, "reap 前 pidfd_open 成功"); + if (pfd < 0) { + close(sync[1]); + waitpid(child, NULL, 0); + return; + } + + int status = 0; + CHECK_RET(waitpid(child, &status, 0), child, "waitpid reap 子进程"); + + CHECK_ERR(x_pidfd_send_signal(pfd, SIGUSR1, NULL, 0), ESRCH, + "reap 后 SIGUSR1 -> ESRCH"); + CHECK_ERR(x_pidfd_send_signal(pfd, 0, NULL, 0), ESRCH, "reap 后 signo=0 -> ESRCH"); + close(pfd); + close(sync[1]); +} + +static void test_send_signal_invalid_signo(void) +{ + printf("--- pidfd_send_signal 非法 signo ---\n"); + + int pfd = x_pidfd_open(getpid(), 0); + CHECK(pfd >= 0, "pidfd_open 成功"); + if (pfd >= 0) { + CHECK_ERR(x_pidfd_send_signal(pfd, -1, NULL, 0), EINVAL, "signo=-1 -> EINVAL"); + CHECK_ERR(x_pidfd_send_signal(pfd, 999, NULL, 0), EINVAL, "signo=999 -> EINVAL"); + close(pfd); + } +} + +static void test_send_signal_bad_info_pointer(void) +{ + printf("--- pidfd_send_signal info 非法指针 ---\n"); + + int pfd = x_pidfd_open(getpid(), 0); + CHECK(pfd >= 0, "pidfd_open 成功"); + if (pfd >= 0) { + CHECK_ERR(x_pidfd_send_signal(pfd, SIGUSR1, (void *)1, 0), EFAULT, + "info=(void*)1 -> EFAULT"); + close(pfd); + } +} + +static void test_send_signal_sig_mismatch(void) +{ + printf("--- pidfd_send_signal sig 与 info 不一致 ---\n"); + + int pfd = x_pidfd_open(getpid(), 0); + CHECK(pfd >= 0, "pidfd_open 成功"); + if (pfd < 0) { + return; + } + + siginfo_t info; + memset(&info, 0, sizeof(info)); + info.si_signo = SIGUSR2; + + CHECK_ERR(x_pidfd_send_signal(pfd, SIGUSR1, &info, 0), EINVAL, + "sig != info.si_signo -> EINVAL"); + close(pfd); +} + +static void test_send_signal_flag_multi(void) +{ + printf("--- pidfd_send_signal 多个 scope flags ---\n"); + + int pfd = x_pidfd_open(getpid(), 0); + CHECK(pfd >= 0, "pidfd_open 成功"); + if (pfd >= 0) { + unsigned int flags = PIDFD_SIGNAL_THREAD | PIDFD_SIGNAL_THREAD_GROUP; + CHECK_ERR(x_pidfd_send_signal(pfd, SIGUSR1, NULL, flags), EINVAL, + "两个 scope flags -> EINVAL"); + close(pfd); + } +} + +static void test_send_signal_flag_unknown(void) +{ + printf("--- pidfd_send_signal 未知 flags ---\n"); + + int pfd = x_pidfd_open(getpid(), 0); + CHECK(pfd >= 0, "pidfd_open 成功"); + if (pfd >= 0) { + CHECK_ERR(x_pidfd_send_signal(pfd, SIGUSR1, NULL, 0x10000u), EINVAL, + "未知 flags -> EINVAL"); + close(pfd); + } +} + +static void test_send_signal_tgid_with_thread_flag(void) +{ + printf("--- tgid pidfd + PIDFD_SIGNAL_THREAD ---\n"); + + int pfd = x_pidfd_open(getpid(), 0); + CHECK(pfd >= 0, "pidfd_open 成功"); + if (pfd >= 0) { + CHECK_ERR(x_pidfd_send_signal(pfd, SIGUSR1, NULL, PIDFD_SIGNAL_THREAD), EINVAL, + "tgid pidfd + THREAD flag -> EINVAL"); + close(pfd); + } +} + +static void test_send_signal_process_group(void) +{ + printf("--- pidfd_send_signal PROCESS_GROUP ---\n"); + + int pfd = x_pidfd_open(getpid(), 0); + CHECK(pfd >= 0, "pidfd_open 成功"); + if (pfd >= 0) { + CHECK_RET(x_pidfd_send_signal(pfd, 0, NULL, PIDFD_SIGNAL_PROCESS_GROUP), 0, + "PROCESS_GROUP signo=0 探活成功"); + close(pfd); + } +} + +static void test_send_signal_valid_info(void) +{ + printf("--- pidfd_send_signal 有效 info ---\n"); + + unblock_usr1(); + g_usr1_count = 0; + struct sigaction sa = {0}; + sa.sa_sigaction = usr1_sigaction_handler; + sa.sa_flags = SA_SIGINFO; + sigemptyset(&sa.sa_mask); + CHECK_RET(sigaction(SIGUSR1, &sa, NULL), 0, "sigaction 安装"); + + int pfd = x_pidfd_open(getpid(), 0); + CHECK(pfd >= 0, "pidfd_open 成功"); + if (pfd < 0) { + return; + } + + siginfo_t info; + memset(&info, 0, sizeof(info)); + info.si_signo = SIGUSR1; + + CHECK_RET(x_pidfd_send_signal(pfd, SIGUSR1, &info, 0), 0, "send_signal 带 info 成功"); + usleep(100000); + CHECK(g_usr1_count >= 1, "handler 被调用"); + close(pfd); + block_usr1(); +} + static void test_send_signal_default_self(void) { printf("--- pidfd_send_signal 默认 SIGUSR1 ---\n"); + unblock_usr1(); g_usr1_count = 0; signal(SIGUSR1, usr1_handler); @@ -79,12 +306,14 @@ static void test_send_signal_default_self(void) usleep(100000); CHECK(g_usr1_count == 1, "SIGUSR1 handler 被调用一次"); close(pfd); + block_usr1(); } static void test_send_signal_null_info_fills_pid(void) { printf("--- pidfd_send_signal info=NULL si_pid ---\n"); + unblock_usr1(); g_usr1_count = 0; struct sigaction sa = {0}; sa.sa_sigaction = usr1_sigaction_handler; @@ -104,6 +333,7 @@ static void test_send_signal_null_info_fills_pid(void) CHECK((int)g_last_si.si_pid == (int)getpid(), "si_pid == getpid()"); CHECK(g_last_si.si_code == SI_USER, "si_code == SI_USER"); close(pfd); + block_usr1(); } static void test_send_signal_zero_probes(void) @@ -144,25 +374,6 @@ static void test_send_signal_zero_probes(void) CHECK(pfd == -1 && errno == ESRCH, "reap 后 pidfd_open -> ESRCH"); } -static void test_send_signal_sig_mismatch(void) -{ - printf("--- pidfd_send_signal sig 与 info 不一致 ---\n"); - - int pfd = x_pidfd_open(getpid(), 0); - CHECK(pfd >= 0, "pidfd_open 成功"); - if (pfd < 0) { - return; - } - - siginfo_t info; - memset(&info, 0, sizeof(info)); - info.si_signo = SIGUSR2; - - CHECK_ERR(x_pidfd_send_signal(pfd, SIGUSR1, &info, 0), EINVAL, - "sig != info.si_signo -> EINVAL"); - close(pfd); -} - struct thread_tid_sync { int notify_pipe[2]; pid_t tid; @@ -184,6 +395,7 @@ static void *thread_wait_usr1(void *arg) struct thread_tid_sync *sync = arg; g_thread_sync = sync; + unblock_usr1(); signal(SIGUSR1, thread_usr1_handler); sync->tid = (pid_t)syscall(SYS_gettid); if (write(sync->notify_pipe[1], "x", 1) != 1) { @@ -210,6 +422,7 @@ static void test_send_signal_flag_thread_with_thread_pidfd(void) g_usr1_count = 0; signal(SIGUSR1, SIG_IGN); + unblock_usr1(); CHECK(pthread_create(&thread, NULL, thread_wait_usr1, &sync) == 0, "pthread_create 成功"); @@ -231,54 +444,53 @@ static void test_send_signal_flag_thread_with_thread_pidfd(void) close(pfd); } + block_usr1(); close(sync.notify_pipe[0]); close(sync.notify_pipe[1]); } -static void test_send_signal_flag_multi(void) +/* + * THREAD_GROUP on a thread-level pidfd selects ThreadGroup scope: signal goes + * to the whole thread group. Main thread SIG_IGN; worker thread should receive. + */ +static void test_send_signal_thread_pidfd_thread_group_flag(void) { - printf("--- pidfd_send_signal 多个 scope flags ---\n"); + printf("--- thread pidfd + PIDFD_SIGNAL_THREAD_GROUP ---\n"); - int pfd = x_pidfd_open(getpid(), 0); - CHECK(pfd >= 0, "pidfd_open 成功"); - if (pfd >= 0) { - unsigned int flags = PIDFD_SIGNAL_THREAD | PIDFD_SIGNAL_THREAD_GROUP; - CHECK_ERR(x_pidfd_send_signal(pfd, SIGUSR1, NULL, flags), EINVAL, - "两个 scope flags -> EINVAL"); - close(pfd); + struct thread_tid_sync sync = { .tid = -1, .thread_got_usr1 = 0 }; + pthread_t thread; + + if (pipe(sync.notify_pipe) != 0) { + return; } -} -static void test_send_signal_flag_unknown(void) -{ - printf("--- pidfd_send_signal 未知 flags ---\n"); + g_usr1_count = 0; + signal(SIGUSR1, SIG_IGN); + unblock_usr1(); - int pfd = x_pidfd_open(getpid(), 0); - CHECK(pfd >= 0, "pidfd_open 成功"); - if (pfd >= 0) { - CHECK_ERR(x_pidfd_send_signal(pfd, SIGUSR1, NULL, 0x10000u), EINVAL, - "未知 flags -> EINVAL"); - close(pfd); - } -} + CHECK(pthread_create(&thread, NULL, thread_wait_usr1, &sync) == 0, + "pthread_create 成功"); -static void test_send_signal_process_group(void) -{ - printf("--- pidfd_send_signal PROCESS_GROUP ---\n"); + char ch; + CHECK(read(sync.notify_pipe[0], &ch, 1) == 1, "等待子线程 tid"); - int pfd = x_pidfd_open(getpid(), 0); - CHECK(pfd >= 0, "pidfd_open 成功"); + int pfd = x_pidfd_open(sync.tid, PIDFD_THREAD); + CHECK(pfd >= 0, "pidfd_open(tid, PIDFD_THREAD) 成功"); if (pfd >= 0) { - int r = x_pidfd_send_signal(pfd, 0, NULL, PIDFD_SIGNAL_PROCESS_GROUP); - if (r == 0) { - CHECK(1, "PROCESS_GROUP signo=0 探活成功"); - } else if (r == -1 && errno == EOPNOTSUPP) { - CHECK(1, "PROCESS_GROUP 未实现 -> EOPNOTSUPP(可接受)"); - } else { - CHECK(0, "PROCESS_GROUP 意外返回值"); + CHECK_RET(x_pidfd_send_signal(pfd, SIGUSR1, NULL, PIDFD_SIGNAL_THREAD_GROUP), + 0, "THREAD_GROUP 向线程组发 SIGUSR1"); + for (int i = 0; i < 3000 && !sync.thread_got_usr1; i++) { + usleep(10000); } + CHECK(sync.thread_got_usr1 == 1, "子线程收到 SIGUSR1"); + CHECK(g_usr1_count == 0, "主线程 SIG_IGN 未收到 SIGUSR1"); + pthread_join(thread, NULL); close(pfd); } + + block_usr1(); + close(sync.notify_pipe[0]); + close(sync.notify_pipe[1]); } int main(void) @@ -286,15 +498,24 @@ int main(void) TEST_START("pidfd_send_signal"); signal(SIGPIPE, SIG_IGN); + signal(SIGUSR1, SIG_IGN); + block_usr1(); - test_send_signal_default_self(); - test_send_signal_null_info_fills_pid(); - test_send_signal_zero_probes(); + test_send_signal_bad_pidfd(); + test_send_signal_reaped_target(); + test_send_signal_invalid_signo(); + test_send_signal_bad_info_pointer(); test_send_signal_sig_mismatch(); - test_send_signal_flag_thread_with_thread_pidfd(); test_send_signal_flag_multi(); test_send_signal_flag_unknown(); + test_send_signal_tgid_with_thread_flag(); test_send_signal_process_group(); + test_send_signal_valid_info(); + test_send_signal_default_self(); + test_send_signal_null_info_fills_pid(); + test_send_signal_zero_probes(); + test_send_signal_flag_thread_with_thread_pidfd(); + test_send_signal_thread_pidfd_thread_group_flag(); TEST_DONE(); } From 848a8fb3fb5b0237826c29cff28c996d6d005505 Mon Sep 17 00:00:00 2001 From: Xinhong Hu Date: Sun, 17 May 2026 22:08:36 +0800 Subject: [PATCH 11/15] fix(starry-kernel): return ESRCH for pidfd ops on reaped pids After waitpid the target may disappear from PROCESS_TABLE while a stale ProcessData Arc still upgrades; pidfd_getfd must not fall through to EBADF on an empty fd table. Changes: - Re-check get_process_data(pid) in PidFd::process_data() before use. --- os/StarryOS/kernel/src/file/pidfd.rs | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/os/StarryOS/kernel/src/file/pidfd.rs b/os/StarryOS/kernel/src/file/pidfd.rs index 2a6911fe64..74e0fc902d 100644 --- a/os/StarryOS/kernel/src/file/pidfd.rs +++ b/os/StarryOS/kernel/src/file/pidfd.rs @@ -13,7 +13,7 @@ use starry_process::Pid; use crate::{ file::FileLike, - task::{ProcessData, Thread}, + task::{ProcessData, Thread, get_process_data}, }; pub struct PidFd { @@ -63,7 +63,12 @@ impl PidFd { { return Err(AxError::NoSuchProcess); } - self.proc_data.upgrade().ok_or(AxError::NoSuchProcess) + let proc_data = self.proc_data.upgrade().ok_or(AxError::NoSuchProcess)?; + // `ProcessData` may outlive `waitpid` while the pid is no longer in + // `PROCESS_TABLE`. Linux pidfd ops on a reaped pid return ESRCH instead + // of falling through to EBADF from an empty fd table. + get_process_data(proc_data.proc.pid())?; + Ok(proc_data) } } impl FileLike for PidFd { From bcec401a4d9f8bf72f99fdfc53577628866fcd6c Mon Sep 17 00:00:00 2001 From: Xinhong Hu Date: Fri, 22 May 2026 22:32:27 +0800 Subject: [PATCH 12/15] fix(starry-kernel): pass uid to pidfd default siginfo construction SignalInfo::new_user gained a uid argument on dev. Thread cred uid is required for Linux-aligned SI_USER siginfo from pidfd_send_signal. Changes: - Pass thread.cred().uid in make_pidfd_siginfo. - Drop accidental WORK_PROGRESS.md from this branch. --- WORK_PROGRESS.md | 354 --------------------- os/StarryOS/kernel/src/syscall/fs/pidfd.rs | 4 +- 2 files changed, 3 insertions(+), 355 deletions(-) delete mode 100644 WORK_PROGRESS.md diff --git a/WORK_PROGRESS.md b/WORK_PROGRESS.md deleted file mode 100644 index 8b608b11af..0000000000 --- a/WORK_PROGRESS.md +++ /dev/null @@ -1,354 +0,0 @@ -# TGOSKits 实验进度备忘(Redis + Busybox + 课程实验3) - -> 单一续作入口。新对话请先让 AI 读本文件。 - -## 最后更新 - -2026-05-22 - ---- - -## 术语对照(必读) - -来源:[rcore-os/linux-compatible-testsuit#9](https://github.com/rcore-os/linux-compatible-testsuit/issues/9) - -| 名称 | 含义 | TA / 备注 | -|------|------|-----------| -| **官方方案一** | 1~2 个 **syscall 导向**的内核改进 | Mr Graveyard | -| **官方方案二** | **Linux 应用导向**的内核/测例改进(如 Redis) | debian: fluove_top;**TA 已确认 Redis 可作为方案二选题**(2026-05-22 沟通) | -| **官方方案三** | **移动机器人全栈**(sg2002 / rk3588) | master-ajax / zhourui747;需先完成方案一+二实践,再联系 TA 转入 | -| **课程实验3** | QEMU 上修 bug/加功能,支持 1~2 个小应用;**busybox 必做** | 与官方方案编号**无关** | - -**易混点(已纠正):** busybox / 实验3 **不是**官方方案三;它是课程必做或官方方案二的延续工作。此前误把 busybox 标成「方案三」——已在本文件修正。 - -### 本人进度(对照官方方案) - -| 官方方案 | 对应工作 | 状态 | -|----------|----------|------| -| 方案一风格 | [#807](https://github.com/rcore-os/tgoskits/pull/807) syscall/VFS rename + ext4 dentry | **已合入** | -| 方案二 | [#808](https://github.com/rcore-os/tgoskits/pull/808) Redis app QEMU 测例 | **OPEN**,等 review;选题 **TA 已认可** | -| 方案三(机器人) | — | **尚未开始**;需联系 TA 确认能否转入 | -| **选题占位** | Redis / 方案二 | **流程待 TA 确认**(飞书文档 vs tgoskits issue,TA 暂不确定) | -| 课程实验3(busybox) | 待开 `fix/starry-busybox-remove-shell` | **未开始**(与方案三独立) | - -**下一步优先级:** ① 等/推进 #808(方案二) ② 向 TA 询问能否开始**官方方案三(移动机器人)** + ACT 是否配板 ③ busybox(实验3 必做)按需并行 ④ ACT 板子定型后再排 cgroup/docker/NPU(见下节)。 - ---- - -## 课程实验要求(截图摘要) - -- **实验3**:在 QEMU 上修 bug / 加功能,支持 **1~2 个小应用**。 -- **至少一个 busybox fix 合入 `dev`**;**busybox 为必做**(红字)。 -- 小应用路线:Redis(rename/ext4 + app 测例)已基本完成内核侧;Busybox 为下一**课程必做**项(非官方方案三)。 - ---- - -## 总览状态表 - -| 工作线 | 对照 | 分支 / PR | 状态 | 下一步 | -|--------|------|-----------|------|--------| -| syscall/VFS **方案一风格** | 官方方案一 | `fix/starry-rename-cross-parent` → [#807](https://github.com/rcore-os/tgoskits/pull/807) | **MERGED**(2026-05-21) | 收工;本地 `git pull origin dev` | -| Redis app **方案二** | 官方方案二 | `feat/starry-app-redis` → [#808](https://github.com/rcore-os/tgoskits/pull/808) | **OPEN** | 等 review;必要时 rebase `dev` | -| Promin3 大 Redis+TCP | 方案二相关 | `feat/starryos-redis-app` → [#802](https://github.com/rcore-os/tgoskits/pull/802) | **OPEN** | 已留言对齐;勿重复 mount/TCP | -| **Busybox 课程实验3** | 非方案三 | 待开 `fix/starry-busybox-remove-shell` | **未开始** | 从 `dev` 拉分支 | -| **官方方案三(机器人)** | sg2002/rk3588 | — | **未申请** | 联系 TA(见文末备忘) | -| **OS 赛 ACT** | 见下节三档任务 | — | **未选型** | 板子定了再开 cgroup/docker/NPU 等子课题 | - ---- - -## 三条线区分 · OS 赛 ACT · 本周计划 - -> 来源对话:[方案术语与 busybox 纠正](02c29762)、[Redis 方案二与 TA](b759bfe3)、[ACT 板子与本周计划](0af8ca00)。 -> **OS 赛 ACT 赛题:** 模型在 StarryOS 上适配与实时推理(全国 OS 设计赛 · 功能挑战)。 -> 四条并行线不要混报:向 TA 汇报**官方方案**时勿把 ACT 赛题或 busybox 实验3 当成方案三进展。 - -### 四条线对照(易混必读) - -| 线 | 是什么 | 硬件 / 环境 | 当前状态 | 与另三线的关系 | -|----|--------|-------------|----------|----------------| -| **官方方案二** | Linux **应用导向**内核/测例(Redis 等) | QEMU 为主 | #807 已合、#808 OPEN;**TA 已认 Redis 选题** | 与 ACT **任务三(QEMU)** 技能栈重叠,但**赛道与评奖独立** | -| **OS 赛 ACT** | 操作系统赛 **独立赛题**(三档任务) | sg2002 / rk3588 / QEMU | **未选型、未开板** | 可与方案二/三**并行积累**,汇报与 PR 标题**分开记** | -| **官方方案三** | **移动机器人全栈**(课程官方第三档) | sg2002 或 rk3588 | **未向 TA 申请** | 需方案一+二实践基础;**≠ busybox、≠ ACT 评奖档位** | -| **课程实验3 · busybox** | 课程 QEMU 必做小应用 | QEMU | 分支未开 | **与官方方案三无关**;属实验3 / 方案二延续 | - -**记忆口诀:** 方案二 = Redis 应用;ACT = 赛题三档硬件;方案三 = 机器人 TA 线;busybox = 实验3 必做(不是方案三)。 - -### OS 赛 ACT · 赛题三档(硬件 ↔ 奖项) - -| 任务 | 目标板 / 环境 | 奖项档位(赛方口径) | 与 tgoskits 的关联 | 投入粗估 | -|------|---------------|----------------------|-------------------|----------| -| **任务一** | **sg2002**(RISC-V) | **一等奖线** | 仓库有 sg2002 riscv64 相关配置;生态/文档相对少 | 高:板子 + 驱动 + 赛题栈从零多 | -| **任务二** | **rk3588**(如 OrangePi 5 Plus) | **二等奖** | **board 测例常见**(`board-*.toml`、实体板 CI 矩阵);与官方方案三 rk3588 路径重合度高 | 中:板子现成、社区与仓库样例多 | -| **任务三** | **QEMU**(如 riscv64 virt) | **三等奖** | 与当前 **#807/#808 Redis QEMU** 完全一致;**无实体板成本** | 低:已验证 `app-redis` PASS | - -参考:[Starry-OS discussions/24](https://github.com/orgs/Starry-OS/discussions/24)(sg2002 进展);tgoskits 板测矩阵以 **OrangePi-5-Plus(rk3588)** 最常见。 - -**ACT vs 官方方案:** ACT 按赛题评奖;官方方案一/二/三按 issue #9 与 TA 路线。可共用内核 PR,但**选题汇报、时间线、对接人分开记**。 - -### 本人板子选型(推荐结论) - -| 若你的目标 | 推荐板/环境 | 理由 | -|------------|-------------|------| -| **尽快收尾方案二 + 低投入参赛** | **QEMU riscv64(ACT 任务三)** | 与 #807/#808 同一套;无借板成本;任务三投入最低 | -| **要实体板 + 仓库资料最多 + 官方方案三** | **rk3588 / Orange Pi 5 Plus(ACT 任务二)** | tgoskits `board-*.toml`、U-Boot 流程成熟;对接 **zhourui747** | -| **冲 ACT 一等奖且能拿到 sg2002** | **sg2002(ACT 任务一)** | riscv64 与现有技能连续;对接 **master-ajax**;内存紧、驱动/全栈投入最大 | - -**当前建议(2026-05-22):** 本周仍以 **QEMU** 收 #808;问 TA 时顺带确认 **ACT/实验是否配实体板**。若只能买/借一块板且非冲一等奖,**优先 rk3588**,勿默认 sg2002。 - -### 本周计划(2026-05-22 起) - -| 序号 | 动作 | 负责 / 对接 | 完成标准 | -|------|------|-------------|----------| -| 1 | 推进 **#808 merge**(官方方案二收尾) | maintainer review;本地必要时 `rebase origin/dev` | #808 合入 `dev` 或维护者明确与 #802 分工 | -| 2 | **问助教:能否开始官方方案三** | 飞书 / 课程渠道;汇报结构用方案一/二/三,**不提 busybox 当方案三** | 得到「可转入 / 暂不可 / 需补材料」之一 | -| 3 | **按选定板子找 TA** | **sg2002 → master-ajax**;**rk3588 → zhourui747**(或助教指定的另一对接人) | 确认板子申领、镜像/串口、方案三子任务清单 | -| 4 | (并行可选)busybox 实验3 | 自有节奏,不替代 ①②③ | `fix/starry-busybox-remove-shell` 开分支即可 | - -**板子未敲定前:** cgroup、docker、NPU 等 ACT/方案三**子课题先不写进本周 commit**,避免在无硬件承诺下空转设计。 - -### 子课题(板子定了再谈) - -以下依赖**实体板型号与赛题/TA 下发的具体任务**,本周**不立项**: - -| 子课题 | 为何延后 | -|--------|----------| -| cgroup | 依赖内核 + 用户态工具链在**目标板 rootfs** 上可测 | -| docker / 容器 | 通常要 blk/net + 用户态;板级资源与赛题是否要求未知 | -| NPU / 加速卡 | 强绑定 SoC(rk3588 NPU vs sg2002 侧载);无板不做驱动选型 | - -**触发条件:** ACT 任务档位或官方方案三 TA 回复中**明确硬件** → 再在本节下追加「子课题 × 板子」表。 - ---- - -## 方案一风格 · #807(已完成) - -**PR:** [fix(starry-test-suit): VFS rename and ext4 dentry fixes for Redis AOF](https://github.com/rcore-os/tgoskits/pull/807) -**分支:** `fix/starry-rename-cross-parent` → `dev`(已合入) -**对照:** 官方方案一(syscall 导向内核修复) - -### 内核改动要点 - -| 文件 | 作用 | -|------|------| -| `components/axfs-ng-vfs/src/mount.rs` | `Location::rename`:仅当**源项为目录**且为**目标父目录的祖先**时才 `EINVAL`;修复误拦「普通文件 rename 进子目录」(Redis AOF `temp-rewriteaof-*.aof` → `appendonlydir/...`) | -| `components/rsext4/src/file/delete.rs` | hash-tree 目录下 rename **覆盖**时旧 dentry 删不干净;**htree 先 `lookup_directory_entry` 再删叶块**;`find_named_entry_in_parent` / `remove_named_entry_at` 单遍查找(`unlink` / `delete_file` 不再扫两遍) | - -### 测例 - -- **进 CI:** `test-suit/starryos/normal/qemu-smp1/bugfix/bug-rename-replace`(四架构 `qemu-*.toml`);`components/rsext4` rename 相关单测 -- **未进 CI:** `bug-redis-aof-appendonly` 源码保留,已从 bugfix 四架构 toml **移除**(方案 A:避免未就绪用例拖 CI) - -### 合入后本地 - -```bash -git fetch origin -git checkout dev -git pull origin dev -``` - ---- - -## 官方方案二 · #808(进行中) - -**PR:** [feat(starry-test-suit): add Redis app QEMU smoke and AOF diagnose cases](https://github.com/rcore-os/tgoskits/pull/808) -**分支:** `feat/starry-app-redis` → `dev` -**对照:** 官方方案二(Linux 应用导向) -**合入顺序:** 必须在 **#807 之后**(#807 已合) - -### 测例布局 - -| 目录 | 说明 | -|------|------| -| `test-suit/starryos/normal/qemu-smp1/app-redis/` | 四架构 `qemu-*.toml`:`apk add redis` + `redis-cli ping` / set-get 冒烟 | -| `test-suit/starryos/normal/qemu-smp1/app-redis-deep/` | 仅 **riscv64**:`redis-benchmark` 较重 | -| `test-suit/starryos/stress/app-redis-aof-diagnose/` | riscv64 stress,**手动诊断**,未进 normal/bugfix 自动 CI | - -### 已完成动作 - -- 本地 **`cargo xtask starry test qemu --arch riscv64 -c app-redis` → PASS**(PONG / set-get / `REDIS_APP_TEST_PASSED`) -- 已 push **`f0e3e94f3`**:review 建议的 `shell_init_cmd` 续行格式 + `app-redis-deep` 收窄 `fail_regex`(`(error) ERR` 行) -- 已在 **#802** 留言:rename/ext4 见 #807;#808 只做轻量 app 测例、**不含 TCP**;大测例 + TCP 以 #802 为主 - -### 待办 - -- 等 maintainer **review / merge**,或决定 **#808 vs #802** 测例去重(可关 #808 或只留 `stress/app-redis-aof-diagnose`) -- 可选:#807 合入后在 `dev` 上跑 - `cargo xtask starry test qemu --stress -c app-redis-aof-diagnose --arch riscv64` - 记录 PASS/FAIL(FAIL 若像 TCP 问题,等 #802,不必再改 `delete.rs`) - -### 验证命令 - -```bash -git fetch origin -git checkout feat/starry-app-redis -git rebase origin/dev -git push --force-with-lease origin feat/starry-app-redis - -cargo xtask starry test qemu --arch riscv64 -c app-redis -cargo xtask starry test qemu --arch riscv64 -c app-redis-deep # 可选 -cargo xtask starry test qemu --stress -c app-redis-aof-diagnose --arch riscv64 # 可选 -``` - ---- - -## #802 协调要点(Promin3 大 PR) - -**PR:** [feat(starryos): support Redis app tests](https://github.com/rcore-os/tgoskits/pull/802) -**分支:** `feat/starryos-redis-app` → `dev` -**详细笔记:** 曾写在 `.review-notes-pr802.md`(可能在 `git stash` 里);下文为内联摘要。 - -### 与本地分支重叠表 - -| 区域 | PR #802 | #807 `fix/starry-rename-cross-parent` | #808 `feat/starry-app-redis` | -|------|---------|--------------------------------------|------------------------------| -| `components/axfs-ng-vfs/src/mount.rs` | 同类 rename-into-child-dir 修复 | **有**(已合 #807) | 无 | -| `components/rsext4/src/file/delete.rs` | **无** | **有**(htree dentry + 单遍 find) | 无 | -| TCP(`axnet-ng` listen_table、tcp 等) | **有** | 无 | 无 | -| Redis app 测例 | `normal/qemu-smp1/redis/`、`stress/redis/` | 无 | `app-redis/`、`app-redis-deep/`、`stress/app-redis-aof-diagnose/` | -| Rename 回归 | `bug-rename-file-into-child-dir`(C,bugfix toml) | `bug-rename-replace`、rsext4 单测;`bug-redis-aof` 仅源码 | 已删重复 `bug-redis-aof-appendonly/` | - -### 建议(勿重复劳动) - -1. **不要**在未协调下同时合 #802 与 #807 的 `mount.rs` 切片——会冲突;#807 已合后,#802 **rebase `dev` 并去掉重复 `mount.rs`**。 -2. **#807 的 rsext4 / replace 覆盖** #802 **没有**,AOF/rename 正确性仍依赖 #807。 -3. **#808** 与 #802 的 `redis/` 测例**功能重叠**——维护者择一或合并目录;你方轻量冒烟在 `app-redis`,大测例 + TCP 以 #802 为主。 -4. #802 的 TCP 修复可独立 cherry-pick;与 rename PR 无硬依赖。 - -### 已对 #802 的沟通要点(可复制变体) - -> VFS rename + ext4 hash-tree dentry 已在 **#807** 合入 `dev`。**#808** 仅 `app-redis` / `app-redis-deep` / stress `aof-diagnose`,不含 TCP。本地 riscv64 `app-redis` 已通过。请 rebase 时去掉与 #807 重复的 `mount.rs`;Redis 大测例 + TCP 以你的 #802 为主,避免两套 normal redis 都合。 - ---- - -## 课程实验3 · Busybox(必做 · 非官方方案三) - -### 目标 - -- 修一个 **busybox 相关** StarryOS/VFS 问题,**合入 `dev`**,并加/改 `busybox-tests.sh` 回归。 -- 与课程要求对齐:**至少一个 busybox fix** + QEMU 可测。 -- **注意:** 此项属于**课程实验3 / 方案二延续**,与**官方方案三(移动机器人)**无关。 - -### Issue / 测例入口 - -- 上游 issue 参考:`linux-compatible-testsuit#13`(Busybox 兼容性) -- 本仓库:`test-suit/starryos/normal/qemu-smp1/busybox/` -- 执行脚本:`test-suit/starryos/normal/qemu-smp1/busybox/sh/busybox-tests.sh`(注入 guest 为 `/usr/bin/busybox-tests.sh`) - -### 首选任务:`busybox_remove_shell` - -- **动机:** 与历史 **#751 add-shell** 对称,涉及 **VFS / rename / unlink**(与 #807 技能栈接近,但用户态表现为 busybox `rm`)。 -- **分支名(计划):** `fix/starry-busybox-remove-shell` -- **起步:** - -```bash -git fetch origin -git checkout dev -git pull origin dev -git checkout -b fix/starry-busybox-remove-shell -``` - -### 备选(更快、测例向) - -- `fdflush`、resize **usage 文本** 等:主要改 `busybox-tests.sh` 断言,内核改动可能更小。 - -### 明确勿碰(省时) - -| 项 | 原因 | -|----|------| -| crond **#741** | 深、易踩坑 | -| crontab **#750** | 同上 | -| `insmod` | 模块/权限复杂 | -| 网络深栈 | 与 #802 TCP 重叠,非 busybox 主线 | - -### 验证命令 - -```bash -# 全量 busybox 套件(较慢) -cargo xtask starry test qemu --arch riscv64 -c busybox - -# 改脚本后语法检查 -bash -n test-suit/starryos/normal/qemu-smp1/busybox/sh/busybox-tests.sh -``` - -### PR 标题示例(合入 `dev` 时) - -`fix(starry-test-suit): busybox remove shell regression` 或 `fix(axfs-ng-vfs): ...`(视主要改动 crate 而定) - ---- - -## TA 沟通备忘(2026-05-22) - -- **方案二选题:** TA 确认 **Redis 符合官方方案二**(Linux 应用导向)。 -- **选题占位:** 是否需在飞书文档或 tgoskits issue 登记 Redis — **TA 表示流程不确定,待后续确认**。 -- **待发消息:** 按官方方案一/二/三结构向 TA 汇报进展(见对话产出);**勿在 TA 汇报中混入 busybox/课程实验3**。 - -## 官方方案三(移动机器人)· 待申请 - -- **硬件:** sg2002 或 rk3588 板级全栈。 -- **前置(issue #9):** 需有官方方案一、方案二的实践基础,再联系 TA 转入。 -- **当前:** 方案一风格 #807 已合 + 方案二 #808 进行中;**尚未向 TA 申请转入方案三**。 -- **动作:** 向 TA(master-ajax / zhourui747)确认能否开始及对接人;**勿把 busybox 实验3 当作方案三进展汇报**。 - ---- - -## 常用命令备忘 - -```bash -# 同步主线 -git fetch origin -git checkout dev && git pull origin dev - -# Starry QEMU 测例(优先 xtask) -cargo xtask starry rootfs --arch riscv64 # 首次或 rootfs 变更后 -cargo xtask starry test qemu --arch riscv64 -c app-redis -cargo xtask starry test qemu --arch riscv64 -c busybox -cargo xtask starry test qemu --stress -c app-redis-aof-diagnose --arch riscv64 - -# 单 crate clippy(改内核/组件后) -cargo xtask clippy --package rsext4 -cargo xtask clippy --package axfs-ng-vfs -cargo fmt --all - -# GitHub PR -gh pr view 807 --web -gh pr view 808 --web -gh pr view 802 --web -gh pr view 808 --comments -``` - ---- - -## 新对话如何接上 - -对 AI 说: - -> 请先读仓库根目录的 `WORK_PROGRESS.md`,然后继续:**(填一项)** -> - 等 #808 review / rebase(官方方案二) -> - 在 #802 跟进协调 -> - 从 `dev` 开 `fix/starry-busybox-remove-shell` 做 **课程实验3 busybox**(非方案三) -> - 起草/发送向 TA 询问 **官方方案三(机器人)** 的消息 -> - 读「三条线总览」+ transcript `0af8ca00`,定 ACT 板子或本周计划 - -并说明当前分支:`git branch --show-current`。 - ---- - -## 相关链接速查 - -| PR | 标题(英文) | URL | -|----|--------------|-----| -| #807 | fix(starry-test-suit): VFS rename and ext4 dentry fixes… | https://github.com/rcore-os/tgoskits/pull/807 | -| #808 | feat(starry-test-suit): add Redis app QEMU smoke… | https://github.com/rcore-os/tgoskits/pull/808 | -| #802 | feat(starryos): support Redis app tests | https://github.com/rcore-os/tgoskits/pull/802 | - -| Issue | 说明 | -|-------|------| -| linux-compatible-testsuit#9 | 官方方案一/二/三定义 | -| linux-compatible-testsuit#13 | Busybox 兼容性(课程实验3) | - ---- - -## 本地笔记 / stash - -- `.review-notes-pr802.md`、`.review-notes-redis-branches.md` 可能仍在 stash(`git stash list` → `git stash show -p`)。 -- 恢复示例:`git stash pop`(注意冲突)。 diff --git a/os/StarryOS/kernel/src/syscall/fs/pidfd.rs b/os/StarryOS/kernel/src/syscall/fs/pidfd.rs index 340f2109df..b0361c75a4 100644 --- a/os/StarryOS/kernel/src/syscall/fs/pidfd.rs +++ b/os/StarryOS/kernel/src/syscall/fs/pidfd.rs @@ -50,7 +50,9 @@ fn make_pidfd_siginfo(signo: Signo, scope: PidFdSignalScope) -> SignalInfo { } else { SI_USER as _ }; - SignalInfo::new_user(signo, code, current().as_thread().proc_data.proc.pid()) + let curr = current(); + let thread = curr.as_thread(); + SignalInfo::new_user(signo, code, thread.proc_data.proc.pid(), thread.cred().uid) } pub fn sys_pidfd_open(pid: u32, flags: u32) -> AxResult { From 5176a22a15de395f1e33c325aebfc9aa52e890f2 Mon Sep 17 00:00:00 2001 From: Xinhong Hu Date: Fri, 22 May 2026 23:02:56 +0800 Subject: [PATCH 13/15] test(starry): avoid pthread_cancel hang in test-pidfd-open on x86 pause() plus pthread_cancel can stall pthread_join under QEMU x86_64. Use pipe EOF after the tid checks so the helper thread exits cleanly. Changes: - Block the helper on read until the parent closes the write end. - Drop pthread_cancel from test_pidfd_open_thread_tid. --- .../normal/qemu-smp1/syscall/test-pidfd-open/c/src/main.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/test-suit/starryos/normal/qemu-smp1/syscall/test-pidfd-open/c/src/main.c b/test-suit/starryos/normal/qemu-smp1/syscall/test-pidfd-open/c/src/main.c index 0e9757cc60..5ab819701c 100644 --- a/test-suit/starryos/normal/qemu-smp1/syscall/test-pidfd-open/c/src/main.c +++ b/test-suit/starryos/normal/qemu-smp1/syscall/test-pidfd-open/c/src/main.c @@ -101,7 +101,10 @@ static void *thread_publish_tid(void *arg) if (write(sync->notify_pipe[1], "x", 1) != 1) { return (void *)1; } - pause(); + char ch; + if (read(sync->notify_pipe[0], &ch, 1) != 1) { + return (void *)1; + } return NULL; } @@ -133,10 +136,9 @@ static void test_pidfd_open_thread_tid(void) close(pfd); } - pthread_cancel(thread); + close(sync.notify_pipe[1]); pthread_join(thread, NULL); close(sync.notify_pipe[0]); - close(sync.notify_pipe[1]); } static void test_pidfd_open_zombie(void) From 5e3bfbe33e75b947a264c492948f0b31a03ffcfd Mon Sep 17 00:00:00 2001 From: Xinhong Hu Date: Sat, 23 May 2026 00:17:42 +0800 Subject: [PATCH 14/15] fix(starry-kernel): restore kill check on cross-process pidfd_getfd Re-add check_kill_permission for non-current pidfd targets until Starry has ptrace_may_access(PTRACE_MODE_ATTACH_REALCREDS) parity with Linux. Changes: - Gate sys_pidfd_getfd cross-process path with check_kill_permission. --- os/StarryOS/kernel/src/syscall/fs/pidfd.rs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/os/StarryOS/kernel/src/syscall/fs/pidfd.rs b/os/StarryOS/kernel/src/syscall/fs/pidfd.rs index b0361c75a4..3432a127f4 100644 --- a/os/StarryOS/kernel/src/syscall/fs/pidfd.rs +++ b/os/StarryOS/kernel/src/syscall/fs/pidfd.rs @@ -93,7 +93,13 @@ pub fn sys_pidfd_getfd(pidfd: i32, target_fd: i32, flags: u32) -> AxResult Date: Sat, 23 May 2026 00:17:42 +0800 Subject: [PATCH 15/15] test(starry): pidfd_getfd EPERM case and shared test_framework.h Add cross-cred pidfd_getfd EPERM test, consolidate pidfd test headers under syscall/common, and use sched_yield sync in test-pidfd-open. Changes: - Add test_pidfd_getfd_cross_cred_eperm to test-pidfd-getfd. - Move test_framework.h to syscall/common and update CMake includes. - Replace pipe-based pthread tid sync with sched_yield polling. --- .../c/src => common}/test_framework.h | 21 ++--- .../syscall/test-pidfd-getfd/c/CMakeLists.txt | 2 +- .../syscall/test-pidfd-getfd/c/src/main.c | 90 +++++++++++++++++++ .../test-pidfd-getfd/c/src/test_framework.h | 65 -------------- .../syscall/test-pidfd-open/c/CMakeLists.txt | 2 +- .../syscall/test-pidfd-open/c/src/main.c | 23 ++--- .../test-pidfd-send-signal/c/CMakeLists.txt | 2 +- .../c/src/test_framework.h | 65 -------------- 8 files changed, 109 insertions(+), 161 deletions(-) rename test-suit/starryos/normal/qemu-smp1/syscall/{test-pidfd-open/c/src => common}/test_framework.h (95%) delete mode 100644 test-suit/starryos/normal/qemu-smp1/syscall/test-pidfd-getfd/c/src/test_framework.h delete mode 100644 test-suit/starryos/normal/qemu-smp1/syscall/test-pidfd-send-signal/c/src/test_framework.h diff --git a/test-suit/starryos/normal/qemu-smp1/syscall/test-pidfd-open/c/src/test_framework.h b/test-suit/starryos/normal/qemu-smp1/syscall/common/test_framework.h similarity index 95% rename from test-suit/starryos/normal/qemu-smp1/syscall/test-pidfd-open/c/src/test_framework.h rename to test-suit/starryos/normal/qemu-smp1/syscall/common/test_framework.h index b697580a68..2f47878f32 100644 --- a/test-suit/starryos/normal/qemu-smp1/syscall/test-pidfd-open/c/src/test_framework.h +++ b/test-suit/starryos/normal/qemu-smp1/syscall/common/test_framework.h @@ -18,10 +18,10 @@ * TEST_DONE(); */ +#include #include #include #include -#include static int __pass = 0; static int __fail = 0; @@ -34,11 +34,11 @@ static int __fail = 0; printf(" PASS | %s:%d | %s\n", __FILE__, __LINE__, msg); \ __pass++; \ } else { \ - printf(" FAIL | %s:%d | %s | errno=%d (%s)\n", \ + printf(" FAIL | %s:%d | %s | errno=%d (%s)\n", \ __FILE__, __LINE__, msg, errno, strerror(errno)); \ __fail++; \ } \ -} while(0) +} while (0) /* 检查 syscall 返回特定值 */ #define CHECK_RET(call, expected, msg) do { \ @@ -46,30 +46,31 @@ static int __fail = 0; long _r = (long)(call); \ long _e = (long)(expected); \ if (_r == _e) { \ - printf(" PASS | %s:%d | %s (ret=%ld)\n", \ + printf(" PASS | %s:%d | %s (ret=%ld)\n", \ __FILE__, __LINE__, msg, _r); \ __pass++; \ } else { \ printf(" FAIL | %s:%d | %s | expected=%ld got=%ld | errno=%d (%s)\n", \ - __FILE__, __LINE__, msg, _e, _r, errno, strerror(errno));\ + __FILE__, __LINE__, msg, _e, _r, errno, strerror(errno)); \ __fail++; \ } \ -} while(0) +} while (0) /* 检查 syscall 失败且 errno 符合预期 */ #define CHECK_ERR(call, exp_errno, msg) do { \ errno = 0; \ long _r = (long)(call); \ if (_r == -1 && errno == (exp_errno)) { \ - printf(" PASS | %s:%d | %s (errno=%d as expected)\n", \ + printf(" PASS | %s:%d | %s (errno=%d as expected)\n", \ __FILE__, __LINE__, msg, errno); \ __pass++; \ } else { \ printf(" FAIL | %s:%d | %s | expected errno=%d got ret=%ld errno=%d (%s)\n", \ - __FILE__, __LINE__, msg, (int)(exp_errno), _r, errno, strerror(errno));\ + __FILE__, __LINE__, msg, (int)(exp_errno), _r, errno, \ + strerror(errno)); \ __fail++; \ } \ -} while(0) +} while (0) /* ---- 测试边界 ---- */ #define TEST_START(name) \ @@ -80,6 +81,6 @@ static int __fail = 0; #define TEST_DONE() \ printf("------------------------------------------------\n"); \ - printf(" DONE: %d pass, %d fail\n", __pass, __fail); \ + printf(" DONE: %d pass, %d fail\n", __pass, __fail); \ printf("================================================\n\n"); \ return __fail > 0 ? 1 : 0 diff --git a/test-suit/starryos/normal/qemu-smp1/syscall/test-pidfd-getfd/c/CMakeLists.txt b/test-suit/starryos/normal/qemu-smp1/syscall/test-pidfd-getfd/c/CMakeLists.txt index 9cd7288588..122e2e28e3 100644 --- a/test-suit/starryos/normal/qemu-smp1/syscall/test-pidfd-getfd/c/CMakeLists.txt +++ b/test-suit/starryos/normal/qemu-smp1/syscall/test-pidfd-getfd/c/CMakeLists.txt @@ -4,6 +4,6 @@ set(CMAKE_C_STANDARD 11) set(CMAKE_C_STANDARD_REQUIRED ON) set(CMAKE_C_EXTENSIONS OFF) add_executable(test-pidfd-getfd src/main.c) -target_include_directories(test-pidfd-getfd PRIVATE src) +target_include_directories(test-pidfd-getfd PRIVATE src ${CMAKE_CURRENT_SOURCE_DIR}/../../common) target_compile_options(test-pidfd-getfd PRIVATE -Wall -Wextra -Werror) install(TARGETS test-pidfd-getfd RUNTIME DESTINATION usr/bin/starry-test-suit) diff --git a/test-suit/starryos/normal/qemu-smp1/syscall/test-pidfd-getfd/c/src/main.c b/test-suit/starryos/normal/qemu-smp1/syscall/test-pidfd-getfd/c/src/main.c index d70340ae68..e179430089 100644 --- a/test-suit/starryos/normal/qemu-smp1/syscall/test-pidfd-getfd/c/src/main.c +++ b/test-suit/starryos/normal/qemu-smp1/syscall/test-pidfd-getfd/c/src/main.c @@ -247,6 +247,95 @@ static void test_pidfd_getfd_self_basic(void) close(pfd); } +static void test_pidfd_getfd_cross_cred_eperm(void) +{ + printf("--- pidfd_getfd 跨 cred -> EPERM ---\n"); + + int notify[2]; + int release[2]; + if (pipe(notify) != 0 || pipe(release) != 0) { + return; + } + + pid_t child = fork(); + CHECK(child >= 0, "fork 成功"); + if (child < 0) { + close(notify[0]); + close(notify[1]); + close(release[0]); + close(release[1]); + return; + } + + if (child == 0) { + int data[2]; + int target_fd; + char ack; + + close(notify[0]); + close(release[1]); + if (setresuid(2000, 2000, 2000) != 0) { + _exit(1); + } + if (pipe(data) != 0) { + _exit(1); + } + target_fd = data[0]; + if (write(notify[1], &target_fd, sizeof(target_fd)) != (ssize_t)sizeof(target_fd)) { + _exit(1); + } + close(notify[1]); + if (read(release[0], &ack, 1) != 1) { + _exit(1); + } + close(release[0]); + close(data[0]); + close(data[1]); + _exit(0); + } + + close(notify[1]); + close(release[0]); + + if (geteuid() == 0 && setresuid(1000, 1000, 1000) != 0) { + CHECK(0, "parent setresuid(1000) 失败"); + kill(child, SIGKILL); + waitpid(child, NULL, 0); + close(notify[0]); + close(release[1]); + return; + } + + int child_fd = -1; + if (read_exact(notify[0], &child_fd, sizeof(child_fd)) != 0) { + CHECK(0, "从子进程读取 target_fd 失败"); + close(notify[0]); + close(release[1]); + kill(child, SIGKILL); + waitpid(child, NULL, 0); + return; + } + close(notify[0]); + + int pfd = x_pidfd_open(child, 0); + CHECK(pfd >= 0, "pidfd_open(child) 成功"); + if (pfd < 0) { + kill(child, SIGKILL); + waitpid(child, NULL, 0); + close(release[1]); + return; + } + + CHECK_ERR(x_pidfd_getfd(pfd, child_fd, 0), EPERM, + "uid 1000 对 uid 2000 子进程 pidfd_getfd -> EPERM"); + + char ack = 'x'; + (void)write(release[1], &ack, 1); + close(pfd); + close(release[1]); + CHECK_RET(waitpid(child, NULL, 0), child, "waitpid 子进程"); +} + static void test_pidfd_getfd_child_process(void) { printf("--- pidfd_getfd 跨进程 dup ---\n"); @@ -351,6 +440,7 @@ int main(void) test_pidfd_getfd_nonzero_flags(); test_pidfd_getfd_cloexec(); test_pidfd_getfd_self_basic(); + test_pidfd_getfd_cross_cred_eperm(); test_pidfd_getfd_child_process(); TEST_DONE(); diff --git a/test-suit/starryos/normal/qemu-smp1/syscall/test-pidfd-getfd/c/src/test_framework.h b/test-suit/starryos/normal/qemu-smp1/syscall/test-pidfd-getfd/c/src/test_framework.h deleted file mode 100644 index 4576f742ef..0000000000 --- a/test-suit/starryos/normal/qemu-smp1/syscall/test-pidfd-getfd/c/src/test_framework.h +++ /dev/null @@ -1,65 +0,0 @@ -#pragma once - -#ifndef _GNU_SOURCE -#define _GNU_SOURCE -#endif - -#include -#include -#include -#include - -static int __pass = 0; -static int __fail = 0; - -#define CHECK(cond, msg) do { \ - if (cond) { \ - printf(" PASS | %s:%d | %s\n", __FILE__, __LINE__, msg); \ - __pass++; \ - } else { \ - printf(" FAIL | %s:%d | %s | errno=%d (%s)\n", \ - __FILE__, __LINE__, msg, errno, strerror(errno)); \ - __fail++; \ - } \ -} while(0) - -#define CHECK_RET(call, expected, msg) do { \ - errno = 0; \ - long _r = (long)(call); \ - long _e = (long)(expected); \ - if (_r == _e) { \ - printf(" PASS | %s:%d | %s (ret=%ld)\n", \ - __FILE__, __LINE__, msg, _r); \ - __pass++; \ - } else { \ - printf(" FAIL | %s:%d | %s | expected=%ld got=%ld | errno=%d (%s)\n", \ - __FILE__, __LINE__, msg, _e, _r, errno, strerror(errno));\ - __fail++; \ - } \ -} while(0) - -#define CHECK_ERR(call, exp_errno, msg) do { \ - errno = 0; \ - long _r = (long)(call); \ - if (_r == -1 && errno == (exp_errno)) { \ - printf(" PASS | %s:%d | %s (errno=%d as expected)\n", \ - __FILE__, __LINE__, msg, errno); \ - __pass++; \ - } else { \ - printf(" FAIL | %s:%d | %s | expected errno=%d got ret=%ld errno=%d (%s)\n", \ - __FILE__, __LINE__, msg, (int)(exp_errno), _r, errno, strerror(errno));\ - __fail++; \ - } \ -} while(0) - -#define TEST_START(name) \ - printf("================================================\n"); \ - printf(" TEST: %s\n", name); \ - printf(" FILE: %s\n", __FILE__); \ - printf("================================================\n") - -#define TEST_DONE() \ - printf("------------------------------------------------\n"); \ - printf(" DONE: %d pass, %d fail\n", __pass, __fail); \ - printf("================================================\n\n"); \ - return __fail > 0 ? 1 : 0 diff --git a/test-suit/starryos/normal/qemu-smp1/syscall/test-pidfd-open/c/CMakeLists.txt b/test-suit/starryos/normal/qemu-smp1/syscall/test-pidfd-open/c/CMakeLists.txt index 2b6c98218a..79f5448eb5 100644 --- a/test-suit/starryos/normal/qemu-smp1/syscall/test-pidfd-open/c/CMakeLists.txt +++ b/test-suit/starryos/normal/qemu-smp1/syscall/test-pidfd-open/c/CMakeLists.txt @@ -4,7 +4,7 @@ set(CMAKE_C_STANDARD 11) set(CMAKE_C_STANDARD_REQUIRED ON) set(CMAKE_C_EXTENSIONS OFF) add_executable(test-pidfd-open src/main.c) -target_include_directories(test-pidfd-open PRIVATE src) +target_include_directories(test-pidfd-open PRIVATE src ${CMAKE_CURRENT_SOURCE_DIR}/../../common) target_compile_options(test-pidfd-open PRIVATE -Wall -Wextra -Werror) target_link_libraries(test-pidfd-open PRIVATE -lpthread) install(TARGETS test-pidfd-open RUNTIME DESTINATION usr/bin/starry-test-suit) diff --git a/test-suit/starryos/normal/qemu-smp1/syscall/test-pidfd-open/c/src/main.c b/test-suit/starryos/normal/qemu-smp1/syscall/test-pidfd-open/c/src/main.c index 5ab819701c..bcb756bd4f 100644 --- a/test-suit/starryos/normal/qemu-smp1/syscall/test-pidfd-open/c/src/main.c +++ b/test-suit/starryos/normal/qemu-smp1/syscall/test-pidfd-open/c/src/main.c @@ -4,6 +4,7 @@ #include #include #include +#include #include #include #include @@ -89,8 +90,7 @@ static void test_pidfd_open_bad_flags(void) } struct thread_tid_sync { - int notify_pipe[2]; - pid_t tid; + volatile pid_t tid; }; static void *thread_publish_tid(void *arg) @@ -98,13 +98,6 @@ static void *thread_publish_tid(void *arg) struct thread_tid_sync *sync = arg; sync->tid = (pid_t)syscall(SYS_gettid); - if (write(sync->notify_pipe[1], "x", 1) != 1) { - return (void *)1; - } - char ch; - if (read(sync->notify_pipe[0], &ch, 1) != 1) { - return (void *)1; - } return NULL; } @@ -115,16 +108,12 @@ static void test_pidfd_open_thread_tid(void) struct thread_tid_sync sync = { .tid = -1 }; pthread_t thread; - if (pipe(sync.notify_pipe) != 0) { - printf(" FAIL | %s:%d | pipe 创建失败\n", __FILE__, __LINE__); - return; - } - CHECK(pthread_create(&thread, NULL, thread_publish_tid, &sync) == 0, "pthread_create 成功"); - char ch; - CHECK(read(sync.notify_pipe[0], &ch, 1) == 1, "等待子线程 gettid"); + for (int i = 0; i < 1000000 && sync.tid <= 0; i++) { + sched_yield(); + } CHECK(sync.tid > 0 && sync.tid != getpid(), "子线程 tid 与 getpid 不同"); CHECK_ERR(x_pidfd_open(sync.tid, 0), ENOENT, @@ -136,9 +125,7 @@ static void test_pidfd_open_thread_tid(void) close(pfd); } - close(sync.notify_pipe[1]); pthread_join(thread, NULL); - close(sync.notify_pipe[0]); } static void test_pidfd_open_zombie(void) diff --git a/test-suit/starryos/normal/qemu-smp1/syscall/test-pidfd-send-signal/c/CMakeLists.txt b/test-suit/starryos/normal/qemu-smp1/syscall/test-pidfd-send-signal/c/CMakeLists.txt index a221499ff2..4b04a50a9a 100644 --- a/test-suit/starryos/normal/qemu-smp1/syscall/test-pidfd-send-signal/c/CMakeLists.txt +++ b/test-suit/starryos/normal/qemu-smp1/syscall/test-pidfd-send-signal/c/CMakeLists.txt @@ -4,7 +4,7 @@ set(CMAKE_C_STANDARD 11) set(CMAKE_C_STANDARD_REQUIRED ON) set(CMAKE_C_EXTENSIONS OFF) add_executable(test-pidfd-send-signal src/main.c) -target_include_directories(test-pidfd-send-signal PRIVATE src) +target_include_directories(test-pidfd-send-signal PRIVATE src ${CMAKE_CURRENT_SOURCE_DIR}/../../common) target_compile_options(test-pidfd-send-signal PRIVATE -Wall -Wextra -Werror) target_link_libraries(test-pidfd-send-signal PRIVATE -lpthread) install(TARGETS test-pidfd-send-signal RUNTIME DESTINATION usr/bin/starry-test-suit) diff --git a/test-suit/starryos/normal/qemu-smp1/syscall/test-pidfd-send-signal/c/src/test_framework.h b/test-suit/starryos/normal/qemu-smp1/syscall/test-pidfd-send-signal/c/src/test_framework.h deleted file mode 100644 index 4576f742ef..0000000000 --- a/test-suit/starryos/normal/qemu-smp1/syscall/test-pidfd-send-signal/c/src/test_framework.h +++ /dev/null @@ -1,65 +0,0 @@ -#pragma once - -#ifndef _GNU_SOURCE -#define _GNU_SOURCE -#endif - -#include -#include -#include -#include - -static int __pass = 0; -static int __fail = 0; - -#define CHECK(cond, msg) do { \ - if (cond) { \ - printf(" PASS | %s:%d | %s\n", __FILE__, __LINE__, msg); \ - __pass++; \ - } else { \ - printf(" FAIL | %s:%d | %s | errno=%d (%s)\n", \ - __FILE__, __LINE__, msg, errno, strerror(errno)); \ - __fail++; \ - } \ -} while(0) - -#define CHECK_RET(call, expected, msg) do { \ - errno = 0; \ - long _r = (long)(call); \ - long _e = (long)(expected); \ - if (_r == _e) { \ - printf(" PASS | %s:%d | %s (ret=%ld)\n", \ - __FILE__, __LINE__, msg, _r); \ - __pass++; \ - } else { \ - printf(" FAIL | %s:%d | %s | expected=%ld got=%ld | errno=%d (%s)\n", \ - __FILE__, __LINE__, msg, _e, _r, errno, strerror(errno));\ - __fail++; \ - } \ -} while(0) - -#define CHECK_ERR(call, exp_errno, msg) do { \ - errno = 0; \ - long _r = (long)(call); \ - if (_r == -1 && errno == (exp_errno)) { \ - printf(" PASS | %s:%d | %s (errno=%d as expected)\n", \ - __FILE__, __LINE__, msg, errno); \ - __pass++; \ - } else { \ - printf(" FAIL | %s:%d | %s | expected errno=%d got ret=%ld errno=%d (%s)\n", \ - __FILE__, __LINE__, msg, (int)(exp_errno), _r, errno, strerror(errno));\ - __fail++; \ - } \ -} while(0) - -#define TEST_START(name) \ - printf("================================================\n"); \ - printf(" TEST: %s\n", name); \ - printf(" FILE: %s\n", __FILE__); \ - printf("================================================\n") - -#define TEST_DONE() \ - printf("------------------------------------------------\n"); \ - printf(" DONE: %d pass, %d fail\n", __pass, __fail); \ - printf("================================================\n\n"); \ - return __fail > 0 ? 1 : 0