Skip to content

Commit d26936c

Browse files
fix(virtio-linux): add CONFIG_PROC_CHILDREN and fix broken kconfigs
1 parent 88a1113 commit d26936c

1 file changed

Lines changed: 241 additions & 67 deletions

File tree

packages/virtio-linux/build.sh

Lines changed: 241 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -44,106 +44,280 @@ fi
4444

4545
CFG="scripts/config --file .config"
4646

47+
# --- modules stay enabled, but we ship none ----------------------------------
48+
# We build only the image target (never `make modules` / `modules_install`), so
49+
# no .ko is ever produced or shipped; every driver we actually want is pinned
50+
# =y below and asserted after olddefconfig.
51+
#
52+
# Leaving MODULES=n would be the tidier invariant, but it is not free: when
53+
# modules are off, kconfig cannot leave a tristate at =m and promotes it to =y
54+
# instead. x86_64_defconfig has exactly one =m symbol so that was invisible
55+
# there — arm64's defconfig has 779, the whole SoC driver zoo, and every one of
56+
# them got compiled into a microVM guest kernel. That is what dragged in
57+
# drivers/gpu/drm/msm (which shells out to python3 to generate headers, absent
58+
# from build_deps) and blew up the arm64 build.
59+
#
60+
# With MODULES=y those 779 stay =m, and since we never invoke the modules
61+
# target they cost nothing: not built, not shipped, not in the image.
62+
$CFG --enable MODULES
63+
64+
# Module signing would make the build generate a fresh RSA keypair and embed
65+
# the cert in the image — a different vmlinuz every run. Nothing turns it on
66+
# today (MODULE_SIG has no `default y` and neither defconfig sets it), but it
67+
# is one stray `select MODULE_SIG if MODULES` away from silently breaking
68+
# reproducibility, so pin it off now that MODULES is back on.
69+
$CFG --disable MODULE_SIG
70+
71+
# `scripts/config --enable FOO` is a silent no-op in three cases: FOO isn't a
72+
# real symbol (typo, or upstream renamed it), FOO's dependencies aren't
73+
# satisfied, or FOO is a tristate whose dependencies are themselves only =m —
74+
# in which case our =y is invalid and kconfig quietly downgrades it back to =m,
75+
# which for us means "not in the shipped image". Either way `make olddefconfig`
76+
# rewrites the line without complaint. So route the must-haves through want()
77+
# and assert on the resulting .config below, instead of finding the hole from
78+
# inside a guest.
79+
REQUIRED=()
80+
want() {
81+
local sym
82+
for sym in "$@"; do
83+
$CFG --enable "$sym"
84+
REQUIRED+=("$sym")
85+
done
86+
}
87+
88+
# The mirror image: `--disable` is just as silent when something else turns the
89+
# symbol back on — a defconfig line, a merged fragment, or a `select` from a
90+
# driver we didn't think about. nope() records those so we can assert they
91+
# really are out of the image. Landing at =m is tolerated: we only ever build
92+
# the image target, so a module is never compiled, let alone shipped.
93+
FORBIDDEN=()
94+
nope() {
95+
local sym
96+
for sym in "$@"; do
97+
$CFG --disable "$sym"
98+
FORBIDDEN+=("$sym")
99+
done
100+
}
101+
47102
# --- virtio guest drivers ----------------------------------------------------
48103
# Pin the core virtio transports and every guest-side device driver we'd
49104
# plausibly want from a microVM hypervisor, plus the modern transports
50105
# (MMIO for firecracker-style, PCI for cloud-hypervisor/qemu).
51-
$CFG --enable VIRTIO
52-
$CFG --enable VIRTIO_PCI
106+
want VIRTIO VIRTIO_PCI VIRTIO_MMIO VIRTIO_MMIO_CMDLINE_DEVICES
107+
want VIRTIO_BLK VIRTIO_NET VIRTIO_CONSOLE VIRTIO_BALLOON VIRTIO_INPUT
108+
want VIRTIO_IOMMU
109+
want HW_RANDOM HW_RANDOM_VIRTIO
53110
$CFG --enable VIRTIO_PCI_LEGACY
54-
$CFG --enable VIRTIO_MMIO
55-
$CFG --enable VIRTIO_MMIO_CMDLINE_DEVICES
56-
$CFG --enable VIRTIO_BLK
57-
$CFG --enable VIRTIO_NET
58-
$CFG --enable VIRTIO_CONSOLE
59-
$CFG --enable VIRTIO_BALLOON
60-
$CFG --enable VIRTIO_INPUT
61-
$CFG --enable VIRTIO_SCSI
62-
$CFG --enable VIRTIO_GPU
63-
$CFG --enable VIRTIO_PMEM
64-
$CFG --enable VIRTIO_IOMMU
65-
$CFG --enable VIRTIO_MEM
66-
$CFG --enable VIRTIO_NET_FAILOVER
67-
$CFG --enable VIRTIO_DMA_SHARED_BUFFER
68-
$CFG --enable HW_RANDOM
69-
$CFG --enable HW_RANDOM_VIRTIO
70-
# vsock + virtio-vsock for host<->guest sockets
71-
$CFG --enable VSOCKETS
72-
$CFG --enable VIRTIO_VSOCKETS
73-
# virtiofs (needs FUSE)
74-
$CFG --enable FUSE_FS
75-
$CFG --enable VIRTIO_FS
111+
112+
# The SCSI guest driver is not called VIRTIO_SCSI (no such symbol), and the
113+
# failover netdev is NET_FAILOVER, which VIRTIO_NET selects for us anyway.
114+
# Asking by the wrong name was invisible on x86 — where kvm_guest.config
115+
# happens to set the real symbols — and a missing driver on arm64, where we
116+
# don't merge that fragment.
117+
want SCSI_VIRTIO NET_FAILOVER
118+
119+
# No GPU. A microVM guest has no display — its console is a serial port — so
120+
# the whole DRM subsystem is dead weight. Dropping DRM_VIRTIO_GPU from the
121+
# want() list above is not enough to be rid of it: x86_64_defconfig sets
122+
# CONFIG_DRM=y and CONFIG_DRM_I915=y outright, and kvm_guest.config re-asserts
123+
# CONFIG_DRM_VIRTIO_GPU=y on top. Turn the subsystem off at the root.
124+
nope DRM_I915 DRM_VIRTIO_GPU DRM
125+
126+
# virtio-mem is gated on the memory hotplug/hotremove machinery; without it the
127+
# driver silently drops out of the config.
128+
want MEMORY_HOTPLUG MEMORY_HOTREMOVE VIRTIO_MEM
129+
130+
# virtio-pmem likewise needs libnvdimm, and DAX is what makes a pmem device (or
131+
# virtiofs, below) mappable without going through the guest page cache.
132+
want ZONE_DEVICE LIBNVDIMM VIRTIO_PMEM DAX FS_DAX
133+
134+
# vsock + virtio-vsock for host<->guest sockets. The loopback transport lets
135+
# guest-local services speak vsock without a host peer, which is what most
136+
# vsock-based agents test against.
137+
want VSOCKETS VSOCKETS_LOOPBACK VIRTIO_VSOCKETS
138+
139+
# virtiofs (needs FUSE). DAX mapping and passthrough are what make it fast
140+
# enough to use as a real rootfs/workspace share.
141+
want FUSE_FS VIRTIO_FS FUSE_DAX FUSE_PASSTHROUGH
142+
143+
# 9p-over-virtio: the older host share, and something x86 gets for free from
144+
# kvm_guest.config — pin it so arm64 has it too.
145+
want NET_9P NET_9P_VIRTIO 9P_FS
146+
147+
# ip=/DHCP autoconfiguration from the kernel cmdline (also x86-only via the
148+
# fragment otherwise).
149+
want IP_PNP IP_PNP_DHCP
76150

