1.3.4
Nine commits since 1.3.3, and git diff v1.3.3 v1.3.4 -- src/ is 1,439
insertions across 10 files. One subject, and it is the one the whole of
src/linux/ was built to reach: an unmodified static binary, downloaded
from busybox.net and touched by nothing here, runs at ring 3 in a kernel with
one address space.
linux run /tmp/busybox uname -a
GLaDOS glados 1.3.3 one address space, no processes x86_64 GNU/Linux
linux run /tmp/busybox sha256sum /tmp/lines.txt
6ecb6686ad1673a0f3c021fc356890758853980fd6465b3b5a04fd859682733a
That digest is the one the host computes over the same file. The bytes went
from FAT into the namespace, out through the projection, through busybox's own
hash at ring 3, and back through writev. It is the first end-to-end check of
this path against an implementation nobody in this tree wrote.
Forty-six syscalls, and not one of them was guessed
Twenty-one became forty-six. Sixteen applets were swept, every gap they named
was implemented, and nothing else was. That is what stage 0 was for: Linux has
no specification you can test against, so the thing worth owning is a record of
what a real binary actually asks for.
The shape of the finding was the useful part. ls ran perfectly on its first
attempt and printed nothing at all -- it opened the directory, walked it with
two getdents64 calls, lstated every entry and exited 0, and everything it
had to say went through writev, which was answering -ENOSYS. A program that
works and is silent is the worst shape a missing syscall can take, and it is
exactly the shape the trace exists to make visible.
uname says GLaDOS, so a program that gates on "Linux" will find out.
Reporting the kernel this is not would buy compatibility with anything checking
the string and nothing else in this tree does that: the wireless driver refuses
to pretend it can associate, and the battery code refuses to invent a reading.
rt_sigaction and rt_sigprocmask are accepted and never deliver, which is
honest here because nothing can raise a signal at a guest -- no other process
to send one, no terminal to generate one, and a fault ends the guest rather
than being offered to it. nanosleep spins on the timer tick and says so,
because there is no guest scheduler to block against, so it costs the CPU it is
not using.
A guest may write, inside a jail
Writes were refused everywhere on the grounds that a write to a
content-addressed store is a new root hash, so an unrestricted O_WRONLY
routes a guest binary around every gate sysbox puts in front of the shell.
The reason was sound and what it argued for was a jail rather than a refusal.
cp /tmp/lines.txt /tmp/copy.txt then ls /tmp
busybox copy.txt lines.txt newdir
mkdir /ai/nope
mkdir: can't create directory '/ai/nope': Read-only file system
/tmp because that is already the scratch area, and everywhere else is
EROFS, checked on the resolved path so a relative one cannot be written
to climb out. Writes buffer and commit on the last close, because a store
keyed by content rewrites the whole blob and re-addresses it: a program writing
a kilobyte a byte at a time would otherwise leave a thousand objects behind.
dup shares an open file description now, the body having moved behind an
Rc<RefCell<..>>, so two descriptors share one cursor the way Linux promises.
It was refused before rather than got wrong, which was the right call and still
cost something: hexdump does dup2(fd, 0) to read its input as stdin and got
ENOSYS on a file it was holding open.
The environment was empty, and empty is not neutral. sh resolves through
PATH, and a program with no HOME writes its dotfiles into the working
directory. Five variables now, each a fact about this machine rather than a
plausible default: TERM=dumb because ioctl already says there is no
terminal, and PWD=/ because there is no chdir.
Placing an image where it insists
busybox's prebuilt is ET_EXEC with an entry at 0x4038b1, so it loads at
0x400000, and the loader refused every ET_EXEC outright. That refusal was
true about what the kernel knew rather than about the machine: nothing could
answer "does anything own four megabytes at four megabytes". Nearly every
prebuilt static binary in the world is non-PIE, so this was not a corner case,
it was most of the software the loader exists to run.
The frame allocator cannot answer it, and why is the interesting part.
EarlyFrames is a bump allocator whose cursor is forward-only by design, so by
the end of boot it sits past the heap three hundred megabytes up and everything
behind it reads as unavailable -- including large conventional regions it
merely stepped over while looking for one span big enough for the heap.
0x400000 is exactly such an address: untouched, and invisible to the only
thing you would think to ask.
So the free set is computed the other way round, as the firmware's conventional
regions minus what boot actually took. A bump allocator never frees, so its
history is a short list and it can record its handouts precisely. handouts()
answers None if one was ever dropped and the snapshot is skipped rather than
approximated, because a free set missing a taken range would place a guest on
top of the live page tables.
[boot] placeable 7 MiB free below the heap and above it, largest run 6 MiB
Seven megabytes, which is not much and is the honest number. Enough for
busybox at 0x400000 with room, and worth watching if anything larger turns
up.
Two things that were not the feature
The run deadline moved from five seconds to thirty because a correct program
met it. BusyBox printing its own applet list makes about seven hundred
write calls of a word each, every one painting the console, and it was killed
two thirds of the way through. Nothing was wrong with the guest and nothing was
wrong with the kernel. The harness was measuring the console.
One commit in this range says UNVERIFIED in its first line. It was written
under a power cut with the build still running, and the commit after it records
the result of actually booting the thing rather than leaving the warning
standing. Both are in the history on purpose.
A real bug, open, found by the release gate
diag paging followed by diag smp faults with a reserved bit set in a page
table entry. It needs the sweep run twice in one boot, which is why nothing
had seen it: nothing had ever run diag all twice in one boot, and the release
gate for this version is the first thing that did.
diag smp passed
diag paging passed
diag smp #PF, reserved bit set in a page table entry
Two things are ruled out and written down rather than suspected. It is not the
Linux work, since it reproduces with no guest ever loaded and the same sequence
passes on 1.3.3's code. And it is not a stale TLB or paging-structure cache on
the workers, since reloading CR3 where a worker takes a job changes nothing.
That leaves the entry genuinely corrupt in memory, with core 0 not faulting
only because its own TLB still holds the translation from before the
corruption. The CR3 reload was written, measured and reverted: keeping a
change whose comment claims a fix it does not deliver is worse than not having
it.
If you are running the sweep, run it once per boot until this is closed.
What is honestly still missing
fork, execve and wait4, which is to say sh running anything that is not
a builtin. That is not a syscall away: fork needs two address spaces, and one
address space is the founding claim of this system rather than a shortcut it
took. Nothing else in the measured surface is blocked on a decision that large.
Dynamically linked binaries are still refused, so this is static binaries only.
There is no chdir and no descriptor-relative openat. An open file holds its
whole contents in the heap, so a guest opening a very large file pays for all
of it. There are no permissions, owners or times: everything reports mode 0644
or 0755, uid 0 and a zero timestamp.
Verified
busybox 16 applets swept, then 10 run again after the work
hexdump, cat, sha256sum, tr, cp, wc, du, find, uptime, printf
all exiting 0; sysinfo the only call left unserved
fixed linux run /tmp/fixed at 0x400000, run twice in one boot
so the claim is released and retaken rather than leaking
diag linux 91 claims
diag place 9 claims
diag all 37 passed, 0 failed
Assets
glados-1.3.4.efi is the kernel image, and manifest plus
glados-1.3.4.efi.sig are what an installed machine verifies before it will
stage an update. The ISOs are install media and differ only in which checkpoint
they carry and its context window; --seq is a four-byte stamp and changes no
weight, so the two 2B images are the same weights stamped differently.
glados-1.3.4.efi 3,653,120 B
glados-1.3.4-qwen3-0.6b.iso 604,872,704 B
glados-1.3.4-qwen35-2b-8k.iso 1,898,938,368 B
glados-1.3.4-qwen35-2b-32k.iso 1,898,938,368 B
Check what you downloaded against SHA256SUMS, which CI wrote from the files
it built rather than from the files it uploaded, so the two are independent.