Skip to content

0.20.0 --- a context can say where it stands, so the C library stops refusing - #50

Merged
Sunrisepeak merged 2 commits into
mainfrom
openkal-0.15.0
Oct 2, 2026
Merged

Sunrisepeak merged 2 commits into
mainfrom
openkal-0.15.0

Conversation

@Sunrisepeak

Copy link
Copy Markdown
Member

A context can say where it stands, so the C library stops refusing

pthread_getattr_np answers with the region the calling thread is actually on, and refuses for any other thread. The specification half is mcpplibs/openkal#45; the design is in that repository's .agents/docs/2026-10-02-stack-bounds-of-a-context-design.md.

What this replaces

0.19.3 answered ENOSYS for every thread, because musl's own answer was wrong in both directions: for the first context it began at the auxiliary vector, which this port points at a static array, and found a bottom by growing a mapping the dispatcher refuses; for a started one it named the mapping pthread_create allocated, which kal_task_start supplies the stack instead of. A caller that trusted either walked off the stack it was on — WebAssembly Micro Runtime 2.4.5 did, and died with SIGSEGV inside wasm_runtime_init. That refusal was honest and was not an answer.

openkal 0.15 lets a running context say where it stands, so the port answers from it.

The refusal that remains is the shape of the interface, not a gap here

A context can only be asked about itself: a handle is meaningful only to the party that obtained it, and there is no handle to a context at all. So another pthread_t is refused — uniformly, never with a range — and that is also the answer an implementation beneath that cannot measure produces.

What is written to the attribute is musl's own arrangement

The stack address is the high end and the size the distance down from it, which is what pthread_attr_getstack subtracts to answer POSIX's lowest usable address. The guard size is zero because the region reported has no guard inside it, and on failure nothing is written at all — not even zeroed, because zero is a value this structure can legitimately hold and a caller that ignored the return would read "no stack recorded" rather than "this call did not answer".

The absent row is withdrawn, with its reasoning

[c-abi-absent] no longer carries pthread_getattr_np: no single form describes the call as a whole, since enosys would be false of the common spelling and accepted-no-effect false of the general case. The reasoning is left where the row was, and the README's table row with it. The assertion moved to the example that reads it rather than being dropped — tools/check-absent.sh still passes, with 24 rows.

Verification

examples/stack-bounds asserts containment — the region contains a local of the context that asked, for the first context and for a started one, and the two are not the same region — and asserts the refusal for another thread. Measured here on x86_64 Linux against openkal-linux 0.16.0:

stack bounds: first e=0 base=0x7fffac5e0000 size=3280896 contained=1
stack bounds: started e=0 base=0x7a2c82f0d000 size=262144 contained=1
stack bounds: another thread refused=1
-- failures: 0 --

The first context is reported a region containing its own stack, a started context the 262144-byte region kal_task_start allocated it, and another thread is refused. The continuous integration step greps for those three readings rather than for a number, because ENOSYS is 38 on Linux and 78 on Darwin and the step runs on both rows.

…refusing

0.19.3 answered `pthread_getattr_np` with `ENOSYS` for every thread, because
musl's own answer was wrong in both directions: for the first context it began at
the auxiliary vector, which this port points at a static array, and found a
bottom by growing a mapping the dispatcher refuses; for a started one it named
the mapping `pthread_create` allocated, which `kal_task_start` supplies the stack
instead of. A caller that trusted either walked off the stack it was on. That was
honest and was not an answer.

OPENKAL 0.15 LETS A RUNNING CONTEXT SAY WHERE IT STANDS, so the port answers with
the region the CALLING thread is actually on --- `kal_task_stack`, measured by
the implementation beneath --- and refuses for any other thread, uniformly and
never with a range. The refusal is not a gap left here: a context can only be
asked about itself, because a handle is meaningful only to the party that
obtained it and there is no handle to a context at all. `ENOSYS` is also what an
implementation that cannot measure produces, and it is what `getrlimit(RLIMIT_STACK)`
already answers here.

WHAT IS WRITTEN TO THE ATTRIBUTE IS MUSL'S OWN ARRANGEMENT: the stack address is
the high end and the size the distance down from it, which is what
`pthread_attr_getstack` subtracts to answer POSIX's lowest usable address. The
guard size is zero because the region reported has no guard inside it, and on
failure nothing is written at all --- not even zeroed, because zero is a value
this structure can legitimately hold and a caller that ignored the return would
read "no stack recorded" rather than "this call did not answer".

The `[c-abi-absent]` row is WITHDRAWN with its reasoning left where the row was,
and the README's absent-table row with it: no single form describes the call as a
whole, since `enosys` would be false of the common spelling and
`accepted-no-effect` false of the general case. The assertion moved to the
example that reads it rather than being dropped.

`examples/stack-bounds` now asserts CONTAINMENT --- the region contains a local of
the context that asked, for the first context and for a started one, and the two
are not the same region --- and asserts the refusal for another thread, whose
identifier is already joined and is compared rather than read. Numbers are not
compared: lengths and addresses are the system's, and containment is the property
both wrong answers violated.

Measured here on x86_64 Linux against openkal-linux 0.16.0: the first context is
reported a 3280896-byte region containing its own stack, a started context the
262144-byte region it was allocated, and another thread is refused.
@Sunrisepeak
Sunrisepeak merged commit 1c59573 into main Oct 2, 2026
5 checks passed
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.

1 participant