77151
# --- namespaces + cgroups (containers / sandboxing) --------------------------
78-
$CFG --enable NAMESPACES
79-
$CFG --enable UTS_NS
80-
$CFG --enable IPC_NS
81-
$CFG --enable PID_NS
82-
$CFG --enable NET_NS
83-
$CFG --enable USER_NS
84-
$CFG --enable TIME_NS
85-
$CFG --enable CGROUPS
86-
$CFG --enable MEMCG
87-
$CFG --enable CPUSETS
88-
$CFG --enable CGROUP_PIDS
89-
$CFG --enable CGROUP_FREEZER
90-
$CFG --enable CGROUP_DEVICE
91-
$CFG --enable CGROUP_CPUACCT
92-
$CFG --enable CGROUP_SCHED
93-
$CFG --enable BLK_CGROUP
152+
want NAMESPACES UTS_NS IPC_NS PID_NS NET_NS USER_NS TIME_NS
153+
want CGROUPS MEMCG CPUSETS CGROUP_PIDS CGROUP_FREEZER CGROUP_DEVICE
154+
want CGROUP_CPUACCT CGROUP_SCHED BLK_CGROUP CGROUP_HUGETLB CGROUP_MISC
155+
want CGROUP_PERF CGROUP_BPF CGROUP_NET_PRIO CGROUP_NET_CLASSID NET_CLS_CGROUP
156+
# Pressure-stall + delay accounting: what memory/CPU-aware schedulers,
157+
# systemd-oomd and cgroup `*.pressure` readers need.
158+
want PSI TASKSTATS TASK_DELAY_ACCT TASK_IO_ACCOUNTING
159+
# Note: cgroup v1's memory and cpuset controllers (MEMCG_V1 / CPUSETS_V1) stay
160+
# off — v2-only, same as modern distros.
161+
162+
# --- /proc + process introspection userspace expects -------------------------
163+
# PROC_CHILDREN backs /proc/<pid>/task/<tid>/children, which process
164+
# supervisors use to walk a tree without scanning all of /proc.
165+
# CHECKPOINT_RESTORE brings /proc/<pid>/map_files plus the rest of the CRIU
166+
# surface, and USERFAULTFD is how CRIU (and lazy restore / live migration)
167+
# faults pages back in. PROC_PAGE_MONITOR is /proc/<pid>/pagemap + smaps,
168+
# which every memory profiler reads.
169+
want PROC_CHILDREN PROC_PAGE_MONITOR PROC_KCORE CHECKPOINT_RESTORE USERFAULTFD
170+
171+
# --- core syscall surface container runtimes assume --------------------------
172+
# seccomp is how every runtime filters syscalls; runc's default spec mounts
173+
# /dev/mqueue (POSIX_MQUEUE) and needs ptys for `exec -t`.
174+
want SECCOMP SECCOMP_FILTER
175+
want SYSVIPC SYSVIPC_SYSCTL POSIX_MQUEUE POSIX_MQUEUE_SYSCTL UNIX98_PTYS
176+
want EPOLL SIGNALFD TIMERFD EVENTFD AIO IO_URING FUTEX
177+
want BINFMT_ELF BINFMT_SCRIPT BINFMT_MISC
178+
want HUGETLBFS TRANSPARENT_HUGEPAGE
179+
180+
# --- BPF ---------------------------------------------------------------------
181+
# systemd's device access control, container runtimes and the whole
182+
# observability stack are BPF-first now; CGROUP_BPF is the cgroup-v2 attach
183+
# point. KPROBES/PERF_EVENTS are what BPF_EVENTS — and therefore BPF_LSM —
184+
# are gated on.
185+
# Not enabled: DEBUG_INFO_BTF. CO-RE tooling (bpftrace, libbpf skeletons)
186+
# wants it, but it needs pahole (dwarves) as a build dep and a DWARF-enabled
187+
# build, so it's a deliberate follow-up rather than a free win.
188+
want BPF_SYSCALL BPF_JIT CGROUP_BPF PERF_EVENTS KPROBES
189+
# BPF_EVENTS is what gates BPF_LSM (and attaching BPF to kprobes/uprobes/
190+
# tracepoints at all), and it needs KPROBE_EVENTS || UPROBE_EVENTS — both of
191+
# which live inside `if FTRACE` in kernel/trace/Kconfig. FTRACE is
192+
# `default y if DEBUG_KERNEL`, which x86_64_defconfig satisfies, but arm64's
193+
# defconfig explicitly carries `# CONFIG_FTRACE is not set` — so on arm64 the
194+
# probe-event symbols were unreachable and BPF_EVENTS silently fell to n,
195+
# taking BPF_LSM with it. Turn the menu on and pin both probe backends.
196+
want FTRACE KPROBE_EVENTS UPROBE_EVENTS BPF_EVENTS
94197

