]> Projects (at) Tadryanom (dot) Me - AdrOS.git/log
AdrOS.git
26 hours agovfs: add vfs_require_writable_path check to syscall_link_impl (P4.1)
Tulio A M Mendes [Mon, 25 May 2026 21:09:37 +0000 (18:09 -0300)]
vfs: add vfs_require_writable_path check to syscall_link_impl (P4.1)

26 hours agovfs: remove g_fat_root global, allocate root per mount (P3.1, P3.2, P3.3)
Tulio A M Mendes [Mon, 25 May 2026 21:05:48 +0000 (18:05 -0300)]
vfs: remove g_fat_root global, allocate root per mount (P3.1, P3.2, P3.3)

- Remove g_fat_root global from fat.c
- Allocate root node dynamically in fat_mount() and ext2_mount()
- Add root field to vfs_superblock_t for cleanup on umount
- Update fat_kill_sb and ext2_kill_sb to free root node
- Remove g_fat_root check from fat_close_impl

26 hours agovfs: add kill_sb callback to vfs_fs_type_t for filesystem cleanup (P2.3)
Tulio A M Mendes [Mon, 25 May 2026 21:01:29 +0000 (18:01 -0300)]
vfs: add kill_sb callback to vfs_fs_type_t for filesystem cleanup (P2.3)

- Add kill_sb function pointer to vfs_fs_type_t
- Update vfs_umount_nolock to call fstype->kill_sb instead of direct fat_umount/ext2_umount
- Implement fat_kill_sb and ext2_kill_sb callbacks in init.c
- Callbacks call filesystem-specific umount and free superblock

26 hours agovfs: change mount API to return vfs_mount_result_t {root, sb} (P2.1)
Tulio A M Mendes [Mon, 25 May 2026 20:58:12 +0000 (17:58 -0300)]
vfs: change mount API to return vfs_mount_result_t {root, sb} (P2.1)

- Add vfs_mount_result_t structure with root and superblock
- Update vfs_fs_type_t.mount() to return vfs_mount_result_t
- Update fat_mount() and ext2_mount() to build and return superblock
- Update init_mount_fs() to handle vfs_mount_result_t and set fstype in sb
- Update vfs_mount_full() to accept sb parameter
- Update all vfs_mount_full() callers to pass sb (NULL for virtual FS)

26 hours agovfs: separate new mount from MS_REMOUNT - reject replacement if mount is active ...
Tulio A M Mendes [Mon, 25 May 2026 20:50:18 +0000 (17:50 -0300)]
vfs: separate new mount from MS_REMOUNT - reject replacement if mount is active (refcount>0) unless MS_REMOUNT flag is set (P1.3)

26 hours agovfs: zero removed mount slot after shifting to prevent stale data in reused slots...
Tulio A M Mendes [Mon, 25 May 2026 20:47:36 +0000 (17:47 -0300)]
vfs: zero removed mount slot after shifting to prevent stale data in reused slots (P1.2)

26 hours agovfs: initialize refcount=0 for new mount entries to prevent inheriting stale values...
Tulio A M Mendes [Mon, 25 May 2026 20:45:08 +0000 (17:45 -0300)]
vfs: initialize refcount=0 for new mount entries to prevent inheriting stale values from reused slots (P1.1)

26 hours agodocs: add security fix TODO implementation plan
Tulio A M Mendes [Mon, 25 May 2026 20:26:46 +0000 (17:26 -0300)]
docs: add security fix TODO implementation plan

- Document implementation plan for 3 remaining security items
- K12/K13/K23: /proc UID check (requires UID infrastructure)
- K15: raw socket privilege (requires UID infrastructure)
- K24: NX flag in SHM (needs additional testing)
- Includes phases, testing strategy, timeline estimates
- Total estimated effort: 9-14 days depending on approach

26 hours agosecurity: audit completion and TODO documentation
Tulio A M Mendes [Mon, 25 May 2026 20:22:46 +0000 (17:22 -0300)]
security: audit completion and TODO documentation

- Verified 23/25 items from SECURITY_FIX_PLAN_2026-05-25.md are implemented
- Documented K12/K13/K23 (/proc UID check) as TODO - requires UID infrastructure
- Documented K15 (raw socket privilege) as TODO - requires UID infrastructure
- Documented K24 (NX in SHM) as TODO - needs additional testing
- K24 NX flag temporarily disabled in shm_at for safety
- Analysis shows 92% completion of security fix plan
- Remaining items depend on multi-user authentication infrastructure

26 hours agokernel: implement NX (No-Execute) support via IA32_EFER.NXE
Tulio A M Mendes [Mon, 25 May 2026 19:57:13 +0000 (16:57 -0300)]
kernel: implement NX (No-Execute) support via IA32_EFER.NXE

Fix A01 (W^X/NX) which was deferred due to IA32_EFER.NXE MSR instability.
Root cause: NX bit was being set in PTEs without NXE enabled, causing
undefined behavior and kernel panic.

Changes:
- boot.S: Check CPUID.0x80000001:EDX bit 20 for NX support before enabling
- boot.S: Enable IA32_EFER.NXE (MSR 0xC0000080, bit 11) if NX supported
- vmm.c: Add g_nxe_enabled flag and check_nxe_enabled() function
- vmm.c: Conditionalize X86_PTE_NX usage based on g_nxe_enabled
- vmm.c: Print NX status in vmm_init()
- Makefile: Add -cpu qemu32,+nx to expose NX support in QEMU
- smoke_test.exp: Add -cpu qemu32,+nx for testing

Behavior:
- With NX support: NXE enabled, VMM uses NX bit for non-executable pages
- Without NX support: NXE not enabled, VMM ignores VMM_FLAG_NX
- W^X now works correctly for ELF loading, mmap/mprotect, etc.

Test: 119/119 PASS (SMP=4)

26 hours agosecurity: Round 6.4 socket copy_to_user SMAP compliance (K35)
Tulio A M Mendes [Mon, 25 May 2026 19:23:16 +0000 (16:23 -0300)]
security: Round 6.4 socket copy_to_user SMAP compliance (K35)

K35: Add bounce buffers to sendmsg/recvmsg for SMAP compliance
- sendmsg: copy_from_user to kernel buffer before ksocket_send/ksocket_sendto
- recvmsg: ksocket_recvfrom to kernel buffer, then copy_to_user to user buffer
- Bounce buffer size limited to 4096 bytes per iov entry
- Ensures SMAP compliance by not passing user buffers directly to lwIP

Tests: 119/119 PASS (smoke test, SMP=4)

26 hours agosecurity: Round 6.3 shell command substitution fix (A18)
Tulio A M Mendes [Mon, 25 May 2026 19:21:33 +0000 (16:21 -0300)]
security: Round 6.3 shell command substitution fix (A18)

A18: Fix shell command substitution  syntax
- expand_vars was adding '(' at the start but missing ')' at the end
- Added closing parenthesis to properly wrap subshell command
- Changed cmd[1 + cmdlen] = '\0' to cmd[1 + cmdlen] = ')' and cmd[2 + cmdlen] = '\0'

Tests: 119/119 PASS (smoke test, SMP=4)

26 hours agosecurity: Round 6.2 O_NOFOLLOW implementation (K21)
Tulio A M Mendes [Mon, 25 May 2026 19:20:33 +0000 (16:20 -0300)]
security: Round 6.2 O_NOFOLLOW implementation (K21)

K21: Implement O_NOFOLLOW flag in open/openat
- Added LOOKUP_FOLLOW and LOOKUP_NOFOLLOW flags to vfs_lookup_depth
- Added vfs_lookup_nofollow() function for O_NOFOLLOW path
- Modified syscall_open_impl to use vfs_lookup_nofollow when O_NOFOLLOW is set
- When O_NOFOLLOW is set, symlinks are not followed - returned as-is

Tests: 119/119 PASS (smoke test, SMP=4)

26 hours agosecurity: Round 6.1 access() mode implementation (A19)
Tulio A M Mendes [Mon, 25 May 2026 19:18:07 +0000 (16:18 -0300)]
security: Round 6.1 access() mode implementation (A19)

A19: Implement mode checking in access() syscall
- F_OK: check file existence (already done)
- R_OK: assume readable if exists (simplified, no granular perms yet)
- W_OK: check mount read-only flag, return EROFS if read-only
- X_OK: check if file is regular file (FS_FILE), return EACCES if not

Tests: 119/119 PASS (smoke test, SMP=4)

26 hours agosecurity: Round 5.5 posix_spawn PID fix (A13)
Tulio A M Mendes [Mon, 25 May 2026 19:16:48 +0000 (16:16 -0300)]
security: Round 5.5 posix_spawn PID fix (A13)

A13: Fix posix_spawn wrapper to preserve child PID
- Kernel copies child PID to *pid via copy_to_user
- Wrapper was overwriting *pid with return value (0 on success)
- Removed the line that overwrote *pid, kernel already filled it in

Tests: 119/119 PASS (smoke test, SMP=4)

26 hours agosecurity: Round 5.4 varargs open/openat/fcntl (A17)
Tulio A M Mendes [Mon, 25 May 2026 19:15:34 +0000 (16:15 -0300)]
security: Round 5.4 varargs open/openat/fcntl (A17)

A17: Fix varargs handling in open/openat/fcntl
- open: Only read mode from varargs when O_CREAT is set
- openat: Only read mode from varargs when O_CREAT is set
- fcntl: Only read arg from varargs for commands that need it (F_DUPFD, F_GETFD, F_SETFD, F_GETFL, F_SETFL, F_DUPFD_CLOEXEC)
- Prevents undefined behavior from reading varargs when not needed

Tests: 119/119 PASS (smoke test, SMP=4)

26 hours agosecurity: Round 5.3 execl/execlp varargs (U04)
Tulio A M Mendes [Mon, 25 May 2026 19:14:03 +0000 (16:14 -0300)]
security: Round 5.3 execl/execlp varargs (U04)

U04: Fix varargs handling in execl/execlp
- execl: Use va_list instead of pointer arithmetic for portability
- execlp: Use va_list instead of pointer arithmetic for portability
- Both functions now use __builtin_va_start/va_arg/va_end properly

Tests: 119/119 PASS (smoke test, SMP=4)

26 hours agosecurity: Round 5.2 mkstemp/tmpfile/tmpnam secure (U01)
Tulio A M Mendes [Mon, 25 May 2026 19:13:02 +0000 (16:13 -0300)]
security: Round 5.2 mkstemp/tmpfile/tmpnam secure (U01)

U01: Secure temporary file creation
- mkstemp: Use /dev/urandom for randomness, fallback to pid+counter
- mkstemp: Use alphanumeric charset (62 chars) instead of only digits
- mkstemp: Always use O_CREAT|O_EXCL with mode 0600
- tmpfile: Use mkstemp for secure creation, unlink immediately for anonymity
- tmpnam: Use mkstemp for secure name generation, don't leave file around

Tests: 119/119 PASS (smoke test, SMP=4)

26 hours agosecurity: Round 5.1 scanf %s limit (U02)
Tulio A M Mendes [Mon, 25 May 2026 19:11:55 +0000 (16:11 -0300)]
security: Round 5.1 scanf %s limit (U02)

U02: Limit %s to 255 chars in scanf/sscanf/fscanf to prevent buffer overflow
- Added check (i < 255) in %s parsing loop for scanf
- Added check (i < 255) in %s parsing loop for sscanf
- Added check (i < 255) in %s parsing loop for fscanf

Tests: 119/119 PASS (smoke test, SMP=4)

26 hours agosecurity: Round 4.4-4.5 futex per-process keying (K17) and dlopen per-process (K22)
Tulio A M Mendes [Mon, 25 May 2026 18:46:19 +0000 (15:46 -0300)]
security: Round 4.4-4.5 futex per-process keying (K17) and dlopen per-process (K22)

K17: Futex keyed by (addr_space, uaddr)
- Added addr_space field to futex_waiters struct
- FUTEX_WAIT now stores current_process->addr_space
- FUTEX_WAKE matches by (addr, addr_space) to prevent cross-process interference
- Cleanup on process exit clears addr_space field

K22: dlopen handles per-process
- Added dl_handles array to struct process (PROCESS_MAX_DLOPEN=4)
- Each handle stores: active, path, base, nsyms, and 64 symbols
- Removed global dl_table and dl_lock
- dlopen/dlsym/dlclose now use current_process->dl_handles
- Cleanup on SYSCALL_EXIT clears all dl_handles

Tests: 119/119 PASS (smoke test, SMP=4)

26 hours agosecurity: Round 4.2 SHM permissions (K14) - NX deferred (K24)
Tulio A M Mendes [Mon, 25 May 2026 18:40:12 +0000 (15:40 -0300)]
security: Round 4.2 SHM permissions (K14) - NX deferred (K24)

