Skip to content

Commit 15f908e

Browse files
committed
Merge branch 'ralph/kernel-hardening'
2 parents c8096f7 + 5ee93ae commit 15f908e

59 files changed

Lines changed: 8288 additions & 903 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

packages/kernel/src/command-registry.ts

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,16 +13,30 @@ export class CommandRegistry {
1313
/** command name → RuntimeDriver */
1414
private commands: Map<string, RuntimeDriver> = new Map();
1515

16+
/** Warning log for command overrides. */
17+
private warnings: string[] = [];
18+
1619
/**
1720
* Register all commands from a driver.
18-
* Last-mounted driver wins on conflicts (allows override).
21+
* Last-mounted driver wins on conflicts (allows override with warning).
1922
*/
2023
register(driver: RuntimeDriver): void {
2124
for (const cmd of driver.commands) {
25+
const existing = this.commands.get(cmd);
26+
if (existing) {
27+
const msg = `command "${cmd}" overridden: ${existing.name}${driver.name}`;
28+
this.warnings.push(msg);
29+
console.warn(`[CommandRegistry] ${msg}`);
30+
}
2231
this.commands.set(cmd, driver);
2332
}
2433
}
2534

35+
/** Get recorded warnings (for testing). */
36+
getWarnings(): readonly string[] {
37+
return this.warnings;
38+
}
39+
2640
/** Resolve a command name to a driver. Returns null if unknown. */
2741
resolve(command: string): RuntimeDriver | null {
2842
return this.commands.get(command) ?? null;

packages/kernel/src/device-layer.ts

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,23 @@ export function createDeviceLayer(vfs: VirtualFileSystem): VirtualFileSystem {
8888
return vfs.readFile(path);
8989
},
9090

91+
async pread(path, offset, length) {
92+
if (path === "/dev/null") return new Uint8Array(0);
93+
if (path === "/dev/zero") return new Uint8Array(length);
94+
if (path === "/dev/urandom") {
95+
const buf = new Uint8Array(length);
96+
if (typeof globalThis.crypto?.getRandomValues === "function") {
97+
globalThis.crypto.getRandomValues(buf);
98+
} else {
99+
for (let i = 0; i < buf.length; i++) {
100+
buf[i] = (Math.random() * 256) | 0;
101+
}
102+
}
103+
return buf;
104+
}
105+
return vfs.pread(path, offset, length);
106+
},
107+
91108
async readTextFile(path) {
92109
if (path === "/dev/null") return "";
93110
const bytes = await this.readFile(path);
@@ -112,7 +129,7 @@ export function createDeviceLayer(vfs: VirtualFileSystem): VirtualFileSystem {
112129
},
113130

114131
async writeFile(path, content) {
115-
if (path === "/dev/null") return; // discard
132+
if (path === "/dev/null" || path === "/dev/zero" || path === "/dev/urandom") return; // discard
116133
return vfs.writeFile(path, content);
117134
},
118135

@@ -171,6 +188,7 @@ export function createDeviceLayer(vfs: VirtualFileSystem): VirtualFileSystem {
171188
},
172189

173190
async realpath(path) {
191+
if (isDevicePath(path) || isDeviceDir(path)) return path;
174192
return vfs.realpath(path);
175193
},
176194

packages/kernel/src/fd-table.ts

Lines changed: 27 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -22,20 +22,8 @@ import {
2222
/** Maximum open FDs per process before allocations are rejected (EMFILE). */
2323
export const MAX_FDS_PER_PROCESS = 256;
2424

25-
let nextDescriptionId = 1;
26-
27-
function createFileDescription(
28-
path: string,
29-
flags: number,
30-
): FileDescription {
31-
return {
32-
id: nextDescriptionId++,
33-
path,
34-
cursor: 0n,
35-
flags,
36-
refCount: 1,
37-
};
38-
}
25+
/** Allocator function that creates a FileDescription with a unique ID. */
26+
export type DescriptionAllocator = (path: string, flags: number) => FileDescription;
3927

4028
/**
4129
* FD table for a single process.
@@ -45,6 +33,11 @@ function createFileDescription(
4533
export class ProcessFDTable {
4634
private entries: Map<number, FDEntry> = new Map();
4735
private nextFd = 3; // 0, 1, 2 reserved
36+
private allocDesc: DescriptionAllocator;
37+
38+
constructor(allocDesc: DescriptionAllocator) {
39+
this.allocDesc = allocDesc;
40+
}
4841

4942
/** Pre-allocate stdin, stdout, stderr */
5043
initStdio(
@@ -93,7 +86,7 @@ export class ProcessFDTable {
9386
/** Open a new FD for the given path and flags */
9487
open(path: string, flags: number, filetype?: number): number {
9588
const fd = this.allocateFd();
96-
const description = createFileDescription(path, flags);
89+
const description = this.allocDesc(path, flags);
9790
this.entries.set(fd, {
9891
fd,
9992
description,
@@ -180,7 +173,7 @@ export class ProcessFDTable {
180173

181174
/** Create a copy of this table for a child process (FD inheritance). */
182175
fork(): ProcessFDTable {
183-
const child = new ProcessFDTable();
176+
const child = new ProcessFDTable(this.allocDesc);
184177
child.nextFd = this.nextFd;
185178
for (const [fd, entry] of this.entries) {
186179
entry.description.refCount++;
@@ -225,14 +218,24 @@ export class ProcessFDTable {
225218
*/
226219
export class FDTableManager {
227220
private tables: Map<number, ProcessFDTable> = new Map();
221+
private nextDescriptionId = 1;
222+
223+
/** Per-instance allocator bound to this manager's ID counter. */
224+
private allocDesc: DescriptionAllocator = (path, flags) => ({
225+
id: this.nextDescriptionId++,
226+
path,
227+
cursor: 0n,
228+
flags,
229+
refCount: 1,
230+
});
228231

229232
/** Create a new FD table for a process with standard FDs. */
230233
create(pid: number): ProcessFDTable {
231-
const table = new ProcessFDTable();
234+
const table = new ProcessFDTable(this.allocDesc);
232235
table.initStdio(
233-
createFileDescription("/dev/stdin", O_RDONLY),
234-
createFileDescription("/dev/stdout", O_WRONLY),
235-
createFileDescription("/dev/stderr", O_WRONLY),
236+
this.allocDesc("/dev/stdin", O_RDONLY),
237+
this.allocDesc("/dev/stdout", O_WRONLY),
238+
this.allocDesc("/dev/stderr", O_WRONLY),
236239
);
237240
this.tables.set(pid, table);
238241
return table;
@@ -249,18 +252,18 @@ export class FDTableManager {
249252
stdoutOverride: { description: FileDescription; filetype: number } | null,
250253
stderrOverride: { description: FileDescription; filetype: number } | null,
251254
): ProcessFDTable {
252-
const table = new ProcessFDTable();
255+
const table = new ProcessFDTable(this.allocDesc);
253256
const stdinDesc = stdinOverride
254257
? stdinOverride.description
255-
: createFileDescription("/dev/stdin", O_RDONLY);
258+
: this.allocDesc("/dev/stdin", O_RDONLY);
256259
const stdinType = stdinOverride?.filetype ?? FILETYPE_CHARACTER_DEVICE;
257260
const stdoutDesc = stdoutOverride
258261
? stdoutOverride.description
259-
: createFileDescription("/dev/stdout", O_WRONLY);
262+
: this.allocDesc("/dev/stdout", O_WRONLY);
260263
const stdoutType = stdoutOverride?.filetype ?? FILETYPE_CHARACTER_DEVICE;
261264
const stderrDesc = stderrOverride
262265
? stderrOverride.description
263-
: createFileDescription("/dev/stderr", O_WRONLY);
266+
: this.allocDesc("/dev/stderr", O_WRONLY);
264267
const stderrType = stderrOverride?.filetype ?? FILETYPE_CHARACTER_DEVICE;
265268

266269
table.initStdioWithTypes(stdinDesc, stdinType, stdoutDesc, stdoutType, stderrDesc, stderrType);

0 commit comments

Comments
 (0)