95198
# --- container networking ----------------------------------------------------
96-
$CFG --enable VETH
97-
$CFG --enable TUN
98-
$CFG --enable WIREGUARD
199+
want VETH TUN WIREGUARD MACVLAN IPVLAN DUMMY BRIDGE BRIDGE_NETFILTER
200+
want VLAN_8021Q IPV6
201+
# Port publishing and egress NAT: docker, podman and CNI plugins program either
202+
# nftables or iptables, and their rules lean on the addrtype/conntrack/multiport
203+
# matches. defconfig sets NETFILTER_ADVANCED=n, which hides most of this and
204+
# defaults the rest to =m — invisible to us, since we ship no modules.
205+
want NETFILTER NETFILTER_ADVANCED NETFILTER_XTABLES
206+
want NF_CONNTRACK NF_NAT NF_NAT_MASQUERADE
207+
want NF_TABLES NF_TABLES_INET NF_TABLES_IPV4 NF_TABLES_IPV6
208+
want NFT_CT NFT_NAT NFT_MASQ NFT_COMPAT NFT_LIMIT NFT_LOG
209+
want NFT_REJECT NFT_REJECT_IPV4 NFT_REJECT_IPV6 NFT_FIB_IPV4 NFT_FIB_IPV6
210+
want IP_NF_IPTABLES IP_NF_IPTABLES_LEGACY IP_NF_FILTER IP_NF_NAT IP_NF_MANGLE
211+
want IP_NF_TARGET_MASQUERADE IP_NF_TARGET_REJECT IP_NF_TARGET_REDIRECT
212+
want IP6_NF_IPTABLES IP6_NF_IPTABLES_LEGACY IP6_NF_FILTER IP6_NF_NAT
213+
want IP6_NF_MANGLE IP6_NF_TARGET_MASQUERADE IP6_NF_TARGET_REJECT
214+
want NETFILTER_XT_NAT NETFILTER_XT_MARK NETFILTER_XT_MATCH_ADDRTYPE
215+
want NETFILTER_XT_MATCH_CONNTRACK NETFILTER_XT_MATCH_MULTIPORT
216+
want NETFILTER_XT_MATCH_COMMENT NETFILTER_XT_MATCH_STATE
217+
want NETFILTER_XT_TARGET_MASQUERADE NETFILTER_XT_TARGET_REDIRECT
218+
# The log backends default to =m, which for us means `nft ... log` and
219+
# `iptables -j LOG` rules would load but never emit anything.
220+
want NETFILTER_XT_TARGET_LOG NF_LOG_SYSLOG NF_LOG_IPV4 NF_LOG_IPV6 NF_LOG_ARP
221+
# `ss` and anything else that enumerates sockets goes through sock_diag.
222+
want INET_DIAG INET_TCP_DIAG INET_UDP_DIAG UNIX_DIAG NETLINK_DIAG PACKET_DIAG
223+
# tc classifiers/qdiscs, including the eBPF datapath hooks. NET_ACT_MIRRED is
224+
# both how `tc` redirects/mirrors traffic and IFB's hard dependency — without
225+
# it the ifb driver drops out of the config.
226+
want NET_SCH_INGRESS NET_SCH_FQ_CODEL NET_CLS_BPF NET_ACT_BPF
227+
want NET_ACT_MIRRED IFB
228+
229+
# --- block devices + filesystems ---------------------------------------------
230+
# Loop devices and device-mapper are how disk images get mounted and how the
231+
# thin/crypt storage stacks work.
232+
#
233+
# Not enabled: DM_VERITY. It exists to authenticate a read-only rootfs against
234+
# a signed root hash, which is a defence against tampering with the backing
235+
# store — not a threat model that applies here, where the rootfs arrives as a
236+
# virtio-blk device handed to us by the hypervisor we already trust.
237+
want BLK_DEV_LOOP BLK_DEV_DM DM_SNAPSHOT DM_THIN_PROVISIONING DM_CRYPT
238+
# initramfs, and the decompressors a packed initrd might use.
239+
want BLK_DEV_INITRD RD_GZIP RD_XZ RD_ZSTD
240+
want EXT4_FS EXT4_FS_POSIX_ACL EXT4_FS_SECURITY
241+
# Read-only image formats: squashfs and erofs both show up as container/rootfs
242+
# images; ISO9660 + vfat are what cloud-init style config drives use.
243+
want SQUASHFS SQUASHFS_XZ SQUASHFS_ZSTD EROFS_FS EROFS_FS_ZIP
244+
want ISO9660_FS VFAT_FS NLS_CODEPAGE_437 NLS_ISO8859_1 NLS_UTF8 NLS_ASCII
245+
# systemd mounts /proc/sys/fs/binfmt_misc and friends through autofs.
246+
want AUTOFS_FS
99247

