From: Tulio A M Mendes Date: Sun, 8 Feb 2026 07:01:01 +0000 (-0300) Subject: x86: fix fork addr_space clone; add tty ioctl + session/pgrp syscalls X-Git-Url: https://projects.tadryanom.me/?a=commitdiff_plain;h=b0913f70e028ac1751bcd89fc9a285f8fb66ac50;p=AdrOS.git x86: fix fork addr_space clone; add tty ioctl + session/pgrp syscalls - fork clones from active CR3 and returns correct pid - vmm_as_clone_user copies user pages via per-AS tmp mapping - tty: implement ioctl (termios + pgrp) and ICANON/ECHO handling - add setsid/setpgid/getpgrp syscalls + init.elf smoke tests - overlayfs: fix cppcheck knownConditionTrueFalse --- diff --git a/include/errno.h b/include/errno.h index 1b0f418..6f45588 100644 --- a/include/errno.h +++ b/include/errno.h @@ -16,6 +16,7 @@ #define ESRCH 3 #define EMFILE 24 #define ESPIPE 29 +#define ENOTTY 25 #define ENOSYS 38 #endif diff --git a/include/process.h b/include/process.h index 1bb1e67..7ce978b 100644 --- a/include/process.h +++ b/include/process.h @@ -25,6 +25,8 @@ struct file { struct process { uint32_t pid; uint32_t parent_pid; + uint32_t session_id; + uint32_t pgrp_id; uintptr_t sp; uintptr_t addr_space; uint32_t* kernel_stack; diff --git a/include/syscall.h b/include/syscall.h index 4156b97..8903e98 100644 --- a/include/syscall.h +++ b/include/syscall.h @@ -29,6 +29,10 @@ enum { SYSCALL_POLL = 18, SYSCALL_KILL = 19, SYSCALL_SELECT = 20, + SYSCALL_IOCTL = 21, + SYSCALL_SETSID = 22, + SYSCALL_SETPGID = 23, + SYSCALL_GETPGRP = 24, }; #endif diff --git a/include/tty.h b/include/tty.h index 31f1370..bf95310 100644 --- a/include/tty.h +++ b/include/tty.h @@ -4,6 +4,15 @@ #include #include +struct termios { + uint32_t c_lflag; +}; + +enum { + TTY_ICANON = 0x0001, + TTY_ECHO = 0x0002, +}; + void tty_init(void); int tty_read(void* user_buf, uint32_t len); @@ -15,6 +24,8 @@ int tty_write_kbuf(const void* kbuf, uint32_t len); int tty_can_read(void); int tty_can_write(void); +int tty_ioctl(uint32_t cmd, void* user_arg); + void tty_input_char(char c); #endif diff --git a/src/arch/x86/vmm.c b/src/arch/x86/vmm.c index ba8b736..7d91f96 100644 --- a/src/arch/x86/vmm.c +++ b/src/arch/x86/vmm.c @@ -111,6 +111,10 @@ uintptr_t vmm_as_create_kernel_clone(void) { uintptr_t vmm_as_clone_user(uintptr_t src_as) { if (!src_as) return 0; + // Temporary kernel-only mapping in the last user PDE (pdi=767). This avoids touching + // shared higher-half kernel page tables copied from boot_pd. + const uintptr_t TMP_MAP_VA = 0xBFF00000U; + uintptr_t new_as = vmm_as_create_kernel_clone(); if (!new_as) return 0; @@ -121,22 +125,19 @@ uintptr_t vmm_as_clone_user(uintptr_t src_as) { } uint32_t* src_pd = (uint32_t*)P2V((uint32_t)src_as); - const uint32_t* const boot_pd_virt = boot_pd; - // Best-effort clone: copy present user mappings, ignore kernel half. + // Best-effort clone: copy present user mappings (USER PTEs), ignore kernel half. for (uint32_t pdi = 0; pdi < 768; pdi++) { uint32_t pde = src_pd[pdi]; if (!(pde & X86_PTE_PRESENT)) continue; - // Skip if this PDE looks like a kernel mapping (shouldn't happen for pdi<768). - if (boot_pd_virt[pdi] == pde) continue; - uint32_t src_pt_phys = pde & 0xFFFFF000; uint32_t* src_pt = (uint32_t*)P2V(src_pt_phys); for (uint32_t pti = 0; pti < 1024; pti++) { uint32_t pte = src_pt[pti]; if (!(pte & X86_PTE_PRESENT)) continue; + if ((pte & X86_PTE_USER) == 0) continue; const uint32_t x86_flags = pte & 0xFFF; // Derive VMM flags. @@ -150,19 +151,24 @@ uintptr_t vmm_as_clone_user(uintptr_t src_as) { return 0; } + uint32_t src_frame = pte & 0xFFFFF000; + uintptr_t va = ((uintptr_t)pdi << 22) | ((uintptr_t)pti << 12); vmm_as_map_page(new_as, (uint64_t)(uintptr_t)dst_frame, (uint64_t)va, flags); - // Copy contents by temporarily switching address spaces. + // Copy contents by mapping frames into a temporary kernel VA under each address space. uintptr_t old_as = hal_cpu_get_address_space(); - vmm_as_activate(src_as); - memcpy(tmp, (const void*)va, 4096); + vmm_map_page((uint64_t)src_frame, (uint64_t)TMP_MAP_VA, VMM_FLAG_PRESENT | VMM_FLAG_RW); + memcpy(tmp, (const void*)TMP_MAP_VA, 4096); + vmm_unmap_page((uint64_t)TMP_MAP_VA); vmm_as_activate(new_as); - memcpy((void*)va, tmp, 4096); - + vmm_map_page((uint64_t)(uintptr_t)dst_frame, (uint64_t)TMP_MAP_VA, VMM_FLAG_PRESENT | VMM_FLAG_RW); + memcpy((void*)TMP_MAP_VA, tmp, 4096); + vmm_unmap_page((uint64_t)TMP_MAP_VA); vmm_as_activate(old_as); + } } diff --git a/src/kernel/overlayfs.c b/src/kernel/overlayfs.c index 3fa32e5..25e3113 100644 --- a/src/kernel/overlayfs.c +++ b/src/kernel/overlayfs.c @@ -56,7 +56,7 @@ static fs_node_t* overlay_copy_up_file(struct overlay_node* on) { if (!buf) return NULL; uint32_t rd = vfs_read(on->lower, 0, len, buf); if (rd != len) { - if (buf) kfree(buf); + kfree(buf); return NULL; } } diff --git a/src/kernel/scheduler.c b/src/kernel/scheduler.c index 6b576e1..0b4f367 100644 --- a/src/kernel/scheduler.c +++ b/src/kernel/scheduler.c @@ -262,6 +262,8 @@ struct process* process_fork_create(uintptr_t child_as, const struct registers* proc->pid = next_pid++; proc->parent_pid = current_process ? current_process->pid : 0; + proc->session_id = current_process ? current_process->session_id : proc->pid; + proc->pgrp_id = current_process ? current_process->pgrp_id : proc->pid; proc->state = PROCESS_READY; proc->addr_space = child_as; proc->wake_at_tick = 0; @@ -322,6 +324,8 @@ void process_init(void) { kernel_proc->pid = 0; kernel_proc->parent_pid = 0; + kernel_proc->session_id = 0; + kernel_proc->pgrp_id = 0; kernel_proc->state = PROCESS_RUNNING; kernel_proc->wake_at_tick = 0; kernel_proc->addr_space = hal_cpu_get_address_space(); @@ -367,6 +371,8 @@ struct process* process_create_kernel(void (*entry_point)(void)) { proc->pid = next_pid++; proc->parent_pid = current_process ? current_process->pid : 0; + proc->session_id = current_process ? current_process->session_id : proc->pid; + proc->pgrp_id = current_process ? current_process->pgrp_id : proc->pid; proc->state = PROCESS_READY; proc->addr_space = kernel_as ? kernel_as : (current_process ? current_process->addr_space : 0); proc->wake_at_tick = 0; diff --git a/src/kernel/syscall.c b/src/kernel/syscall.c index c0342fb..c99711b 100644 --- a/src/kernel/syscall.c +++ b/src/kernel/syscall.c @@ -128,7 +128,12 @@ static int syscall_fork_impl(struct registers* regs) { if (!regs) return -EINVAL; if (!current_process) return -EINVAL; - uintptr_t child_as = vmm_as_clone_user(current_process->addr_space); + uintptr_t src_as = hal_cpu_get_address_space() & ~(uintptr_t)0xFFFU; + if (current_process->addr_space != src_as) { + current_process->addr_space = src_as; + } + + uintptr_t child_as = vmm_as_clone_user(src_as); if (!child_as) return -ENOMEM; struct registers child_regs = *regs; @@ -772,9 +777,13 @@ static int syscall_read_impl(int fd, void* user_buf, uint32_t len) { total += rd; if (rd < chunk) break; } + return (int)total; } + if (f->node->flags != FS_FILE) return -ESPIPE; + if (!f->node->read) return -ESPIPE; + uint8_t kbuf[256]; uint32_t total = 0; while (total < len) { @@ -788,11 +797,8 @@ static int syscall_read_impl(int fd, void* user_buf, uint32_t len) { return -EFAULT; } - if (f->node->flags == FS_FILE) { - f->offset += rd; - } + f->offset += rd; total += rd; - if (rd < chunk) break; } @@ -830,10 +836,42 @@ static int syscall_write_impl(int fd, const void* user_buf, uint32_t len) { total += wr; if (wr < chunk) break; } - return (int)total; } +static int syscall_ioctl_impl(int fd, uint32_t cmd, void* user_arg) { + struct file* f = fd_get(fd); + if (!f || !f->node) return -EBADF; + + fs_node_t* n = f->node; + if (n->flags != FS_CHARDEVICE) return -ENOTTY; + if (n->inode != 3) return -ENOTTY; + + return tty_ioctl(cmd, user_arg); +} + +static int syscall_setsid_impl(void) { + if (!current_process) return -EINVAL; + if (current_process->pid != 0 && current_process->pgrp_id == current_process->pid) return -EPERM; + current_process->session_id = current_process->pid; + current_process->pgrp_id = current_process->pid; + return (int)current_process->session_id; +} + +static int syscall_setpgid_impl(int pid, int pgid) { + if (!current_process) return -EINVAL; + if (pid != 0 && pid != (int)current_process->pid) return -EINVAL; + if (pgid == 0) pgid = (int)current_process->pid; + if (pgid < 0) return -EINVAL; + current_process->pgrp_id = (uint32_t)pgid; + return 0; +} + +static int syscall_getpgrp_impl(void) { + if (!current_process) return 0; + return (int)current_process->pgrp_id; +} + static void syscall_handler(struct registers* regs) { uint32_t syscall_no = regs->eax; @@ -1007,6 +1045,31 @@ static void syscall_handler(struct registers* regs) { return; } + if (syscall_no == SYSCALL_IOCTL) { + int fd = (int)regs->ebx; + uint32_t cmd = (uint32_t)regs->ecx; + void* arg = (void*)regs->edx; + regs->eax = (uint32_t)syscall_ioctl_impl(fd, cmd, arg); + return; + } + + if (syscall_no == SYSCALL_SETSID) { + regs->eax = (uint32_t)syscall_setsid_impl(); + return; + } + + if (syscall_no == SYSCALL_SETPGID) { + int pid = (int)regs->ebx; + int pgid = (int)regs->ecx; + regs->eax = (uint32_t)syscall_setpgid_impl(pid, pgid); + return; + } + + if (syscall_no == SYSCALL_GETPGRP) { + regs->eax = (uint32_t)syscall_getpgrp_impl(); + return; + } + regs->eax = (uint32_t)-ENOSYS; } diff --git a/src/kernel/tty.c b/src/kernel/tty.c index d240392..4462216 100644 --- a/src/kernel/tty.c +++ b/src/kernel/tty.c @@ -26,6 +26,8 @@ static struct process* waitq[TTY_WAITQ_MAX]; static uint32_t waitq_head = 0; static uint32_t waitq_tail = 0; +static uint32_t tty_lflag = TTY_ICANON | TTY_ECHO; + static int canon_empty(void) { return canon_head == canon_tail; } @@ -135,13 +137,75 @@ static void tty_wake_one(void) { } } +enum { + TTY_TCGETS = 0x5401, + TTY_TCSETS = 0x5402, + TTY_TIOCGPGRP = 0x540F, + TTY_TIOCSPGRP = 0x5410, +}; + +int tty_ioctl(uint32_t cmd, void* user_arg) { + if (!user_arg) return -EFAULT; + + if (cmd == TTY_TIOCGPGRP) { + if (user_range_ok(user_arg, sizeof(int)) == 0) return -EFAULT; + int fg = 0; + if (copy_to_user(user_arg, &fg, sizeof(fg)) < 0) return -EFAULT; + return 0; + } + + if (cmd == TTY_TIOCSPGRP) { + if (user_range_ok(user_arg, sizeof(int)) == 0) return -EFAULT; + int fg = 0; + if (copy_from_user(&fg, user_arg, sizeof(fg)) < 0) return -EFAULT; + if (fg != 0) return -EINVAL; + return 0; + } + + if (user_range_ok(user_arg, sizeof(struct termios)) == 0) return -EFAULT; + + if (cmd == TTY_TCGETS) { + struct termios t; + uintptr_t flags = spin_lock_irqsave(&tty_lock); + t.c_lflag = tty_lflag; + spin_unlock_irqrestore(&tty_lock, flags); + if (copy_to_user(user_arg, &t, sizeof(t)) < 0) return -EFAULT; + return 0; + } + + if (cmd == TTY_TCSETS) { + struct termios t; + if (copy_from_user(&t, user_arg, sizeof(t)) < 0) return -EFAULT; + uintptr_t flags = spin_lock_irqsave(&tty_lock); + tty_lflag = t.c_lflag & (TTY_ICANON | TTY_ECHO); + spin_unlock_irqrestore(&tty_lock, flags); + return 0; + } + + return -EINVAL; +} + void tty_input_char(char c) { uintptr_t flags = spin_lock_irqsave(&tty_lock); + uint32_t lflag = tty_lflag; + + if ((lflag & TTY_ICANON) == 0) { + if (c == '\r') c = '\n'; + canon_push(c); + tty_wake_one(); + if (lflag & TTY_ECHO) { + uart_put_char(c); + } + spin_unlock_irqrestore(&tty_lock, flags); + return; + } if (c == '\b') { if (line_len > 0) { line_len--; - uart_print("\b \b"); + if (lflag & TTY_ECHO) { + uart_print("\b \b"); + } } spin_unlock_irqrestore(&tty_lock, flags); return; @@ -150,7 +214,9 @@ void tty_input_char(char c) { if (c == '\r') c = '\n'; if (c == '\n') { - uart_put_char('\n'); + if (lflag & TTY_ECHO) { + uart_put_char('\n'); + } for (uint32_t i = 0; i < line_len; i++) { canon_push(line_buf[i]); @@ -166,7 +232,9 @@ void tty_input_char(char c) { if (c >= ' ' && c <= '~') { if (line_len + 1 < sizeof(line_buf)) { line_buf[line_len++] = c; - uart_put_char(c); + if (lflag & TTY_ECHO) { + uart_put_char(c); + } } } diff --git a/user/init.c b/user/init.c index 5c7cb3a..429c94c 100644 --- a/user/init.c +++ b/user/init.c @@ -21,6 +21,26 @@ enum { SYSCALL_POLL = 18, SYSCALL_KILL = 19, SYSCALL_SELECT = 20, + SYSCALL_IOCTL = 21, + SYSCALL_SETSID = 22, + SYSCALL_SETPGID = 23, + SYSCALL_GETPGRP = 24, +}; + +enum { + TCGETS = 0x5401, + TCSETS = 0x5402, + TIOCGPGRP = 0x540F, + TIOCSPGRP = 0x5410, +}; + +enum { + ICANON = 0x0001, + ECHO = 0x0002, +}; + +struct termios { + uint32_t c_lflag; }; struct pollfd { @@ -81,6 +101,17 @@ static int sys_select(uint32_t nfds, uint64_t* readfds, uint64_t* writefds, uint return ret; } +static int sys_ioctl(int fd, uint32_t cmd, void* arg) { + int ret; + __asm__ volatile( + "int $0x80" + : "=a"(ret) + : "a"(SYSCALL_IOCTL), "b"(fd), "c"(cmd), "d"(arg) + : "memory" + ); + return ret; +} + static int sys_kill(int pid, int sig) { int ret; __asm__ volatile( @@ -103,6 +134,39 @@ static int sys_poll(struct pollfd* fds, uint32_t nfds, int32_t timeout) { return ret; } +static int sys_setsid(void) { + int ret; + __asm__ volatile( + "int $0x80" + : "=a"(ret) + : "a"(SYSCALL_SETSID) + : "memory" + ); + return ret; +} + +static int sys_setpgid(int pid, int pgid) { + int ret; + __asm__ volatile( + "int $0x80" + : "=a"(ret) + : "a"(SYSCALL_SETPGID), "b"(pid), "c"(pgid) + : "memory" + ); + return ret; +} + +static int sys_getpgrp(void) { + int ret; + __asm__ volatile( + "int $0x80" + : "=a"(ret) + : "a"(SYSCALL_GETPGRP) + : "memory" + ); + return ret; +} + static int sys_getpid(void) { int ret; __asm__ volatile( @@ -620,6 +684,70 @@ void _start(void) { (uint32_t)(sizeof("[init] select(pipe) OK\n") - 1)); } + { + int fd = sys_open("/dev/tty", 0); + if (fd < 0) { + sys_write(1, "[init] ioctl(/dev/tty) open failed\n", + (uint32_t)(sizeof("[init] ioctl(/dev/tty) open failed\n") - 1)); + sys_exit(1); + } + + int fg = -1; + if (sys_ioctl(fd, TIOCGPGRP, &fg) < 0 || fg != 0) { + sys_write(1, "[init] ioctl TIOCGPGRP failed\n", + (uint32_t)(sizeof("[init] ioctl TIOCGPGRP failed\n") - 1)); + sys_exit(1); + } + + fg = 0; + if (sys_ioctl(fd, TIOCSPGRP, &fg) < 0) { + sys_write(1, "[init] ioctl TIOCSPGRP failed\n", + (uint32_t)(sizeof("[init] ioctl TIOCSPGRP failed\n") - 1)); + sys_exit(1); + } + + fg = 1; + if (sys_ioctl(fd, TIOCSPGRP, &fg) >= 0) { + sys_write(1, "[init] ioctl TIOCSPGRP expected fail\n", + (uint32_t)(sizeof("[init] ioctl TIOCSPGRP expected fail\n") - 1)); + sys_exit(1); + } + + struct termios oldt; + if (sys_ioctl(fd, TCGETS, &oldt) < 0) { + sys_write(1, "[init] ioctl TCGETS failed\n", + (uint32_t)(sizeof("[init] ioctl TCGETS failed\n") - 1)); + sys_exit(1); + } + + struct termios t = oldt; + t.c_lflag &= ~(uint32_t)(ECHO | ICANON); + if (sys_ioctl(fd, TCSETS, &t) < 0) { + sys_write(1, "[init] ioctl TCSETS failed\n", + (uint32_t)(sizeof("[init] ioctl TCSETS failed\n") - 1)); + sys_exit(1); + } + + struct termios chk; + if (sys_ioctl(fd, TCGETS, &chk) < 0) { + sys_write(1, "[init] ioctl TCGETS2 failed\n", + (uint32_t)(sizeof("[init] ioctl TCGETS2 failed\n") - 1)); + sys_exit(1); + } + + if ((chk.c_lflag & (uint32_t)(ECHO | ICANON)) != 0) { + sys_write(1, "[init] ioctl verify failed\n", + (uint32_t)(sizeof("[init] ioctl verify failed\n") - 1)); + sys_exit(1); + } + + (void)sys_ioctl(fd, TCSETS, &oldt); + (void)sys_close(fd); + + sys_write(1, "[init] ioctl(/dev/tty) OK\n", + (uint32_t)(sizeof("[init] ioctl(/dev/tty) OK\n") - 1)); + } + { int fd = sys_open("/dev/null", 0); if (fd < 0) { @@ -641,6 +769,47 @@ void _start(void) { sys_write(1, "[init] poll(/dev/null) OK\n", (uint32_t)(sizeof("[init] poll(/dev/null) OK\n") - 1)); } + + { + sys_write(1, "[init] setsid test: before fork\n", + (uint32_t)(sizeof("[init] setsid test: before fork\n") - 1)); + int pid = sys_fork(); + if (pid < 0) { + sys_write(1, "[init] setsid test fork failed\n", + (uint32_t)(sizeof("[init] setsid test fork failed\n") - 1)); + sys_exit(1); + } + + if (pid == 0) { + sys_write(1, "[init] setsid test: child start\n", + (uint32_t)(sizeof("[init] setsid test: child start\n") - 1)); + int me = sys_getpid(); + int sid = sys_setsid(); + if (sid != me) sys_exit(2); + + int pg = sys_getpgrp(); + if (pg != me) sys_exit(3); + + int newpg = me + 1; + if (sys_setpgid(0, newpg) < 0) sys_exit(4); + if (sys_getpgrp() != newpg) sys_exit(5); + + sys_exit(0); + } + + sys_write(1, "[init] setsid test: parent waitpid\n", + (uint32_t)(sizeof("[init] setsid test: parent waitpid\n") - 1)); + int st = 0; + int wp = sys_waitpid(pid, &st, 0); + if (wp != pid || st != 0) { + sys_write(1, "[init] setsid/setpgid/getpgrp failed\n", + (uint32_t)(sizeof("[init] setsid/setpgid/getpgrp failed\n") - 1)); + sys_exit(1); + } + + sys_write(1, "[init] setsid/setpgid/getpgrp OK\n", + (uint32_t)(sizeof("[init] setsid/setpgid/getpgrp OK\n") - 1)); + } } fd = sys_open("/tmp/hello.txt", 0);