Skip to content

linux: signals recorded, sockets and poll served - #512

Closed
eKisNonos wants to merge 4 commits into
linux/threadsfrom
linux/signals
Closed

eKisNonos wants to merge 4 commits into
linux/threadsfrom
linux/signals

Conversation

@eKisNonos

Copy link
Copy Markdown
Contributor

Builds on #511. Two pieces: signal dispositions, and sockets with poll.
Signals are recorded and never delivered, and the code says so rather than hiding it behind a success. A runtime installs handlers before main and checks the return, so answering ENOSYS made programs abort at startup that would otherwise run to completion, because most of them never raise anything. rt_sigaction, rt_sigprocmask and sigaltstack now succeed and remember what was asked. Delivery means pushing a frame onto a guest thread's stack and redirecting it, and the trap mechanism hands out a register frame with no way to rewrite one, so it cannot be done from here yet. A program that depends on SIGALRM will hang rather than misbehave quietly, which is the failure you can diagnose. SIGKILL and SIGSTOP are refused as uncatchable, as Linux refuses them.
Sockets go over net.sockets, which already offers socket, connect, send, recv, close and a readiness poll, keyed by the caller's pid. That maps onto the Linux calls almost one to one. A guest's descriptor holds a handle the service issued to this capsule, so a guest reaches only the sockets this capsule opened for it. read and write route by descriptor kind, so a program that treats a socket as a file, which most do, works without knowing the difference, and closing the descriptor closes the handle behind it.
poll answers for every descriptor kind. A file or a console is always ready, which is what Linux reports for them. A socket is asked one handle at a time, because that is the shape of the readiness call the service serves; a batched form invented here would be a second protocol with nobody on the other end.
The net.sockets opcodes are transcribed rather than imported, because the server is a binary and its protocol module is not a library. The file they came from is named beside them, since a number that changes there and not here is a wrong operation rather than a failed one.
Not built, like the rest of this stack.

Zero capabilities was being treated as containment. brk, munmap and
the wayland shm path took a guest number and acted on it, so a guest
could unmap the personality or ask for a buffer no machine has. Limits
come from one address plan in guest/layout.rs now. They had already
drifted: only map.rs knew the mapping cursor's neighbour is EXEC_BASE
and not the stack.

Paths resolve to a Key only file::resolve can mint and the store
wrappers take nothing else, so /linux is the root by construction.

PT_INTERP and library mappings were loaded unproven, which left the
attestation gate on the main image doing nothing useful. Both proven,
ELF arithmetic checked. argv was read under MAX_PATH, so long
arguments failed execve with nothing saying why.

The resolver hands out 100.64/10 addresses and maps them back at
connect, and the installer runs over the mixnet. resolve_host in
net_sockets still does a clearnet lookup and is not fixed here.

Package bytes are still unauthenticated, so place_entry refuses to
vouch for them and the exec gate refuses what lands.

tar::entries stopped at the first end-of-archive block, which in an
apk is the end of the signature stream, so unpack had never written a
file. Mirrored in python: one stream, sig+ctl+data, unterminated
middle, garbage tail.
A runtime installs handlers before main and checks the return. Answering
ENOSYS made programs abort at startup that would otherwise have run to
completion, because most of them never raise anything.

rt_sigaction, rt_sigprocmask and sigaltstack now succeed and record what
was asked. Nothing is ever raised. Delivery means pushing a frame onto a
guest thread's stack and redirecting it, and the trap mechanism hands out
a register frame without any way to rewrite one, so it cannot be done
from here yet.

That limit is written in the file rather than hidden behind the success:
a program that depends on SIGALRM will hang rather than misbehave
quietly, which is the failure that can be diagnosed. SIGKILL and SIGSTOP
are refused as uncatchable, as Linux refuses them.
net.sockets offers socket, connect, send, recv, close and a readiness
poll, keyed by the caller's pid, which maps onto the Linux calls almost
one to one. A guest's descriptor now holds a handle that service issued
to this capsule, so a guest reaches only the sockets opened for it.

read and write route by descriptor kind, so a program that treats a
socket as a file, which most do, works without knowing the difference.
Closing one closes the handle behind it.

poll answers for every descriptor. A file or a console is always ready,
which is what Linux reports too. A socket is asked one handle at a time,
because that is the shape of the readiness call the service serves, and
inventing a batched form here would mean a second protocol with nobody
on the other end.

The opcodes are transcribed rather than imported: the server is a binary
and its protocol module is not a library. The file they came from is
named beside them, since a number that changes there and not here is a
wrong operation rather than a failed one.
supervised_asid returned an asid and dropped the lock that made it
true, so peer map, unmap, copy and protect all ran against an asid the
supervisor no longer held. It returns the guard with it now.

The entry stub saves the callee-saved five plus a pad so a forked
child resumes on the parent's register state. Nothing else on the path
writes them to memory and a handler's prologue may already be using
them, so it happens in the stub or not at all. Six slots keeps the
frame 16-byte aligned and leaves existing offsets alone.

libc gains wrappers for foreign exec, fork and resume, peer TLS and
unmap, and the local signing and consent calls.
@senseix21

Copy link
Copy Markdown
Collaborator

Merge blocker: symbols this PR needs are not defined anywhere.

This PR uses CryptoMachineKey, N_CRYPTO_MACHINE_KEY and request_install, but none of them are defined on main. They also do not appear in the diffs of the other open PRs checked so far (#505–#512, #535). The build fails with unresolved-symbol errors.

Could the author push the branch that defines them, or say which PR adds them? Until then this PR can't compile, even once its stack base (#506) has merged.

senseix21 added a commit that referenced this pull request Sep 26, 2026
linux: finish the stack (#532 #535 #512 + install queue)
@senseix21

Copy link
Copy Markdown
Collaborator

Landed on main through #559 (merge commit 31659011b), with #532 and #535. Every commit of this branch (head 747edf55c) is in main. GitHub can't mark this PR as merged because its base, linux/threads, is not main, so I'm closing it as landed. Boot verification is in #559 (comment)

@senseix21 senseix21 closed this Sep 26, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants