Skip to content
Closed
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
80 changes: 76 additions & 4 deletions os/StarryOS/kernel/src/syscall/task/schedule.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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};

Expand All @@ -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<isize> {
ax_task::yield_now();
Ok(0)
Expand Down Expand Up @@ -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(());

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ 阻塞性问题:当目标 task 为自身时直接返回 Ok(()),跳过所有权限检查。

在 Linux 中,sched_setschedulerSCHED_FIFO/SCHED_RR 实时调度策略要求 CAP_SYS_NICE 权限,即使目标是自身进程也不例外(除非进程拥有非零 RLIMIT_RTPRIO,而 StarryOS 尚未实现该限制)。

当前实现允许任何非特权进程通过 sched_setscheduler(0, SCHED_FIFO, &{.sched_priority=99}) 将自身提升为最高优先级实时进程,构成权限提升风险。

建议修复方向
不在 check_sched_permission 中修改自检查逻辑(因为 setaffinity 对自身确实不需要权限),而是在 sys_sched_setscheduler 的 policy 校验阶段,对 SCHED_FIFO/SCHED_RR 额外检查 caller.has_cap_sys_nice()

// 在 sys_sched_setscheduler 中,match policy 之后,set_sched_policy 之前:
if matches!(policy, SCHED_FIFO | SCHED_RR) && !caller.has_cap_sys_nice() {
    return Err(AxError::PermissionDenied);
}

}
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<isize> {
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();
Expand All @@ -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 {
Expand All @@ -141,14 +166,61 @@ fn get_task_by_sched_pid(pid: i32) -> AxResult<ax_task::AxTaskRef> {
}

pub fn sys_sched_getscheduler(_pid: i32) -> AxResult<isize> {
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<isize> {
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::<SchedParam>(_param.cast(), 1)?;
let user_param = user_param[0];
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 {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

建议补充注释说明 SCHED_RESET_ON_FORK 已正确解析但目前未实际使用(未记录到 task 中),例如:

// TODO: SCHED_RESET_ON_FORK — 已解析,待后续 fork 时实现继承逻辑

这样后续开发者能明确知道这是待完成项而非遗漏。

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<isize> {
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(
&param as *const SchedParam as *const u8,
core::mem::size_of::<SchedParam>(),
);
vm_write_slice(ptr as *mut u8, bytes)?;
}
Ok(0)
}

Expand Down
28 changes: 28 additions & 0 deletions os/arceos/modules/axtask/src/task.rs
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,12 @@ pub struct TaskInner {
/// CPU affinity mask.
cpumask: SpinNoIrq<AxCpuMask>,

/// 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,

Expand Down Expand Up @@ -264,6 +270,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<()> {
Expand Down Expand Up @@ -324,6 +350,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),
Expand Down
Original file line number Diff line number Diff line change
@@ -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)
Loading