100248
# --- bind mounts / pivot_root substrate --------------------------------------
101249
# Bind mounts are part of core VFS, but pivot_root + the filesystems people
102250
# commonly use to assemble container roots are configurable. Make sure the
103-
# usual suspects are present.
104-
$CFG --enable TMPFS
105-
$CFG --enable TMPFS_POSIX_ACL
106-
$CFG --enable TMPFS_XATTR
107-
$CFG --enable PROC_FS
108-
$CFG --enable SYSFS
109-
$CFG --enable DEVTMPFS
110-
$CFG --enable DEVTMPFS_MOUNT
251+
# usual suspects are present. TMPFS_INODE64 avoids inode-number reuse on
252+
# long-lived tmpfs trees.
253+
want TMPFS TMPFS_POSIX_ACL TMPFS_XATTR TMPFS_INODE64
254+
want PROC_FS SYSFS DEVTMPFS DEVTMPFS_MOUNT
111255

112256
# --- overlayfs ---------------------------------------------------------------
113-
$CFG --enable OVERLAY_FS
114-
$CFG --enable OVERLAY_FS_REDIRECT_DIR
115-
$CFG --enable OVERLAY_FS_INDEX
116-
$CFG --enable OVERLAY_FS_XINO_AUTO
117-
$CFG --enable OVERLAY_FS_METACOPY
257+
want OVERLAY_FS OVERLAY_FS_REDIRECT_DIR OVERLAY_FS_INDEX
258+
want OVERLAY_FS_XINO_AUTO OVERLAY_FS_METACOPY
118259

