detached 2bb0741

fix(vm): recognise the Firecracker the jailer execs into its chroot

The jailer starts Firecracker as '/firecracker --id <id> --start-time-us ...':
no --api-sock and no --exec-file on its command line. The /proc scan used by
Stop and Reap only knew those two markers, so a jailed VMM was invisible: it
survived a kill-by-id, left its cgroup busy ('remove cgroup: device or
resource busy') and Reap could not find it. Accept --start-time-us (the
jailer's own argument) as the marker too, in the driver and in the leak check
of the integration tests.
commit
2bb0741d311eccfe139550d653858c5993c2f38c
author
brice · · 2026-10-12 21:27 UTC
parent
ea344ad
signature
GPG VerifiedCD88F9E564E0102646FB6077A1A07C7C7C3E349D · arch-gpg Verified for the committer email
3 files · +15 -11
M internal/sandbox/integration/integration_test.go +2 -2
@@ -214,7 +214,7 @@ func leftovers(stateDir string) []string {
214214 }
215215
216216 // vmmProcs returns the processes whose command line carries one of our VM ids
217-// (and --api-sock or --exec-file), whatever the program is called: the rule the
217+// (and --api-sock, --exec-file or the jailer's --start-time-us), whatever the program is called: the rule the
218218 // driver's Reap uses.
219219 func vmmProcs() map[int]string {
220220 out := map[int]string{}
@@ -233,7 +233,7 @@ func vmmProcs() map[int]string {
233233 if i+1 < len(args) && sandbox.ValidID(args[i+1]) {
234234 id = args[i+1]
235235 }
236- case "--api-sock", "--exec-file":
236+ case "--api-sock", "--exec-file", "--start-time-us":
237237 marker = true
238238 }
239239 }
@@ -214,7 +214,7 @@ func leftovers(stateDir string) []string {
214 }214 }
215 215
216 // vmmProcs returns the processes whose command line carries one of our VM ids216 // vmmProcs returns the processes whose command line carries one of our VM ids
217-// (and --api-sock or --exec-file), whatever the program is called: the rule the217+// (and --api-sock, --exec-file or the jailer's --start-time-us), whatever the program is called: the rule the
218 // driver's Reap uses.218 // driver's Reap uses.
219 func vmmProcs() map[int]string {219 func vmmProcs() map[int]string {
220 out := map[int]string{}220 out := map[int]string{}
@@ -233,7 +233,7 @@ func vmmProcs() map[int]string {
233 if i+1 < len(args) && sandbox.ValidID(args[i+1]) {233 if i+1 < len(args) && sandbox.ValidID(args[i+1]) {
234 id = args[i+1]234 id = args[i+1]
235 }235 }
236- case "--api-sock", "--exec-file":236+ case "--api-sock", "--exec-file", "--start-time-us":
237 marker = true237 marker = true
238 }238 }
239 }239 }
M internal/sandbox/vm/proc.go +4 -2
@@ -18,7 +18,9 @@ import (
1818 // vmmProcs scans a /proc tree for processes started by this runner for a VM:
1919 // the Firecracker process and the jailer. A process is ours when its command
2020 // line has "--id <id>" with an id of the form sandbox.NewID produces and also
21-// "--api-sock" (Firecracker) or "--exec-file" (jailer). The program name is not
21+// "--api-sock" (Firecracker), "--exec-file" (jailer) or "--start-time-us" (the
22+// Firecracker the jailer execs into its chroot: it gets neither of the first two,
23+// only --id and the jailer's --start-time-* arguments). The program name is not
2224 // looked at: the binary may be renamed or versioned (firecracker-v1.17), and a
2325 // jailer in a PID namespace leaves a Firecracker whose argv[0] is whatever the
2426 // jailer chose. Processes of other users of Firecracker (other ids) are never
@@ -55,7 +57,7 @@ func vmIDFromCmdline(args []string) string {
5557 if i+1 < len(args) && sandbox.ValidID(args[i+1]) {
5658 id = args[i+1]
5759 }
58- case "--api-sock", "--exec-file":
60+ case "--api-sock", "--exec-file", "--start-time-us":
5961 marker = true
6062 }
6163 }
@@ -18,7 +18,9 @@ import (
18 // vmmProcs scans a /proc tree for processes started by this runner for a VM:18 // vmmProcs scans a /proc tree for processes started by this runner for a VM:
19 // the Firecracker process and the jailer. A process is ours when its command19 // the Firecracker process and the jailer. A process is ours when its command
20 // line has "--id <id>" with an id of the form sandbox.NewID produces and also20 // line has "--id <id>" with an id of the form sandbox.NewID produces and also
21-// "--api-sock" (Firecracker) or "--exec-file" (jailer). The program name is not21+// "--api-sock" (Firecracker), "--exec-file" (jailer) or "--start-time-us" (the
22+// Firecracker the jailer execs into its chroot: it gets neither of the first two,
23+// only --id and the jailer's --start-time-* arguments). The program name is not
22 // looked at: the binary may be renamed or versioned (firecracker-v1.17), and a24 // looked at: the binary may be renamed or versioned (firecracker-v1.17), and a
23 // jailer in a PID namespace leaves a Firecracker whose argv[0] is whatever the25 // jailer in a PID namespace leaves a Firecracker whose argv[0] is whatever the
24 // jailer chose. Processes of other users of Firecracker (other ids) are never26 // jailer chose. Processes of other users of Firecracker (other ids) are never
@@ -55,7 +57,7 @@ func vmIDFromCmdline(args []string) string {
55 if i+1 < len(args) && sandbox.ValidID(args[i+1]) {57 if i+1 < len(args) && sandbox.ValidID(args[i+1]) {
56 id = args[i+1]58 id = args[i+1]
57 }59 }
58- case "--api-sock", "--exec-file":60+ case "--api-sock", "--exec-file", "--start-time-us":
59 marker = true61 marker = true
60 }62 }
61 }63 }
M internal/sandbox/vm/vm_test.go +9 -7
@@ -724,13 +724,15 @@ func TestVMIDFromCmdline(t *testing.T) {
724724 args []string
725725 want string
726726 }{
727- "firecracker": {[]string{"firecracker", "--id", "er00112233", "--api-sock", "/x"}, "er00112233"},
728- "jailer": {[]string{"jailer", "--id", "er00112233", "--exec-file", "/fc", "--uid", "1"}, "er00112233"},
729- "renamed": {[]string{"anything", "--api-sock", "/x", "--id", "er44556677"}, "er44556677"},
730- "foreign id": {[]string{"firecracker", "--id", "my-vm", "--api-sock", "/x"}, ""},
731- "no marker": {[]string{"python", "--id", "er00112233"}, ""},
732- "id without value": {[]string{"firecracker", "--api-sock", "/x", "--id"}, ""},
733- "path-like id": {[]string{"firecracker", "--id", "er../../../x", "--api-sock", "/x"}, ""},
727+ "firecracker": {[]string{"firecracker", "--id", "er00112233", "--api-sock", "/x"}, "er00112233"},
728+ "jailer": {[]string{"jailer", "--id", "er00112233", "--exec-file", "/fc", "--uid", "1"}, "er00112233"},
729+ "renamed": {[]string{"anything", "--api-sock", "/x", "--id", "er44556677"}, "er44556677"},
730+ // What a real jailer 1.17 leaves: /firecracker --id <id> --start-time-us ...
731+ "jailed firecracker": {[]string{"/firecracker", "--id", "er00112233", "--start-time-us", "150801025577", "--start-time-cpu-us", "60", "--parent-cpu-time-us", "1332"}, "er00112233"},
732+ "foreign id": {[]string{"firecracker", "--id", "my-vm", "--api-sock", "/x"}, ""},
733+ "no marker": {[]string{"python", "--id", "er00112233"}, ""},
734+ "id without value": {[]string{"firecracker", "--api-sock", "/x", "--id"}, ""},
735+ "path-like id": {[]string{"firecracker", "--id", "er../../../x", "--api-sock", "/x"}, ""},
734736 } {
735737 if got := vmIDFromCmdline(tc.args); got != tc.want {
736738 t.Errorf("%s: got %q want %q", name, got, tc.want)
@@ -724,13 +724,15 @@ func TestVMIDFromCmdline(t *testing.T) {
724 args []string724 args []string
725 want string725 want string
726 }{726 }{
727- "firecracker": {[]string{"firecracker", "--id", "er00112233", "--api-sock", "/x"}, "er00112233"},727+ "firecracker": {[]string{"firecracker", "--id", "er00112233", "--api-sock", "/x"}, "er00112233"},
728- "jailer": {[]string{"jailer", "--id", "er00112233", "--exec-file", "/fc", "--uid", "1"}, "er00112233"},728+ "jailer": {[]string{"jailer", "--id", "er00112233", "--exec-file", "/fc", "--uid", "1"}, "er00112233"},
729- "renamed": {[]string{"anything", "--api-sock", "/x", "--id", "er44556677"}, "er44556677"},729+ "renamed": {[]string{"anything", "--api-sock", "/x", "--id", "er44556677"}, "er44556677"},
730- "foreign id": {[]string{"firecracker", "--id", "my-vm", "--api-sock", "/x"}, ""},730+ // What a real jailer 1.17 leaves: /firecracker --id <id> --start-time-us ...
731- "no marker": {[]string{"python", "--id", "er00112233"}, ""},731+ "jailed firecracker": {[]string{"/firecracker", "--id", "er00112233", "--start-time-us", "150801025577", "--start-time-cpu-us", "60", "--parent-cpu-time-us", "1332"}, "er00112233"},
732- "id without value": {[]string{"firecracker", "--api-sock", "/x", "--id"}, ""},732+ "foreign id": {[]string{"firecracker", "--id", "my-vm", "--api-sock", "/x"}, ""},
733- "path-like id": {[]string{"firecracker", "--id", "er../../../x", "--api-sock", "/x"}, ""},733+ "no marker": {[]string{"python", "--id", "er00112233"}, ""},
734+ "id without value": {[]string{"firecracker", "--api-sock", "/x", "--id"}, ""},
735+ "path-like id": {[]string{"firecracker", "--id", "er../../../x", "--api-sock", "/x"}, ""},
734 } {736 } {
735 if got := vmIDFromCmdline(tc.args); got != tc.want {737 if got := vmIDFromCmdline(tc.args); got != tc.want {
736 t.Errorf("%s: got %q want %q", name, got, tc.want)738 t.Errorf("%s: got %q want %q", name, got, tc.want)

Keyboard shortcuts

Single-key shortcuts are on. ctrl k and esc work whatever this says.

ctrl k :
go to a section, branch, tag, file or repository
?
this list
j k
move the row cursor down / up
enter
open the row under the cursor; on Files, a directory opens or closes in place
↑ ↓ ← → home end
in the file tree (focus on a row): previous / next row, close / open a directory or go to its parent / first entry, first / last row. They never open a file
l h
on Files: open a directory or a file / close a directory or go up to its parent, then to the parent page. Elsewhere: into the side panel (on a merge request, its files) / back to the content; there j k move between its controls
g g G
first / last row
/
filter the list; on Files, find a file anywhere in the repository (esc clears, enter opens the best match)
b
switch branch or tag
g s g f g c g b g m g i g r g a
tabs: summary, files, commits, branches & tags, merge requests, issues, releases, actions (when available)
g h
home
w
wide mode: use the whole width of the screen, or not (remembered)
y
copy a permalink pinned to the commit
d u x
diff: split / unified / collapse or expand all
e E
diff: the whole file in view / every file, with the lines the hunks hide
n
merge request: next note in the diff
esc
close a dialog, leave a field

Branches 4

Tags 11

↑↓ moveenter openesc close