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
9 changes: 9 additions & 0 deletions kernel/src/arch_impl/aarch64/syscall_entry.rs
Original file line number Diff line number Diff line change
Expand Up @@ -564,6 +564,15 @@ fn dispatch_syscall_enum(
// Testing/diagnostic syscalls
SyscallNumber::CowStats => sys_cow_stats_aarch64(arg1),
SyscallNumber::SimulateOom => sys_simulate_oom_aarch64(arg1),
// Resource limits and system info
SyscallNumber::Getrlimit => result_to_u64(crate::syscall::handlers::sys_getrlimit(arg1, arg2)),
SyscallNumber::Prlimit64 => result_to_u64(crate::syscall::handlers::sys_prlimit64(arg1, arg2, arg3, arg4)),
SyscallNumber::Uname => result_to_u64(crate::syscall::handlers::sys_uname(arg1)),
// epoll
SyscallNumber::EpollCreate1 => result_to_u64(crate::syscall::epoll::sys_epoll_create1(arg1 as u32)),
SyscallNumber::EpollCtl => result_to_u64(crate::syscall::epoll::sys_epoll_ctl(arg1 as i32, arg2 as i32, arg3 as i32, arg4)),
SyscallNumber::EpollWait => result_to_u64(crate::syscall::epoll::sys_epoll_pwait(arg1 as i32, arg2, arg3 as i32, arg4 as i32, 0, 0)),
SyscallNumber::EpollPwait => result_to_u64(crate::syscall::epoll::sys_epoll_pwait(arg1 as i32, arg2, arg3 as i32, arg4 as i32, arg5, arg6)),
}
}

Expand Down
5 changes: 4 additions & 1 deletion kernel/src/boot/test_list.rs
Original file line number Diff line number Diff line change
Expand Up @@ -77,8 +77,11 @@ pub const TEST_BINARIES: &[&str] = &[
"ls_test",
// Rust std library test (installed as hello_world.elf on ext2)
"hello_world",
// musl libc C program (cross-compiled with musl libc for aarch64)
// musl libc C programs (cross-compiled with musl libc for aarch64)
"hello_musl",
"env_musl_test",
"uname_musl_test",
"rlimit_musl_test",
// Fork / CoW tests
"fork_memory_test",
"fork_state_test",
Expand Down
7 changes: 7 additions & 0 deletions kernel/src/ipc/fd.rs
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,8 @@ pub enum FdKind {
ProcfsFile { content: alloc::string::String, position: usize },
/// Procfs directory listing (for /proc and /proc/[pid])
ProcfsDirectory { path: alloc::string::String, position: u64 },
/// Epoll instance file descriptor
Epoll(u64),
}

impl core::fmt::Debug for FdKind {
Expand Down Expand Up @@ -168,6 +170,7 @@ impl core::fmt::Debug for FdKind {
FdKind::FifoWrite(path, _) => write!(f, "FifoWrite({})", path),
FdKind::ProcfsFile { content, position } => write!(f, "ProcfsFile(len={}, pos={})", content.len(), position),
FdKind::ProcfsDirectory { path, position } => write!(f, "ProcfsDirectory(path={}, pos={})", path, position),
FdKind::Epoll(id) => write!(f, "Epoll({})", id),
Copy link

Choose a reason for hiding this comment

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

Epoll instance leaked when dup2 overwrites epoll fd

Low Severity

When dup2 (or dup3) overwrites a new_fd that is an epoll fd, the old entry is taken from the table and its kind is matched for cleanup. FdKind::Epoll falls through to _ => {} without calling remove_instance, leaking the epoll instance in the global EPOLL_INSTANCES registry.

Fix in Cursor Fix in Web

}
}
}
Expand Down Expand Up @@ -735,6 +738,10 @@ impl Drop for FdTable {
FdKind::ProcfsDirectory { .. } => {
// Procfs directory doesn't need cleanup
}
FdKind::Epoll(id) => {
Copy link

Choose a reason for hiding this comment

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

Epoll instance leaked during exec close_cloexec cleanup

Medium Severity

When close_cloexec runs during exec(), epoll fds marked with FD_CLOEXEC are removed from the fd table but fall through to the _ => {} catch-all without calling remove_instance. This permanently leaks the epoll instance in the global EPOLL_INSTANCES registry. This is especially likely since EPOLL_CLOEXEC is the standard flag passed to epoll_create1.

Fix in Cursor Fix in Web

// Clean up the epoll instance
crate::syscall::epoll::remove_instance(id);
}
Copy link

Choose a reason for hiding this comment

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

Epoll instance removed prematurely after fork

Medium Severity

FdTable::clone() (used during fork) copies FdKind::Epoll(id) without incrementing any reference count (falls through to the _ => {} arm). Both parent and child point to the same global epoll instance. When either process closes the epoll fd or exits, remove_instance(id) unconditionally removes the shared instance from the global registry, leaving the other process with a dangling epoll reference that returns EBADF.

Additional Locations (1)

Fix in Cursor Fix in Web

}
}
}
Expand Down
3 changes: 3 additions & 0 deletions kernel/src/ipc/poll.rs
Original file line number Diff line number Diff line change
Expand Up @@ -301,6 +301,9 @@ pub fn poll_fd(fd_entry: &FileDescriptor, events: i16) -> i16 {
revents |= events::POLLIN;
}
}
FdKind::Epoll(_) => {
// Epoll fds are not directly pollable
}
}

