Decisions
0024: First-use malware check
Files are checked the first time they run, instead of a crawler that reads the whole disk.
Status: accepted. ADR 0028 changes how ClamAV and its data are used.
Context
WolOS needs a layer of protection against known malware at almost no cost. A scanner that walks the disk spends the machine's time on files that never change, and the sealed system needs no scanning at all. Files become interesting at one moment: when they arrive and first open.
Decision
- Check at first use.
crates/xguardholds fanotify permission marks only on the mounts where new programs arrive, and on files tagged as downloads. It never scans the sealed root. - The kernel is the verdict cache. A clean file gets an evictable ignore mark, which the kernel drops when the file changes. No kernel patch: measurement did not ask for one. eBPF LSM was not tried for the same reason.
- Verdicts survive restarts in a mapped store, keyed by file identity and sha256, or by fs-verity digest.
- Fail open. Only a signature match stops a file.
- An own matcher in a flat, mapped, shared file. Rule sets (YARA-X) run only in a short-lived unprivileged worker, because they cost 28 to 51 MB resident.
- The signature file format is ours (
.xgdb), built from public signature lists at release time and delivered signed, like other updates. - libclamav is not linked: 998 MB resident and 7 s to load the full database. Anything it reads that we do not is listed in
archicture/modules/security.md. - No file, hash or verdict leaves the computer. Sample submission does not exist.
- A small stable scan API for installers:
libxguard.so.1andwol-xguard scan --json. Archives are read in memory.
Measured (16 vCPU virtual machine, btrfs, 99 MB signature file, guard off and on in alternating rounds):
| What | Off | On |
|---|---|---|
| Matcher, 96,371 patterns (others: 49 and 68 MB/s) | 450 MB/s, 7 MB file | |
| Cached exec | 182.6 us | 182.8 us (within noise) |
open in scope | 1044 ns | 1065 ns |
stat | 590 ns | 590 ns |
| Game asset load, 30,000 files, 7.5 GB, cold | 11.52 s | 11.52 s |
| Kernel compile, cold caches, 3 rounds | 98.67 s | 99.64 s (+0.99%) |
| Idle memory, zero wakeups in 60 s | 8.2 MB resident |
A first run pays once: about 4 ms for a small static binary, 140 ms for 32 MB, 1 s for 256 MB. Up to 58 MB of the mapped file is resident for the 50 seconds after a scan. App launch was not measured on its own; a launch from cache is the cached exec above.
Consequences
- Signatures miss new malware. This is a layer of protection, and the copy never says more.
- Interpreters,
dlopen,memfdand packed Windows programs get past the first-use check. - Every open in scope pays about 20 ns while the guard runs.
- Redistribution terms of the signature data need checking before any public list ships in an update. Until then a system ships with its test signatures only.
Reference synced from wonderlandos/wolos docs/adr/0024-first-use-malware-check.md.