SchOS/include
Repository files (latest commit first)
Filename Latest commit message Latest commit date
Nexoniarz 3185685997 syscall: three globals on the entry path, shared by every CPU
One to four processes died per boot with a fault they could not have caused:
`rip 0x0` -- an instruction fetch at zero, which a process does not branch to
-- or a dereference of `0xffffffffffffffea`, which is `-EINVAL`. It hit shells
and schinitd alike and had been carried for a long time as "zsh sometimes jumps
into .data".

`syscall_entry` has to swap FS_BASE from the user's TLS to the kernel's before
it can touch any `%fs:` data, and `wrmsr` destroys rax, rcx and rdx. Those have
to be parked somewhere first -- not on the user stack, which is untrusted and
has not been switched away from yet. They were parked in three globals:

    // Single-CPU scratch for syscall_entry FS swap
    // (safe while IF cleared by SYSCALL/SFMASK)
    uint64 _sc_rax, _sc_rcx, _sc_rdx;

IF being clear answers for an interrupt arriving on *this* CPU. It says nothing
whatever about a second CPU executing the same three stores at the same
instant, and with more than one processor entering syscalls that is not a rare
interleaving -- it is the ordinary one.

**%rcx is the user return address.** SYSCALL puts the user's rip there and
SYSRET takes it back from there. So the CPU that lost the race returned its
process into *another process's* instruction stream, with %rax -- the syscall
number on the way in, the return value on the way out -- swapped alongside it.
Both symptoms fall straight out of that: a zero handed over and followed, and
an errno used as a pointer.

Per-CPU words at %gs:32..48 now. %gs is usable there, before the FS swap, for
exactly the reason RESTORE_KERNEL_FS can already read %gs:0: GS base is per-CPU
in hardware, seginit() sets it once, and userspace cannot change it --
sys_arch_prctl() implements ARCH_SET_FS and ARCH_GET_FS and rejects the rest.

Measured rather than argued. Both variants built once, `build/esp` saved for
each, the same pristine schfs.img restored before every boot, -smp 4:

    three globals     10 boots    10 faults, in five of the ten
    three per-CPU     18 boots     0 faults

A _Static_assert pins all seven per-CPU offsets, because trapasm.S spells them
as literals and cannot include a header -- a slot that moves would leave a
register quietly holding somebody else's value, which is the hardest possible
thing to find.

**This is the second bug of this exact shape in this file.** The first was
`kernel_fs_base`, a single global every CPU wrote during its own seginit(), so
the last writer won and every CPU restored one CPU's per-CPU block;
RESTORE_KERNEL_FS reads %gs:0 for that reason. The lesson generalises and is
now written down in both places: **on the entry path, a global is a bug.**

