From 820896f0d8a30d8ca2a76d9df4e95d1235dc5305 Mon Sep 17 00:00:00 2001 From: Alberto Mardegan Date: Tue, 31 May 2022 09:00:06 +0300 Subject: [PATCH] i/b/kernel-module-load: support "dynamic" load attribute As far as the code in the interfaces/buildint module is concerned, this is nearly equivalent to the "none" load type. It's the code in snapctl that will be checking for this attribute and enable runtime loading and unloading of the allowed kernel modules if the attribute is set. --- interfaces/builtin/kernel_module_load.go | 11 ++++++++--- interfaces/builtin/kernel_module_load_test.go | 19 ++++++++++++++++--- 2 files changed, 24 insertions(+), 6 deletions(-) diff --git a/interfaces/builtin/kernel_module_load.go b/interfaces/builtin/kernel_module_load.go index ebacfa28c15..f61c04aaa1d 100644 --- a/interfaces/builtin/kernel_module_load.go +++ b/interfaces/builtin/kernel_module_load.go @@ -59,6 +59,7 @@ const ( loadNone loadOption = iota loadDenied loadOnBoot + loadDynamic ) type ModuleInfo struct { @@ -95,6 +96,8 @@ func enumerateModules(plug interfaces.Attrer, handleModule func(moduleInfo *Modu load = loadDenied case "on-boot": load = loadOnBoot + case "dynamic": + load = loadDynamic default: return fmt.Errorf(`kernel-module-load "load" value is unrecognized: %q`, loadString) } @@ -139,7 +142,9 @@ func validateOptionsAttr(moduleInfo *ModuleInfo) error { return errors.New(`kernel-module-load "options" attribute incompatible with "load: denied"`) } - if !kernelModuleOptionsRegexp.MatchString(moduleInfo.options) { + // Use a variable to make the next `if` more readable + dynamicLoadingWithAnyOptions := moduleInfo.load == loadDynamic && moduleInfo.options == "*" + if !dynamicLoadingWithAnyOptions && !kernelModuleOptionsRegexp.MatchString(moduleInfo.options) { return fmt.Errorf(`kernel-module-load "options" attribute contains invalid characters: %q`, moduleInfo.options) } @@ -194,8 +199,8 @@ func (iface *kernelModuleLoadInterface) KModConnectedPlug(spec *kmod.Specificati break } fallthrough - case loadNone: - if len(moduleInfo.options) > 0 { + case loadNone, loadDynamic: + if len(moduleInfo.options) > 0 && moduleInfo.options != "*" { // module options might include filesystem paths. Beside // supporting hardcoded paths, it makes sense to support also // paths to files provided by the snap; for this reason, we diff --git a/interfaces/builtin/kernel_module_load_test.go b/interfaces/builtin/kernel_module_load_test.go index e9b426f78f4..b6e368de102 100644 --- a/interfaces/builtin/kernel_module_load_test.go +++ b/interfaces/builtin/kernel_module_load_test.go @@ -60,6 +60,12 @@ plugs: options: param_1=ok param_2=false - name: expandvar options: opt=$FOO path=$SNAP_COMMON/bar + - name: dyn-module1 + load: dynamic + options: opt1=v1 opt2=v2 + - name: dyn-module2 + load: dynamic + options: "*" apps: app: plugs: [kmod] @@ -155,6 +161,11 @@ apps: "modules:\n - name: pcspkr\n options: \"no-dashes\"", `kernel-module-load "options" attribute contains invalid characters: "no-dashes"`, }, + { + // "*" is only allowed for `load: dynamic` + "modules:\n - name: pcspkr\n options: \"*\"", + `kernel-module-load "options" attribute contains invalid characters: "\*"`, + }, { "modules:\n - name: pcspkr\n load: denied\n options: p1=true", `kernel-module-load "options" attribute incompatible with "load: denied"`, @@ -176,9 +187,11 @@ func (s *KernelModuleLoadInterfaceSuite) TestKModSpec(c *C) { "mymodule1": true, }) c.Check(spec.ModuleOptions(), DeepEquals, map[string]string{ - "mymodule1": "p1=3 p2=true p3", - "mymodule2": "param_1=ok param_2=false", - "expandvar": "opt=$FOO path=/var/snap/consumer/common/bar", + "mymodule1": "p1=3 p2=true p3", + "mymodule2": "param_1=ok param_2=false", + "expandvar": "opt=$FOO path=/var/snap/consumer/common/bar", + "dyn-module1": "opt1=v1 opt2=v2", + // No entry for dyn-module2, which has options set to "*" }) c.Check(spec.DisallowedModules(), DeepEquals, []string{"forbidden"}) }