revents
Expand Down
72 changes: 67 additions & 5 deletions kernel/src/process/manager.rs
Original file line number Diff line number Diff line change
Expand Up @@ -584,11 +584,19 @@ impl ProcessManager {

// Set up argc/argv/envp/auxv on the stack following Linux ABI
// The stack is now mapped, so we can write to it via physical addresses
let default_env: [&[u8]; 5] = [
b"PATH=/bin:/sbin\0",
b"HOME=/\0",
b"TERM=vt100\0",
b"USER=root\0",
b"SHELL=/bin/bsh\0",
];
Copy link

Choose a reason for hiding this comment

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

Triplicated default_env array across process creation paths

Low Severity

The identical default_env array (5 entries, same values) is defined three separate times in process/manager.rs — once in create_process_with_argv and twice in different exec_process_with_argv variants. A single shared constant would avoid the risk of inconsistent updates (like the HOME bug) and reduce maintenance burden.

Additional Locations (2)

Fix in Cursor Fix in Web

let initial_sp = if let Some(ref page_table) = process.page_table {
self.setup_argv_on_stack(
page_table,
user_stack_top,
argv,
&default_env,
loaded_elf.phdr_vaddr,
loaded_elf.phnum,
loaded_elf.phentsize,
Expand Down Expand Up @@ -2576,10 +2584,18 @@ impl ProcessManager {
// We need to write to the new stack pages that we just mapped
// Since the new page table is not active yet, we need to translate addresses
// and write via the physical frames
let default_env: [&[u8]; 5] = [
b"PATH=/bin:/sbin\0",
b"HOME=/\0",
b"TERM=vt100\0",
b"USER=root\0",
b"SHELL=/bin/bsh\0",
];
let initial_rsp = self.setup_argv_on_stack(
&new_page_table,
USER_STACK_TOP,
argv,
&default_env,
loaded_elf.phdr_vaddr,
loaded_elf.phnum,
loaded_elf.phentsize,
Expand Down Expand Up @@ -2812,10 +2828,18 @@ impl ProcessManager {
)?;
}

let default_env: [&[u8]; 5] = [
b"PATH=/bin:/sbin\0",
b"HOME=/\0",
b"TERM=vt100\0",
b"USER=root\0",
b"SHELL=/bin/bsh\0",
];
let initial_rsp = self.setup_argv_on_stack(
&new_page_table,
user_stack_top,
argv,
&default_env,
loaded_elf.phdr_vaddr,
loaded_elf.phnum,
loaded_elf.phentsize,
Expand Down Expand Up @@ -3251,6 +3275,7 @@ impl ProcessManager {
///
/// 16 random bytes (for AT_RANDOM)
/// argv string data (null-terminated strings)
/// envp string data (null-terminated strings)
/// --- 8-byte alignment padding ---
/// AT_NULL (0, 0) // auxv terminator
/// AT_RANDOM (25, ptr_to_random) // pointer to 16 random bytes
Expand All @@ -3260,6 +3285,9 @@ impl ProcessManager {
/// AT_PHDR (3, phdr_vaddr) // address of program headers in memory
/// AT_ENTRY (9, entry_point) // program entry point
/// NULL (envp terminator) // 8 bytes of 0
/// envp[m-1] pointer
/// ...
/// envp[0] pointer
/// NULL (argv terminator) // 8 bytes of 0
/// argv[n-1] pointer
/// ...
Expand All @@ -3282,6 +3310,7 @@ impl ProcessManager {
page_table: &crate::memory::process_memory::ProcessPageTable,
stack_top: u64,
argv: &[&[u8]],
envp: &[&[u8]],
phdr_vaddr: u64,
phnum: u16,
phentsize: u16,
Expand Down Expand Up @@ -3325,7 +3354,7 @@ impl ProcessManager {
self.write_byte_to_stack(page_table, random_addr + i as u64, *byte)?;
}

// Calculate total space needed for argv strings
// Calculate total space needed for argv and envp strings
let mut total_string_space: usize = 0;
for arg in argv.iter() {
let len = arg.len();
Expand All @@ -3335,6 +3364,14 @@ impl ProcessManager {
total_string_space += len + 1;
}
}
for env in envp.iter() {
let len = env.len();
if len > 0 && env[len - 1] == 0 {
total_string_space += len;
} else {
total_string_space += len + 1;
}
}

// Reserve space for strings below the random bytes
cursor -= total_string_space as u64;
Expand Down Expand Up @@ -3363,15 +3400,34 @@ impl ProcessManager {
}
}

// Write envp strings and collect their addresses
let mut envp_addrs: Vec<u64> = Vec::with_capacity(envp.len());

for env in envp.iter() {
envp_addrs.push(current_string_addr);

for byte in env.iter() {
self.write_byte_to_stack(page_table, current_string_addr, *byte)?;
current_string_addr += 1;
}

// Add null terminator if not present
let len = env.len();
if len == 0 || env[len - 1] != 0 {
self.write_byte_to_stack(page_table, current_string_addr, 0)?;
current_string_addr += 1;
}
}

// --- Phase 2: Build the pointer/value section below the strings ---

// Auxiliary vector entries (each is two u64 values: type, value)
// AT_ENTRY, AT_PHDR, AT_PHNUM, AT_PHENT, AT_PAGESZ, AT_RANDOM, AT_NULL = 7 entries = 14 u64s
let auxv_count = 7;
let auxv_space = auxv_count * 2 * 8; // 7 entries * 2 u64s * 8 bytes

// envp: just a NULL terminator (empty environment) = 1 u64
let envp_space = 8;
// envp: envp.len() pointers + NULL terminator
let envp_space = (envp.len() + 1) * 8;

// argv: argc pointers + NULL terminator = (argc + 1) u64s
let argv_space = (argc + 1) * 8;
Expand Down Expand Up @@ -3405,11 +3461,17 @@ impl ProcessManager {
self.write_u64_to_stack(page_table, write_pos, 0)?;
write_pos += 8;

// 4. Write envp NULL terminator (empty environment)
// 4. Write envp pointers
for addr in envp_addrs.iter() {
self.write_u64_to_stack(page_table, write_pos, *addr)?;
write_pos += 8;
}

// 5. Write envp NULL terminator
self.write_u64_to_stack(page_table, write_pos, 0)?;
write_pos += 8;

// 5. Write auxiliary vector entries
// 6. Write auxiliary vector entries
// AT_ENTRY (9) - program entry point
self.write_u64_to_stack(page_table, write_pos, 9)?; // AT_ENTRY
write_pos += 8;
Expand Down
9 changes: 9 additions & 0 deletions kernel/src/syscall/dispatcher.rs
Original file line number Diff line number Diff line change
Expand Up @@ -173,5 +173,14 @@ pub fn dispatch_syscall(
// Testing/diagnostic syscalls (Breenix-specific)
SyscallNumber::CowStats => super::handlers::sys_cow_stats(arg1),
SyscallNumber::SimulateOom => super::handlers::sys_simulate_oom(arg1),
// Resource limits and system info
SyscallNumber::Getrlimit => handlers::sys_getrlimit(arg1, arg2),
SyscallNumber::Prlimit64 => handlers::sys_prlimit64(arg1, arg2, arg3, arg4),
SyscallNumber::Uname => handlers::sys_uname(arg1),
// epoll
SyscallNumber::EpollCreate1 => super::epoll::sys_epoll_create1(arg1 as u32),
SyscallNumber::EpollCtl => super::epoll::sys_epoll_ctl(arg1 as i32, arg2 as i32, arg3 as i32, arg4),
SyscallNumber::EpollWait => super::epoll::sys_epoll_pwait(arg1 as i32, arg2, arg3 as i32, arg4 as i32, 0, 0),
SyscallNumber::EpollPwait => super::epoll::sys_epoll_pwait(arg1 as i32, arg2, arg3 as i32, arg4 as i32, arg5, arg6),
}
}
Loading
Loading