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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 13 additions & 1 deletion os/StarryOS/kernel/src/file/epoll.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ use ax_kspin::SpinNoIrq;
use axpoll::{IoEvents, PollSet, Pollable};
use bitflags::bitflags;
use hashbrown::HashMap;
use linux_raw_sys::general::{EPOLLET, EPOLLONESHOT, epoll_event};
use linux_raw_sys::general::{EPOLLET, EPOLLEXCLUSIVE, EPOLLONESHOT, epoll_event};

use crate::file::{FileLike, get_file_like};

Expand All @@ -38,6 +38,7 @@ bitflags! {
pub struct EpollFlags: u32 {
const EDGE_TRIGGER = EPOLLET;
const ONESHOT = EPOLLONESHOT;
const EXCLUSIVE = EPOLLEXCLUSIVE;
Comment thread
ZR233 marked this conversation as resolved.
Comment thread
ZR233 marked this conversation as resolved.
Comment thread
ZR233 marked this conversation as resolved.
}
}

Expand Down Expand Up @@ -146,6 +147,7 @@ struct EpollInterest {
key: EntryKey,
event: EpollEvent,
mode: SpinNoIrq<TriggerMode>,
exclusive: bool,
in_ready_queue: AtomicBool,
}

Expand All @@ -155,10 +157,16 @@ impl EpollInterest {
key,
event,
mode: SpinNoIrq::new(TriggerMode::from_flags(flags)),
exclusive: flags.contains(EpollFlags::EXCLUSIVE),
in_ready_queue: AtomicBool::new(false),
}
}

#[inline]
fn is_exclusive(&self) -> bool {
self.exclusive
}

