Description
When generating the Firecracker JSON config, the vsock device is always written into the config, even when vAccel/vsock is not in use. The FirecrackerConfig.VSock field is tagged json:"vsock,omitempty", but omitempty has no effect on a struct value in Go's encoding/json a struct is never considered "empty" so the key is always marshalled.
As a result, every non-vAccel Firecracker boot emits:
"vsock":{"guest_cid":0,"uds_path":"","vsock_id":""}
This is an invalid/meaningless vsock device: guest CID 0 is reserved (valid guest CIDs start at 3) and uds_path is empty. The code intends the opposite, the field is only populated under if args.VAccelType == "vsock" and tagged omitempty so the section should only appear when vAccel is configured.
Note: this is incorrect config generation, not a boot failure, Firecracker currently tolerates the empty section (the non-vAccel e2e tests pass on main). The issue is that the generated config contradicts the code's clear intent.
Relevant code: pkg/unikontainers/hypervisors/firecracker.go:
- Line 76:
VSock FirecrackerVSockDev \json:"vsock,omitempty"`` (struct value)
- Lines 178-193: the vsock struct is left as its zero value unless
args.VAccelType == "vsock", but is always assigned into the config.
The sibling NetIfs []FirecrackerNet field works correctly because omitempty does apply to empty slices. The same "don't emit config that isn't needed" issue was already fixed for the network section in #310 this is the same class of bug for vsock.
Introduced in commit 1c9c720 ("feat: add vAccel support to urunc").
System info
- urunc version: 0.7.0 (also present on
main)
- Arch: any (config-generation logic, architecture-independent)
- VMM: Firecracker
- Unikernel: any (reproduces for any Firecracker guest not using vAccel)
Steps to reproduce
The bug is deterministic and can be shown without running Firecracker, since it's purely in JSON config generation.
-
Run any non-vAccel Firecracker container (e.g. a hello/nginx Firecracker image) and inspect the generated config (the "Firecracker json config" debug log line). The vsock key is present with guest_cid: 0 and an empty uds_path.
-
Minimal standalone reproduction of the marshalling behaviour:
package main
import (
"encoding/json"
"fmt"
)
type VSockDev struct {
GuestCID int `json:"guest_cid"`
UDSPath string `json:"uds_path"`
VSockID string `json:"vsock_id"`
}
type Config struct {
VSock VSockDev `json:"vsock,omitempty"`
}
func main() {
b, _ := json.Marshal(Config{}) // no vAccel -> zero value
fmt.Println(string(b))
}
Output: `{"vsock":{"guest_cid":0,"uds_path":"","vsock_id":""}}`
Expected with a working `omitempty`: `{}`
Suggested fix
Make VSock a pointer so omitempty takes effect, and only set it in the vAccel branch:
// struct
VSock *FirecrackerVSockDev `json:"vsock,omitempty"`
// in BuildExecCmd
var FCVSockDev *FirecrackerVSockDev
if args.VAccelType == "vsock" {
FCVSockDev = &FirecrackerVSockDev{ ... }
}
// leave nil otherwise -> vsock key omitted
Verified: with the pointer change, the no-vsock case marshals to {}, and the vAccel case still emits the full vsock section.
Description
When generating the Firecracker JSON config, the
vsockdevice is always written into the config, even when vAccel/vsock is not in use. TheFirecrackerConfig.VSockfield is taggedjson:"vsock,omitempty", butomitemptyhas no effect on a struct value in Go'sencoding/jsona struct is never considered "empty" so the key is always marshalled.As a result, every non-vAccel Firecracker boot emits:
This is an invalid/meaningless vsock device: guest CID 0 is reserved (valid guest CIDs start at 3) and
uds_pathis empty. The code intends the opposite, the field is only populated underif args.VAccelType == "vsock"and taggedomitemptyso the section should only appear when vAccel is configured.Note: this is incorrect config generation, not a boot failure, Firecracker currently tolerates the empty section (the non-vAccel e2e tests pass on
main). The issue is that the generated config contradicts the code's clear intent.Relevant code:
pkg/unikontainers/hypervisors/firecracker.go:VSock FirecrackerVSockDev \json:"vsock,omitempty"`` (struct value)args.VAccelType == "vsock", but is always assigned into the config.The sibling
NetIfs []FirecrackerNetfield works correctly becauseomitemptydoes apply to empty slices. The same "don't emit config that isn't needed" issue was already fixed for the network section in #310 this is the same class of bug forvsock.Introduced in commit 1c9c720 ("feat: add vAccel support to urunc").
System info
main)Steps to reproduce
The bug is deterministic and can be shown without running Firecracker, since it's purely in JSON config generation.
Run any non-vAccel Firecracker container (e.g. a hello/nginx Firecracker image) and inspect the generated config (the "Firecracker json config" debug log line). The
vsockkey is present withguest_cid: 0and an emptyuds_path.Minimal standalone reproduction of the marshalling behaviour:
Suggested fix
Make
VSocka pointer soomitemptytakes effect, and only set it in the vAccel branch:Verified: with the pointer change, the no-vsock case marshals to
{}, and the vAccel case still emits the full vsock section.