Closes the row added to Gate 0 four commits ago. It was the right row to put
first -- a compositor built on this would have failed in ways indistinguishable
from its own bugs.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-21 22:56:45 +02:00
..
asm.h Initial commit: Schunix as it stands before the SchOS rename 2026-08-09 13:47:01 +02:00
buf.h Raise the disk ceiling: 64-bit blocks, no FSSIZE panic, LBA48 2026-08-10 18:50:31 +02:00
config_sinit.h Initial commit: Schunix as it stands before the SchOS rename 2026-08-09 13:47:01 +02:00
date.h Initial commit: Schunix as it stands before the SchOS rename 2026-08-09 13:47:01 +02:00
dbg.h debugger: D5, the network transport -- and the plan closes 2026-09-14 15:33:53 +02:00
defs.h errno: -1 is EPERM, and four places were saying it by accident 2026-09-14 22:12:28 +02:00
device.h docs: four directories, and a rule for when a thing leaves the roadmap 2026-09-12 17:13:35 +02:00
display.h Initial commit: Schunix as it stands before the SchOS rename 2026-08-09 13:47:01 +02:00
dma.h dma: the 4 MB cap was the refusal, not the arena 2026-09-14 22:52:20 +02:00
elf.h Initial commit: Schunix as it stands before the SchOS rename 2026-08-09 13:47:01 +02:00
errno.h schfs: a format version policy, and refusing is not falling back 2026-09-12 17:13:59 +02:00
fb.h Rename Schunix to SchOS; the kernel becomes SchIFT 2026-08-09 14:09:59 +02:00
fcntl.h Rename Schunix to SchOS; the kernel becomes SchIFT 2026-08-09 14:09:59 +02:00
file.h fs: devtmpfs, so /dev is generated rather than written to a disk 2026-09-12 20:18:30 +02:00
font.h Rename Schunix to SchOS; the kernel becomes SchIFT 2026-08-09 14:09:59 +02:00
fs.h init survives stray signals; commands are 4-34x faster 2026-08-09 15:54:05 +02:00
fsstat.h fs: timestamps come from the clock, and /proc/fsstat says how full the tables are 2026-09-12 17:14:14 +02:00
gfx.h font: the console draws outlines now, and a byte could not have held Ą 2026-09-20 00:35:34 +02:00
input.h usb-hid: bind an absolute pointer, and plumb EV_ABS through 2026-08-10 18:27:01 +02:00
ioperm.h ldls: a second driver, and everything it found 2026-09-14 15:20:07 +02:00
irq.h hybrid: interrupts to userspace, and a crashed driver that stays crashed 2026-09-13 18:11:00 +02:00
kbd.h kbd: three layouts, and the arrow keys were sitting on top of UTF-8 2026-09-20 01:09:32 +02:00
keymap.h kbd: three layouts, and the arrow keys were sitting on top of UTF-8 2026-09-20 01:09:32 +02:00
klog.h debugger: D5, the network transport -- and the plan closes 2026-09-14 15:33:53 +02:00
logo.h docs: a branding folder, and the three marks are not interchangeable 2026-09-12 18:19:09 +02:00
memlayout.h Free every usable memory region, not one contiguous run 2026-08-09 20:03:51 +02:00
mmu.h Initial commit: Schunix as it stands before the SchOS rename 2026-08-09 13:47:01 +02:00
mp.h Initial commit: Schunix as it stands before the SchOS rename 2026-08-09 13:47:01 +02:00
netdev.h net: a frame wakes the stack, instead of waiting for the clock 2026-09-12 17:23:46 +02:00
netuser.h ldls: L4 was free after all, and the plan closes 2026-09-14 13:37:04 +02:00
param.h kstack: 3736 of 4096 bytes, measured, and the AP got a page it did not own 2026-09-21 21:05:15 +02:00
pci.h hybrid: the GPU driver now runs in userspace 2026-09-13 18:27:41 +02:00
pciuser.h hybrid: the GPU driver now runs in userspace 2026-09-13 18:27:41 +02:00
posix.h kbd: three layouts, and the arrow keys were sitting on top of UTF-8 2026-09-20 01:09:32 +02:00
proc.h syscall: three globals on the entry path, shared by every CPU 2026-09-21 22:56:45 +02:00
qr.h docs: four directories, and a rule for when a thing leaves the roadmap 2026-09-12 17:13:35 +02:00
sbuf.h debugger: D5, the network transport -- and the plan closes 2026-09-14 15:33:53 +02:00
schboot.h boot/uefi: the memory map the kernel gets is the one the firmware agreed to 2026-08-11 11:42:22 +02:00
schfs.h schfs: records at arbitrary offsets are packed, and the compiler is told 2026-09-12 17:56:36 +02:00
schinit.h hybrid: the driver is killed, and the driver comes back 2026-09-14 00:05:43 +02:00
sleeplock.h Initial commit: Schunix as it stands before the SchOS rename 2026-08-09 13:47:01 +02:00
socket.h debugger: D5, the network transport -- and the plan closes 2026-09-14 15:33:53 +02:00
spinlock.h Initial commit: Schunix as it stands before the SchOS rename 2026-08-09 13:47:01 +02:00
stat.h Initial commit: Schunix as it stands before the SchOS rename 2026-08-09 13:47:01 +02:00
syscall.h Initial commit: Schunix as it stands before the SchOS rename 2026-08-09 13:47:01 +02:00
text.h Initial commit: Schunix as it stands before the SchOS rename 2026-08-09 13:47:01 +02:00
theme.h gfx: the console and the crash screen stop drawing on a driver by name 2026-08-15 17:04:10 +02:00
traps.h Initial commit: Schunix as it stands before the SchOS rename 2026-08-09 13:47:01 +02:00
ttf.h Initial commit: Schunix as it stands before the SchOS rename 2026-08-09 13:47:01 +02:00
types.h Initial commit: Schunix as it stands before the SchOS rename 2026-08-09 13:47:01 +02:00
uaccess.h Rename Schunix to SchOS; the kernel becomes SchIFT 2026-08-09 14:09:59 +02:00
usb.h usb-hid: bind an absolute pointer, and plumb EV_ABS through 2026-08-10 18:27:01 +02:00
user.h sockets: a descriptor can cross between processes 2026-09-13 19:49:37 +02:00
virtio.h hybrid: a modeset, from a process 2026-09-14 01:36:05 +02:00
vt.h font: the console draws outlines now, and a byte could not have held Ą 2026-09-20 00:35:34 +02:00
x86.h schfs: the commit barrier, reflink, and attributes with an index over them 2026-08-15 16:02:02 +02:00