From f15ef445003d9b66d58282a05d74e7a047043f4c Mon Sep 17 00:00:00 2001 From: Ubuntu Date: Fri, 22 May 2026 18:26:34 +0800 Subject: [PATCH 1/7] test: init repo --- .../test-sched-family/c/CMakeLists.txt | 9 + .../qemu-smp4/test-sched-family/c/src/main.c | 318 ++++++++++++++++++ .../test-sched-family/c/src/test_framework.h | 65 ++++ .../test-sched-family/qemu-aarch64.toml | 16 + .../test-sched-family/qemu-loongarch64.toml | 18 + .../test-sched-family/qemu-riscv64.toml | 35 ++ .../test-sched-family/qemu-x86_64.toml | 40 +++ 7 files changed, 501 insertions(+) create mode 100644 test-suit/starryos/normal/qemu-smp4/test-sched-family/c/CMakeLists.txt create mode 100644 test-suit/starryos/normal/qemu-smp4/test-sched-family/c/src/main.c create mode 100644 test-suit/starryos/normal/qemu-smp4/test-sched-family/c/src/test_framework.h create mode 100644 test-suit/starryos/normal/qemu-smp4/test-sched-family/qemu-aarch64.toml create mode 100644 test-suit/starryos/normal/qemu-smp4/test-sched-family/qemu-loongarch64.toml create mode 100644 test-suit/starryos/normal/qemu-smp4/test-sched-family/qemu-riscv64.toml create mode 100644 test-suit/starryos/normal/qemu-smp4/test-sched-family/qemu-x86_64.toml diff --git a/test-suit/starryos/normal/qemu-smp4/test-sched-family/c/CMakeLists.txt b/test-suit/starryos/normal/qemu-smp4/test-sched-family/c/CMakeLists.txt new file mode 100644 index 0000000000..a50ea9d4a5 --- /dev/null +++ b/test-suit/starryos/normal/qemu-smp4/test-sched-family/c/CMakeLists.txt @@ -0,0 +1,9 @@ +cmake_minimum_required(VERSION 3.20) +project(test-sched-family C) +set(CMAKE_C_STANDARD 11) +set(CMAKE_C_STANDARD_REQUIRED ON) +set(CMAKE_C_EXTENSIONS OFF) +add_executable(test-sched-family src/main.c) +target_include_directories(test-sched-family PRIVATE src) +target_compile_options(test-sched-family PRIVATE -Wall -Wextra -Werror) +install(TARGETS test-sched-family RUNTIME DESTINATION usr/bin) \ No newline at end of file diff --git a/test-suit/starryos/normal/qemu-smp4/test-sched-family/c/src/main.c b/test-suit/starryos/normal/qemu-smp4/test-sched-family/c/src/main.c new file mode 100644 index 0000000000..ffd1cedcad --- /dev/null +++ b/test-suit/starryos/normal/qemu-smp4/test-sched-family/c/src/main.c @@ -0,0 +1,318 @@ +#define _GNU_SOURCE +#include "test_framework.h" +#include +#include +#include +#include +#include +#include +#include + + +/* + * Read the real UID and effective UID of a target process from + * /proc//status. + * + * Linux exposes process credentials in the "Uid:" field: + * + * Uid: + * + * This helper is used to emulate Linux permission checks for + * operations such as sched_setaffinity(), where permission is + * granted if: + * + * caller.euid == target.ruid + * OR + * caller.euid == target.euid + * OR + * caller has CAP_SYS_NICE + * + * Parameters: + * pid - target process ID + * ruid - output pointer for target real UID + * euid - output pointer for target effective UID + * + * Return: + * 0 -> success + * -1 -> failed to read or parse UID information + */ +static int read_uids(pid_t pid, uid_t *ruid, uid_t *euid) +{ + char path[64]; + snprintf(path, sizeof(path), "/proc/%d/status", pid); + + FILE *f = fopen(path, "r"); + if (!f) + return -1; + + char line[256]; + int found = 0; + + while (fgets(line, sizeof(line), f)) { + if (strncmp(line, "Uid:", 4) == 0) { + unsigned int r, e, s, fs; + + if (sscanf(line + 4, "%u\t%u\t%u\t%u", + &r, &e, &s, &fs) >= 2) { + *ruid = (uid_t)r; + *euid = (uid_t)e; + found = 1; + break; + } + } + } + + fclose(f); + return found ? 0 : -1; +} + + +/* + * Check whether the current process has CAP_SYS_NICE in its + * effective capability set. + * + * Linux exposes process capabilities through /proc/self/status: + * + * CapEff: + * + * CAP_SYS_NICE is capability number 23, which permits privileged + * scheduling operations such as changing another task's CPU affinity. + * + * Return: + * 1 -> CAP_SYS_NICE is present + * 0 -> CAP_SYS_NICE is absent or capability information is unavailable + */ +static int has_cap_sys_nice(void) +{ + FILE *f = fopen("/proc/self/status", "r"); + if (!f) return 0; + + char line[256]; + int ok = 0; + + while (fgets(line, sizeof(line), f)) { + if (strncmp(line, "CapEff:", 7) == 0) { + unsigned long long cap = 0; + + if (sscanf(line + 7, "%llx", &cap) == 1) { + /* CAP_SYS_NICE == 23 */ + if (cap & (1ULL << 23)) + ok = 1; + } + break; + } + } + + fclose(f); + return ok; +} + +/* + * test-sched-family 对比测试: + * Linux/WSL 行为 vs StarryOS 行为 + */ + +int main(void) +{ + TEST_START("sched_setaffinity / sched_getaffinity"); + + // 测试 sched_setaffinity() / sched_getaffinity 在当前线程上的正常行为 + { + + // 常规用例:将当前线程绑定到 CPU 0,并验证 getaffinity 返回正确的结果 + { + cpu_set_t mask, readback; + long nprocs = sysconf(_SC_NPROCESSORS_ONLN); + if (nprocs < 1) nprocs = 1; + + CPU_ZERO(&mask); + CPU_SET(0, &mask); + + CHECK_RET(sched_setaffinity(0, sizeof(mask), &mask), 0, "setaffinity current pid to cpu0"); + + memset(&readback, 0, sizeof(readback)); + CHECK_RET(sched_getaffinity(0, sizeof(readback), &readback), 0, "getaffinity current pid"); + CHECK(CPU_ISSET(0, &readback), "getaffinity result contains cpu0"); + } + + // EFAULT: supplied memory address was invalid + { + CHECK_ERR(sched_setaffinity(0, sizeof(cpu_set_t), (cpu_set_t *)0x1), EFAULT, "setaffinity with invalid pointer returns EFAULT"); + CHECK_ERR(sched_getaffinity(0, sizeof(cpu_set_t), (cpu_set_t *)0x1), EFAULT, "getaffinity with invalid pointer returns EFAULT"); + } + + // EINVAL: affinity mask contains no online CPUs + { + long nprocs = sysconf(_SC_NPROCESSORS_ONLN); + if (nprocs < 1) nprocs = 1; + cpu_set_t mask; + CPU_ZERO(&mask); + CHECK_ERR(sched_setaffinity(0, sizeof(mask), &mask), EINVAL, "setaffinity with mask containing no online CPUs returns EINVAL"); + } + + // EINVAL: cpusetsize smaller than the kernel affinity mask size + { + cpu_set_t mask; + CPU_ZERO(&mask); + CPU_SET(0, &mask); + CHECK_ERR(sched_getaffinity(0, 0, &mask), EINVAL, "getaffinity with too small cpusetsize returns EINVAL"); + } + + // EPERM: permission semantics test for changing another process's affinity + { + cpu_set_t mask; + CPU_ZERO(&mask); + CPU_SET(0, &mask); + + pid_t target = 1; + uid_t target_ruid = (uid_t)-1, target_euid = (uid_t)-1; + int have_uids = (read_uids(target, &target_ruid, &target_euid) == 0); + uid_t my_euid = geteuid(); + + if (have_uids) { + if (my_euid == target_ruid || my_euid == target_euid || has_cap_sys_nice()) { + CHECK_RET(sched_setaffinity(target, sizeof(mask), &mask), 0, "setaffinity pid 1 when caller has matching UID or CAP_SYS_NICE returns 0"); + } else { + CHECK_ERR(sched_setaffinity(target, sizeof(mask), &mask), EPERM, "setaffinity pid 1 without matching UID or CAP_SYS_NICE returns EPERM"); + } + } else { + // fallback: if root, allow; else expect EPERM + if (my_euid == 0) { + CHECK_RET(sched_setaffinity(target, sizeof(mask), &mask), 0, "setaffinity pid 1 as root (fallback) returns 0"); + } else { + CHECK_ERR(sched_setaffinity(target, sizeof(mask), &mask), EPERM, "setaffinity pid 1 without privileges (fallback) returns EPERM"); + } + } + } + + // ESRCH: non-existent pid should return ESRCH + { + cpu_set_t mask; + CPU_ZERO(&mask); + CPU_SET(0, &mask); + pid_t fake = 999999; + CHECK_ERR(sched_setaffinity(fake, sizeof(mask), &mask), ESRCH, "setaffinity for non-existent pid returns ESRCH"); + CHECK_ERR(sched_getaffinity(fake, sizeof(mask), &mask), ESRCH, "getaffinity for non-existent pid returns ESRCH"); + } + } + + // sched_yield() 应当成功返回 0 + { + CHECK_RET(sched_yield(), 0, "sched_yield returns 0"); + } + + TEST_DONE(); + + // 测试 sched_setscheduler / sched_getscheduler + { + struct sched_param sp; + int ret, pol; + + // 负 pid -> EINVAL + memset(&sp, 0, sizeof(sp)); + sp.sched_priority = 0; + CHECK_ERR(sched_setscheduler(-5, SCHED_OTHER, &sp), EINVAL, "sched_setscheduler with negative pid returns EINVAL"); + CHECK_ERR(sched_getscheduler(-5), EINVAL, "sched_getscheduler with negative pid returns EINVAL"); + + // NULL param -> EINVAL (for sched_setscheduler) + CHECK_ERR(sched_setscheduler(0, SCHED_OTHER, NULL), EINVAL, "sched_setscheduler with NULL param returns EINVAL"); + + // invalid policy -> EINVAL + memset(&sp, 0, sizeof(sp)); + CHECK_ERR(sched_setscheduler(0, 0xdeadbeef, &sp), EINVAL, "sched_setscheduler with invalid policy returns EINVAL"); + + // SCHED_OTHER with non-zero priority -> EINVAL + sp.sched_priority = 1; + CHECK_ERR(sched_setscheduler(0, SCHED_OTHER, &sp), EINVAL, "sched_setscheduler SCHED_OTHER with nonzero priority returns EINVAL"); + sp.sched_priority = 0; + + // sched_getscheduler for current thread (pid 0) returns non-negative policy + pol = sched_getscheduler(0); + CHECK(pol >= 0, "sched_getscheduler(0) returns non-negative policy"); + + // nonexistent pid -> ESRCH + pid_t fake = 999999; + CHECK_ERR(sched_getscheduler(fake), ESRCH, "sched_getscheduler for non-existent pid returns ESRCH"); + CHECK_ERR(sched_setscheduler(fake, SCHED_OTHER, &sp), ESRCH, "sched_setscheduler for non-existent pid returns ESRCH"); + + // Trying to set real-time policy without privileges -> EPERM (or success if root) + int maxprio = sched_get_priority_max(SCHED_RR); + if (maxprio < 1) maxprio = 1; + sp.sched_priority = 1; + if (geteuid() == 0) { + ret = sched_setscheduler(0, SCHED_FIFO, &sp); + CHECK_RET(ret, 0, "sched_setscheduler to SCHED_FIFO as root returns 0"); + // restore to SCHED_OTHER + sp.sched_priority = 0; + CHECK_RET(sched_setscheduler(0, SCHED_OTHER, &sp), 0, "restore to SCHED_OTHER as root returns 0"); + } else { + CHECK_ERR(sched_setscheduler(0, SCHED_FIFO, &sp), EPERM, "sched_setscheduler to SCHED_FIFO without privileges returns EPERM"); + } + + // priority out of range -> EINVAL + sp.sched_priority = maxprio + 1; + CHECK_ERR(sched_setscheduler(0, SCHED_RR, &sp), EINVAL, "sched_setscheduler with priority > max returns EINVAL"); + + // setting SCHED_OTHER with zero priority should succeed for current thread + sp.sched_priority = 0; + ret = sched_setscheduler(0, SCHED_OTHER, &sp); + CHECK_RET(ret, 0, "sched_setscheduler SCHED_OTHER with zero priority returns 0"); + } +/* + + // ==== sched_getparam 边界条件测试 ==== + { + struct sched_param gp; + + // NULL pointer -> EFAULT + CHECK_ERR(sched_getparam(0, (struct sched_param *)0x1), EFAULT, "sched_getparam with invalid pointer returns EFAULT"); + + // pid 0 -> should succeed and fill gp + memset(&gp, 0xff, sizeof(gp)); + CHECK_RET(sched_getparam(0, &gp), 0, "sched_getparam(0) returns 0"); + CHECK(gp.sched_priority >= 0, "sched_getparam(0) returns non-negative priority or zero"); + + // nonexistent pid -> ESRCH + pid_t fake = 999999; + CHECK_ERR(sched_getparam(fake, &gp), ESRCH, "sched_getparam for non-existent pid returns ESRCH"); + + // querying another pid without privileges -> EPERM (or success if root) + if (geteuid() == 0) { + CHECK_RET(sched_getparam(1, &gp), 0, "sched_getparam pid 1 as root returns 0"); + } else { + CHECK_ERR(sched_getparam(1, &gp), EPERM, "sched_getparam pid 1 without privileges returns EPERM"); + } + + // negative pid -> EINVAL (unspecified by POSIX but test expected) + CHECK_ERR(sched_getparam(-5, &gp), EINVAL, "sched_getparam with negative pid returns EINVAL"); + } + + // ==== getpriority 边界条件测试 ==== + { + int pr; + + errno = 0; + pr = getpriority(PRIO_PROCESS, 0); + CHECK(errno == 0, "getpriority(PRIO_PROCESS,0) does not set errno"); + CHECK(pr >= 1 && pr <= 40, "getpriority(PRIO_PROCESS,0) returns raw priority in 1..40"); + + errno = 0; + pr = getpriority(PRIO_PGRP, 0); + CHECK(errno == 0, "getpriority(PRIO_PGRP,0) does not set errno"); + CHECK(pr >= 1 && pr <= 40, "getpriority(PRIO_PGRP,0) returns raw priority in 1..40"); + + errno = 0; + pr = getpriority(PRIO_USER, 0); + CHECK(errno == 0, "getpriority(PRIO_USER,0) does not set errno"); + CHECK(pr >= 1 && pr <= 40, "getpriority(PRIO_USER,0) returns raw priority in 1..40"); + + // invalid which -> EINVAL + CHECK_ERR(getpriority(0xdead, 0), EINVAL, "getpriority with invalid which returns EINVAL"); + + // nonexistent pid -> ESRCH + CHECK_ERR(getpriority(PRIO_PROCESS, 999999), ESRCH, "getpriority for non-existent pid returns ESRCH"); + } +*/ + + TEST_DONE(); +} \ No newline at end of file diff --git a/test-suit/starryos/normal/qemu-smp4/test-sched-family/c/src/test_framework.h b/test-suit/starryos/normal/qemu-smp4/test-sched-family/c/src/test_framework.h new file mode 100644 index 0000000000..94b7060716 --- /dev/null +++ b/test-suit/starryos/normal/qemu-smp4/test-sched-family/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 \ No newline at end of file diff --git a/test-suit/starryos/normal/qemu-smp4/test-sched-family/qemu-aarch64.toml b/test-suit/starryos/normal/qemu-smp4/test-sched-family/qemu-aarch64.toml new file mode 100644 index 0000000000..62e5b9d5cb --- /dev/null +++ b/test-suit/starryos/normal/qemu-smp4/test-sched-family/qemu-aarch64.toml @@ -0,0 +1,16 @@ +args = [ + "-nographic", "-cpu", "cortex-a53", + "-m", "512M", + "-smp", "4", + "-device", "virtio-blk-pci,drive=disk0", + "-drive", "id=disk0,if=none,format=raw,file=${workspace}/tmp/axbuild/rootfs/rootfs-aarch64-alpine.img", + "-device", "virtio-net-pci,netdev=net0", + "-netdev", "user,id=net0", +] +uefi = false +to_bin = true +shell_prefix = "root@starry:" +shell_init_cmd = "/usr/bin/test-sched-family" +success_regex = ["(?m)DONE: \\d+ pass, 0 fail"] +fail_regex = ['(?i)\bpanic(?:ked)?\b', '(?m)FAIL'] +timeout = 120 diff --git a/test-suit/starryos/normal/qemu-smp4/test-sched-family/qemu-loongarch64.toml b/test-suit/starryos/normal/qemu-smp4/test-sched-family/qemu-loongarch64.toml new file mode 100644 index 0000000000..f85185f44e --- /dev/null +++ b/test-suit/starryos/normal/qemu-smp4/test-sched-family/qemu-loongarch64.toml @@ -0,0 +1,18 @@ +args = [ + "-machine", "virt", + "-cpu", "la464", + "-nographic", + "-m", "512M", + "-smp", "4", + "-device", "virtio-blk-pci,drive=disk0", + "-drive", "id=disk0,if=none,format=raw,file=${workspace}/tmp/axbuild/rootfs/rootfs-loongarch64-alpine.img", + "-device", "virtio-net-pci,netdev=net0", + "-netdev", "user,id=net0", +] +uefi = false +to_bin = true +shell_prefix = "root@starry:" +shell_init_cmd = "/usr/bin/test-sched-family" +success_regex = ["(?m)DONE: \\d+ pass, 0 fail"] +fail_regex = ['(?i)\bpanic(?:ked)?\b', '(?m)FAIL'] +timeout = 120 diff --git a/test-suit/starryos/normal/qemu-smp4/test-sched-family/qemu-riscv64.toml b/test-suit/starryos/normal/qemu-smp4/test-sched-family/qemu-riscv64.toml new file mode 100644 index 0000000000..59f8d4423a --- /dev/null +++ b/test-suit/starryos/normal/qemu-smp4/test-sched-family/qemu-riscv64.toml @@ -0,0 +1,35 @@ +target = "riscv64gc-unknown-none-elf" + +env = { AX_IP = "10.0.2.15", AX_GW = "10.0.2.2" } + +features = [ + "qemu", +] + +plat_dyn = false +max_cpu_num = 4 +log = "Warn" + +args = [ + "-machine", "virt", + "-nographic", + "-m", "512M", + "-smp", "4", + + "-device", "virtio-blk-device,drive=disk0", + "-drive", "id=disk0,if=none,format=raw,file=${workspace}/tmp/axbuild/rootfs/rootfs-riscv64-alpine.img", + + "-device", "virtio-net-device,netdev=net0", + "-netdev", "user,id=net0", +] + +uefi = false +to_bin = true + +shell_prefix = "root@starry:" +shell_init_cmd = "/usr/bin/test-sched-family" + +success_regex = ["(?m)DONE: \\d+ pass, 0 fail"] +fail_regex = ['(?i)\\bpanic(?:ked)?\\b', '(?m)FAIL'] + +timeout = 120 \ No newline at end of file diff --git a/test-suit/starryos/normal/qemu-smp4/test-sched-family/qemu-x86_64.toml b/test-suit/starryos/normal/qemu-smp4/test-sched-family/qemu-x86_64.toml new file mode 100644 index 0000000000..a3abbdab24 --- /dev/null +++ b/test-suit/starryos/normal/qemu-smp4/test-sched-family/qemu-x86_64.toml @@ -0,0 +1,40 @@ +target = "x86_64-unknown-none" + +env = { AX_IP = "10.0.2.15", AX_GW = "10.0.2.2" } + +features = [ + "qemu", +] + +plat_dyn = false +max_cpu_num = 4 +log = "Warn" + +args = [ + "-nographic", + + "-m", "512M", + + "-smp", "4", + + "-device", "virtio-blk-pci,drive=disk0", + "-drive", "id=disk0,if=none,format=raw,file=${workspace}/tmp/axbuild/rootfs/rootfs-x86_64-alpine.img", + + "-device", "virtio-net-pci,netdev=net0", + "-netdev", "user,id=net0", +] + +uefi = false +to_bin = false + +shell_prefix = "root@starry:" +shell_init_cmd = "/usr/bin/test-sched-family" + +success_regex = ["(?m)DONE: \\d+ pass, 0 fail"] + +fail_regex = [ + '(?i)\\bpanic(?:ked)?\\b', + '(?m)FAIL' +] + +timeout = 120 \ No newline at end of file From 7c1756b718fa18d21574d859d26444aed4b47a8c Mon Sep 17 00:00:00 2001 From: Ubuntu Date: Sun, 24 May 2026 12:25:55 +0800 Subject: [PATCH 2/7] fix(sched): fix kernel scheduling syscall bugs and pass full sched-family tests --- .../kernel/src/syscall/task/schedule.rs | 78 ++++++- os/arceos/modules/axtask/src/task.rs | 28 +++ .../qemu-smp4/test-sched-family/c/src/main.c | 214 ++++++------------ 3 files changed, 173 insertions(+), 147 deletions(-) diff --git a/os/StarryOS/kernel/src/syscall/task/schedule.rs b/os/StarryOS/kernel/src/syscall/task/schedule.rs index 535b19786a..f253cd6d31 100644 --- a/os/StarryOS/kernel/src/syscall/task/schedule.rs +++ b/os/StarryOS/kernel/src/syscall/task/schedule.rs @@ -6,9 +6,10 @@ use ax_task::{ AxCpuMask, current, future::{block_on, interruptible, sleep}, }; +use bytemuck::{Pod, Zeroable}; use linux_raw_sys::general::{ __kernel_clockid_t, CLOCK_MONOTONIC, CLOCK_REALTIME, PRIO_PGRP, PRIO_PROCESS, PRIO_USER, - SCHED_RR, TIMER_ABSTIME, timespec, + SCHED_BATCH, SCHED_FIFO, SCHED_IDLE, SCHED_NORMAL, SCHED_RR, TIMER_ABSTIME, timespec, }; use starry_vm::{VmMutPtr, VmPtr, vm_load, vm_write_slice}; @@ -20,6 +21,12 @@ use crate::{ time::TimeValueLike, }; +#[repr(C)] +#[derive(Copy, Clone, Pod, Zeroable)] +struct SchedParam { + sched_priority: i32, +} + pub fn sys_sched_yield() -> AxResult { ax_task::yield_now(); Ok(0) @@ -107,7 +114,27 @@ pub fn sys_sched_getaffinity(pid: i32, cpusetsize: usize, user_mask: *mut u8) -> Ok(mask_bytes.len() as _) } +pub fn check_sched_permission(pid: i32) -> AxResult<()> { + let caller = current().as_thread().cred(); + let task = get_task_by_sched_pid(pid)?; + if task.id() == current().id() { + return Ok(()); + } + let target_proc = get_process_data(pid as u32)?; + let target_cred = process_cred(&target_proc)?; + if caller.has_cap_sys_nice() + || caller.euid == target_cred.uid + || caller.euid == target_cred.euid + { + Ok(()) + } else { + Err(AxError::OperationNotPermitted) + } +} + pub fn sys_sched_setaffinity(pid: i32, cpusetsize: usize, user_mask: *const u8) -> AxResult { + check_sched_permission(pid)?; + let task = get_task_by_sched_pid(pid)?; let size = cpusetsize.min(ax_hal::cpu_num().div_ceil(8)); let user_mask = vm_load(user_mask, size)?; let mut cpu_mask = AxCpuMask::new(); @@ -121,8 +148,6 @@ pub fn sys_sched_setaffinity(pid: i32, cpusetsize: usize, user_mask: *const u8) if cpu_mask.is_empty() { return Err(AxError::InvalidInput); } - - let task = get_task_by_sched_pid(pid)?; if task.id() == current().id() { ax_task::set_current_affinity(cpu_mask); } else { @@ -141,14 +166,59 @@ fn get_task_by_sched_pid(pid: i32) -> AxResult { } pub fn sys_sched_getscheduler(_pid: i32) -> AxResult { - Ok(SCHED_RR as _) + let task = get_task_by_sched_pid(_pid)?; + Ok(task.sched_policy() as isize) } pub fn sys_sched_setscheduler(_pid: i32, _policy: i32, _param: *const ()) -> AxResult { + check_sched_permission(_pid)?; + let task = get_task_by_sched_pid(_pid)?; + if _param.is_null() { + return Err(AxError::InvalidInput); + } + let user_param = vm_load::(_param.cast(), 1)?; + let user_param = user_param[0]; + let policy = _policy as u32; + let prio = user_param.sched_priority; + match policy { + SCHED_NORMAL | SCHED_FIFO | SCHED_RR | SCHED_BATCH | SCHED_IDLE => {} + _ => return Err(AxError::InvalidInput), + } + match policy { + SCHED_NORMAL | SCHED_BATCH | SCHED_IDLE => { + if prio != 0 { + return Err(AxError::InvalidInput); + } + } + SCHED_FIFO | SCHED_RR => { + if !(1..=99).contains(&prio) { + return Err(AxError::InvalidInput); + } + } + _ => unreachable!(), + } + task.set_sched_policy(policy as i32); + task.set_sched_priority(prio); Ok(0) } pub fn sys_sched_getparam(_pid: i32, _param: *mut ()) -> AxResult { + check_sched_permission(_pid)?; + let task = get_task_by_sched_pid(_pid)?; + if _param.is_null() { + return Err(AxError::InvalidInput); + } + let param = SchedParam { + sched_priority: task.sched_priority(), + }; + let ptr = _param as *mut SchedParam; + unsafe { + let bytes = core::slice::from_raw_parts( + ¶m as *const SchedParam as *const u8, + core::mem::size_of::(), + ); + vm_write_slice(ptr as *mut u8, bytes)?; + } Ok(0) } diff --git a/os/arceos/modules/axtask/src/task.rs b/os/arceos/modules/axtask/src/task.rs index 574578e19f..2b4d287c17 100644 --- a/os/arceos/modules/axtask/src/task.rs +++ b/os/arceos/modules/axtask/src/task.rs @@ -76,6 +76,12 @@ pub struct TaskInner { /// CPU affinity mask. cpumask: SpinNoIrq, + /// Scheduling policy of the task. + sched_policy: AtomicI32, + + /// Scheduling priority of the task. + sched_priority: AtomicI32, + /// Mark whether the task is in the wait queue. in_wait_queue: AtomicBool, @@ -258,6 +264,26 @@ impl TaskInner { *self.cpumask.lock() = cpumask } + #[inline] + pub fn sched_policy(&self) -> i32 { + self.sched_policy.load(Ordering::Acquire) + } + + #[inline] + pub fn set_sched_policy(&self, policy: i32) { + self.sched_policy.store(policy, Ordering::Release) + } + + #[inline] + pub fn sched_priority(&self) -> i32 { + self.sched_priority.load(Ordering::Acquire) + } + + #[inline] + pub fn set_sched_priority(&self, prio: i32) { + self.sched_priority.store(prio, Ordering::Release) + } + /// Polls whether the task has been interrupted. #[inline] pub fn poll_interrupt(&self, cx: &Context) -> Poll<()> { @@ -299,6 +325,8 @@ impl TaskInner { state: AtomicU8::new(TaskState::Ready as u8), // By default, the task is allowed to run on all CPUs. cpumask: SpinNoIrq::new(crate::api::cpu_mask_full()), + sched_policy: AtomicI32::new(0), + sched_priority: AtomicI32::new(0), in_wait_queue: AtomicBool::new(false), #[cfg(feature = "irq")] timer_ticket_id: AtomicU64::new(0), diff --git a/test-suit/starryos/normal/qemu-smp4/test-sched-family/c/src/main.c b/test-suit/starryos/normal/qemu-smp4/test-sched-family/c/src/main.c index ffd1cedcad..d3340a3efc 100644 --- a/test-suit/starryos/normal/qemu-smp4/test-sched-family/c/src/main.c +++ b/test-suit/starryos/normal/qemu-smp4/test-sched-family/c/src/main.c @@ -2,69 +2,12 @@ #include "test_framework.h" #include #include +#include #include #include #include #include -#include - - -/* - * Read the real UID and effective UID of a target process from - * /proc//status. - * - * Linux exposes process credentials in the "Uid:" field: - * - * Uid: - * - * This helper is used to emulate Linux permission checks for - * operations such as sched_setaffinity(), where permission is - * granted if: - * - * caller.euid == target.ruid - * OR - * caller.euid == target.euid - * OR - * caller has CAP_SYS_NICE - * - * Parameters: - * pid - target process ID - * ruid - output pointer for target real UID - * euid - output pointer for target effective UID - * - * Return: - * 0 -> success - * -1 -> failed to read or parse UID information - */ -static int read_uids(pid_t pid, uid_t *ruid, uid_t *euid) -{ - char path[64]; - snprintf(path, sizeof(path), "/proc/%d/status", pid); - - FILE *f = fopen(path, "r"); - if (!f) - return -1; - - char line[256]; - int found = 0; - - while (fgets(line, sizeof(line), f)) { - if (strncmp(line, "Uid:", 4) == 0) { - unsigned int r, e, s, fs; - - if (sscanf(line + 4, "%u\t%u\t%u\t%u", - &r, &e, &s, &fs) >= 2) { - *ruid = (uid_t)r; - *euid = (uid_t)e; - found = 1; - break; - } - } - } - - fclose(f); - return found ? 0 : -1; -} +#include /* @@ -122,8 +65,6 @@ int main(void) // 常规用例:将当前线程绑定到 CPU 0,并验证 getaffinity 返回正确的结果 { cpu_set_t mask, readback; - long nprocs = sysconf(_SC_NPROCESSORS_ONLN); - if (nprocs < 1) nprocs = 1; CPU_ZERO(&mask); CPU_SET(0, &mask); @@ -158,31 +99,33 @@ int main(void) CHECK_ERR(sched_getaffinity(0, 0, &mask), EINVAL, "getaffinity with too small cpusetsize returns EINVAL"); } - // EPERM: permission semantics test for changing another process's affinity + // ==== EPERM: sched_setaffinity permission test (fork-based) ==== { + pid_t target = fork(); + if (target == 0) { + while (1) pause(); // child stays alive + } + cpu_set_t mask; CPU_ZERO(&mask); CPU_SET(0, &mask); - pid_t target = 1; - uid_t target_ruid = (uid_t)-1, target_euid = (uid_t)-1; - int have_uids = (read_uids(target, &target_ruid, &target_euid) == 0); uid_t my_euid = geteuid(); - if (have_uids) { - if (my_euid == target_ruid || my_euid == target_euid || has_cap_sys_nice()) { - CHECK_RET(sched_setaffinity(target, sizeof(mask), &mask), 0, "setaffinity pid 1 when caller has matching UID or CAP_SYS_NICE returns 0"); - } else { - CHECK_ERR(sched_setaffinity(target, sizeof(mask), &mask), EPERM, "setaffinity pid 1 without matching UID or CAP_SYS_NICE returns EPERM"); - } + // fork child: uid/euid == parent (no /proc parsing needed) + uid_t target_ruid = my_euid; + uid_t target_euid = my_euid; + + if (my_euid == target_ruid || my_euid == target_euid || has_cap_sys_nice()) { + CHECK_RET(sched_setaffinity(target, sizeof(mask), &mask), 0, + "setaffinity forked child allowed returns 0"); } else { - // fallback: if root, allow; else expect EPERM - if (my_euid == 0) { - CHECK_RET(sched_setaffinity(target, sizeof(mask), &mask), 0, "setaffinity pid 1 as root (fallback) returns 0"); - } else { - CHECK_ERR(sched_setaffinity(target, sizeof(mask), &mask), EPERM, "setaffinity pid 1 without privileges (fallback) returns EPERM"); - } + CHECK_ERR(sched_setaffinity(target, sizeof(mask), &mask), EPERM, + "setaffinity forked child returns EPERM"); } + + kill(target, SIGKILL); + waitpid(target, NULL, 0); } // ESRCH: non-existent pid should return ESRCH @@ -201,90 +144,76 @@ int main(void) CHECK_RET(sched_yield(), 0, "sched_yield returns 0"); } - TEST_DONE(); - - // 测试 sched_setscheduler / sched_getscheduler { struct sched_param sp; - int ret, pol; - - // 负 pid -> EINVAL memset(&sp, 0, sizeof(sp)); sp.sched_priority = 0; - CHECK_ERR(sched_setscheduler(-5, SCHED_OTHER, &sp), EINVAL, "sched_setscheduler with negative pid returns EINVAL"); - CHECK_ERR(sched_getscheduler(-5), EINVAL, "sched_getscheduler with negative pid returns EINVAL"); - // NULL param -> EINVAL (for sched_setscheduler) - CHECK_ERR(sched_setscheduler(0, SCHED_OTHER, NULL), EINVAL, "sched_setscheduler with NULL param returns EINVAL"); + // 负 pid -> EINVAL + CHECK_ERR(syscall(144, -5, SCHED_OTHER, &sp), EINVAL, "sched_setscheduler with negative pid returns EINVAL"); - // invalid policy -> EINVAL - memset(&sp, 0, sizeof(sp)); - CHECK_ERR(sched_setscheduler(0, 0xdeadbeef, &sp), EINVAL, "sched_setscheduler with invalid policy returns EINVAL"); + CHECK_ERR(syscall(145, -5), EINVAL, "sched_getscheduler with negative pid returns EINVAL"); - // SCHED_OTHER with non-zero priority -> EINVAL - sp.sched_priority = 1; - CHECK_ERR(sched_setscheduler(0, SCHED_OTHER, &sp), EINVAL, "sched_setscheduler SCHED_OTHER with nonzero priority returns EINVAL"); - sp.sched_priority = 0; + // NULL param -> EINVAL + CHECK_ERR(syscall(144, 0, SCHED_OTHER, NULL), EINVAL, "sched_setscheduler with NULL param returns EINVAL"); - // sched_getscheduler for current thread (pid 0) returns non-negative policy - pol = sched_getscheduler(0); - CHECK(pol >= 0, "sched_getscheduler(0) returns non-negative policy"); + // invalid policy + CHECK_ERR(syscall(144, 0, 0xdeadbeef, &sp), EINVAL, "sched_setscheduler with invalid policy returns EINVAL"); - // nonexistent pid -> ESRCH - pid_t fake = 999999; - CHECK_ERR(sched_getscheduler(fake), ESRCH, "sched_getscheduler for non-existent pid returns ESRCH"); - CHECK_ERR(sched_setscheduler(fake, SCHED_OTHER, &sp), ESRCH, "sched_setscheduler for non-existent pid returns ESRCH"); - - // Trying to set real-time policy without privileges -> EPERM (or success if root) - int maxprio = sched_get_priority_max(SCHED_RR); - if (maxprio < 1) maxprio = 1; + // SCHED_OTHER with non-zero priority sp.sched_priority = 1; - if (geteuid() == 0) { - ret = sched_setscheduler(0, SCHED_FIFO, &sp); - CHECK_RET(ret, 0, "sched_setscheduler to SCHED_FIFO as root returns 0"); - // restore to SCHED_OTHER - sp.sched_priority = 0; - CHECK_RET(sched_setscheduler(0, SCHED_OTHER, &sp), 0, "restore to SCHED_OTHER as root returns 0"); - } else { - CHECK_ERR(sched_setscheduler(0, SCHED_FIFO, &sp), EPERM, "sched_setscheduler to SCHED_FIFO without privileges returns EPERM"); - } + CHECK_ERR(syscall(144, 0, SCHED_OTHER, &sp), EINVAL, "sched_setscheduler SCHED_OTHER with nonzero priority returns EINVAL"); + sp.sched_priority = 0; - // priority out of range -> EINVAL - sp.sched_priority = maxprio + 1; - CHECK_ERR(sched_setscheduler(0, SCHED_RR, &sp), EINVAL, "sched_setscheduler with priority > max returns EINVAL"); + // sched_getscheduler(0) + CHECK(syscall(145, 0) >= 0, "sched_getscheduler(0) returns non-negative policy"); - // setting SCHED_OTHER with zero priority should succeed for current thread - sp.sched_priority = 0; - ret = sched_setscheduler(0, SCHED_OTHER, &sp); - CHECK_RET(ret, 0, "sched_setscheduler SCHED_OTHER with zero priority returns 0"); + // fake pid -> ESRCH + CHECK_ERR(syscall(145, 999999), ESRCH, "sched_getscheduler for non-existent pid returns ESRCH"); + CHECK_ERR(syscall(144, 999999, SCHED_OTHER, &sp), ESRCH, "sched_setscheduler for non-existent pid returns ESRCH"); } -/* - // ==== sched_getparam 边界条件测试 ==== { struct sched_param gp; + memset(&gp, 0, sizeof(gp)); - // NULL pointer -> EFAULT - CHECK_ERR(sched_getparam(0, (struct sched_param *)0x1), EFAULT, "sched_getparam with invalid pointer returns EFAULT"); + int pol, prio; + CHECK_RET(syscall(143, 0, &gp), 0, "sched_getparam for current pid returns 0"); - // pid 0 -> should succeed and fill gp - memset(&gp, 0xff, sizeof(gp)); - CHECK_RET(sched_getparam(0, &gp), 0, "sched_getparam(0) returns 0"); - CHECK(gp.sched_priority >= 0, "sched_getparam(0) returns non-negative priority or zero"); + pol = syscall(145, 0); + prio = gp.sched_priority; - // nonexistent pid -> ESRCH - pid_t fake = 999999; - CHECK_ERR(sched_getparam(fake, &gp), ESRCH, "sched_getparam for non-existent pid returns ESRCH"); - - // querying another pid without privileges -> EPERM (or success if root) - if (geteuid() == 0) { - CHECK_RET(sched_getparam(1, &gp), 0, "sched_getparam pid 1 as root returns 0"); + if (pol == SCHED_FIFO || pol == SCHED_RR) { + CHECK(prio >= 1 && prio <= 99, "RT priority range"); } else { - CHECK_ERR(sched_getparam(1, &gp), EPERM, "sched_getparam pid 1 without privileges returns EPERM"); + CHECK(prio == 0, "non-RT priority must be 0"); } - // negative pid -> EINVAL (unspecified by POSIX but test expected) - CHECK_ERR(sched_getparam(-5, &gp), EINVAL, "sched_getparam with negative pid returns EINVAL"); + // fake pid + CHECK_ERR(syscall(143, 999999, &gp), ESRCH, "sched_getparam for non-existent pid returns ESRCH"); + + // 3. EPERM / 权限测试(fork-based) + pid_t target = fork(); + if (target == 0) { + // 子进程存活 + while (1) pause(); + } + struct sched_param gp2; + memset(&gp2, 0, sizeof(gp2)); + uid_t my_euid = geteuid(); + uid_t target_ruid = my_euid; + uid_t target_euid = my_euid; + if (my_euid == target_ruid || my_euid == target_euid || has_cap_sys_nice()) { + // 有权限:必须成功 + CHECK_RET(syscall(143, target, &gp2), 0, + "sched_getparam forked child allowed returns 0"); + } else { + // 无权限:必须 EPERM + CHECK_ERR(syscall(143, target, &gp2), EPERM, + "sched_getparam forked child without privilege returns EPERM"); + } + kill(target, SIGKILL); + waitpid(target, NULL, 0); } // ==== getpriority 边界条件测试 ==== @@ -294,17 +223,17 @@ int main(void) errno = 0; pr = getpriority(PRIO_PROCESS, 0); CHECK(errno == 0, "getpriority(PRIO_PROCESS,0) does not set errno"); - CHECK(pr >= 1 && pr <= 40, "getpriority(PRIO_PROCESS,0) returns raw priority in 1..40"); + CHECK(pr >= -20 && pr <= 19, "getpriority(PRIO_PROCESS,0) returns raw priority in -20..19"); errno = 0; pr = getpriority(PRIO_PGRP, 0); CHECK(errno == 0, "getpriority(PRIO_PGRP,0) does not set errno"); - CHECK(pr >= 1 && pr <= 40, "getpriority(PRIO_PGRP,0) returns raw priority in 1..40"); + CHECK(pr >= -20 && pr <= 19, "getpriority(PRIO_PGRP,0) returns raw priority in -20..19"); errno = 0; pr = getpriority(PRIO_USER, 0); CHECK(errno == 0, "getpriority(PRIO_USER,0) does not set errno"); - CHECK(pr >= 1 && pr <= 40, "getpriority(PRIO_USER,0) returns raw priority in 1..40"); + CHECK(pr >= -20 && pr <= 19, "getpriority(PRIO_USER,0) returns raw priority in -20..19"); // invalid which -> EINVAL CHECK_ERR(getpriority(0xdead, 0), EINVAL, "getpriority with invalid which returns EINVAL"); @@ -312,7 +241,6 @@ int main(void) // nonexistent pid -> ESRCH CHECK_ERR(getpriority(PRIO_PROCESS, 999999), ESRCH, "getpriority for non-existent pid returns ESRCH"); } -*/ TEST_DONE(); } \ No newline at end of file From 9bbb3e13d80ccdcc4d4dfeedd59df9567b428d70 Mon Sep 17 00:00:00 2001 From: Ubuntu Date: Tue, 26 May 2026 10:39:22 +0800 Subject: [PATCH 3/7] test(sched): pass scheduler family tests on x86_64, aarch64, riscv64 and loongarch64 --- .../qemu-smp4/test-sched-family/c/src/main.c | 28 +++++++++--------- .../test-sched-family/c/src/test_framework.h | 24 +++++++++++++++ .../test-sched-family/qemu-riscv64.toml | 29 ++++--------------- 3 files changed, 43 insertions(+), 38 deletions(-) diff --git a/test-suit/starryos/normal/qemu-smp4/test-sched-family/c/src/main.c b/test-suit/starryos/normal/qemu-smp4/test-sched-family/c/src/main.c index d3340a3efc..0413494445 100644 --- a/test-suit/starryos/normal/qemu-smp4/test-sched-family/c/src/main.c +++ b/test-suit/starryos/normal/qemu-smp4/test-sched-family/c/src/main.c @@ -150,27 +150,27 @@ int main(void) sp.sched_priority = 0; // 负 pid -> EINVAL - CHECK_ERR(syscall(144, -5, SCHED_OTHER, &sp), EINVAL, "sched_setscheduler with negative pid returns EINVAL"); + CHECK_ERR(syscall(SYS_SCHED_SETSCHEDULER, -5, SCHED_OTHER, &sp), EINVAL, "sched_setscheduler with negative pid returns EINVAL"); - CHECK_ERR(syscall(145, -5), EINVAL, "sched_getscheduler with negative pid returns EINVAL"); + CHECK_ERR(syscall(SYS_SCHED_GETSCHEDULER, -5), EINVAL, "sched_getscheduler with negative pid returns EINVAL"); // NULL param -> EINVAL - CHECK_ERR(syscall(144, 0, SCHED_OTHER, NULL), EINVAL, "sched_setscheduler with NULL param returns EINVAL"); + CHECK_ERR(syscall(SYS_SCHED_SETSCHEDULER, 0, SCHED_OTHER, NULL), EINVAL, "sched_setscheduler with NULL param returns EINVAL"); // invalid policy - CHECK_ERR(syscall(144, 0, 0xdeadbeef, &sp), EINVAL, "sched_setscheduler with invalid policy returns EINVAL"); + CHECK_ERR(syscall(SYS_SCHED_SETSCHEDULER, 0, 0xdeadbeef, &sp), EINVAL, "sched_setscheduler with invalid policy returns EINVAL"); // SCHED_OTHER with non-zero priority sp.sched_priority = 1; - CHECK_ERR(syscall(144, 0, SCHED_OTHER, &sp), EINVAL, "sched_setscheduler SCHED_OTHER with nonzero priority returns EINVAL"); + CHECK_ERR(syscall(SYS_SCHED_SETSCHEDULER, 0, SCHED_OTHER, &sp), EINVAL, "sched_setscheduler SCHED_OTHER with nonzero priority returns EINVAL"); sp.sched_priority = 0; // sched_getscheduler(0) - CHECK(syscall(145, 0) >= 0, "sched_getscheduler(0) returns non-negative policy"); + CHECK(syscall(SYS_SCHED_GETSCHEDULER, 0) >= 0, "sched_getscheduler(0) returns non-negative policy"); // fake pid -> ESRCH - CHECK_ERR(syscall(145, 999999), ESRCH, "sched_getscheduler for non-existent pid returns ESRCH"); - CHECK_ERR(syscall(144, 999999, SCHED_OTHER, &sp), ESRCH, "sched_setscheduler for non-existent pid returns ESRCH"); + CHECK_ERR(syscall(SYS_SCHED_GETSCHEDULER, 999999), ESRCH, "sched_getscheduler for non-existent pid returns ESRCH"); + CHECK_ERR(syscall(SYS_SCHED_SETSCHEDULER, 999999, SCHED_OTHER, &sp), ESRCH, "sched_setscheduler for non-existent pid returns ESRCH"); } { @@ -178,9 +178,9 @@ int main(void) memset(&gp, 0, sizeof(gp)); int pol, prio; - CHECK_RET(syscall(143, 0, &gp), 0, "sched_getparam for current pid returns 0"); + CHECK_RET(syscall(SYS_SCHED_GETPARAM, 0, &gp), 0, "sched_getparam for current pid returns 0"); - pol = syscall(145, 0); + pol = syscall(SYS_SCHED_GETSCHEDULER, 0); prio = gp.sched_priority; if (pol == SCHED_FIFO || pol == SCHED_RR) { @@ -190,7 +190,7 @@ int main(void) } // fake pid - CHECK_ERR(syscall(143, 999999, &gp), ESRCH, "sched_getparam for non-existent pid returns ESRCH"); + CHECK_ERR(syscall(SYS_SCHED_GETPARAM, 999999, &gp), ESRCH, "sched_getparam for non-existent pid returns ESRCH"); // 3. EPERM / 权限测试(fork-based) pid_t target = fork(); @@ -205,11 +205,11 @@ int main(void) uid_t target_euid = my_euid; if (my_euid == target_ruid || my_euid == target_euid || has_cap_sys_nice()) { // 有权限:必须成功 - CHECK_RET(syscall(143, target, &gp2), 0, + CHECK_RET(syscall(SYS_SCHED_GETPARAM, target, &gp2), 0, "sched_getparam forked child allowed returns 0"); } else { // 无权限:必须 EPERM - CHECK_ERR(syscall(143, target, &gp2), EPERM, + CHECK_ERR(syscall(SYS_SCHED_GETPARAM, target, &gp2), EPERM, "sched_getparam forked child without privilege returns EPERM"); } kill(target, SIGKILL); @@ -243,4 +243,4 @@ int main(void) } TEST_DONE(); -} \ No newline at end of file +} diff --git a/test-suit/starryos/normal/qemu-smp4/test-sched-family/c/src/test_framework.h b/test-suit/starryos/normal/qemu-smp4/test-sched-family/c/src/test_framework.h index 94b7060716..c3e49b7e4f 100644 --- a/test-suit/starryos/normal/qemu-smp4/test-sched-family/c/src/test_framework.h +++ b/test-suit/starryos/normal/qemu-smp4/test-sched-family/c/src/test_framework.h @@ -2,6 +2,30 @@ #ifndef _GNU_SOURCE #define _GNU_SOURCE + +#endif +#if defined(__x86_64__) + #define SYS_SCHED_GETPARAM 143 + #define SYS_SCHED_GETSCHEDULER 145 + #define SYS_SCHED_SETSCHEDULER 144 + +#elif defined(__riscv) + #define SYS_SCHED_GETPARAM 121 + #define SYS_SCHED_GETSCHEDULER 120 + #define SYS_SCHED_SETSCHEDULER 119 + +#elif defined(__aarch64__) + #define SYS_SCHED_GETPARAM 121 + #define SYS_SCHED_GETSCHEDULER 120 + #define SYS_SCHED_SETSCHEDULER 119 + +#elif defined(__loongarch64) + #define SYS_SCHED_GETPARAM 121 + #define SYS_SCHED_GETSCHEDULER 120 + #define SYS_SCHED_SETSCHEDULER 119 + +#else + #error "unsupported architecture for sched syscalls" #endif #include diff --git a/test-suit/starryos/normal/qemu-smp4/test-sched-family/qemu-riscv64.toml b/test-suit/starryos/normal/qemu-smp4/test-sched-family/qemu-riscv64.toml index 59f8d4423a..b325150b5c 100644 --- a/test-suit/starryos/normal/qemu-smp4/test-sched-family/qemu-riscv64.toml +++ b/test-suit/starryos/normal/qemu-smp4/test-sched-family/qemu-riscv64.toml @@ -1,35 +1,16 @@ -target = "riscv64gc-unknown-none-elf" - -env = { AX_IP = "10.0.2.15", AX_GW = "10.0.2.2" } - -features = [ - "qemu", -] - -plat_dyn = false -max_cpu_num = 4 -log = "Warn" - args = [ - "-machine", "virt", - "-nographic", + "-nographic", "-cpu", "rv64", "-m", "512M", "-smp", "4", - - "-device", "virtio-blk-device,drive=disk0", + "-device", "virtio-blk-pci,drive=disk0", "-drive", "id=disk0,if=none,format=raw,file=${workspace}/tmp/axbuild/rootfs/rootfs-riscv64-alpine.img", - - "-device", "virtio-net-device,netdev=net0", + "-device", "virtio-net-pci,netdev=net0", "-netdev", "user,id=net0", ] - uefi = false to_bin = true - shell_prefix = "root@starry:" shell_init_cmd = "/usr/bin/test-sched-family" - success_regex = ["(?m)DONE: \\d+ pass, 0 fail"] -fail_regex = ['(?i)\\bpanic(?:ked)?\\b', '(?m)FAIL'] - -timeout = 120 \ No newline at end of file +fail_regex = ['(?i)\bpanic(?:ked)?\b', '(?m)FAIL'] +timeout = 360 \ No newline at end of file From e696895936eb18553a0e6dd05caac3ee3919673f Mon Sep 17 00:00:00 2001 From: Ubuntu Date: Tue, 26 May 2026 15:27:39 +0800 Subject: [PATCH 4/7] test(sched): add extended syscall test cases for scheduler family --- .../qemu-smp4/test-sched-family/c/src/main.c | 98 ++++++++++++++----- 1 file changed, 71 insertions(+), 27 deletions(-) diff --git a/test-suit/starryos/normal/qemu-smp4/test-sched-family/c/src/main.c b/test-suit/starryos/normal/qemu-smp4/test-sched-family/c/src/main.c index 0413494445..a80c76e6dc 100644 --- a/test-suit/starryos/normal/qemu-smp4/test-sched-family/c/src/main.c +++ b/test-suit/starryos/normal/qemu-smp4/test-sched-family/c/src/main.c @@ -6,7 +6,6 @@ #include #include #include -#include #include @@ -68,10 +67,8 @@ int main(void) CPU_ZERO(&mask); CPU_SET(0, &mask); - - CHECK_RET(sched_setaffinity(0, sizeof(mask), &mask), 0, "setaffinity current pid to cpu0"); - memset(&readback, 0, sizeof(readback)); + CHECK_RET(sched_setaffinity(0, sizeof(mask), &mask), 0, "setaffinity current pid to cpu0"); CHECK_RET(sched_getaffinity(0, sizeof(readback), &readback), 0, "getaffinity current pid"); CHECK(CPU_ISSET(0, &readback), "getaffinity result contains cpu0"); } @@ -144,11 +141,16 @@ int main(void) CHECK_RET(sched_yield(), 0, "sched_yield returns 0"); } + // 测试 sched_setscheduler() / sched_getscheduler { struct sched_param sp; memset(&sp, 0, sizeof(sp)); sp.sched_priority = 0; + // 正常情况测试 + CHECK_RET(syscall(SYS_SCHED_SETSCHEDULER, 0, SCHED_OTHER, &sp), 0, "sched_setscheduler with valid parameters returns 0"); + CHECK_RET(syscall(SYS_SCHED_GETSCHEDULER, 0), SCHED_OTHER, "sched_getscheduler for current pid returns SCHED_OTHER"); + // 负 pid -> EINVAL CHECK_ERR(syscall(SYS_SCHED_SETSCHEDULER, -5, SCHED_OTHER, &sp), EINVAL, "sched_setscheduler with negative pid returns EINVAL"); @@ -165,14 +167,54 @@ int main(void) CHECK_ERR(syscall(SYS_SCHED_SETSCHEDULER, 0, SCHED_OTHER, &sp), EINVAL, "sched_setscheduler SCHED_OTHER with nonzero priority returns EINVAL"); sp.sched_priority = 0; + // SCHED_BATCH with non-zero priority + sp.sched_priority = 1; + CHECK_ERR(syscall(SYS_SCHED_SETSCHEDULER, 0, SCHED_BATCH, &sp), EINVAL, "sched_setscheduler SCHED_BATCH with nonzero priority returns EINVAL"); + sp.sched_priority = 0; + + // SCHED_IDLE with non-zero priority + sp.sched_priority = 1; + CHECK_ERR(syscall(SYS_SCHED_SETSCHEDULER, 0, SCHED_IDLE, &sp), EINVAL, "sched_setscheduler SCHED_IDLE with nonzero priority returns EINVAL"); + sp.sched_priority = 0; + // sched_getscheduler(0) CHECK(syscall(SYS_SCHED_GETSCHEDULER, 0) >= 0, "sched_getscheduler(0) returns non-negative policy"); // fake pid -> ESRCH CHECK_ERR(syscall(SYS_SCHED_GETSCHEDULER, 999999), ESRCH, "sched_getscheduler for non-existent pid returns ESRCH"); CHECK_ERR(syscall(SYS_SCHED_SETSCHEDULER, 999999, SCHED_OTHER, &sp), ESRCH, "sched_setscheduler for non-existent pid returns ESRCH"); + + // ==== EPERM: sched_setscheduler permission test (fork-based) ==== + { + pid_t target = fork(); + if (target == 0) { + while (1) pause(); // child stays alive + } + + cpu_set_t mask; + CPU_ZERO(&mask); + CPU_SET(0, &mask); + + uid_t my_euid = geteuid(); + + // fork child: uid/euid == parent (no /proc parsing needed) + uid_t target_ruid = my_euid; + uid_t target_euid = my_euid; + + if (my_euid == target_ruid || my_euid == target_euid || has_cap_sys_nice()) { + CHECK_RET(syscall(SYS_SCHED_SETSCHEDULER, target, SCHED_OTHER, &sp), 0, + "sched_setscheduler forked child allowed returns 0"); + } else { + CHECK_ERR(syscall(SYS_SCHED_SETSCHEDULER, target, SCHED_OTHER, &sp), EPERM, + "sched_setscheduler forked child returns EPERM"); + } + + kill(target, SIGKILL); + waitpid(target, NULL, 0); + } } + // 测试 sched_getparam() 边界条件和权限 { struct sched_param gp; memset(&gp, 0, sizeof(gp)); @@ -189,31 +231,33 @@ int main(void) CHECK(prio == 0, "non-RT priority must be 0"); } - // fake pid + // fake pid -> ESRCH CHECK_ERR(syscall(SYS_SCHED_GETPARAM, 999999, &gp), ESRCH, "sched_getparam for non-existent pid returns ESRCH"); - // 3. EPERM / 权限测试(fork-based) - pid_t target = fork(); - if (target == 0) { - // 子进程存活 - while (1) pause(); - } - struct sched_param gp2; - memset(&gp2, 0, sizeof(gp2)); - uid_t my_euid = geteuid(); - uid_t target_ruid = my_euid; - uid_t target_euid = my_euid; - if (my_euid == target_ruid || my_euid == target_euid || has_cap_sys_nice()) { - // 有权限:必须成功 - CHECK_RET(syscall(SYS_SCHED_GETPARAM, target, &gp2), 0, - "sched_getparam forked child allowed returns 0"); - } else { - // 无权限:必须 EPERM - CHECK_ERR(syscall(SYS_SCHED_GETPARAM, target, &gp2), EPERM, - "sched_getparam forked child without privilege returns EPERM"); + // 3. EPERM / 权限测试(fork-based) + { + pid_t target = fork(); + if (target == 0) { + // 子进程存活 + while (1) pause(); + } + struct sched_param gp2; + memset(&gp2, 0, sizeof(gp2)); + uid_t my_euid = geteuid(); + uid_t target_ruid = my_euid; + uid_t target_euid = my_euid; + if (my_euid == target_ruid || my_euid == target_euid || has_cap_sys_nice()) { + // 有权限:必须成功 + CHECK_RET(syscall(SYS_SCHED_GETPARAM, target, &gp2), 0, + "sched_getparam forked child allowed returns 0"); + } else { + // 无权限:必须 EPERM + CHECK_ERR(syscall(SYS_SCHED_GETPARAM, target, &gp2), EPERM, + "sched_getparam forked child without privilege returns EPERM"); + } + kill(target, SIGKILL); + waitpid(target, NULL, 0); } - kill(target, SIGKILL); - waitpid(target, NULL, 0); } // ==== getpriority 边界条件测试 ==== @@ -243,4 +287,4 @@ int main(void) } TEST_DONE(); -} +} \ No newline at end of file From d23a9c9c74d93b542b9d81682623394026581f30 Mon Sep 17 00:00:00 2001 From: Ubuntu Date: Tue, 26 May 2026 17:56:33 +0800 Subject: [PATCH 5/7] sched: support SCHED_RESET_ON_FORK and fix sched_getparam permission issue --- .../kernel/src/syscall/task/schedule.rs | 6 ++-- .../qemu-smp4/test-sched-family/c/src/main.c | 30 ++++--------------- 2 files changed, 9 insertions(+), 27 deletions(-) diff --git a/os/StarryOS/kernel/src/syscall/task/schedule.rs b/os/StarryOS/kernel/src/syscall/task/schedule.rs index f253cd6d31..29c7154725 100644 --- a/os/StarryOS/kernel/src/syscall/task/schedule.rs +++ b/os/StarryOS/kernel/src/syscall/task/schedule.rs @@ -178,7 +178,10 @@ pub fn sys_sched_setscheduler(_pid: i32, _policy: i32, _param: *const ()) -> AxR } let user_param = vm_load::(_param.cast(), 1)?; let user_param = user_param[0]; - let policy = _policy as u32; + let mut policy = _policy as u32; + const SCHED_RESET_ON_FORK: u32 = 0x40000000; + let _reset_on_fork = (policy & SCHED_RESET_ON_FORK) != 0; + policy &= !SCHED_RESET_ON_FORK; let prio = user_param.sched_priority; match policy { SCHED_NORMAL | SCHED_FIFO | SCHED_RR | SCHED_BATCH | SCHED_IDLE => {} @@ -203,7 +206,6 @@ pub fn sys_sched_setscheduler(_pid: i32, _policy: i32, _param: *const ()) -> AxR } pub fn sys_sched_getparam(_pid: i32, _param: *mut ()) -> AxResult { - check_sched_permission(_pid)?; let task = get_task_by_sched_pid(_pid)?; if _param.is_null() { return Err(AxError::InvalidInput); diff --git a/test-suit/starryos/normal/qemu-smp4/test-sched-family/c/src/main.c b/test-suit/starryos/normal/qemu-smp4/test-sched-family/c/src/main.c index a80c76e6dc..5293075d46 100644 --- a/test-suit/starryos/normal/qemu-smp4/test-sched-family/c/src/main.c +++ b/test-suit/starryos/normal/qemu-smp4/test-sched-family/c/src/main.c @@ -143,9 +143,11 @@ int main(void) // 测试 sched_setscheduler() / sched_getscheduler { + #define SCHED_RESET_ON_FORK 0x40000000 struct sched_param sp; memset(&sp, 0, sizeof(sp)); sp.sched_priority = 0; + int policy = SCHED_FIFO | SCHED_RESET_ON_FORK; // 正常情况测试 CHECK_RET(syscall(SYS_SCHED_SETSCHEDULER, 0, SCHED_OTHER, &sp), 0, "sched_setscheduler with valid parameters returns 0"); @@ -162,6 +164,9 @@ int main(void) // invalid policy CHECK_ERR(syscall(SYS_SCHED_SETSCHEDULER, 0, 0xdeadbeef, &sp), EINVAL, "sched_setscheduler with invalid policy returns EINVAL"); + // FIFO | RESET_ON_FORK is valid combination + CHECK_RET(syscall(SYS_SCHED_SETSCHEDULER, 0, policy, &sp), 0, "sched_setscheduler with SCHED_RESET_ON_FORK should succeed"); + // SCHED_OTHER with non-zero priority sp.sched_priority = 1; CHECK_ERR(syscall(SYS_SCHED_SETSCHEDULER, 0, SCHED_OTHER, &sp), EINVAL, "sched_setscheduler SCHED_OTHER with nonzero priority returns EINVAL"); @@ -233,31 +238,6 @@ int main(void) // fake pid -> ESRCH CHECK_ERR(syscall(SYS_SCHED_GETPARAM, 999999, &gp), ESRCH, "sched_getparam for non-existent pid returns ESRCH"); - - // 3. EPERM / 权限测试(fork-based) - { - pid_t target = fork(); - if (target == 0) { - // 子进程存活 - while (1) pause(); - } - struct sched_param gp2; - memset(&gp2, 0, sizeof(gp2)); - uid_t my_euid = geteuid(); - uid_t target_ruid = my_euid; - uid_t target_euid = my_euid; - if (my_euid == target_ruid || my_euid == target_euid || has_cap_sys_nice()) { - // 有权限:必须成功 - CHECK_RET(syscall(SYS_SCHED_GETPARAM, target, &gp2), 0, - "sched_getparam forked child allowed returns 0"); - } else { - // 无权限:必须 EPERM - CHECK_ERR(syscall(SYS_SCHED_GETPARAM, target, &gp2), EPERM, - "sched_getparam forked child without privilege returns EPERM"); - } - kill(target, SIGKILL); - waitpid(target, NULL, 0); - } } // ==== getpriority 边界条件测试 ==== From 65747e033c4158580cc48de2ecd1f242ad0982a6 Mon Sep 17 00:00:00 2001 From: Ubuntu Date: Tue, 26 May 2026 19:04:42 +0800 Subject: [PATCH 6/7] sched: fix uninitialized sched_param in RESET_ON_FORK test --- .../starryos/normal/qemu-smp4/test-sched-family/c/src/main.c | 1 + 1 file changed, 1 insertion(+) diff --git a/test-suit/starryos/normal/qemu-smp4/test-sched-family/c/src/main.c b/test-suit/starryos/normal/qemu-smp4/test-sched-family/c/src/main.c index 5293075d46..b13b8b6c42 100644 --- a/test-suit/starryos/normal/qemu-smp4/test-sched-family/c/src/main.c +++ b/test-suit/starryos/normal/qemu-smp4/test-sched-family/c/src/main.c @@ -165,6 +165,7 @@ int main(void) CHECK_ERR(syscall(SYS_SCHED_SETSCHEDULER, 0, 0xdeadbeef, &sp), EINVAL, "sched_setscheduler with invalid policy returns EINVAL"); // FIFO | RESET_ON_FORK is valid combination + sp.sched_priority = 2; CHECK_RET(syscall(SYS_SCHED_SETSCHEDULER, 0, policy, &sp), 0, "sched_setscheduler with SCHED_RESET_ON_FORK should succeed"); // SCHED_OTHER with non-zero priority From 716247e10625e51bad7b10f06b08c594c49b4378 Mon Sep 17 00:00:00 2001 From: Ubuntu Date: Tue, 26 May 2026 19:13:18 +0800 Subject: [PATCH 7/7] ci: retrigger pipeline