#[inline]
fn is_enabled(&self) -> bool {
self.mode.lock().is_enabled()
Expand Down Expand Up @@ -353,6 +361,10 @@ impl Epoll {

let mut guard = self.inner.interests.lock();
let old = guard.get_mut(&key).ok_or(AxError::NotFound)?;
// Linux forbids modifying an entry that was added as exclusive.
if old.is_exclusive() {
return Err(AxError::InvalidInput);
}

// Preserve ready-queue membership across the swap. The ready_queue
// only holds Weak<EpollInterest> pointing at the old Arc, so
Expand Down
29 changes: 24 additions & 5 deletions os/StarryOS/kernel/src/syscall/io_mpx/epoll.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@ use ax_task::future::{self, block_on, poll_io};
use axpoll::IoEvents;
use bitflags::bitflags;
use linux_raw_sys::general::{
EPOLL_CLOEXEC, EPOLL_CTL_ADD, EPOLL_CTL_DEL, EPOLL_CTL_MOD, epoll_event, timespec,
EPOLL_CLOEXEC, EPOLL_CTL_ADD, EPOLL_CTL_DEL, EPOLL_CTL_MOD, EPOLLEXCLUSIVE, epoll_event,
timespec,
};
use starry_signal::SignalSet;
use starry_vm::{vm_read_slice, vm_write_slice};
Expand All @@ -25,6 +26,12 @@ use crate::{
};

const EP_MAX_EVENTS: usize = i32::MAX as usize / size_of::<epoll_event>();
const EPOLLEXCLUSIVE_OK_BITS: u32 = IoEvents::IN.bits()
| IoEvents::OUT.bits()
| IoEvents::ERR.bits()
| IoEvents::HUP.bits()
| EpollFlags::EDGE_TRIGGER.bits()
| EpollFlags::EXCLUSIVE.bits();

fn check_epoll_events_access(events: UserPtr<epoll_event>, maxevents: usize) -> AxResult<()> {
let len = maxevents
Expand Down Expand Up @@ -111,12 +118,14 @@ pub fn sys_epoll_ctl(
let epoll = Epoll::from_fd(epfd)?;
debug!("sys_epoll_ctl <= epfd: {epfd}, op: {op}, fd: {fd}");

let parse_event = || -> AxResult<(EpollEvent, EpollFlags)> {
let parse_event = || -> AxResult<(u32, EpollEvent, EpollFlags)> {
let event = read_epoll_event(event)?;
let raw_events = event.events;
let events = IoEvents::from_bits_truncate(event.events);
let flags =
EpollFlags::from_bits(event.events & !events.bits()).ok_or(AxError::InvalidInput)?;
EpollFlags::from_bits(raw_events & !events.bits()).ok_or(AxError::InvalidInput)?;
Ok((
raw_events,
EpollEvent {
events,
user_data: event.data,
Expand All @@ -126,11 +135,21 @@ pub fn sys_epoll_ctl(
};
match op {
EPOLL_CTL_ADD => {
let (event, flags) = parse_event()?;
let (raw_events, event, flags) = parse_event()?;
// Linux only permits EPOLLEXCLUSIVE on ADD, and it is rejected for
// epoll targets as well.
if raw_events & EPOLLEXCLUSIVE != 0
&& (raw_events & !EPOLLEXCLUSIVE_OK_BITS != 0 || Epoll::from_fd(fd).is_ok())
{
return Err(AxError::InvalidInput);
}
epoll.add(fd, event, flags)?;
}
EPOLL_CTL_MOD => {
let (event, flags) = parse_event()?;
let (_, event, flags) = parse_event()?;
if flags.contains(EpollFlags::EXCLUSIVE) {
return Err(AxError::InvalidInput);
}
epoll.modify(fd, event, flags)?;
}
EPOLL_CTL_DEL => {
Expand Down
8 changes: 5 additions & 3 deletions os/StarryOS/kernel/src/task/signal.rs
Original file line number Diff line number Diff line change
Expand Up @@ -472,7 +472,7 @@ pub fn send_signal_to_thread(tgid: Option<Pid>, tid: Pid, sig: Option<SignalInfo
// tkill/tgkill must NOT interrupt the target per POSIX; the signal
// is queued as pending and stays invisible until unblocked.
if thread.signal.send_signal(sig) {
ax_task::wake_task(&task);
task.interrupt();
}
}

Expand Down Expand Up @@ -521,9 +521,11 @@ pub fn send_signal_to_process(pid: Pid, sig: Option<SignalInfo>) -> AxResult<()>
proc_data.clear_ptrace_stop();
}
if let Some(tid) = proc_data.signal.send_signal(sig) {
// A thread was found that doesn't have the signal blocked — wake it.
// A thread was found that doesn't have the signal blocked.
// Mark it interrupted so blocking syscalls wrapped by
// `future::interruptible` can return EINTR promptly.
if let Ok(task) = get_task(tid) {
ax_task::wake_task(&task);
task.interrupt();
Comment thread
ZR233 marked this conversation as resolved.
Comment thread
ZR233 marked this conversation as resolved.
}
} else {
// All threads have this signal blocked — the signal is now pending
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
cmake_minimum_required(VERSION 3.20)
project(test-epoll-exclusive C)

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

add_executable(test-epoll-exclusive src/main.c)
target_include_directories(test-epoll-exclusive PRIVATE ../../common)
target_compile_options(test-epoll-exclusive PRIVATE -Wall -Wextra -Werror)

install(TARGETS test-epoll-exclusive RUNTIME DESTINATION usr/bin/starry-test-suit)
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
#include "test_framework.h"

#include <stdint.h>
#include <sys/epoll.h>
#include <unistd.h>

static void close_pipe(int pipefd[2])
{
if (pipefd[0] >= 0) {
close(pipefd[0]);
}
if (pipefd[1] >= 0) {
close(pipefd[1]);
}
}

int main(void)
{
/*
* 回归目标:
* 1) Linux 合法的 EPOLLEXCLUSIVE ADD 必须被接受;
* 2) Linux 明确非法的 EPOLLEXCLUSIVE 组合必须返回 EINVAL。
* 3) StarryOS 目前不放行 EPOLLWAKEUP,因此相关组合仍应返回 EINVAL。
*/
TEST_START("epoll_ctl: EPOLLEXCLUSIVE Linux ABI checks");

int epfd = epoll_create1(EPOLL_CLOEXEC);
CHECK(epfd >= 0, "epoll_create1 succeeds");
if (epfd < 0) {
TEST_DONE();
}

int ok_pipe[2] = {-1, -1};
CHECK_RET(pipe(ok_pipe), 0, "pipe for EPOLLEXCLUSIVE positive case");
if (ok_pipe[0] >= 0) {
struct epoll_event ev = {
.events = EPOLLIN | EPOLLEXCLUSIVE,
.data.fd = ok_pipe[0],
};
CHECK_RET(epoll_ctl(epfd, EPOLL_CTL_ADD, ok_pipe[0], &ev), 0,
"epoll_ctl ADD with EPOLLEXCLUSIVE returns 0");

CHECK_ERR(epoll_ctl(epfd, EPOLL_CTL_MOD, ok_pipe[0], &ev), EINVAL,
"epoll_ctl MOD with EPOLLEXCLUSIVE returns EINVAL");

ev.events = EPOLLIN;
CHECK_ERR(epoll_ctl(epfd, EPOLL_CTL_MOD, ok_pipe[0], &ev), EINVAL,
"epoll_ctl MOD after exclusive ADD returns EINVAL");
}

int oneshot_pipe[2] = {-1, -1};
CHECK_RET(pipe(oneshot_pipe), 0, "pipe for EPOLLONESHOT negative case");
if (oneshot_pipe[0] >= 0) {
struct epoll_event ev = {
.events = EPOLLIN | EPOLLONESHOT | EPOLLEXCLUSIVE,
.data.fd = oneshot_pipe[0],
};
CHECK_ERR(epoll_ctl(epfd, EPOLL_CTL_ADD, oneshot_pipe[0], &ev), EINVAL,
"epoll_ctl ADD rejects EPOLLONESHOT with EPOLLEXCLUSIVE");
}

int rdhup_pipe[2] = {-1, -1};
CHECK_RET(pipe(rdhup_pipe), 0, "pipe for EPOLLRDHUP negative case");
if (rdhup_pipe[0] >= 0) {
struct epoll_event ev = {
.events = EPOLLIN | EPOLLRDHUP | EPOLLEXCLUSIVE,
.data.fd = rdhup_pipe[0],
};
CHECK_ERR(epoll_ctl(epfd, EPOLL_CTL_ADD, rdhup_pipe[0], &ev), EINVAL,
"epoll_ctl ADD rejects EPOLLRDHUP with EPOLLEXCLUSIVE");
}

int pri_pipe[2] = {-1, -1};
CHECK_RET(pipe(pri_pipe), 0, "pipe for EPOLLPRI negative case");
if (pri_pipe[0] >= 0) {
struct epoll_event ev = {
.events = EPOLLIN | EPOLLPRI | EPOLLEXCLUSIVE,
.data.fd = pri_pipe[0],
};
CHECK_ERR(epoll_ctl(epfd, EPOLL_CTL_ADD, pri_pipe[0], &ev), EINVAL,
"epoll_ctl ADD rejects EPOLLPRI with EPOLLEXCLUSIVE");
}

int wake_pipe[2] = {-1, -1};
CHECK_RET(pipe(wake_pipe), 0, "pipe for EPOLLWAKEUP negative case");
if (wake_pipe[0] >= 0) {
struct epoll_event ev = {
.events = EPOLLIN | EPOLLWAKEUP | EPOLLEXCLUSIVE,
.data.fd = wake_pipe[0],
};
CHECK_ERR(epoll_ctl(epfd, EPOLL_CTL_ADD, wake_pipe[0], &ev), EINVAL,
"epoll_ctl ADD rejects EPOLLWAKEUP with EPOLLEXCLUSIVE on StarryOS");
}

struct epoll_event ev = {
.events = EPOLLIN | EPOLLEXCLUSIVE,
.data.fd = epfd,
};
CHECK_ERR(epoll_ctl(epfd, EPOLL_CTL_ADD, epfd, &ev), EINVAL,
"epoll_ctl ADD rejects epoll target with EPOLLEXCLUSIVE");

close_pipe(ok_pipe);
close_pipe(oneshot_pipe);
close_pipe(rdhup_pipe);
close_pipe(pri_pipe);
close_pipe(wake_pipe);
close(epfd);

TEST_DONE();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
cmake_minimum_required(VERSION 3.20)
project(test-signal-interrupt-eintr C)

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

add_executable(test-signal-interrupt-eintr src/main.c)
target_include_directories(test-signal-interrupt-eintr PRIVATE src)
target_compile_options(test-signal-interrupt-eintr PRIVATE -Wall -Wextra -Werror)

install(TARGETS test-signal-interrupt-eintr RUNTIME DESTINATION usr/bin)
Loading