Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions build/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3461,6 +3461,7 @@ mod tests {
"tests/images/virtpaniccase.toml",
"tests/images/virtrebootcase.toml",
"tests/images/virtsmpcase.toml",
"tests/images/virttmpcase.toml",
];

fn load(cfg: &str) -> SystemConfig {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,12 @@ which 32 MiB may be dirty. The eighth is the bound netstack and the
compositor take for want of a kernel memory limit
(`issues/no-physical-memory-fairness.md`); it is an interim, not a sizing.

A pager also serves at most the objects it was made for: DATA one per 16
pages of its capacity, between 1,024 and 65,536, LOG and BOOT 1,024 each. So
the DATA server holds at most that many files' objects machine-wide; past it,
a file nobody holds goes to make room, and an open is refused `NoSpace` only
while every one is held open.
A pager's objects are charged as they are made. A server on a disk keeps at
most a count of files (`Files::opened` in `system/fileserver/src/pages.rs`):
DATA one per 16 pages of its capacity, between 1,024 and 65,536, LOG and
BOOT 1,024 each. Past it, a file nobody holds goes to make room, and an open
is refused `NoSpace` only while every one is held open. A memory volume keeps
no count (`issues/one-client-can-fill-the-tmp-servers-memory-and-deny-every-other-client.md`).

No other job can take a pager's clean pages back. A pager withdraws them
only for its own sake (`system/fileserver/src/pages.rs`, `Files::make_room`),
Expand Down
3 changes: 3 additions & 0 deletions issues/no-physical-memory-fairness.md
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,9 @@ supervisor's own memory, which closes it (below).
instead. Owner: the supervisor track. Exit: a start refused for
the supervisor's own memory waits on the supervisor's own job's readiness,
as one refused for the service's waits on the service's.
- **A client's `/tmp` files are counted to the file server that holds them**,
so the job a person would stop for them is a service every client needs
(`issues/one-client-can-fill-the-tmp-servers-memory-and-deny-every-other-client.md`).
- **Connect-time capacity.** Both rings of a connection are its connecting
job's, allocated at each one's first write, so a server's reply spends
nothing of its own; but a client's sends are not yet capacity its receiver
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
---
status: open
kind: defect
opened: 2026-11-12
severity: [security]
---

# One client can fill the `/tmp` server's memory and deny every other client

A memory volume's files are bounded only by what its server's job can be
charged (`pager` in `system/fileserver/src/main.rs`). Everything a `/tmp` file
costs is the MEMORY file server's:

- its object's slot in the kernel (`kernel/src/object/paged.rs`);
- the server's own record of it;
- its pages, up to the pager's capacity.

A directory or a link there costs the server's record alone. None of it is
charged to the client that made it, and nothing bounds one client's part. So
one client can make files, directories and links in `/tmp` until the memory is
spent. Then every other client's create there is refused. The server's next
allocation of its own may be among the refused, which ends it, and with it
every client's `/tmp`. The memory is counted to the file server, so nothing
names the client that took it (`issues/no-physical-memory-fairness.md`).

**What one client leaves charged.** The kernel makes a pager's object slots
256 at a time and keeps every piece for the pager's life (`kernel/pure/slots.rs`).
A freed slot is taken again, but its piece never goes back. A slot is 72
bytes: a 56-byte entry and a 12-byte mark, measured with a host replica of
the layouts:
https://github.com/ToyOSOrg/evidence/tree/f81e4b2ddb7d89a92f82f9eb8da1fdf58587a563/pr-1273/sizes
So the server's job stays charged 72 bytes for each `/tmp` file of the most
it held at once, until the server ends. A client that makes and removes a
million files leaves about 72 MB. The manual has unused backing return under
pressure; this never returns.

Owner: the file server and the kernel's paged objects, once the owner has
decided the charge below.

**Exit**:

- **The charge, proposed and the owner's to decide.** What a server keeps for
one client is charged to that client's job: a pager's object, and the pages
supplied to it, are charged to a job the client's grant names, which the
kernel lets the server charge by that authority. This changes the manual's
rule that services own their private state. The alternatives are a
per-share allowance in the server, which is a fixed part again, and leaving
the charge with the server.
- **The slots.** A piece whose every slot is free goes back to the heap, and
its charge with it. The free list takes the lowest free slot first, so the
highest pieces drain. A host test of `Slots` shows a table that held many
slots and let them go holding one piece's charge.
1 change: 1 addition & 0 deletions kernel/pure/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ pub mod pipe;
pub mod pointers;
pub mod rmrr;
pub mod sched;
pub mod slots;
pub mod stackslots;
pub mod vm;
pub mod wx;
Expand Down
Loading