K14: SHM permission model
- Added uid, gid, mode fields to struct shm_segment
- Initialize uid/gid from current_process on shm_get
- Default mode = 0600 (rw-------)
- shm_at checks: only owner or root can attach

K24: NX flag deferred
- NX flag causes SIGSEGV because IA32_EFER.NXE MSR not enabled
- NX enforcement deferred until A01 (NX MSR enablement) is implemented

Tests: 119/119 PASS (smoke test, SMP=4)

26 hours agosecurity: Round 3 complete - parsers boot/storage validation
Tulio A M Mendes [Mon, 25 May 2026 18:15:35 +0000 (15:15 -0300)]
security: Round 3 complete - parsers boot/storage validation

- A15: Multiboot2 parser validation (arch_early_setup.c):
  - Validate total_size range (8-65536 bytes)
  - Validate tag size (minimum 8 bytes)
  - Validate tag doesn't exceed buffer
  - Use cursor-based iteration with 8-byte alignment for next tag

- F01: ext2 strict validation (ext2.c):
  - Validate rec_len >= 8 in all directory entry loops
  - Validate rec_len % 4 == 0 (4-byte alignment)
  - Validate rec_len doesn't exceed block boundary
  - Validate name_len < rec_len - 8
  - Applied to: ext2_finddir, ext2_readdir_impl, ext2_dir_add_entry, ext2_dir_remove_entry, ext2_dir_find_entry, ext2_dir_is_empty

Tests: 119/119 PASS (smoke test, SMP=4)

26 hours agosecurity: A07 complete, Round 3.1 initrd/LZ4/TAR validation (A05)
Tulio A M Mendes [Mon, 25 May 2026 18:11:07 +0000 (15:11 -0300)]
security: A07 complete, Round 3.1 initrd/LZ4/TAR validation (A05)

- A07: vfs_check_permission moved to fs.c, vfs_check_parent_permission now validates real permissions
- A05: initrd parser validation:
  - Minimum size checks for magic (4 bytes), LZ4 frame header (10 bytes), LZ4B header (12 bytes)
  - Update size variable after decompression to reflect decompressed size
  - TAR file size limit (256MB max)
  - Removed overly strict buffer overflow check that rejected valid TAR

Tests: 119/119 PASS (smoke test, SMP=4)

26 hours agosecurity: Round 1-2 fixes (A02, A03, A04, A14, A16, partial A07)
Tulio A M Mendes [Mon, 25 May 2026 18:02:55 +0000 (15:02 -0300)]
security: Round 1-2 fixes (A02, A03, A04, A14, A16, partial A07)

- A16: pmm_decref underflow guard in src/mm/pmm.c
- A02: sysenter.S stack pointer validation with 8-byte kernel margin
- A03: pread/pwrite mode validation (rejects O_WRONLY for pread, O_RDONLY for pwrite, checks MS_RDONLY)
- A04: Reject O_RDONLY|O_TRUNC in open syscall
- A14: truncate/ftruncate storage update via vfs_truncate_node helper with fallback
- A07: vfs_check_parent_permission helper added to fs.c, applied to unlink/rmdir/rename/mkdir/link/create (allows all for now - single-user root system)

Note: A01 (W^X/NX default) removed temporarily due to kernel panic - needs NX bit investigation
Note: K02 (mprotect per VMA) deferred - requires per-page VMA architecture

Tests: 119/119 PASS (smoke test, SMP=4)

26 hours agovfs: complete Fase 5 - /proc/mounts improvements and mountpoint validation
Tulio A M Mendes [Mon, 25 May 2026 17:22:42 +0000 (14:22 -0300)]
vfs: complete Fase 5 - /proc/mounts improvements and mountpoint validation

- Refactor vfs_mounts_read to support offset-based reading like a file
- Add escape_mount_string helper to escape special characters (space, tab, newline, backslash)
- Replace fixed 2048-byte buffer with dynamic allocation based on actual content size
- Escape spaces as \040, tabs as \011, newlines as \012, backslashes as \\
- Update proc_mounts_read to use new vfs_mounts_read signature with offset parameter
- Remove auto-mkdirp from sys_mount (vfs_mkdirp calls removed)
- Add mountpoint existence check before mount (vfs_lookup + directory check)
- Return -ENOENT if mountpoint does not exist, -ENOTDIR if not a directory
- Add heap.h include for kmalloc/kfree in fs.c
- Test: smoke test 119/119 PASS

26 hours agovfs: refactor FAT and EXT2 to support multiple independent mounts
Tulio A M Mendes [Mon, 25 May 2026 17:13:38 +0000 (14:13 -0300)]
vfs: refactor FAT and EXT2 to support multiple independent mounts

- Replace global g_fat/g_ext2 singletons with per-mount state structures
- Add struct fat_mount and struct ext2_mount for filesystem-specific state
- Update all FAT/EXT2 functions to accept mount pointer parameter
- Refactor fat_mount/ext2_mount to allocate per-mount structures dynamically
- Add fat_umount/ext2_umount functions to free allocated resources
- Integrate filesystem-specific umount calls in vfs_umount_nolock
- Remove global state dependencies to enable multiple independent mounts
- Fix duplicate enum fat_type definition in fat.c
- Test: smoke test 119/119 PASS

26 hours agovfs: Fase 3 - filesystem type registry and superblock
Tulio A M Mendes [Mon, 25 May 2026 16:27:57 +0000 (13:27 -0300)]
vfs: Fase 3 - filesystem type registry and superblock

