exec took 32 arguments and 64 environment variables, in fixed arrays, so a glob over more than 31 files failed with E2BIG. It now reads the caller's argv and envp where they are, through the uaccess helpers and a bounce page -- a second thread rewriting the array after a check cannot slip a kernel address in -- and sizes the new stack to fit them. The limits are Linux's: ARG_MAX, 2 MB of strings and pointers, and 128 KB for any one string (USER_STR_MAX follows it). Every mmap took one of a process's 256 VMA slots, and zsh takes one per 16 KB heap block, so `x=($(<file))` over 30000 words ran out after 4 MB of heap and exited 1 without a word. Neighbouring anonymous mappings now share a record, as Linux merges them -- the bump allocator puts each new one where the last ended -- and munmap trims or splits it. Partial unmaps of device or segment mappings are refused with EINVAL instead of kfree()ing pages that are not the process's, and a failed allocation half way through a mapping no longer leaks what it had. Checked under QEMU: touch over 3000 names, /bin/echo with 50000 arguments, E2BIG past 2 MB, zsh splitting 100000 words, fork loops. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01S1giNvrHTrPMPw7V6x1kQ4
71 lines
3.7 KiB
C
71 lines
3.7 KiB
C
#pragma once
|
|
|
|
// The timer interrupt rate the kernel is built around. Everything that turns
|
|
// ticks into a duration divides by this. It used to be the literal 100 written
|
|
// out at each site, which made it look like a fact about the hardware; it is
|
|
// not. It is a target, and kernel/arch/x86_64/lapic.c calibrates the LAPIC
|
|
// timer against the PIT to actually hit it.
|
|
#define HZ 100 // timer interrupts per second
|
|
|
|
// The tick rate the *user-space ABI* is defined in, which is a different thing
|
|
// from HZ and must not follow it. glibc and musl both answer sysconf(_SC_CLK_TCK)
|
|
// with a hardcoded 100, so times() and /proc/stat are read as hundredths of a
|
|
// second no matter what the kernel's internal rate is. Linux calls this USER_HZ
|
|
// and scales at the syscall boundary; so does SchOS, in the two places below.
|
|
#define USER_HZ 100
|
|
|
|
// Converting a POSIX timespec/timeval into ticks. These used to appear as the
|
|
// bare literals 10000000 and 10000 at each call site, where they looked like
|
|
// magic numbers rather than what they are: the length of one tick, expressed
|
|
// in the units the caller happens to be using.
|
|
#define NSEC_PER_TICK (1000000000 / HZ)
|
|
#define USEC_PER_TICK (1000000 / HZ)
|
|
#define MSEC_PER_TICK (1000 / HZ)
|
|
|
|
#define NPROC 128 // maximum number of processes
|
|
#define KSTACKSIZE 8192 // per-process kernel stack; see kstacks[] in proc.c
|
|
#define KSTACK_CANARY 0xDEADC0DEDEADC0DEULL // sentinel at kstack[0], see kstack_check_canary()
|
|
#define NCPU 16 // maximum number of CPUs
|
|
#define NOFILE 128 // open files per process
|
|
#define NFILE 512 // open files per system
|
|
#define NINODE 200 // maximum number of active i-nodes
|
|
#define NDEV 13 // maximum major device number
|
|
#define RAMDISK_DEV 2 // memory-backed block device (kernel/dev/ramdisk.c)
|
|
#define USBDISK_DEV 3 // USB mass storage (kernel/drivers/usb_storage.c)
|
|
|
|
// Device the root filesystem is mounted from. Building with RAMROOT=1 defines
|
|
// RAMDISK_ROOT and boots from the SCHFS image linked into the kernel instead
|
|
// of the IDE disk, leaving the physical disk free to be reformatted from a
|
|
// running system. See kernel/dev/ramdisk.c.
|
|
#ifdef RAMDISK_ROOT
|
|
#define ROOTDEV RAMDISK_DEV
|
|
#else
|
|
#define ROOTDEV 1 // device number of file system root disk
|
|
#endif
|
|
// exec's limits, Linux's: ARG_MAX bytes of argument and environment strings
|
|
// and their pointers together, and no one string over ARG_STRLEN_MAX (Linux's
|
|
// MAX_ARG_STRLEN). There is no limit on the count beyond that.
|
|
#define ARG_MAX (2 * 1024 * 1024)
|
|
#define ARG_STRLEN_MAX (128 * 1024)
|
|
#define MAXPATH 128 // max path
|
|
#define MAXOPBLOCKS 30 // max # of blocks any FS op writes
|
|
#define LOGSIZE (MAXOPBLOCKS*3) // max data blocks in on-disk log
|
|
// Disk block cache, in blocks. 256 * BSIZE = 1 MB.
|
|
//
|
|
// Was MAXOPBLOCKS*3 = 90 blocks, and at the old 1 KB BSIZE that is a 90 KB
|
|
// cache -- smaller than the toybox binary that every single command execs. So
|
|
// nothing was ever still resident on the next command and each one paid the
|
|
// full read again. 1 MB holds toybox with room to spare, which is what makes
|
|
// the *second* command fast rather than just the first.
|
|
//
|
|
// Sized rather than maximised: these are statically allocated in bio.c, so
|
|
// this is memory the system always spends. 1 MB is affordable on a machine
|
|
// with tens of megabytes; 8 MB would not be.
|
|
#define NBUF 256
|
|
#define FSSIZE 150000 // size of file system in blocks
|
|
|
|
// Network interfaces the stack will attach. Two is already an ordinary machine
|
|
// -- a laptop with Ethernet plus a virtual adapter -- and each one costs a
|
|
// struct netif, so the limit is about keeping a fixed array honest rather than
|
|
// about memory.
|
|
#define NETIF_MAX 4
|