119260
# --- fanotify (privileged FS event monitoring + access control) -------------
120-
$CFG --enable FANOTIFY
121-
$CFG --enable FANOTIFY_ACCESS_PERMISSIONS
261+
want FANOTIFY FANOTIFY_ACCESS_PERMISSIONS
122262

123-
# --- landlock (unprivileged sandboxing LSM) ----------------------------------
124-
$CFG --enable SECURITY
125-
$CFG --enable SECURITY_LANDLOCK
126-
# Landlock is a stackable LSM; make sure it's actually in the active list.
127-
$CFG --set-str LSM "landlock,lockdown,yama,integrity,apparmor,bpf"
263+
# --- LSMs --------------------------------------------------------------------
264+
# landlock for unprivileged sandboxing, yama for ptrace scoping, bpf-lsm for
265+
# programmable policy.
266+
want SECURITY SECURITY_NETWORK SECURITY_LANDLOCK SECURITY_YAMA BPF_LSM
267+
# Deliberately not enabling SECURITY_LOCKDOWN_LSM: it does
268+
# `select MODULE_SIG if MODULES` — a condition we now satisfy — and MODULE_SIG
269+
# with the default
270+
# CONFIG_MODULE_SIG_KEY="certs/signing_key.pem" makes the build generate a
271+
# fresh RSA keypair and embed the cert in the image — a different vmlinuz on
272+
# every build. Lockdown buys us nothing here anyway (we ship no modules and
273+
# there's no secure-boot chain in a microVM guest).
274+
# Stackable LSMs only run if they're in this list, and only names that are
275+
# actually compiled in mean anything — so keep the list and the config in sync.
276+
# (The list previously named lockdown, apparmor and integrity, none of which
277+
# was built.)
278+
$CFG --set-str LSM "landlock,yama,bpf"
128279

