]> Projects (at) Tadryanom (dot) Me - AdrOS.git/commitdiff
x86: fix fork addr_space clone; add tty ioctl + session/pgrp syscalls
authorTulio A M Mendes <[email protected]>
Sun, 8 Feb 2026 07:01:01 +0000 (04:01 -0300)
committerTulio A M Mendes <[email protected]>
Sun, 8 Feb 2026 07:07:36 +0000 (04:07 -0300)
- 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

include/errno.h
include/process.h
include/syscall.h
include/tty.h
src/arch/x86/vmm.c
src/kernel/overlayfs.c
src/kernel/scheduler.c
src/kernel/syscall.c
src/kernel/tty.c
user/init.c

index 1b0f418a07e86af4060f1611231b6f3bd11ff07b..6f455883afc6b310c4997534be9db2bbe6d13092 100644 (file)
@@ -16,6 +16,7 @@
 #define ESRCH 3
 #define EMFILE 24
 #define ESPIPE 29
+#define ENOTTY 25
 #define ENOSYS 38
 
 #endif
index 1bb1e6758614ba92ded21c2eb720c75019c69272..7ce978bd6419cbd42f13d3d270407f61400a06cc 100644 (file)
@@ -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;
index 4156b978c03d9f7c0d61cfabe153216b5db5a4f3..8903e98d716518b0b5774a24f907f3faedf070cb 100644 (file)
@@ -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
index 31f13704ad93fe7d2e9a9a72bf6a92a911109fe4..bf95310e84209015b7d3d34f510b011442e81753 100644 (file)
@@ -4,6 +4,15 @@
 #include <stddef.h>
 #include <stdint.h>
 
+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
index ba8b73681af75e58ba7e933c2d964c0becb5c9cd..7d91f9663a99fba386512c4e1b2105132d2d1ae3 100644 (file)
@@ -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);
+
         }
     }
 
index 3fa32e596d67dcbfd93b3b899a81a2acffe15f4a..25e31130d938be51a8e2314cc88da2181643a082 100644 (file)
@@ -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;
         }
     }
index 6b576e1fb7f8fd77cafa185711dc0753f2e88d12..0b4f367e691b1c75c5a9cafc56542daa08088ee9 100644 (file)
@@ -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;
index c0342fbdc35862ecf08610e6ad562e0439295577..c99711b4994bca86f275997172c22c4dffc935f4 100644 (file)
@@ -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;
 }
 
index d24039257f1efc1e77521ebfde32b6a8e84328c2..44622169894d8d07d49e5d23ffa62ffad5b5c44d 100644 (file)
@@ -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);
+            }
         }
     }
 
index 5c7cb3a0afb6bd0ac1f1cfcefe63df7380f0f25f..429c94cbb09a0c7ef89e01adbad1253870f267a2 100644 (file)
@@ -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);