- Added vfs_fs_type_t structure with name, flags, and mount function
- Implemented vfs_fs_type_register and vfs_fs_type_find functions
- Registered FAT and ext2 filesystem types in init_start
- Refactored init_mount_fs to use vfs_fs_type_find instead of hardcoded strcmp
- Added vfs_superblock_t structure with fstype, bdev, lba, and private_data
- Added sb field to vfs_mount struct
- Updated vfs_mount_nolock_full to accept and store vfs_superblock_t*
- Updated all callers to pass NULL for sb parameter (virtual FS don't use it yet)

26 hours agovfs: Fase 2 - block device abstraction and refcount
Tulio A M Mendes [Mon, 25 May 2026 16:24:15 +0000 (13:24 -0300)]
vfs: Fase 2 - block device abstraction and refcount

- Modified init_mount_fs to accept block_device_t* instead of int drive
- Updated sys_mount, init.c, kconsole.c to use blockdev_find instead of ata_name_to_drive
- Registered virtio-blk as block_device_t (/dev/vda) with blockdev ops wrappers
- Updated fat_mount and ext2_mount to accept block_device_t*
- Added refcount field to block_device_t
- Implemented blockdev_claim and blockdev_release
- Added bdev field to vfs_mount struct
- Integrated blockdev_claim/release in init_mount_fs and vfs_umount_nolock
- Updated vfs_mount_full and vfs_mount_nolock_full signatures to accept bdev
- Added blockdev.h include to fs.h

26 hours agokernel: hardening and cleanup fixes (Round 4)
Tulio A M Mendes [Mon, 25 May 2026 14:34:19 +0000 (11:34 -0300)]
kernel: hardening and cleanup fixes (Round 4)

- Rename socket_syscall_dispatch to extended_syscall_dispatch (handles
  MQ, SEM, DLOPEN, EPOLL, INOTIFY, AIO, MOUNT, etc., not just sockets)
- Implement POSIX saved set-user-ID / set-group-ID:
  - Add suid/sgid fields to struct process (at end to preserve offsets)
  - setuid(2): when root, sets uid/euid/suid; non-root can set euid
    to uid or suid (POSIX spec)
  - setgid(2): same pattern for gid/egid/sgid
  - seteuid(2): saves old euid to suid, allows switch to suid
  - setegid(2): saves old egid to sgid, allows switch to sgid
  - suid/sgid inherited on fork and clone
- Add SYSCALL_REBOOT (142): root-only, cmd 0=halt, 1=reboot,
  2=poweroff. Uses hal_system_reboot() and new hal_system_shutdown()
  (QEMU ACPI port 0x604 for poweroff)

Fixes: L04 (saved set-user-ID), L05 (reboot syscall), L06 (naming)
26 hours agokernel: POSIX compliance and robustness fixes (Round 3)
Tulio A M Mendes [Mon, 25 May 2026 13:31:24 +0000 (10:31 -0300)]
kernel: POSIX compliance and robustness fixes (Round 3)

Round 3 of the audit fix plan — medium-severity POSIX compliance fixes:

- ftruncate: reject O_RDONLY fd with EBADF (write permission check)
- truncate: check vfs_check_permission for write access (EACCES)
- O_EXCL: return EEXIST when O_CREAT|O_EXCL on existing file
- O_DIRECTORY: return ENOTDIR when O_DIRECTORY on non-directory
- posix_spawn: fix _syscall2→_syscall4 to pass all 4 args (pid_out,
  path, argv, envp) matching kernel handler expectations
- SYSCALL_MKDIR: accept mode argument from user space (passed through
  to syscall_mkdir_impl; VFS backends don't use it yet)
- CLONE_VM: add address-space refcount table to prevent use-after-free
  when thread group leader exits before threads. g_as_refcnt[] tracks
  refs per addr_space value; parent and child each hold a ref; last
  ref to exit destroys the AS.

Tests: 116/116 smoke, 142/142 battery, 111/111 host, cppcheck clean

26 hours agosecurity: high-priority kernel memory fixes (Round 2)
Tulio A M Mendes [Mon, 25 May 2026 12:38:59 +0000 (09:38 -0300)]
security: high-priority kernel memory fixes (Round 2)

A03: POSIX fd mode checks for read/write syscalls
- read: reject O_WRONLY fds (except char devices)
- write: reject O_RDONLY fds (except char devices)
- Fix pipe fd flags: read=O_RDONLY, write=O_WRONLY
- Fix socket fd flags: O_RDWR (bidirectional)

A06: POSIX permission checks for kill signals
- process_kill: sender must be root or same uid
- process_kill_pgrp: skip processes not owned by sender

SYSENTER: validate user ESP before dereferencing
- If ECX points into kernel space, zero args and return -EFAULT
- Prevents kernel data leak via malicious ESP

AIO/Socket: kernel bounce buffers for SMAP safety
- AIO read/write: copy_from_user/copy_to_user via kmalloc buffer
- Socket send/recv/sendto/recvfrom: same bounce buffer pattern
- Max bounce size 4096 bytes

vDSO: fix tick_hz mismatch
- Use TIMER_HZ (100) instead of hardcoded 50
- Add timer.h include

fulltest: fix fd open modes for POSIX compliance
- All O_CREAT|O_TRUNC opens now include O_WRONLY
- O_APPEND open includes O_WRONLY
- Overlay test opens with O_RDWR
- dup2 redirect test opens with O_RDWR
- tmpfs append test opens with O_RDWR

Tests: 116/116 smoke, 142/142 battery, 111/111 host, cppcheck clean

26 hours agosecurity: critical kernel memory isolation and W^X fixes (Round 1)
Tulio A M Mendes [Mon, 25 May 2026 00:53:58 +0000 (21:53 -0300)]
security: critical kernel memory isolation and W^X fixes (Round 1)

K01: mmap MAP_FIXED end address validation - prevent user from mapping
     across kernel boundary or overflow
K02: mprotect range kernel boundary check - reject ranges crossing into
     kernel space before permissive stack fallback
K03: shm_at address validation - check alignment and kernel boundary for
     user-supplied shmaddr, use vmm_find_free_area for auto-assigned
A01: NX flag preservation in COW - vmm_as_clone_user_cow and
     vmm_handle_cow_fault now preserve X86_PTE_NX to maintain W^X
     protection across fork and page fault resolution

Tests: 116/116 QEMU, 142/142 battery, 111/111 host, cppcheck clean

26 hours agovfs: block device layer, mount flags enforcement, refcount busy-checks, fstab options
Tulio A M Mendes [Mon, 25 May 2026 00:18:15 +0000 (21:18 -0300)]
vfs: block device layer, mount flags enforcement, refcount busy-checks, fstab options

Patch 1: Remove diskfs/persistfs source code from kernel
- Delete include/diskfs.h, include/persistfs.h
- Delete src/kernel/diskfs.c, src/kernel/persistfs.c
- Remove diskfs/persistfs auto-mount from init.c
- Remove diskfs/persistfs kconsole commands
- Remove diskfs.img creation from Makefile
- Remove diskfs/persistfs entries from rootfs/etc/fstab
- Update BUILD_GUIDE.md, README.md, docs/* to reflect removal

Patch 2: Clean fstab and Makefile
- Simplify rootfs/etc/fstab (remove diskfs/persistfs entries)
- Update fstab comments to reflect current mount layout
- Clean up Makefile (remove diskfs-related targets)
- Update documentation (BUILD_GUIDE.md, POSIX_ROADMAP.md)

Patch 3: Update fulltest, smoke_test, test_battery, statvfs for tmpfs ENOSYS
- fulltest.c: skip/rename/ftruncate tests now expect ENOSYS on tmpfs
  (tmpfs doesn't support these operations)
- statvfs.c: handle tmpfs fstype (report ~64MB blocks, zero free)
- smoke_test.exp: update patterns for tmpfs ENOSYS handling
- test_battery.exp: update patterns for tmpfs ENOSYS handling
- docs/TESTING_PLAN.md, SYSCALL_TEST_COVERAGE.md: update counts

Patch 4: Raw ATA block device test
- Add D7b test in fulltest that reads /dev/hda MBR sector (512 bytes)
- Non-fatal when /dev/hda absent (diskless boot)
- Add test patterns to smoke_test.exp and test_battery.exp

Patch 5: Generic block device layer
- New include/blockdev.h: block_device_t with ops->read/write,
  blockdev_register/find/by_id API, inline blockdev_read/write
- New src/kernel/blockdev.c: registry + ATA block device ops adapter
- Refactor fat.c: replace ata_pio_read28/write28 with blockdev_read/write
- Refactor ext2.c: same pattern via g_ext2.bdev
- init.c: call blockdev_register_ata() before mounting disk filesystems

Patch 6: Enforce mount flags (MS_RDONLY, MS_NOEXEC, MS_NODEV, MS_NOSUID)
- Add MS_* constants to fs.h (match userspace sys/mount.h values)
- Add vfs_mount_flags(), vfs_node_mount_flags(), vfs_find_mount_root()
- Add mount_root field to struct file (set on open, used for flag checks)
- syscall_open_impl: reject O_WRONLY/O_RDWR on MS_RDONLY (-EROFS),
  reject device open on MS_NODEV (-EACCES)
- syscall_write_impl: reject writes on MS_RDONLY mounts (-EROFS)
- syscall_execve_impl: reject exec on MS_NOEXEC mounts (-EPERM)
- Use MS_REMOUNT constant instead of hardcoded 0x20

Patch 7: Mount refcount and umount busy checks
- Add refcount field to vfs_mount struct
- vfs_umount_nolock rejects umount if refcount > 0 (-EBUSY)
- Add vfs_mount_ref/unref helpers, called on file open/close
- Fix uninitialized mount_root in console file setup (arch_platform.c)

Patch 8: Improve fstab options parsing and /proc/mounts
- init.c fstab parser now parses options field (ro,nosuid,nodev,noexec)
  and converts to MS_* mount flags
- vfs_mounts_read uses named MS_* constants instead of hardcoded bits

Tests: smoke 116/116, battery 142/142, host 111/111 — zero regressions

26 hours agomount/VFS: remove all auto-format paths; add mkfs; fix persistfs and flags
Tulio A M Mendes [Wed, 20 May 2026 17:06:48 +0000 (14:06 -0300)]
mount/VFS: remove all auto-format paths; add mkfs; fix persistfs and flags

Patch A (CRITICAL): diskfs_create_root() no longer auto-formats on
missing superblock.  Any error from diskfs_super_load() (ENODEV,
EINVAL, EIO) now returns NULL immediately.  g_ready is only set when
the superblock is valid.  This eliminates all destructive mount paths.

Patch B: Added 'mkfs diskfs /dev/hdX' kconsole command for explicit
diskfs formatting.  Added diskfs_mkfs() public API that wraps
diskfs_format().  This is the ONLY way to create a new diskfs
filesystem.  Battery/smoke tests now pre-format disk images with a
minimal diskfs superblock (magic+version+next_free_lba+root_inode)
at LBA 2 before QEMU launch.

Patch C (CRITICAL): persistfs_create_root() now calls diskfs_probe()
before diskfs_create_root().  If the drive does not contain a valid
diskfs superblock, the diskfs initialization is skipped entirely.
This prevents persistfs from triggering diskfs_format() on a FAT/ext2
disk via the indirect path: boot detects ext2 -> mounts /disk as ext2
-> init_mount_fs(persistfs) -> persistfs_create_root() -> [was]
diskfs_create_root() -> diskfs_format() -> corrupts ext2 superblock.

Patch D: init_mount_fs() now accepts unsigned long flags parameter
and passes it to vfs_mount_full().  sys_mount passes mount_flags
through to init_mount_fs.  Boot-internal callers pass flags=0.
This allows mount -o ro,nodev,noexec to work for disk-based FS.

Patch E: mount CLI improvements — unknown -o options now cause error
exit (not just warning).  Added nosuid, nodev, noexec option parsing.
show_mounts() now returns error code when /proc/mounts is unavailable.

Patch F: Updated all test disk images to use create_diskfs_disk
helper that stamps a minimal diskfs superblock.  Updated Makefile
run/test-gdb targets similarly.  Updated fstab comment.

26 hours agomount/VFS: make diskfs non-destructive; fix error propagation and syscall safety
Tulio A M Mendes [Wed, 20 May 2026 15:52:55 +0000 (12:52 -0300)]
mount/VFS: make diskfs non-destructive; fix error propagation and syscall safety

Patch 1 (CRITICAL): diskfs_super_load now returns -ENODEV on bad
magic and -EINVAL on unknown version instead of auto-formatting.
Added diskfs_format() as explicit separate function.  diskfs_create_root
preserves auto-format for blank disks (boot/battery test), but
autodetect probing uses new diskfs_probe() which is non-destructive.

Patch 2: Autodetect order changed from {diskfs,fat,ext2} to
{ext2,fat,diskfs}.  diskfs_probe() checked before diskfs_create_root
in autodetect path, preventing corruption of FAT/ext2 disks.

Patch 3: init_mount_fs returns negative errno (-EINVAL, -ENODEV)
instead of -1.  sys_mount passes through rc directly instead of
converting all errors to -EIO.

Patch 4: fulltest sys_mount wrapper now passes flags=0 as 4th
argument via esi register, preventing garbage MS_REMOUNT bits.

Patch 5: vfs_mount_nolock_full reordered to check existing entry
(remount) before table-full check, preventing -ENOSPC on remount.
All strncpy calls now have explicit NUL-termination.

Patch 6: mount userland requires -t fstype (no more diskfs default),
warns on unknown -o options.

Patch 7: Added copy_user_cstr() helper for byte-by-byte C-string
copy from userspace with -ENAMETOOLONG on overflow.  Replaces
fixed-size copy_from_user for mount syscall strings.

26 hours agoports: move bash from toolchain/ to ports/bash/
Tulio A M Mendes [Wed, 20 May 2026 15:44:02 +0000 (12:44 -0300)]
ports: move bash from toolchain/ to ports/bash/

- Create ports/bash/build.sh with full cross-compile logic (download,
  patch config.sub, config.cache, configure, build, install)
- Remove Step 5 (bash build) from toolchain/build.sh: SKIP_BASH,
  BASH_VER, BASH_URL, --skip-bash arg, patch_bash(), config.cache,
  configure, build, and summary reference
- Update ports/README.md to reference ports/bash/build.sh instead
  of toolchain/build.sh

26 hours agokernel: remove dead net_ping_test(), ring3 flag; fix CROSS toolchain override
Tulio A M Mendes [Wed, 20 May 2026 15:36:48 +0000 (12:36 -0300)]
kernel: remove dead net_ping_test(), ring3 flag; fix CROSS toolchain override

- Remove net_ping_test() declaration from include/net.h and gut the
  implementation in src/net/net_ping.c (~190 lines of dead code).
  ICMP ping is now tested in userspace via fulltest I7b (SOCK_RAW).
  File kept as empty placeholder for Makefile wildcard enumeration.

- Remove 'ring3' from known_flags in src/kernel/cmdline.c — the
  ring3 boot test was removed in a prior session and no code
  consumes this flag anymore.

- Add 'override' directive to CC/AS/LD in the CROSS block of the
  Makefile, ensuring the cross-toolchain is forced even if the
  variables are set in the environment or on the make command line.

2 weeks agosmp: print active CPU count for single-CPU boot
Tulio A M Mendes [Wed, 20 May 2026 12:07:37 +0000 (09:07 -0300)]
smp: print active CPU count for single-CPU boot

smp_start_aps() returned early without printing the 'CPU(s) active.'
message when g_cpu_count <= 1, causing the battery test SMP1 boot
pattern to fail. Now prints '[SMP] 1 CPU(s) active.' in that case.

Battery test: 148/148 PASS (was 147/148 with SMP1 boot FAIL)

2 weeks agokernel: add SOCK_RAW support and ICMP ping test
Tulio A M Mendes [Wed, 20 May 2026 12:00:45 +0000 (09:00 -0300)]
kernel: add SOCK_RAW support and ICMP ping test

- Add SOCK_RAW/IPPROTO_ICMP/IPPROTO_RAW constants to socket.h
- Extend ksocket struct with protocol field and raw_pcb pointer
- Add raw_recv_cb callback for receiving raw IP packets into ring buffer
- Extend ksocket_create/bind/send/sendto/recv/recvfrom/close/getsockname
  to handle SOCK_RAW using lwIP raw API
- Add O_NONBLOCK support to ksocket_recv (returns -EAGAIN instead of
  blocking forever) and merge fd flags in RECV/RECVFROM syscall handlers
- Add sys_sendto/sys_recvfrom wrappers and ICMP ping test (I7b) to fulltest
- Add ICMP ping test pattern to smoke_test.exp and test_battery.exp
- Migrate fstab parsing from kernel to userspace /sbin/init
- Auto-mount /disk and /persist in kernel init for init= binaries

2 weeks agovfs: mount/umount/df overhaul — fstype/source metadata, /proc/mounts, mount flags...
Tulio A M Mendes [Wed, 20 May 2026 03:36:16 +0000 (00:36 -0300)]
vfs: mount/umount/df overhaul — fstype/source metadata, /proc/mounts, mount flags, recursive mkdir-p, busy-check

Kernel changes:
- Extended vfs_mount struct with fstype[32], source[64], flags fields
- Increased mount table capacity from 16 to 32 entries
- Added vfs_mount_full/vfs_mount_nolock_full for metadata-aware mounts
- Added vfs_mounts_read() exposing mount table for /proc/mounts
- Added vfs_mkdirp() for recursive mkdir -p (auto-create mountpoints)
- Added busy-check in vfs_umount_nolock: reject if child mounts exist
- Fixed MS_REMOUNT: allow NULL root (update flags only on existing mount)
- Updated kernel boot-time mounts to pass fstype/source metadata
- Updated SYSCALL_MOUNT: parse flags, MS_REMOUNT support, vfs_mkdirp
- Updated SYSCALL_UMOUNT2: read flags arg (MNT_FORCE/MNT_DETACH)
- Updated pivot_root to use vfs_mount_nolock_full
- Added /proc/mounts procfs entry with proc_mounts_read handler

Userland changes:
- Rewrote statvfs: parses /proc/mounts with longest-prefix match,
  fstype-aware block count estimation (tmpfs/procfs/devfs/overlayfs)
- mount: added -o flag parsing (ro, rw, remount)
- umount: added -f (MNT_FORCE) and -l (MNT_DETACH) via umount2
- init: added is_mounted() check to avoid duplicate virtual FS mounts

Test results: 120/120 smoke, 152/152 battery, 111/111 host PASS

2 weeks agokernel+init: migrate /dev /proc /tmp mounts to userspace init
Tulio A M Mendes [Wed, 20 May 2026 02:39:58 +0000 (23:39 -0300)]
kernel+init: migrate /dev /proc /tmp mounts to userspace init

Add devfs, procfs, and auto-mkdir support to SYSCALL_MOUNT so that
userspace can mount virtual filesystems via the mount() syscall.

Userspace migration:
- init (user/cmds/init/init.c) now calls mount() directly to mount
  devfs on /dev, procfs on /proc, and tmpfs on /tmp before spawning
  the shell or running inittab entries.
- This mirrors how Linux init handles these mounts from userspace.

Kernel fallback:
- Kernel-side mounts for /dev, /proc, /tmp are kept as fallback for
  non-init boot paths (e.g. init=/sbin/fulltest). When /sbin/init
  runs, it re-mounts these; vfs_mount replaces existing entries so
  this is a harmless overlap.

Other changes:
- SYSCALL_MOUNT: add devfs and procfs as recognized fstype strings,
  with auto-mkdir of the mountpoint directory (like Linux mount(8))
- Makefile: add /etc/init.d/rcS to initrd (placeholder for future
  shell-script-based startup when shebang/-c support is added)
- rootfs/etc/init.d/rcS: placeholder script

4 weeks agokernel: add VFS spinlock (g_vfs_lock) to fix SMP mount-table races
Tulio A M Mendes [Mon, 4 May 2026 04:41:13 +0000 (01:41 -0300)]
kernel: add VFS spinlock (g_vfs_lock) to fix SMP mount-table races

The VFS mount table (g_mounts[], g_mount_count, fs_root) had zero
locking protection. On SMP (4 CPUs), concurrent access during
pivot_root/mount/umount2 corrupted the mount table, causing:
- Mount points disappearing: vfs_umount shifts array entries while
  another CPU's vfs_lookup iterates, skipping/duplicating entries
- Sporadic QEMU reboot: corrupted fs_node_t* pointer from torn
  reads causes kernel page fault -> triple fault -> reboot

Fix:
- Add spinlock_t g_vfs_lock in fs.c protecting fs_root, g_mounts[],
  and g_mount_count
- Create _nolock variants (vfs_mount_nolock, vfs_umount_nolock) for
  compound operations that need atomicity across multiple mutations
- Public vfs_mount/vfs_umount acquire the lock automatically
- vfs_lookup_depth snapshots mount table under lock before traversal
- SYSCALL_PIVOT_ROOT holds g_vfs_lock across the entire compound
  mutation (fs_root update + two vfs_mount_nolock calls)
- Export g_vfs_lock and _nolock variants in fs.h

Also:
- fulltest pivot_root test: undo pivot_root in child before exit,
  verify /dev/null accessible after undo
- ulibc strrchr: add null-pointer guard (cppcheck ctunullpointer)
- fulltest: struct init {0} instead of {{0}} (cppcheck uninitvar)

4 weeks agoFIX security audit: 9 vulnerabilities across kernel and userland
Tulio A M Mendes [Tue, 28 Apr 2026 02:49:18 +0000 (23:49 -0300)]
FIX security audit: 9 vulnerabilities across kernel and userland

CRITICAL:
- C3: heap.c corruption handlers — replace for(;;) infinite loop with
  schedule()+return, marking process ZOMBIE (exit_status=128)

HIGH:
- H1: syscall.c FCNTL_F_DUPFD_CLOEXEC — replace plain refcount++ with
  __sync_fetch_and_add for SMP atomicity
- H2: ulibc stdlib.c free() — add iteration limit (1024) and null-ptr
  guard to backward coalescing search to prevent infinite loop on
  corrupted next_free chain
- H3: ulibc stdio.c vfprintf — increase buffer 1024→4096, write full
  buffer on truncation instead of silently capping

MEDIUM:
- M1: sed.c parse_cmd — add regfree cleanup for addr1/addr2 regexes
  when parse_cmd fails after compilation
- M2: grep.c — fix -e option to extract pattern from same arg
  (-ePATTERN) or next arg (-e PATTERN)
- M4: syscall.c FUTEX_WAIT — accept timespec* timeout via arg3 instead
  of fixed 5000-tick (~100s); supports zero-timeout poll; update
  futex.h/futex.c/pthread.c/fulltest.c ABI

LOW:
- L1: dd.c parse_size — use long/strtol to prevent integer overflow on
  large values (e.g. bs=2048M)
- L2: init.c parse_inittab — warn when /etc/inittab exceeds 2047-byte
  buffer (truncation detection)

cppcheck:
- fulltest.c: initialize oldact struct to silence uninitvar warning

Tests: 120/120 QEMU, 152/152 battery, 111/111 host — zero regressions

4 weeks agodocs: update all documentation with recent kernel/userspace/test changes
Tulio A M Mendes [Mon, 27 Apr 2026 17:13:09 +0000 (14:13 -0300)]
docs: update all documentation with recent kernel/userspace/test changes

README.md:
- Host tests: 69 → 212 (test_utils 63, test_security 38, host_utils 111)
- Battery checks: 33 → 152 (120 smoke + SMP=1/2 + multi-disk + VFS + ping)
- GDB checks: 6 → 10 (PID state, scheduler bitmap, mount count, frame refcount)
- W^X description: updated for elf32_reprotect_segments() + boundary page handling
- tests/ directory description updated

TESTING_PLAN.md:
- All test counts updated
- Pure function list expanded with tar_parse_octal, mount prefix/normalize,
  vfs_check_permission, elf32_validate, signal mask logic, parse_symbolic
- Makefile targets updated

SYSCALL_TEST_COVERAGE.md:
- Date updated to 2026-04-27
- Battery: 33 → 152, Host: 69 → 212

POSIX_ROADMAP.md:
- Added host unit tests (212), test battery (152), GDB checks (10) rows
- Host utility test harness: 68 → 111

AUDIT_REPORT.md:
- Added post-audit fix table: ELF W^X reprotect, vfs_lookup_initrd,
  heap corruption handling, SHM UAF, procfs UAF, ext2 partial inode,
  rq_remove_if_queued, execve_copy_user_str, frame refcount overflow

TIER6_PLAN.md / FULL_POSIX_AUDIT.md:
- Test counts and commit references updated

4 weeks agocppcheck: fix overlayfs unreadVariable, scheduler duplicateCondition, syscall duplica...
Tulio A M Mendes [Mon, 27 Apr 2026 17:10:39 +0000 (14:10 -0300)]
cppcheck: fix overlayfs unreadVariable, scheduler duplicateCondition, syscall duplicateConditionalAssign

- overlayfs: remove dead 'written' assignment before early return
- scheduler: merge two consecutive if(current_process) blocks
- syscall: simplify addr_space assignment (condition was always true)
- test_utils: suppress unusedStructMember for ELF test struct fields

4 weeks agotests: expand host unit tests from 47 to 101 checks
Tulio A M Mendes [Mon, 27 Apr 2026 16:58:38 +0000 (13:58 -0300)]
tests: expand host unit tests from 47 to 101 checks

test_utils.c (28→63):
- tar_parse_octal: zero, simple, large, empty, null-term, non-octal skip
- path_is_mountpoint_prefix: root, exact, child, no-match, partial, empty
- normalize_mountpoint: root, simple, trailing-slash, empty, null, normalized
- vfs_check_permission: root, owner/group/other read/write, mode-0, 0644
- elf32_validate: valid ET_EXEC/ET_DYN, bad magic/class/type/machine,
  no phnum, truncated, null

test_security.c (19→38):
- Signal mask: valid excludes KILL/STOP, includes USR1,
  pending&blocked, deliverable/blocked, KILL/STOP always deliverable
- chmod symbolic: u+x, go-w, a+x, a=rw, u+s, g+s, +t, o-x,
  u=rw,go=r, no-change

gdb_checks.py (6→10):
- Process table PID 1 state check
- Scheduler active runqueue bitmap
- Mount table count
- Frame 0 refcount

Test results: 63/63 + 38/38 + 111/111 = 212 host PASS

4 weeks agotests: expand battery test from 33 to 152 checks
Tulio A M Mendes [Mon, 27 Apr 2026 16:48:37 +0000 (13:48 -0300)]
tests: expand battery test from 33 to 152 checks

Section 1 (Full smoke + Ping + diskfs, SMP=4):
- Add all 120 smoke test patterns (was only 24)
- Covers: boot, VFS, syscalls, IPC, signals, memory, diskfs, network

New sections:
- TEST 6: SMP=1 boot regression (12 checks)
  Verifies single-CPU boot, fork, signals, pipe, diskfs, network
- TEST 7: SMP=2 boot (6 checks)
  Verifies dual-CPU boot, CoW fork, parallel fork, diskfs, network

Existing sections 2-5 unchanged (multi-disk ATA detection).

Test results: 152/152 PASS

4 weeks agotests: update host utility tests for new features; fix chmod symbolic modes and grep -l
Tulio A M Mendes [Mon, 27 Apr 2026 16:40:36 +0000 (13:40 -0300)]
tests: update host utility tests for new features; fix chmod symbolic modes and grep -l

Host utility tests (tests/test_host_utils.sh):
- Add getdents shim for host builds (glibc only exposes getdents64)
- Add _DEFAULT_SOURCE to CFLAGS for BSD extensions (DT_DIR, etc.)
- Enhanced grep tests: -i (case-insensitive), -l (list files), -q (quiet), -E (extended regex)
- Enhanced sed tests: -n/p (suppress auto-print), d (delete), y (transliterate), line addressing
- Enhanced awk tests: BEGIN/END blocks, -v var=val, NR, NF
- Enhanced find tests: -name single file, -type f, -maxdepth, ! negation
- Enhanced dd tests: conv=ucase, count=1
- Enhanced rm test: -f flag
- Enhanced cp/mv tests: permission preservation
- New chmod tests: symbolic modes (u+x, go-w, a+x, a=rw)
- New stat tests: filename, type, mtime formatting
- New kill tests: -l signal listing, bad PID
- New ls tests: compilation and flag parsing (-l, -a, -n)
- New date tests: output format
- New du tests: single file
- New env tests: environment variable display
- New hostname tests: output
- New sleep tests: 1s delay
- New uptime tests: output

chmod (user/cmds/chmod/chmod.c):
- Fix symbolic mode parsing: who mask was incorrectly ANDed with perm bits,
  causing u+x to only add setuid+user-x overlap (0100) instead of user-x (0100).
  Now permissions are mapped per-who: u+x→0100, g+x→0010, o+x→0001, etc.

grep (user/cmds/grep/grep.c):
- Fix grep -l: was returning 1 (no match) immediately on first match without
  printing the filename. Now prints filename and returns 0 on match.

Test results: 111/111 host PASS, 120/120 QEMU PASS, 33/33 battery PASS

4 weeks agouserland: enhance 17 utilities and ulibc with POSIX features
Tulio A M Mendes [Mon, 27 Apr 2026 02:34:15 +0000 (23:34 -0300)]
userland: enhance 17 utilities and ulibc with POSIX features

ulibc:
- Add sys/statvfs.h and statvfs() implementation
- Extend time.h: strftime, localtime, gmtime, ctime, asctime, difftime
- Fix time_t conflict (was uint32_t in time.h, int32_t in sys/types.h)
- Add unistd.h include to statvfs.c for close()

High-priority commands:
- awk: BEGIN/END blocks, NF/NR vars, print with OFS, -v var=val, /bin/bash
- find: -mtime, -size, -perm, -user, -maxdepth, -delete, -exec
- chmod: symbolic mode support (u+x, go-w, a=rw)
- grep: BRE/ERE regex, -i, -r, -l, -q, -n flags
- sed: d/p/q/a/i/c/y commands, line/regex addresses, -n mode
- ps: BSD-style output with UID/PID/PPID/STAT/TIME/CMD
- top: system summary header, process list with -n iterations

Medium-priority commands:
- sh: command substitution $(...), tilde expansion ~, $$/$! vars,
  variable overflow protection
- cp/mv: preserve source file permissions via fstat
- rm: proper -rf recursive directory removal with getdents
- kill: -l flag to list signal names
- init: SIGUSR1/SIGUSR2 handlers for poweroff/reboot

Low-priority commands:
- stat: date/time display, UID/GID name lookup, file type
- dd: conv= (ucase/lcase/swab/noerror/sync/trunc), skip=, seek=
- ls: -n flag for numeric UID/GID

Tests: 120/120 QEMU, 33/33 battery, 57/57 host (3 skipped)

5 weeks agocppcheck: fix unreadVariable in elf.c and sync.c
Tulio A M Mendes [Mon, 27 Apr 2026 01:59:00 +0000 (22:59 -0300)]
cppcheck: fix unreadVariable in elf.c and sync.c

- elf.c: remove dead seg_vmm_flags computation from elf32_load_segments
  (re-protection is done by elf32_reprotect_segments after relocations)
- sync.c: eliminate unused deadline variable, inline into wake_at_tick

Tests: 120/120 QEMU, 33/33 battery, 69/69 host

5 weeks agokernel: security audit fixes — C1-C6, H3-H4, M5
Tulio A M Mendes [Mon, 27 Apr 2026 01:41:15 +0000 (22:41 -0300)]
kernel: security audit fixes — C1-C6, H3-H4, M5

Critical fixes:
- C1: procfs UAF — add g_pid_cmdline pool, stop overwriting g_pid_status
- C2: heap corruption — kill process + schedule() instead of infinite loop
- C3: ext2 consistency — write inode after each i_blocks increment
- C4: shm UAF — reject shm_at on IPC_RMID'd segments (-EIDRM),
  skip marked_rm segments in shm_get lookup
- C6: ELF W^X — parse p_flags for segment permissions, re-protect
  after relocations, only re-protect full pages within non-writable
  segments (partial pages may be shared with .data)

High fixes:
- H3: execve_copy_user_str — add upfront user_range_ok check
- H4: rq_remove_if_queued — scan all priority queues in both
  active and expired runqueues (not just current priority)

Low fixes:
- M5: frame_refcount uint16_t → uint32_t to prevent overflow

Also adds EIDRM/ENOMSG errno definitions.

Tests: 120/120 QEMU, 33/33 battery, 69/69 host — zero regressions

5 weeks agodocs: add SYSCALL_TEST_COVERAGE.md — detailed per-syscall test mapping and POSIX...
Tulio A M Mendes [Mon, 27 Apr 2026 00:54:42 +0000 (21:54 -0300)]
docs: add SYSCALL_TEST_COVERAGE.md — detailed per-syscall test mapping and POSIX gap analysis

5 weeks agodocs: update all documentation with current test coverage and POSIX gap analysis
Tulio A M Mendes [Mon, 27 Apr 2026 00:51:02 +0000 (21:51 -0300)]
docs: update all documentation with current test coverage and POSIX gap analysis

- Update test counts across all docs: 120 smoke tests, 33 battery, 69 host
- Update syscall count: 141 syscalls, 124/141 tested (87.9% coverage)
- Add syscall test coverage section listing 17 untested syscalls
- Add comprehensive POSIX gaps section for 100% compliance
- Categories: process/credentials, filesystem, signals, POSIX timers,
  IPC, memory, network, threads
- Mark madvise and execveat as implemented in FULL_POSIX_AUDIT.md

5 weeks agokernel: fix execve after pivot_root via vfs_lookup_initrd(); fork execve test
Tulio A M Mendes [Mon, 27 Apr 2026 00:28:10 +0000 (21:28 -0300)]
kernel: fix execve after pivot_root via vfs_lookup_initrd(); fork execve test

pivot_root changes the global fs_root and '/' mount entry to a tmpfs,
which breaks subsequent execve calls because vfs_lookup('/bin/echo')
fails in the new root.  This caused the 'echo execve' flaky test
failure (it always failed when run after the pivot_root test).

Fix: save the original initrd overlayfs root at boot and add
vfs_lookup_initrd() which does a direct finddir traversal from that
saved root, bypassing the VFS mount table.  Use it in:
- elf32_load_user_from_initrd (binary lookup)
- elf32_load_interp (ld.so lookup)
- elf32_load_shared_lib_at (libc.so lookup)
- syscall_execve_impl (early ENOENT check)

Also change the execve test to fork a child (like execveat), so PID 1
is not replaced and the test harness can verify the output.

Tests: 120/120 smoke, 33/33 battery, 69/69 host, cppcheck clean.

5 weeks agokernel: fix COW page write + signal delivery; add 6 new tests
Tulio A M Mendes [Sun, 26 Apr 2026 23:51:23 +0000 (20:51 -0300)]
kernel: fix COW page write + signal delivery; add 6 new tests

Kernel fixes:
- uaccess: x86_user_page_writable_user() now recognizes COW pages as
  logically writable (checks X86_PTE_COW flag bit 9).  Previously,
  copy_to_user() rejected writes to forked COW pages, preventing
  signal frame delivery after fork().
- idt: handle COW faults in kernel mode before uaccess_try_recover().
  A write from copy_to_user() to a COW page now triggers page fault
  resolution (private copy) instead of returning -EFAULT.
- scheduler: fork inherits sigactions and sig_blocked_mask from parent
  (POSIX requirement).  Pending signals stay 0 per POSIX spec.
- syscall: sigprocmask how values now match POSIX (0=BLOCK, 1=UNBLOCK,
  2=SETMASK).  sigsuspend no longer restores old mask before signal
  delivery, allowing the handler to run.

New tests (fulltest.c):
- I12: clone — CLONE_VM|CLONE_FS|CLONE_FILES|CLONE_SIGHAND thread
  creation with shared memory verification
- I13: sigqueue — send SIGUSR1 with value via sigsuspend
- I14: inotify_init1 — basic inotify with flags=0
- I15: dlopen/dlsym/dlclose — dynamic linker via libpietest.so
- I16: execveat — execute /bin/echo with AT_FDCWD
- I17: pivot_root — mount tmpfs, pivot, verify in isolated fork

Test harness updates:
- smoke_test.exp: +6 patterns (114→120)
- test_battery.exp: +6 patterns (27→33)

Results: smoke 119/120 (echo execve flaky), battery 33/33 PASS

6 weeks agofulltest: add 11 new POSIX syscall tests and wrappers
Tulio A M Mendes [Sun, 19 Apr 2026 22:36:29 +0000 (19:36 -0300)]
fulltest: add 11 new POSIX syscall tests and wrappers

New tests (I1-I12):
- geteuid/getegid: verify euid/egid match uid/gid for root
- seteuid/setegid: root can change and restore euid/egid
- CLOCK_MONOTONIC: monotonic clock advances after nanosleep
- chown: change file owner
- futex: FUTEX_WAIT returns EAGAIN on mismatch, WAKE returns 0
- sigaltstack: set/query/disable alternate signal stack
- socket API: TCP socket create/bind/listen/getsockname/shutdown
- mqueue: POSIX message queue open/send/receive/close/unlink
- named semaphore: sem_open/post/wait/getvalue/close/unlink
- getrusage: query RUSAGE_SELF (non-zero fields)
- mount/umount2: mount tmpfs, create file, unmount

New syscall wrappers added to fulltest:
sys_chown, sys_futex, sys_sigaltstack, sys_socket, sys_bind,
sys_listen, sys_accept, sys_connect, sys_send, sys_recv,
sys_shutdown, sys_getsockname, sys_mq_open/close/send/receive/unlink,
sys_sem_open/close/wait/post/unlink/getvalue, sys_getrusage,
sys_sigqueue, sys_mount, sys_umount2, sys_execveat

New constants: FUTEX_WAIT/WAKE, AF_INET, SOCK_STREAM, IPPROTO_TCP,
RUSAGE_SELF, struct sockaddr_in, plus enum values for all new syscalls

Updated smoke_test.exp and test_battery.exp to track new tests.
Test results: 114/114 QEMU, 27/27 battery, 69/69 host

6 weeks agofix select() timeout conversion — was passing raw pointer as kernel timeout
Tulio A M Mendes [Sun, 19 Apr 2026 21:30:12 +0000 (18:30 -0300)]
fix select() timeout conversion — was passing raw pointer as kernel timeout

The select() wrapper in both newlib/posix_stubs.c and ulibc/unistd.c
was casting the struct timeval* pointer directly to int and passing it
as the 5th syscall argument. The kernel expects an int32_t timeout:
  -1 = infinite wait, 0 = poll (return immediately), >0 = ticks.

When bash calls select() with timeout=NULL (infinite wait), the raw
pointer value was interpreted as a large positive number (poll with
timeout), not as -1 (infinite). Worse, on some code paths the pointer
could be 0 (NULL), which the kernel treats as timeout=0 (poll only),
causing select() to return 0 immediately with no fds ready — which
bash interprets as EOF on stdin, causing it to exit immediately.

Fix: convert struct timeval to int32_t ticks before passing to the
kernel. NULL timeout → -1 (infinite). tv_sec=0, tv_usec=0 → 0 (poll).
Otherwise convert ms → ticks (TIMER_HZ=100, 10ms/tick).

6 weeks agolibgloss: fix isatty() and ttyname() — use TCGETS instead of TIOCGPGRP with NULL
Tulio A M Mendes [Sun, 19 Apr 2026 21:05:46 +0000 (18:05 -0300)]
libgloss: fix isatty() and ttyname() — use TCGETS instead of TIOCGPGRP with NULL

isatty() in syscalls.c used ioctl(TIOCGPGRP, 0) which always failed
because the kernel rejects NULL user_arg with -EFAULT. This caused
bash to think stdin was not a terminal, running in non-interactive
mode (no prompt, no line editing).

Fix: use TCGETS (0x5401) with a stack-allocated termios struct instead.
TCGETS succeeds on any tty regardless of process group state — this
is the standard POSIX way to test for a terminal.

Also fix ttyname() in posix_stubs.c which had the same bug.

6 weeks agotoolchain: fix libgloss/adros build errors
Tulio A M Mendes [Sun, 19 Apr 2026 20:41:52 +0000 (17:41 -0300)]
toolchain: fix libgloss/adros build errors

- Add sys/uio.h compat header (struct iovec + readv/writev declarations)
  needed by posix_stubs.c which was missing from sysroot_headers
- Remove duplicate fcntl() definition in posix_stubs.c (line 541 was
  identical to line 256, causing redefinition error with newlib headers)
- Move libgloss/adros source sync outside patch_newlib() marker guard
  so edits to stubs are always picked up on rebuild (cp -f instead of
  cp -u); keep mkdir -p inside patch_newlib() for autoconf file creation

6 weeks agokernel: remove dead-code duplicate SETITIMER/GETITIMER from socket_syscall_dispatch
Tulio A M Mendes [Sun, 19 Apr 2026 19:16:01 +0000 (16:16 -0300)]
kernel: remove dead-code duplicate SETITIMER/GETITIMER from socket_syscall_dispatch

The SETITIMER and GETITIMER syscall handlers were implemented twice:
1. In syscall_handler (lines 3941/4005) — using proper POSIX struct
   k_itimerval with tv_sec/tv_usec fields and timeval_to_ticks/
   ticks_to_timeval conversion helpers.
2. In socket_syscall_dispatch (lines 4673/4729) — using raw uint32_t
   tick pairs, which would be incorrect if ever reached.

The socket_syscall_dispatch versions were dead code because the main
handler returns before falling through to socket dispatch. Removing
them eliminates confusion and prevents accidental use of the incorrect
tick-based format.

Part of POSIX compliance audit: all 141 SYSCALL_ enum values now
have exactly one handler each.

Tests: 103/103 QEMU, 16/16 battery, 69/69 host — zero regressions.

6 weeks agoPOSIX compliance audit: fix syscall wrappers, headers, and commands
Tulio A M Mendes [Sun, 19 Apr 2026 18:38:13 +0000 (15:38 -0300)]
POSIX compliance audit: fix syscall wrappers, headers, and commands

ulibc fixes:
- mkdir(): change from variadic to (path, mode_t) per POSIX
- stat/fstat/fstatat: use struct stat* instead of void*
- wait4: use pid_t return and struct rusage* parameter
- waitid: use siginfo_t* instead of void*
- lseek: return off_t instead of int
- pread/pwrite: return ssize_t, use off_t offset
- truncate/ftruncate: use off_t length
- chmod: use mode_t instead of int
- open(): extract mode from va_args (was silently ignored)
- openat(): extract mode and pass as 4th syscall arg
- Add pivot_root() wrapper (syscall 120 existed but had no wrapper)
- Add lstat() (delegates to stat, no symlinks in AdrOS)
- Add fchmod() stub (ENOSYS, no kernel syscall yet)

ulibc header fixes:
- unistd.h: add sys/types.h include, forward declare struct stat/rusage
- fcntl.h: add O_EXCL, O_NOCTTY, O_DIRECTORY, O_NOFOLLOW, AT_FDCWD,
  AT_SYMLINK_NOFOLLOW, AT_REMOVEDIR, AT_SYMLINK_FOLLOW, AT_EACCESS
- sys/stat.h: add lstat, fchmod declarations, fix mkdir/chmod types

newlib fixes:
- openat(): extract mode from va_args, pass as 4th arg (was _sc3)
- Add pivot_root() wrapper

command fixes:
- mkdir: pass mode 0755 to mkdir() calls (was calling with 0 args)
- stat: remove unnecessary void* cast
- df: replace hardcoded stub with /proc/mounts reader

Tests: 103/103 PASS, 16/16 battery, 69/69 host

6 weeks agoFix mount/umount commands: use proper syscall wrappers and error messages
Tulio A M Mendes [Sun, 19 Apr 2026 18:23:01 +0000 (15:23 -0300)]
Fix mount/umount commands: use proper syscall wrappers and error messages

mount.c: Replace direct _syscall3 call with mount() wrapper from
sys/mount.h. Print strerror(errno) instead of raw -1 return value
from __syscall_ret, which was always -1 and unhelpful for debugging.

umount.c: Replace stub that always printed 'operation not supported'
with a working implementation using the umount() wrapper from
sys/mount.h. The SYS_UMOUNT2 syscall existed in the kernel but the
umount command never used it.

Tests: 103/103 PASS

6 weeks agoComplete syscall coverage in ulibc and newlib wrappers
Tulio A M Mendes [Sun, 19 Apr 2026 18:12:28 +0000 (15:12 -0300)]
Complete syscall coverage in ulibc and newlib wrappers

Audit of all 141 kernel syscalls vs ulibc/newlib wrappers revealed
many missing implementations. Both libraries had syscall numbers
defined but no corresponding POSIX wrapper functions.

ulibc additions (unistd.c):
  - select, fcntl, rename (was stub returning -1), umask
  - dup3, openat, fstatat, unlinkat
  - mount, umount2, umount
  - wait4, waitid, sigreturn, sigqueue, set_thread_area

ulibc header updates:
  - unistd.h: declarations for all new functions
  - signal.h: union sigval, sigqueue, sigreturn declarations
  - sys/mount.h: new header with mount/umount2/umount + flags
  - Removed old rename() stub from stdio.c

newlib additions (posix_stubs.c):
  - Complete syscall number table (was 32 entries, now all 141)
  - All missing POSIX wrappers: fcntl, rename, rmdir, mkdir, unlink,
    dup3, openat, fstatat, unlinkat, mount, umount2, umount, wait4,
    waitid, sigreturn, sigqueue, set_thread_area, pread, pwrite,
    truncate, ftruncate, fsync, fdatasync, sigpending, sigsuspend,
    readv, writev, times, flock, setitimer, getitimer, link, symlink,
    readlink, pipe2, clock_gettime, gettimeofday, mmap, munmap,
    mprotect, madvise, getrlimit, setrlimit, getrusage, uname,
    brk, sbrk
  - Added includes: sys/mman.h, sys/resource.h, sys/utsname.h,
    sys/uio.h, sys/times.h, sys/wait.h

Fix: setitimer/getitimer signatures corrected from void* to
struct itimerval* to match sys/time.h declarations.

Tests: 103/103 PASS, 16/16 battery PASS, 69/69 host PASS.

6 weeks agoFix getcwd(NULL, 0): allocate buffer when buf is NULL (glibc extension)
Tulio A M Mendes [Sun, 19 Apr 2026 17:55:59 +0000 (14:55 -0300)]
Fix getcwd(NULL, 0): allocate buffer when buf is NULL (glibc extension)

Bash calls getcwd(NULL, 0) — a glibc extension where NULL means
"allocate a buffer for me". Both ulibc and newlib wrappers passed
NULL straight to the kernel syscall, which returned -EFAULT because
the kernel rejects user_buf == NULL.

This caused:
  shell-init: error retrieving current directory: getcwd:
  cannot access parent directories: Bad address

Now both wrappers handle NULL buf by malloc'ing a buffer (4096 bytes
if size is 0), passing it to the kernel, and returning the allocated
pointer on success (or freeing it on error). This matches glibc
behavior and unblocks bash and other ported software.

6 weeks agoFix getcwd: return char* (POSIX) instead of int
Tulio A M Mendes [Sun, 19 Apr 2026 17:38:54 +0000 (14:38 -0300)]
Fix getcwd: return char* (POSIX) instead of int

ulibc getcwd() returned int (0 on success, -1 on error) but POSIX
specifies char* (buf on success, NULL on error). Bash and other
ported software call getcwd expecting a pointer return; getting 0
(NULL) on success caused:

  shell-init: error retrieving current directory: getcwd:
  cannot access parent directories: Bad address

The kernel syscall already returns 0 on success, so the fix is in
the ulibc wrapper: return buf on success, NULL on error (setting
errno). This matches the newlib wrapper in posix_stubs.c which had
the correct signature all along.

Updated all internal callers (sh, pwd, realpath) from int-style
checks (>= 0 / < 0) to pointer-style checks (truthy / !ptr).

6 weeks agoFix initrd LZ4 OOM: use page-level allocation instead of kmalloc
Tulio A M Mendes [Sun, 19 Apr 2026 01:25:23 +0000 (22:25 -0300)]
Fix initrd LZ4 OOM: use page-level allocation instead of kmalloc

initrd_init used kmalloc() to allocate the decompression buffer for
LZ4-compressed initrd images. The kernel buddy heap is only 8 MB, so
a 4 MB allocation (the default content size) can easily fail due to
fragmentation from earlier allocations, producing:

  [HEAP] OOM: kmalloc failed.
  [INITRD] OOM decompressing LZ4 (4194304 bytes)

This was especially likely with custom initrd images compressed with
lz4 -1 or -9, which can declare larger content sizes.

Replace kmalloc(orig_sz) with initrd_alloc_pages(), which allocates
individual physical pages via pmm_alloc_page() and maps them into a
dedicated virtual region (0xD0800000, above the 8MB heap). This
bypasses the buddy allocator entirely, using the physical memory
manager directly. On OOM, already-mapped pages are rolled back.

Both LZ4 Frame and legacy LZ4B paths are updated.

6 weeks agoFix FAT/ext2 heap corruption: skip kfree on static root nodes
Tulio A M Mendes [Sun, 19 Apr 2026 00:17:24 +0000 (21:17 -0300)]
Fix FAT/ext2 heap corruption: skip kfree on static root nodes

fat_close_impl and ext2_close_impl unconditionally called kfree() on
the node passed to vfs_close(). When a directory fd pointed to a
filesystem mount root (a static variable like g_fat_root or
g_ext2_root), this kfree corrupted the heap (bad magic 0x0).

diskfs_close_impl already had the guard pattern (dn == &g_root).
Apply the same guard to fat and ext2 close handlers.

6 weeks agoFix job control TIOCSPGRP: allow session leader to claim TTY/PTY
Tulio A M Mendes [Fri, 17 Apr 2026 21:46:32 +0000 (18:46 -0300)]
Fix job control TIOCSPGRP: allow session leader to claim TTY/PTY

When fulltest runs from the shell, the TTY's tty_session_id is already
set to the shell's session. The test's leader child calls setsid() then
TIOCSPGRP, which fails with EPERM because the new session doesn't match
tty_session_id. The kernel never allowed a new session leader to claim
the TTY as its controlling terminal.

Fix: in TIOCSPGRP handler for both tty.c and pty.c, allow a session
leader (session_id == pid) to claim the terminal by updating the
session when it explicitly sets its own pgrp as foreground. This
matches POSIX semantics where a session leader may establish a new
controlling terminal.

Test results: 103/103 smoke test PASS, 16/16 battery PASS.

6 weeks agodiskfs: fix LBA space leak on unlink/rmdir + add spinlock protection + ATA DMA irqsave
Tulio A M Mendes [Fri, 17 Apr 2026 21:08:02 +0000 (18:08 -0300)]
diskfs: fix LBA space leak on unlink/rmdir + add spinlock protection + ATA DMA irqsave

Root cause: diskfs_unlink and diskfs_rmdir zeroed freed inodes but
never reclaimed LBA space -- next_free_lba only grew, never shrank.
After many test runs, next_free_lba exceeded the 8192-sector disk,
causing ata_pio_read28 to fail with -EIO on out-of-range LBAs,
making writes to newly created files fail silently.

Fixes:
- Add diskfs_reclaim_space() that recalculates next_free_lba by
  scanning all inodes for the highest used LBA extent. Called from
  both diskfs_unlink and diskfs_rmdir after freeing an inode.
- Add g_diskfs_lock spinlock with irqsave/irqrestore variants
  protecting all superblock load/modify/store operations across all
  diskfs functions. All early returns properly unlock.
- Restructure diskfs_read_impl and diskfs_write_impl to cache inode
  metadata under lock, then release before sector I/O, avoiding long
  interrupt-disabled periods that could cause deadlocks/timeouts.
- Replace spin_lock/spin_unlock with spin_lock_irqsave/spin_unlock_
  irqrestore in all 4 ATA DMA channel functions to prevent deadlocks
  from timer interrupts during lock hold.

Test results: 103/103 PASS (3 consecutive runs, SMP=4),
16/16 battery PASS, 69/69 host PASS.

6 weeks agoFix fulltest TIOCGPGRP failure when run from shell
Tulio A M Mendes [Fri, 17 Apr 2026 17:01:34 +0000 (14:01 -0300)]
Fix fulltest TIOCGPGRP failure when run from shell

When fulltest runs from /bin/sh (rather than as init directly), the
shell has already claimed the controlling TTY via setsid(), setting
tty_fg_pgrp to a non-zero value. The test assumed fg == 0, which
only holds when running as PID 1 (session_id=0, pgrp_id=0).

Fix: compare TIOCGPGRP result against sys_getpgrp() instead of
hardcoded 0, use our own pgrp for TIOCSPGRP success test, and use
fg=-1 (EINVAL) instead of fg=1 for the expected-failure case.

Also rename 'hello from init.elf' to 'hello from fulltest.elf' in
both the test binary and the smoke test expectations.

6 weeks agofix(mm): munmap/brk page leaks — free physical frames on unmap, rollback on partial...
Tulio A M Mendes [Fri, 17 Apr 2026 04:09:18 +0000 (01:09 -0300)]
fix(mm): munmap/brk page leaks — free physical frames on unmap, rollback on partial failure

1. munmap: vmm_unmap_page only clears PTEs without freeing physical
   frames. For anonymous mmaps (shmid == -1), call vmm_virt_to_phys +
   pmm_free_page before vmm_unmap_page. Device-backed/shared mappings
   keep their frames (managed by their own subsystems).

2. brk shrink: same leak — free physical frames before unmapping when
   the heap shrinks.

3. brk grow partial failure: if pmm_alloc_page fails mid-expansion,
   rollback all pages already mapped in this call (unmap + free), then
   return old heap_break. Previously these pages were leaked permanently.

4. mmap anonymous partial failure: same rollback pattern applied to
   syscall_mmap_impl — if pmm_alloc_page fails mid-allocation, unmap
   and free all pages already mapped before returning -ENOMEM.

All tests pass: 69/69 host + 103/103 QEMU, zero regressions.

6 weeks agofix: 3 residual bugs from round-3 audit
Tulio A M Mendes [Fri, 17 Apr 2026 03:54:10 +0000 (00:54 -0300)]
fix: 3 residual bugs from round-3 audit

5. dlopen page leak: if pmm_alloc_page() fails mid-segment-load,
   rollback all previously mapped pages (unmap + pmm_free_page)
   instead of leaking them. Added vmm_virt_to_phys() API to
   recover physical frames before unmapping.

6. CLONE_THREAD without CLONE_VM: Linux requires CLONE_THREAD to
   imply CLONE_VM (threads share address space). Now returns
   -EINVAL if CLONE_THREAD is set without CLONE_VM, preventing
   unexpected behavior where a "thread" gets its own AS copy.

7. pipe_close SMP race: readers/writers decrement and conditional
   kfree(ps) were unprotected by a lock. Added spinlock_t to
   pipe_state and wrapped the critical section in
   spin_lock_irqsave, preventing underflow/double-free when both
   pipe ends are closed concurrently on different CPUs.

6 weeks agofix: 4 security/robustness bugs from audit
Tulio A M Mendes [Fri, 17 Apr 2026 03:16:43 +0000 (00:16 -0300)]
fix: 4 security/robustness bugs from audit

1. epoll: remove fd from epoll instances on close() — prevents
   use-after-close where a reused fd number would monitor the
   wrong file. Also auto-remove stale entries in epoll_wait.

2. clone: validate flags against CLONE_SUPPORTED_MASK — unknown
   flags (CLONE_NEWPID, CLONE_NEWUSER, etc.) now return EINVAL
   instead of being silently ignored.

3. futex: cleanup waiters on process exit — futex_waiters table
   moved to file scope; futex_cleanup_process() called from
   SYSCALL_EXIT prevents UAF when FUTEX_WAKE dereferences a
   freed process pointer.

4. ksem: fix infinite spin when waiters array full — replace
   schedule() spin-yield with process_sleep(1), and increase
   KSEM_MAX_WAITERS/KCOND_MAX_WAITERS from 16 to 64.

7 weeks agofix(test): skip /persist/counter test when no disk is available
Tulio A M Mendes [Thu, 16 Apr 2026 05:42:54 +0000 (02:42 -0300)]
fix(test): skip /persist/counter test when no disk is available

Instead of failing with sys_exit(1) when /persist/counter can't
be opened, skip the test with goto. This allows the fulltest to
continue running other tests in environments without persistent
storage.

7 weeks agoperf(net): reduce ping test sleep times for faster test runs
Tulio A M Mendes [Thu, 16 Apr 2026 05:42:40 +0000 (02:42 -0300)]
perf(net): reduce ping test sleep times for faster test runs

QEMU virtual NIC doesn't need 2s to stabilize. Reduced initial
wait from 2s to 500ms and inter-ping delay from 1s to 250ms.

7 weeks agocleanup(idt): remove debug page fault traces for PID 1
Tulio A M Mendes [Thu, 16 Apr 2026 05:36:59 +0000 (02:36 -0300)]
cleanup(idt): remove debug page fault traces for PID 1

Removed debug kprintf statements that were added during the
execve/SIGSEGV debugging session: PDE/PTE dumps, CR3 comparison,
GOT dumps, and LAPIC ID traces. These are no longer needed now
that the root cause has been fixed.

7 weeks agofix(ulibc): add null-pointer guards in __libc_init_array/fini_array
Tulio A M Mendes [Thu, 16 Apr 2026 05:36:47 +0000 (02:36 -0300)]
fix(ulibc): add null-pointer guards in __libc_init_array/fini_array

In a PIC shared library build, linker-generated symbols like
__init_array_start, __fini_array_end, and _init may remain
SHN_UNDEF (value 0) when the library has no .init_array/.fini_array
sections. The GOT entries for these resolve to 0.

Added null-pointer checks using volatile pointers (to prevent the
compiler from assuming the address is always non-null) for all
init/fini array pointers and for _init/_fini function pointers.
This prevents crashes when these symbols are unresolved.

7 weeks agofix(elf): skip SHN_UNDEF symbols in GLOB_DAT/32 relocations
Tulio A M Mendes [Thu, 16 Apr 2026 05:36:15 +0000 (02:36 -0300)]
fix(elf): skip SHN_UNDEF symbols in GLOB_DAT/32 relocations

The kernel's elf32_process_relocations incorrectly resolved R_386_GLOB_DAT
relocations for SHN_UNDEF symbols (st_shndx == 0) by setting the GOT
entry to base_offset. For libc.so loaded at 0x11000000, this meant
symbols like __init_array_start, __fini_array_end, and _init were
resolved to 0x11000000 (the ELF header), causing __libc_init_array
to execute garbage code at the ELF header and crash with SIGSEGV
at eip=0x11000012.

Now, SHN_UNDEF symbols are set to 0 in the GOT, leaving them for
the dynamic linker to resolve. Defined symbols (st_shndx != 0)
continue to be resolved as sym->st_value + base_offset.

Also removed debug kprintf from elf32_load_needed_libs.

7 weeks agofix(ld.so): use correct AdrOS syscall numbers instead of Linux numbers
Tulio A M Mendes [Thu, 16 Apr 2026 05:35:55 +0000 (02:35 -0300)]
fix(ld.so): use correct AdrOS syscall numbers instead of Linux numbers

dbg_write() was using Linux syscall number 4 (write) in EAX, but
AdrOS's SYSCALL_WRITE is 1. This caused dbg_write to call open()
instead of write(), so ld.so debug output never appeared.

Also fixed the inline asm to properly declare EAX as an output
operand (int $0x80 returns the result in EAX), preventing potential
compiler misoptimization.

Removed all debug prints from _start_c and dl_fixup that were
added during debugging.

7 weeks agofix(execve): reset signal handlers and clear pending signals on execve
Tulio A M Mendes [Thu, 16 Apr 2026 05:35:36 +0000 (02:35 -0300)]
fix(execve): reset signal handlers and clear pending signals on execve

POSIX requires that execve resets all signal handlers to SIG_DFL
(except SIG_IGN) and clears pending signals. Without this, the
kernel could attempt to deliver a signal to a handler address in
the destroyed old address space, causing a crash.

Also removed debug trace code (ldso trace, write trace, exit trace,
PTE dumps, GOT dumps) that was added during debugging and was
interfering with test output.

8 weeks agofix: resolve all sparse static analysis warnings
Tulio A M Mendes [Mon, 6 Apr 2026 23:37:35 +0000 (20:37 -0300)]
fix: resolve all sparse static analysis warnings

- Replace plain integer 0 with NULL for pointer assignments, returns,
  and comparisons across 24 source files (kernel, drivers, HAL, arch, net)
- Add missing <stddef.h> includes for NULL in keyboard.c and uart.c
- Make file-local symbols static in idt.c (idt, idtp, interrupt_handlers,
  idt_set_gate, pic_remap, print_reg) and scheduler.c (ready_queue_tail)
- Add missing extern/prototype declarations to headers:
  idt.h (isr_handler), syscall.h (syscall_handler),
  process.h (ready_queue_head, sched_lock, sched_set_init_pid,
  sched_assign_pid1, sched_ap_init), net.h (net_dhcp_start),
  percpu.h (_percpu_gs_lut, percpu_get_ptr), smp.h (ap_sched_go,
  ap_entry), arch_syscall.h (x86_sysenter_set_kernel_stack,
  sysenter_init_ap), hal/usermode.h (x86_usermode_test_start),
  hal/cpu.h (g_smap_enabled), ata_pio.h (ata_register_devfs),
  boot_info.h (kernel_main)
- Add forward declarations for compiler ABI symbols in utils.c
- Include own headers in source files so sparse sees declarations
- Add lwIP/net include paths to SPARSE_FLAGS in Makefile

Verified: make check (0 warnings), make test-host (116/116 pass),
make test (102/103 pass, 1 pre-existing timeout)

8 weeks agofix(security): mq_receive_impl TOCTOU race — copy msg data under lock
Tulio A M Mendes [Sun, 5 Apr 2026 15:30:44 +0000 (12:30 -0300)]
fix(security): mq_receive_impl TOCTOU race — copy msg data under lock

Mirror the mq_send_impl fix: copy message data into a kernel buffer
while holding mq_lock, then copy_to_user after release. Without this,
another thread could overwrite the dequeued slot via mq_send before
the copy_to_user completes.

Found during deep re-scan. All tests pass (102/103, 1 pre-existing timeout).

8 weeks agofix(security): Red Team bug fixes + deep analysis hardening
Tulio A M Mendes [Sun, 5 Apr 2026 15:19:46 +0000 (12:19 -0300)]
fix(security): Red Team bug fixes + deep analysis hardening

Red Team report fixes (8 bugs):
- pmm_free_page: add rc==0 double-free guard with BUG log
- uaccess: move global state to per-CPU via GS segment (SMP safety)
- elf.c: add overflow checks for p_vaddr+base_offset and vaddr+p_memsz
- ksem_wait: retry loop on waiter-array-full instead of silent discard
- kstack: add free-slot recycling stack (256 entries) to prevent leak
- schedule(): fix fallback dequeue from active (not expired) after swap
- buddy_of(): add bounds check, kfree handles NULL return
- e1000: add compiler memory barriers to MMIO read/write helpers

Deep analysis fixes (4 additional bugs):
- readdir: fix strcpy buffer overflow d_name[24] from name[128] in
  tmpfs, devfs, initrd (use strncpy + null termination)
- mq_send_impl: fix TOCTOU race — copy user data to kbuf before lock
- dlopen: add 32-bit overflow checks for p_vaddr+base and vaddr+p_memsz
- tmpfs/overlayfs: replace unbounded strcpy into vfs.name[128] and
  symlink_target[128] with strncpy

Regression fix:
- percpu: add self-pointer as first field of percpu_data so that
  percpu_get() returns a valid pointer (was returning cpu_index=0 as
  NULL pointer on BSP, causing triple fault)

Verified: make ARCH=x86 builds clean, cppcheck+sparse pass,
102/103 smoke tests PASS (1 pre-existing timeout).

2 months agodocs: add submodule setup instructions, renumber BUILD_GUIDE sections
Tulio A M Mendes [Fri, 3 Apr 2026 20:07:49 +0000 (17:07 -0300)]
docs: add submodule setup instructions, renumber BUILD_GUIDE sections

- Add new section 2 'Getting the Source' with git clone --recursive
  and fallback 'git submodule update --init --recursive' instructions.
- Note that the build system auto-initializes submodules and applies
  patches if not done manually.
- Fix DOOM section: reference submodule init instead of manual git
  clone; simplify to 'make doom'.
- Renumber sections 3-8 to accommodate the new section.

2 months agofix(build): auto-init submodules, auto-apply lwIP patch, fix corrupt patch
Tulio A M Mendes [Fri, 3 Apr 2026 20:07:27 +0000 (17:07 -0300)]
fix(build): auto-init submodules, auto-apply lwIP patch, fix corrupt patch

The build system now handles submodules and patches automatically so
a fresh 'git clone' + 'make' works without manual steps:

- Makefile: add LWIP_SENTINEL target that runs 'git submodule update
  --init --recursive' if lwIP sources are missing.
- Makefile: add .lwip_patched marker target that applies the lwIP
  volatile patch (patches/lwip-tcpip-volatile.patch) once after
  submodule init. Kernel build depends on this marker.
- Makefile: clean target now removes .lwip_patched marker.
- patches/lwip-tcpip-volatile.patch: regenerated with correct hunk
  header (@@ -55,9 +55,9 @@ instead of broken @@ -55,8 +55,8 @@)
  and repo-root-relative paths (third_party/lwip/...).
- user/doom/Makefile: fix error message to reference 'git submodule
  update --init' instead of 'git clone'. Check for actual header file
  instead of just directory existence.
- .gitignore: add .lwip_patched marker, remove temp hello/hello.c.

2 months agofeat(toolchain): dual-libc support — ulibc default, Newlib optional
Tulio A M Mendes [Fri, 3 Apr 2026 19:53:38 +0000 (16:53 -0300)]
feat(toolchain): dual-libc support — ulibc default, Newlib optional

Redesign the toolchain build to safely support both ulibc (dynamic
linking) and Newlib (static linking) with a safe-by-default approach:

Architecture:
- adros.h now has Newlib-safe defaults (LIB_SPEC with -ladros, no
  dynamic-linker in LINK_SPEC) so a fresh 'git clone' + 'build.sh'
  always produces a working toolchain even without prior 'make iso'.
- Step 4b: generates specs from dumpspecs (Newlib-compatible).
- Step 4c: if ulibc build artifacts exist, re-patches specs for ulibc
  (removes -ladros, adds -dynamic-linker /lib/ld.so).
- Step 4d: creates newlib.specs + i686-adros-gcc-newlib wrapper for
  optional Newlib builds.

Bug fixes for fresh-clone reproducibility:
- Newlib build skip check changed from libc.a (overwritten by ulibc)
  to a .built marker file.
- Newlib headers/libs backed up unconditionally (before ulibc check)
  so newlib.specs always has valid include/newlib/ directory.
- Bash cross-compilation now uses newlib.specs explicitly (Bash needs
  Newlib's full POSIX libc: regex, locale, wchar).
- Wrapper script uses expanded paths instead of hardcoded GCC version.

Usage:
  i686-adros-gcc -o out in.c          # ulibc dynamic (default)
  i686-adros-gcc -static -o out in.c  # ulibc static
  i686-adros-gcc-newlib -o out in.c   # Newlib static

2 months agofeat(ulibc): add missing fdopen() and fileno() to stdio
Tulio A M Mendes [Fri, 3 Apr 2026 19:53:14 +0000 (16:53 -0300)]
feat(ulibc): add missing fdopen() and fileno() to stdio

Add POSIX fdopen() and fileno() implementations required by
popen/pclose and other stdio operations:
- fdopen(): wraps an existing file descriptor into a FILE*
- fileno(): returns the file descriptor from a FILE*

2 months agofeat(ulibc): add __libc_init_array/__libc_fini_array and update crt0.S
Tulio A M Mendes [Fri, 3 Apr 2026 19:52:58 +0000 (16:52 -0300)]
feat(ulibc): add __libc_init_array/__libc_fini_array and update crt0.S

Add constructor/destructor support to ulibc:
- New file init_fini.c: implements __libc_init_array() to run .preinit_array
  and .init_array global constructors, and __libc_fini_array() to run
  .fini_array global destructors.
- Updated crt0.S: calls __libc_init_array before main and __libc_fini_array
  after main returns (matching Newlib crt0 behavior). Also uses 'environ'
  symbol (POSIX) and adds .note.GNU-stack for NX compatibility.

2 months agofix(ulibc): rename __environ to environ for POSIX compliance
Tulio A M Mendes [Fri, 3 Apr 2026 19:52:43 +0000 (16:52 -0300)]
fix(ulibc): rename __environ to environ for POSIX compliance

Replace __environ with environ as the primary environment pointer
symbol throughout ulibc and userland commands. POSIX specifies
'environ' as the standard name (IEEE Std 1003.1).

Files changed:
- user/ulibc/src/stdlib.c: environ as primary symbol, update getenv()
- user/ulibc/src/environ.c: all references updated
- user/ulibc/src/execvp.c: pass environ to execve()
- user/ulibc/include/unistd.h: declare extern environ
- user/cmds/env/env.c: use environ
- user/cmds/printenv/printenv.c: use environ

2 months agofix: toolchain build reproducibility — header ordering, libcody, specs, GNU-stack
Tulio A M Mendes [Fri, 3 Apr 2026 15:11:39 +0000 (12:11 -0300)]
fix: toolchain build reproducibility — header ordering, libcody, specs, GNU-stack

- Move AdrOS sysroot headers installation to AFTER Newlib install so
  Newlib's make install doesn't overwrite our custom sys/dirent.h
  (fixes DIR unknown type error on first build)
- Move Newlib header patching (sys/stat.h, sys/signal.h, sys/wait.h,
  glob.h) to after Newlib install (fixes sed on non-existent files)
- Keep libcody in GCC host_libs — required by cc1plus (C++ frontend)
- Add all-libcody to step 4 make targets
- Move specs file creation to after step 4 (GCC full install overwrites it)
- Add .note.GNU-stack to crti-adros.S (fixes executable stack warning)
- Fix *.h glob in libgloss/adros cp (no .h files exist)
- Add toolchain/build/ and toolchain/logs/ to .gitignore
- Update README.md: clarify patches/ are reference diffs, add native-toolchain.patch

2 months agorefactor: build system improvements — ulibc out-of-tree, -Werror, noexecstack, doom...
Tulio A M Mendes [Sat, 14 Mar 2026 17:07:48 +0000 (14:07 -0300)]
refactor: build system improvements — ulibc out-of-tree, -Werror, noexecstack, doom target

1. ulibc out-of-tree build: .o files now go to build/$ARCH/user/ulibc/
   instead of in-tree user/ulibc/src/. BUILDDIR passed from root Makefile.

2. ulibc -Werror: added -Werror, removed -Wno-incompatible-pointer-types.
   Fixed warnings:
   - execve signature in unistd.h: const char* const* -> char* const* (POSIX)
   - execve impl in unistd.c, execle: match new signature
   - pthread.c: removed unused thread_entry_trampoline
   - socket.c: suppress unused addrlen in sendto/recvfrom
   - sh.c, env.c, init.c: remove stale const casts for execve

3. .note.GNU-stack: added -z noexecstack to linker flags in common.mk
   and doom Makefile. GNU_STACK segment now RW (not RWE).

4. make doom: new phony target in root Makefile builds doom via sub-Make,
   passing ULIBC_BUILDDIR. Doom LDFLAGS now use out-of-tree ulibc path.

5. which test: added positive test (which finds sh) alongside the existing
   negative test (which rejects missing cmd).

Tests: 103/103 smoke, 69/69 host utils, cppcheck clean

2 months agofix: doom build + move userspace build output to build/$ARCH/user/
Tulio A M Mendes [Sat, 14 Mar 2026 16:14:55 +0000 (13:14 -0300)]
fix: doom build + move userspace build output to build/$ARCH/user/

- Fix doom.elf build: force i686-elf toolchain (CC/LD := instead of ?=),
  add --unresolved-symbols=ignore-in-shared-libs for libc.so undefined refs
- Reorganize userspace build output from build/user/ to build/$ARCH/user/
  (e.g. build/x86/user/) so all arch artifacts live under one directory
- Sub-Makefiles (common.mk, fulltest, ldso, pie_test) now accept BUILDDIR
  override from root Makefile via ?= operator
- Root Makefile passes BUILDDIR=$(CURDIR)/$(USER_BUILD)/cmds/<name> to
  all sub-Make invocations

Tests: 103/103 smoke, 68/68 host utils, cppcheck clean

2 months agorefactor: reorganize userland into user/cmds/<name>/ with per-program Makefiles
Tulio A M Mendes [Sat, 14 Mar 2026 15:40:11 +0000 (12:40 -0300)]
refactor: reorganize userland into user/cmds/<name>/ with per-program Makefiles

- Move 53 user commands from user/<name>.c to user/cmds/<name>/<name>.c
- Add user/cmds/common.mk shared build rules for dynamically-linked commands
- Add per-program Makefiles for all commands (including fulltest, ldso, pie_test)
- Build all .o/.elf into build/user/cmds/<name>/ (out-of-tree)
- Replace [init] test prefix with [test] in fulltest.c, pie_main.c, test scripts
- Fix find.c and which.c: use opendir/readdir/closedir instead of raw getdents
- Fix ulibc glob.c missing stdio.h include
- Fix ulibc -Wno-incompatible-pointer-types for GCC 14+
- Fix test_host_utils.sh which test set -e issue
- Doom rootfs path changed to /usr/games/doom
- make clean now also cleans ulibc and doom in-tree artifacts

Tests: 103/103 smoke, 28/28 unit, 19/19 security, 68/68 host utils, cppcheck clean

2 months agodocs: mark Tier 6E (USTAR+LZ4 InitRD) as DONE — update all documentation
Tulio A M Mendes [Sat, 14 Mar 2026 14:11:44 +0000 (11:11 -0300)]
docs: mark Tier 6E (USTAR+LZ4 InitRD) as DONE — update all documentation

- TIER6_PLAN.md: 6E marked DONE (mkinitrd produces USTAR+LZ4, kernel parses both)
- POSIX_ROADMAP.md: USTAR initrd format added as feature 91, removed from remaining work
- SUPPLEMENTARY_ANALYSIS.md: boot flow updated to USTAR+LZ4, feature count 91
- README.md: feature count updated to 91, mkinitrd description updated

Tests: 103/103 smoke, 64/64 host, cppcheck clean

2 months agofeat: Tier 6F + 6G — minor POSIX gaps, Bash/Busybox port infrastructure
Tulio A M Mendes [Sat, 14 Mar 2026 13:58:06 +0000 (10:58 -0300)]
feat: Tier 6F + 6G — minor POSIX gaps, Bash/Busybox port infrastructure

Tier 6F — Minor POSIX gaps:
- madvise kernel syscall (no-op, always succeeds) + ulibc wrapper
- execveat kernel syscall (AT_FDCWD mode, delegates to execve) + ulibc wrapper
- mntent.h + mntent.c: setmntent/getmntent/addmntent/endmntent/hasmntopt
  with full /etc/fstab-style whitespace-delimited parsing
- utmp.h + utmp.c: setutent/getutent/getutid/getutline/pututline/endutent
  stubs for login record compatibility (Bash, w, who, login)
- Fix duplicate ssize_t typedef: stdio.h now includes sys/types.h instead
  of redefining ssize_t as 'long' (was conflicting with int32_t in types.h)
- MADV_NORMAL/RANDOM/SEQUENTIAL/WILLNEED/DONTNEED constants in sys/mman.h
- New smoke test: madvise (103/103 pass)

Tier 6G — Bash & Busybox port infrastructure:
- Bash: already integrated in toolchain/build.sh (cross-compiles with
  i686-adros-gcc, static, config.cache for cross-compilation)
- Busybox: ports/busybox/build.sh — download, configure, cross-compile
  script with adros_defconfig (~60 applets: ash, ls, cat, cp, grep, sed,
  awk, sort, ps, kill, mount, find, xargs, etc.)
- ports/busybox/adros_defconfig — minimal static config for AdrOS
- ports/README.md — documentation for building and installing ports

Tests: 103/103 smoke, cppcheck clean, 64/64 host tests

2 months agodocs: mark Tier 6A (Full SMP) as completed in TIER6_PLAN.md
Tulio A M Mendes [Sat, 14 Mar 2026 13:25:12 +0000 (10:25 -0300)]
docs: mark Tier 6A (Full SMP) as completed in TIER6_PLAN.md

2 months agofeat: full SMP scheduling — AP tick accounting, IPI wakeups, load balancing
Tulio A M Mendes [Sat, 14 Mar 2026 13:23:54 +0000 (10:23 -0300)]
feat: full SMP scheduling — AP tick accounting, IPI wakeups, load balancing

Scheduler changes (src/kernel/scheduler.c):
- sched_ap_tick(): per-CPU tick accounting for APs (utime, ITIMER_VIRTUAL,
  ITIMER_PROF) — previously only BSP tracked CPU time
- sched_load_balance(): periodic work stealing — migrates one process from
  busiest to idlest CPU when imbalance >= 2 (avoids ping-pong)
- IPI resched after sleep wakeups: process_wake_check() now sends IPI to
  remote CPUs that received newly-ready processes from the sleep queue
- IPI resched after parent wakeup: process_exit_notify() sends IPI when
  waking a parent blocked in waitpid on a different CPU
- Load counter (sched_pcpu_inc_load) added to wakeup paths that were
  missing it (exit_notify parent wake, sleep queue wake)

Timer changes:
- src/drivers/timer.c: hal_tick_bridge() now calls sched_ap_tick() on APs
  and sched_load_balance() on BSP every 10 ticks (~200ms at 50Hz)
- src/hal/x86/timer.c: APs now go through hal_tick_bridge instead of
  calling bare schedule(), enabling proper AP tick accounting
- Uses percpu_cpu_index() (GS segment read) instead of smp_current_cpu()
  (LAPIC ID linear scan) for faster CPU identification

Tests:
- New SMP parallel fork smoke test: forks 8 children with busy loops,
  verifies all complete with correct exit status (exercises multi-CPU
  scheduling, IPI wakeups, and load balancing)
- 102/102 smoke tests pass, cppcheck clean, 64/64 host tests pass

2 months agodocs: add Tier 6 implementation plan — SMP, multi-arch, Rump, HDA, Bash/Busybox
Tulio A M Mendes [Sat, 14 Mar 2026 12:58:41 +0000 (09:58 -0300)]
docs: add Tier 6 implementation plan — SMP, multi-arch, Rump, HDA, Bash/Busybox

Comprehensive plan for remaining large architectural features:
- 6A: Full SMP scheduling (per-CPU current, AP activation, load balancing)
- 6B: Multi-arch subsystems (ARM64/RISC-V/MIPS PMM+VMM+scheduler)
- 6C: Rump Kernel integration (USB, production drivers)
- 6D: Intel HDA audio driver
- 6E: USTAR initrd format
- 6F: Minor POSIX gaps (madvise, mntent, utmp)
- 6G: Bash & Busybox ports (all blockers resolved)

Recommended order: Bash/Busybox → minor gaps → SMP → Rump → USTAR → HDA → multi-arch

2 months agodocs: update FULL_POSIX_AUDIT.md — all Tier 1-5 items resolved
Tulio A M Mendes [Sat, 14 Mar 2026 12:56:49 +0000 (09:56 -0300)]
docs: update FULL_POSIX_AUDIT.md — all Tier 1-5 items resolved

- umount2 (syscall 138) marked implemented
- wait4 (syscall 139) marked implemented
- All 16 new ulibc headers marked implemented
- All missing functions in stdio/stdlib/string/unistd/termios marked resolved
- pthread sync primitives (mutex/cond/rwlock/key/once/barrier) marked implemented
- /etc/passwd and /etc/group parsing marked implemented
- ENOTSOCK/ENETUNREACH errno codes marked implemented
- Syscall count updated: 139 (138 entries), ~99% POSIX
- All Bash blockers resolved (getpwnam etc.)
- Busybox blockers reduced (getpwent/syslog now done)

2 months agofeat: implement POSIX Tiers 1-5 — complete ulibc headers, syscalls, and sync primitives
Tulio A M Mendes [Sat, 14 Mar 2026 12:54:07 +0000 (09:54 -0300)]
feat: implement POSIX Tiers 1-5 — complete ulibc headers, syscalls, and sync primitives

Tier 1: Add ENOTSOCK/ENETUNREACH errno codes (kernel + ulibc)

Tier 2: 13 new ulibc wrapper headers + source files:
  - sys/socket.h, netinet/in.h, arpa/inet.h, netdb.h, sys/un.h
  - sys/epoll.h, sys/inotify.h, dlfcn.h, spawn.h
  - semaphore.h, mqueue.h, aio.h, sys/shm.h
  - All with syscall wrappers matching kernel syscall numbers

Tier 3: Missing functions in existing headers:
  - termios: cfmakeraw, cfget/setispeed, tcdrain, tcflush, tcgetpgrp/tcsetpgrp
  - string: strsignal
  - stdlib: bsearch, div/ldiv, mkstemp, strtod/strtof
  - stdio: scanf, fscanf, tmpfile, tmpnam
  - unistd: execle, getlogin, confstr, sbrk, link/symlink/readlink wrappers
  - New syslog.h stub (openlog/syslog/closelog)

Tier 4: Kernel syscalls:
  - umount2 (SYSCALL_UMOUNT2=138) with vfs_umount in fs.c
  - wait4 (SYSCALL_WAIT4=139) wrapping process_waitpid + rusage

Tier 5: Medium implementations:
  - glob.h/wordexp.h with directory walk + fnmatch pattern matching
  - pthread_mutex/cond/rwlock/key/once/barrier (futex-based)
  - /etc/passwd + /etc/group file parsing with static root fallback

Build: make clean && make OK, cppcheck clean, 101/101 smoke tests pass