runtime: support kernel params including spaces

Support quoted kernel command line parameters that include space
characters. Example:

dm-mod.create="dm-verity,,,ro,0 736328 verity 1
/dev/vda1 /dev/vda2 4096 4096 92041 0 sha256
f211b9f1921ef726d57a72bf82be23a510076639fa8549ade10f85e214e0ddb4
065c13dfb5b4e0af034685aa5442bddda47b17c182ee44ba55a373835d18a038"

Fixes: #8003

Signed-off-by: Dan Mihai <dmihai@microsoft.com>
This commit is contained in:
Dan Mihai 2023-09-19 17:19:41 +00:00
parent 5560e72024
commit 82ff2db460
4 changed files with 175 additions and 8 deletions

View File

@ -720,7 +720,7 @@ func newFirecrackerHypervisorConfig(h hypervisor) (vc.HypervisorConfig, error) {
ImagePath: image,
RootfsType: rootfsType,
FirmwarePath: firmware,
KernelParams: vc.DeserializeParams(strings.Fields(kernelParams)),
KernelParams: vc.DeserializeParams(vc.KernelParamFields(kernelParams)),
NumVCPUs: h.defaultVCPUs(),
DefaultMaxVCPUs: h.defaultMaxVCPUs(),
MemorySize: h.defaultMemSz(),
@ -853,7 +853,7 @@ func newQemuHypervisorConfig(h hypervisor) (vc.HypervisorConfig, error) {
PFlash: pflashes,
MachineAccelerators: machineAccelerators,
CPUFeatures: cpuFeatures,
KernelParams: vc.DeserializeParams(strings.Fields(kernelParams)),
KernelParams: vc.DeserializeParams(vc.KernelParamFields(kernelParams)),
HypervisorMachineType: machineType,
NumVCPUs: h.defaultVCPUs(),
DefaultMaxVCPUs: h.defaultMaxVCPUs(),
@ -965,7 +965,7 @@ func newAcrnHypervisorConfig(h hypervisor) (vc.HypervisorConfig, error) {
HypervisorCtlPath: hypervisorctl,
HypervisorCtlPathList: h.CtlPathList,
FirmwarePath: firmware,
KernelParams: vc.DeserializeParams(strings.Fields(kernelParams)),
KernelParams: vc.DeserializeParams(vc.KernelParamFields(kernelParams)),
NumVCPUs: h.defaultVCPUs(),
DefaultMaxVCPUs: h.defaultMaxVCPUs(),
MemorySize: h.defaultMemSz(),
@ -1055,7 +1055,7 @@ func newClhHypervisorConfig(h hypervisor) (vc.HypervisorConfig, error) {
RootfsType: rootfsType,
FirmwarePath: firmware,
MachineAccelerators: machineAccelerators,
KernelParams: vc.DeserializeParams(strings.Fields(kernelParams)),
KernelParams: vc.DeserializeParams(vc.KernelParamFields(kernelParams)),
HypervisorMachineType: machineType,
NumVCPUs: h.defaultVCPUs(),
DefaultMaxVCPUs: h.defaultMaxVCPUs(),
@ -1130,7 +1130,7 @@ func newDragonballHypervisorConfig(h hypervisor) (vc.HypervisorConfig, error) {
KernelPath: kernel,
ImagePath: image,
RootfsType: rootfsType,
KernelParams: vc.DeserializeParams(strings.Fields(kernelParams)),
KernelParams: vc.DeserializeParams(vc.KernelParamFields(kernelParams)),
NumVCPUs: h.defaultVCPUs(),
DefaultMaxVCPUs: h.defaultMaxVCPUs(),
MemorySize: h.defaultMemSz(),

View File

@ -159,7 +159,7 @@ func createAllRuntimeConfigFiles(dir, hypervisor string) (testConfig testRuntime
KernelPath: kernelPath,
ImagePath: imagePath,
RootfsType: rootfsType,
KernelParams: vc.DeserializeParams(strings.Fields(kernelParams)),
KernelParams: vc.DeserializeParams(vc.KernelParamFields(kernelParams)),
HypervisorMachineType: machineType,
NumVCPUs: defaultVCPUCount,
DefaultMaxVCPUs: getCurrentCpuNum(),

View File

@ -919,7 +919,7 @@ func CheckCmdline(kernelCmdlinePath, searchParam string, searchValues []string)
scanner := bufio.NewScanner(f)
for scanner.Scan() {
for _, field := range strings.Fields(scanner.Text()) {
for _, field := range KernelParamFields(scanner.Text()) {
if check(field, searchParam, searchValues) {
return true, nil
}
@ -941,7 +941,7 @@ func CPUFlags(cpuInfoPath string) (map[string]bool, error) {
scanner := bufio.NewScanner(f)
for scanner.Scan() {
// Expected format: ["flags", ":", ...] or ["flags:", ...]
fields := strings.Fields(scanner.Text())
fields := KernelParamFields(scanner.Text())
if len(fields) < 2 {
continue
}
@ -1085,3 +1085,35 @@ type Hypervisor interface {
// check if hypervisor supports built-in rate limiter.
IsRateLimiterBuiltin() bool
}
// KernelParamFields is similar to strings.Fields(), but doesn't split
// based on space characters that are part of a quoted substring. Example
// of quoted kernel command line parameter value:
// dm-mod.create="dm-verity,,,ro,0 736328 verity 1
// /dev/vda1 /dev/vda2 4096 4096 92041 0 sha256
// f211b9f1921ef726d57a72bf82be23a510076639fa8549ade10f85e214e0ddb4
// 065c13dfb5b4e0af034685aa5442bddda47b17c182ee44ba55a373835d18a038"
func KernelParamFields(s string) []string {
var params []string
start := 0
inQuote := false
for current, c := range s {
if c == '"' {
inQuote = !inQuote
} else if c == ' ' && !inQuote {
newParam := strings.TrimSpace(s[start:current])
if newParam != "" {
params = append(params, newParam)
}
start = current + 1
}
}
newParam := strings.TrimSpace(s[start:])
if newParam != "" {
params = append(params, newParam)
}
return params
}

View File

@ -10,6 +10,7 @@ import (
"github.com/kata-containers/kata-containers/src/runtime/virtcontainers/types"
"github.com/stretchr/testify/assert"
"os"
"strings"
"testing"
)
@ -490,3 +491,137 @@ func TestAssetPath(t *testing.T) {
assert.Equal(expected, p, msg)
}
}
func TestKernelParamFields(t *testing.T) {
assert := assert.New(t)
tests := []struct {
cmdLine string
expectedFieldsResult []string
expectedKernelParamFieldsResult []string
}{
{
cmdLine: "a=b x=y",
expectedFieldsResult: []string{
"a=b",
"x=y",
},
expectedKernelParamFieldsResult: []string{
"a=b",
"x=y",
},
},
{
cmdLine: "a=b x=y foo=bar",
expectedFieldsResult: []string{
"a=b",
"x=y",
"foo=bar",
},
expectedKernelParamFieldsResult: []string{
"a=b",
"x=y",
"foo=bar",
},
},
{
cmdLine: "a x=y foo=bar",
expectedFieldsResult: []string{
"a",
"x=y",
"foo=bar",
},
expectedKernelParamFieldsResult: []string{
"a",
"x=y",
"foo=bar",
},
},
{
cmdLine: "a=b x foo=bar",
expectedFieldsResult: []string{
"a=b",
"x",
"foo=bar",
},
expectedKernelParamFieldsResult: []string{
"a=b",
"x",
"foo=bar",
},
},
{
cmdLine: "a=b x foo ",
expectedFieldsResult: []string{
"a=b",
"x",
"foo",
},
expectedKernelParamFieldsResult: []string{
"a=b",
"x",
"foo",
},
},
{
cmdLine: "a=b x=\"y z\"",
expectedFieldsResult: []string{
"a=b",
"x=\"y",
"z\"",
},
expectedKernelParamFieldsResult: []string{
"a=b",
"x=\"y z\"",
},
},
{
cmdLine: "foo=\"bar baz\"",
expectedFieldsResult: []string{
"foo=\"bar",
"baz\"",
},
expectedKernelParamFieldsResult: []string{
"foo=\"bar baz\"",
},
},
{
cmdLine: "foo=\"bar baz\" abc=\"123\"",
expectedFieldsResult: []string{
"foo=\"bar",
"baz\"",
"abc=\"123\"",
},
expectedKernelParamFieldsResult: []string{
"foo=\"bar baz\"",
"abc=\"123\"",
},
},
{
cmdLine: "\"a=b",
expectedFieldsResult: []string{
"\"a=b",
},
expectedKernelParamFieldsResult: []string{
"\"a=b",
},
},
{
cmdLine: "\"a=b x=y",
expectedFieldsResult: []string{
"\"a=b",
"x=y",
},
expectedKernelParamFieldsResult: []string{
"\"a=b x=y",
},
},
}
for _, t := range tests {
params := strings.Fields(t.cmdLine)
assert.Equal(params, t.expectedFieldsResult, "Unexpected strings.Fields behavior")
params = KernelParamFields(t.cmdLine)
assert.Equal(params, t.expectedKernelParamFieldsResult, "Unexpected KernelParamFields behavior")
}
}