init: a protocol you were not granted does not exist
The registry consults the open rows it has been parsing since P2, so reaching a contract now takes a grant as well as a binding. A caller without one is answered exactly as it would be for a name nobody ever bound: same status, same empty reply, same absent capability, byte for byte, and no log line on either path — klog_read is ungated, so a line on one and not the other would be the oracle the design set out to remove. Refusal and absence being one answer is what lets a supervisor later narrow, fake or park a child's namespace without the child learning what it was denied. The manifest gains a third permission for a shape the plan did not foresee: attestation is one hop, but the driver tree is three deep — the PS/2 keyboard and mouse are spawned by ps2-bus, which the device manager spawned — so no row could name them and PS/2 input would simply stop. lets a delegate vouch for what its children *reach*, never for what they claim; the bind path is untouched, and the laundering deputy is still refused. The review found the receive side of a rule this track had already written down. Every process holds a sendable handle to the registrar — resolve installs one for anyone who asks — and ipc_reply_wait never asked who owned the endpoint, so a stranger could dequeue there: take the provider endpoints riding bind requests, and answer other clients' opens in the registrar's name. Receiving is the owner's privilege, like binding a signal or a timer; sending remains anyone's. Suite 109/109.
This commit is contained in:
@@ -1,9 +1,17 @@
|
||||
# /system/configuration/protocol.csv — who may claim, and who may reach, a name
|
||||
# under /protocol (docs/os-development/protocol-namespace.md).
|
||||
#
|
||||
# init is the registrar: it serves /protocol, and every bind is checked against
|
||||
# this file. It is AUTHORITATIVE — a name no row grants cannot be bound, and a
|
||||
# missing file means nothing may be bound at all.
|
||||
# init is the registrar: it serves /protocol, and every bind AND every open is
|
||||
# checked against this file. It is AUTHORITATIVE — a name no row grants cannot be
|
||||
# bound or reached, and a missing file means nothing may be bound or reached at
|
||||
# all.
|
||||
#
|
||||
# A refused open is answered exactly as a name nobody bound is: -ENOENT, and no
|
||||
# capability. That is not politeness, it is the model — the namespace IS the
|
||||
# restriction, so what a process may not open simply does not exist for it, and
|
||||
# there is no "permission denied" for it to tell apart from "no such contract".
|
||||
# Which is why a missing row here shows up as a client retrying forever rather
|
||||
# than as an error: check this file first, and `readdir /protocol` second.
|
||||
#
|
||||
# '#' starts a comment (whole-line or trailing); blank lines are ignored.
|
||||
# Whitespace around a field is trimmed, so columns may be padded. Four
|
||||
@@ -26,13 +34,21 @@
|
||||
# confer; init's own path means this init; any other path means a
|
||||
# task init spawned itself or one the kernel spawned. Task ids are
|
||||
# monotonic and never reused, so an id cannot be borrowed.
|
||||
# permission bind (provide this contract) | open (speak to it)
|
||||
# permission bind (provide this contract) | open (speak to it) |
|
||||
# supervise (stand in someone else's chain — see below)
|
||||
# name the contract, relative to /protocol
|
||||
#
|
||||
# A trailing '*' on any field matches any tail — how a subtree is granted whole.
|
||||
#
|
||||
# NOTE: 'open' rows are parsed but not yet enforced; every open resolves today.
|
||||
# The milestone that turns them into refusals is P3 (docs/security-track-plan.md).
|
||||
# 'supervise' exists because attestation is one hop deep and the driver tree is
|
||||
# three: the device manager starts the PS/2 bus, and the bus starts the keyboard
|
||||
# and mouse drivers. Init never met the bus, so it cannot vouch for it by
|
||||
# acquaintance — and it must not vouch for it by name, or the laundering deputy
|
||||
# walks straight in. A 'supervise' row is the manifest saying it: a task running
|
||||
# this binary, under this supervisor, may be the supervising task an 'open' row
|
||||
# names, for this contract and no other. It grants the delegate nothing itself,
|
||||
# and it is deliberately open-only — a delegate may vouch for what its children
|
||||
# REACH, never for what they CLAIM, so every bind refusal is untouched by it.
|
||||
#
|
||||
# binary supervisor permission name
|
||||
|
||||
@@ -68,3 +84,72 @@
|
||||
# another fixture did.
|
||||
/test/*, kernel, bind, test/*
|
||||
/test/*, /test/*, bind, test/*
|
||||
|
||||
|
||||
# ============================================================================
|
||||
# open — who may REACH each contract. One row per client per contract; a client
|
||||
# with no row here simply finds the name absent, forever.
|
||||
# ============================================================================
|
||||
|
||||
# --- init's own services ----------------------------------------------------
|
||||
# fat reaches the block device behind the volume it mounts; the compositor
|
||||
# reaches the scanout its driver announced, its own endpoint (the mouse-listener
|
||||
# thread opens /protocol/display like any other client — threads share no
|
||||
# handles), and the input stream that moves the cursor.
|
||||
/system/services/fat, /system/services/init, open, block
|
||||
/system/services/display, /system/services/init, open, scanout
|
||||
/system/services/display, /system/services/init, open, display
|
||||
/system/services/display, /system/services/init, open, input
|
||||
/system/services/display-demo, /system/services/init, open, display
|
||||
|
||||
# --- the same two when the kernel test harness starts them directly ---------
|
||||
/system/services/display, kernel, open, scanout
|
||||
/system/services/display, kernel, open, display
|
||||
/system/services/display, kernel, open, input
|
||||
/system/services/display-demo, kernel, open, display
|
||||
|
||||
# --- the drivers, and the discovery service ---------------------------------
|
||||
# Every driver says hello to the manager that started it — one row for the whole
|
||||
# subtree, because that handshake is what being a driver means. The rest are per
|
||||
# driver: the storage and HID class drivers talk to their controller, the HID
|
||||
# drivers publish into the input stream, and the GPU driver announces its scanout
|
||||
# to the compositor.
|
||||
/system/drivers/*, /system/services/device-manager, open, device-manager
|
||||
/system/services/discovery, /system/services/device-manager, open, device-manager
|
||||
/system/drivers/usb-storage, /system/services/device-manager, open, usb-transfer
|
||||
/system/drivers/usb-hid-keyboard, /system/services/device-manager, open, usb-transfer
|
||||
/system/drivers/usb-hid-keyboard, /system/services/device-manager, open, input
|
||||
/system/drivers/usb-hid-mouse, /system/services/device-manager, open, usb-transfer
|
||||
/system/drivers/usb-hid-mouse, /system/services/device-manager, open, input
|
||||
/system/drivers/virtio-gpu, /system/services/device-manager, open, display
|
||||
|
||||
# --- the PS/2 child drivers, one hop further down ---------------------------
|
||||
# The keyboard and mouse drivers are started by the BUS driver, not by the
|
||||
# device manager — the one three-deep chain in the tree. Init cannot vouch for
|
||||
# the bus by acquaintance (it never started it), so the manifest authorizes it
|
||||
# explicitly, and only for the two contracts its children need.
|
||||
/system/drivers/ps2-bus, /system/services/device-manager, supervise, ps2-bus
|
||||
/system/drivers/ps2-bus, /system/services/device-manager, supervise, input
|
||||
/system/drivers/ps2-keyboard, /system/drivers/ps2-bus, open, ps2-bus
|
||||
/system/drivers/ps2-keyboard, /system/drivers/ps2-bus, open, input
|
||||
/system/drivers/ps2-mouse, /system/drivers/ps2-bus, open, ps2-bus
|
||||
/system/drivers/ps2-mouse, /system/drivers/ps2-bus, open, input
|
||||
|
||||
# --- test fixtures ----------------------------------------------------------
|
||||
# The /protocol/test subtree is theirs whole, the way the bind rows give it to
|
||||
# them. Everything ABOVE that subtree is named one fixture at a time, so a
|
||||
# fixture reaches a system contract only where a scenario needs it — which is
|
||||
# what leaves the rest genuinely absent for the rest of them (the protocol-denied
|
||||
# case asks for one it was not given, and is told there is no such thing).
|
||||
/test/*, kernel, open, test/*
|
||||
/test/*, /test/*, open, test/*
|
||||
/test/*, kernel, open, device-manager
|
||||
/test/*, /system/services/device-manager, open, device-manager
|
||||
/test/system/services/input-source, kernel, open, input
|
||||
/test/system/services/input-test, kernel, open, input
|
||||
|
||||
# The laundering-deputy probe (test/system/services/protocol-registry-test) runs
|
||||
# a grandchild whose supervisor is a fixture nobody authorized — that is the
|
||||
# point of it, and its bind must stay refused. It still has to report the verdict
|
||||
# it got, so its reporting channel, and nothing else, is delegated.
|
||||
/test/*, /test/*, supervise, test/verdict
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 12 and column 15.
|
Reference in New Issue
Block a user