129280
# --- nested KVM (let this guest itself host VMs) -----------------------------
130281
# CONFIG_KVM is host-side virtualization; enabling it inside the guest is
131282
# what makes nesting work from the guest's point of view. Whether nesting
132283
# is *actually* available depends on the outer hypervisor exposing
133284
# vmx/svm/EL2-virt to us, but the kernel side has to be built either way.
134285
# KVM_INTEL/KVM_AMD only exist on x86; on arm64 the equivalent is folded
135-
# into CONFIG_KVM. scripts/config silently no-ops on unknown symbols and
136-
# olddefconfig drops them, so listing both arches' symbols here is safe.
286+
# into CONFIG_KVM. These stay outside want() precisely because they're
287+
# arch-conditional, so olddefconfig is free to drop the ones that don't apply.
137288
$CFG --enable VIRTUALIZATION
138289
$CFG --enable KVM
139290
$CFG --enable KVM_INTEL
140291
$CFG --enable KVM_AMD
141-
$CFG --enable KVM_XFER_TO_GUEST_WORK
142-
$CFG --enable KVM_GENERIC_DIRTYLOG_READ_PROTECT
143292

144293
# Resolve any new dependencies / silently drop options renamed upstream.
145294
make olddefconfig
146295

296+
# --- assert the config we asked for is the config we got ---------------------
297+
missing=()
298+
for sym in "${REQUIRED[@]}"; do
299+
grep -qx "CONFIG_${sym}=y" .config || missing+=("CONFIG_${sym}")
300+
done
301+
if [ "${#missing[@]}" -gt 0 ]; then
302+
echo "error: required kernel options absent from .config after olddefconfig:" >&2
303+
printf ' %s\n' "${missing[@]}" >&2
304+
echo "cause is one of: symbol renamed/removed upstream, unmet dependency," >&2
305+
echo "or downgraded to =m (only the image target is built, so a module is" >&2
306+
echo "never compiled into the shipped kernel)." >&2
307+
exit 1
308+
fi
309+
310+
resurrected=()
311+
for sym in "${FORBIDDEN[@]}"; do
312+
grep -qx "CONFIG_${sym}=y" .config && resurrected+=("CONFIG_${sym}")
313+
done
314+
if [ "${#resurrected[@]}" -gt 0 ]; then
315+
echo "error: kernel options we disabled came back =y after olddefconfig:" >&2
316+
printf ' %s\n' "${resurrected[@]}" >&2
317+
echo "something selects them — find it and disable that instead." >&2
318+
exit 1
319+
fi
320+
147321
make -j"$JOBS" "$KERNEL_TARGET"
148322

149323
OUT=$OUTPUT_DIR/usr/share/virtio-linux

0 commit comments

Comments
 (0)