From e7dfd95e7324adc840ad056c4f86f7ed4d19fbe3 Mon Sep 17 00:00:00 2001 From: Kir Kolyshkin Date: Thu, 16 Sep 2021 11:17:07 -0700 Subject: [PATCH 1/4] TestFilterAttributeGettersAndSetters: fixup Commit 23edf066d25 moved the SetBadArchAction(ActInvalid) check to the end of TestFilterAttributeGettersAndSetters. A few subsequent commits added more code before it, until finally commit 541420d6f0c added a conditional t.Skipf before it. As a result, this check is not performed with latest libseccomp. Move it back to where it belongs (before the API/version checks). Signed-off-by: Kir Kolyshkin --- seccomp_test.go | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/seccomp_test.go b/seccomp_test.go index 52af6e8..4cdf29c 100644 --- a/seccomp_test.go +++ b/seccomp_test.go @@ -427,6 +427,11 @@ func TestFilterAttributeGettersAndSetters(t *testing.T) { t.Errorf("Bad arch action was not set correctly!") } + err = filter.SetBadArchAction(ActInvalid) + if err == nil { + t.Errorf("Setting bad arch action to an invalid action should error") + } + err = filter.SetNoNewPrivsBit(false) if err != nil { t.Errorf("Error setting no new privileges bit") @@ -502,11 +507,6 @@ func TestFilterAttributeGettersAndSetters(t *testing.T) { } else if ssb != true { t.Errorf("SSB bit was not set correctly") } - - err = filter.SetBadArchAction(ActInvalid) - if err == nil { - t.Errorf("Setting bad arch action to an invalid action should error") - } } func TestMergeFilters(t *testing.T) { From b34ed642b444456586b8f7814ca868f9021f62b6 Mon Sep 17 00:00:00 2001 From: Kir Kolyshkin Date: Thu, 16 Sep 2021 11:41:58 -0700 Subject: [PATCH 2/4] tests: fix wrt API level and libseccomp version checks 1. Use checkAPI to check for minimal API level and libseccomp version requirements. 2. TestFilterAttributeGettersAndSetters tests multiple things, requiring different API levels and libseccomp versions. It should not use t.Skip in the middle of the test, since the end result is going to be "SKIP" instead of "PASS". In the middle of the test, use return to skip the part of the test. 3. While at it, don't use t.Logf/t.Errorf if there are no %-style arguments. Signed-off-by: Kir Kolyshkin --- seccomp_test.go | 84 +++++++++++++------------------------------------ 1 file changed, 22 insertions(+), 62 deletions(-) diff --git a/seccomp_test.go b/seccomp_test.go index 4cdf29c..bd0933a 100644 --- a/seccomp_test.go +++ b/seccomp_test.go @@ -444,68 +444,40 @@ func TestFilterAttributeGettersAndSetters(t *testing.T) { t.Errorf("No new privileges bit was not set correctly") } - if APILevelIsSupported() { - api, err := GetAPI() - if err != nil { - t.Errorf("Error getting API level: %s", err) - } else if api < 3 { - err = SetAPI(3) - if err != nil { - t.Errorf("Error setting API level: %s", err) - } - } + // Checks that require API level >= 3 and libseccomp >= 2.4.0. + if err := checkAPI(t.Name(), 3, 2, 4, 0); err != nil { + t.Logf("Skipping the rest of the test: %v", err) + return } err = filter.SetLogBit(true) if err != nil { - if !APILevelIsSupported() { - t.Logf("Ignoring failure: %s\n", err) - } else { - t.Errorf("Error setting log bit") - } + t.Errorf("Error setting log bit: %v", err) } log, err := filter.GetLogBit() if err != nil { - if !APILevelIsSupported() { - t.Logf("Ignoring failure: %s\n", err) - } else { - t.Errorf("Error getting log bit") - } + t.Errorf("Error getting log bit: %v", err) } else if log != true { - t.Errorf("Log bit was not set correctly") + t.Error("Log bit was not set correctly") } - if APILevelIsSupported() { - api, err := GetAPI() - if err != nil { - t.Errorf("Error getting API level: %s", err) - } else if api < 4 { - err = SetAPI(4) - if err != nil { - t.Skipf("Skipping test: API level %d is less than 4", api) - } - } + // Checks that require API level >= 4 and libseccomp >= 2.5.0. + if err := checkAPI(t.Name(), 4, 2, 5, 0); err != nil { + t.Logf("Skipping the rest of the test: %v", err) + return } err = filter.SetSSB(true) if err != nil { - if !APILevelIsSupported() { - t.Logf("Ignoring failure: %s\n", err) - } else { - t.Errorf("Error setting SSB bit") - } + t.Errorf("Error setting SSB bit: %v", err) } ssb, err := filter.GetSSB() if err != nil { - if !APILevelIsSupported() { - t.Logf("Ignoring failure: %s\n", err) - } else { - t.Errorf("Error getting SSB bit") - } + t.Errorf("Error getting SSB bit: %v", err) } else if ssb != true { - t.Errorf("SSB bit was not set correctly") + t.Error("SSB bit was not set correctly") } } @@ -645,19 +617,13 @@ func TestLogAct(t *testing.T) { } func subprocessLogAct(t *testing.T) { - expectedPid := syscall.Getpid() - - api, err := GetAPI() - if err != nil { - if !APILevelIsSupported() { - t.Skipf("Skipping test: %s", err) - } - - t.Errorf("Error getting API level: %s", err) - } else if api < 3 { - t.Skipf("Skipping test: API level %d is less than 3", api) + // ActLog requires API >=3 and libseccomp >= 2.4.0. + if err := checkAPI(t.Name(), 3, 2, 4, 0); err != nil { + t.Skip(err) } + expectedPid := syscall.Getpid() + filter, err := NewFilter(ActAllow) if err != nil { t.Errorf("Error creating filter: %s", err) @@ -706,15 +672,9 @@ func TestCreateActKillProcessFilter(t *testing.T) { } func subprocessCreateActKillProcessFilter(t *testing.T) { - api, err := GetAPI() - if err != nil { - if !APILevelIsSupported() { - t.Skipf("Skipping test: %s", err) - } - - t.Errorf("Error getting API level: %s", err) - } else if api < 3 { - t.Skipf("Skipping test: API level %d is less than 3", api) + // Requires API level >= 3 and libseccomp >= 2.4.0 + if err := checkAPI(t.Name(), 3, 2, 4, 0); err != nil { + t.Skip(err) } filter, err := NewFilter(ActKillThread) From 2ea6e39175ba6127ae90175507f4154d2593a458 Mon Sep 17 00:00:00 2001 From: Kir Kolyshkin Date: Thu, 16 Sep 2021 12:14:11 -0700 Subject: [PATCH 3/4] Add support for SCMP_FLTATR_CTL_OPTIMIZE Introduce (*ScmpFilter).GetOptimize and (*ScmpFilter).SetOptimize methods, together with the documentation and trivial tests. Signed-off-by: Kir Kolyshkin --- seccomp.go | 41 +++++++++++++++++++++++++++++++++++++++++ seccomp_internal.go | 21 ++++++++++++++------- seccomp_test.go | 12 ++++++++++++ 3 files changed, 67 insertions(+), 7 deletions(-) diff --git a/seccomp.go b/seccomp.go index ab14bac..a08ebdf 100644 --- a/seccomp.go +++ b/seccomp.go @@ -901,6 +901,22 @@ func (f *ScmpFilter) GetSSB() (bool, error) { return true, nil } +// GetOptimize returns the current optimization level of the filter, +// or an error if an issue was encountered retrieving the value. +// See SetOptimize for more details. +func (f *ScmpFilter) GetOptimize() (int, error) { + level, err := f.getFilterAttr(filterAttrOptimize) + if err != nil { + if e := checkAPI("GetOptimize", 4, 2, 5, 0); e != nil { + err = e + } + + return 0, err + } + + return int(level), nil +} + // SetBadArchAction sets the default action taken on a syscall for an // architecture not in the filter, or an error if an issue was encountered // setting the value. @@ -969,6 +985,31 @@ func (f *ScmpFilter) SetSSB(state bool) error { return err } +// SetOptimize sets optimization level of the seccomp filter. By default +// libseccomp generates a set of sequential "if" statements for each rule in +// the filter. SetSyscallPriority can be used to prioritize the order for the +// default cause. The binary tree optimization sorts by syscall numbers and +// generates consistent O(log n) filter traversal for every rule in the filter. +// The binary tree may be advantageous for large filters. Note that +// SetSyscallPriority is ignored when level == 2. +// +// The different optimization levels are: +// 0: Reserved value, not currently used. +// 1: Rules sorted by priority and complexity (DEFAULT). +// 2: Binary tree sorted by syscall number. +func (f *ScmpFilter) SetOptimize(level int) error { + cLevel := C.uint32_t(level) + + err := f.setFilterAttr(filterAttrOptimize, cLevel) + if err != nil { + if e := checkAPI("SetOptimize", 4, 2, 5, 0); e != nil { + err = e + } + } + + return err +} + // SetSyscallPriority sets a syscall's priority. // This provides a hint to the filter generator in libseccomp about the // importance of this syscall. High-priority syscalls are placed diff --git a/seccomp_internal.go b/seccomp_internal.go index f008047..8d67f0e 100644 --- a/seccomp_internal.go +++ b/seccomp_internal.go @@ -119,16 +119,20 @@ const uint32_t C_ACT_NOTIFY = SCMP_ACT_NOTIFY; #if SCMP_VER_MAJOR == 2 && SCMP_VER_MINOR < 4 #define SCMP_FLTATR_CTL_LOG _SCMP_FLTATR_MIN #endif + +// The following SCMP_FLTATR_* were added in libseccomp v2.5.0. #if SCMP_VER_MAJOR == 2 && SCMP_VER_MINOR < 5 -#define SCMP_FLTATR_CTL_SSB _SCMP_FLTATR_MIN +#define SCMP_FLTATR_CTL_SSB _SCMP_FLTATR_MIN +#define SCMP_FLTATR_CTL_OPTIMIZE _SCMP_FLTATR_MIN #endif -const uint32_t C_ATTRIBUTE_DEFAULT = (uint32_t)SCMP_FLTATR_ACT_DEFAULT; -const uint32_t C_ATTRIBUTE_BADARCH = (uint32_t)SCMP_FLTATR_ACT_BADARCH; -const uint32_t C_ATTRIBUTE_NNP = (uint32_t)SCMP_FLTATR_CTL_NNP; -const uint32_t C_ATTRIBUTE_TSYNC = (uint32_t)SCMP_FLTATR_CTL_TSYNC; -const uint32_t C_ATTRIBUTE_LOG = (uint32_t)SCMP_FLTATR_CTL_LOG; -const uint32_t C_ATTRIBUTE_SSB = (uint32_t)SCMP_FLTATR_CTL_SSB; +const uint32_t C_ATTRIBUTE_DEFAULT = (uint32_t)SCMP_FLTATR_ACT_DEFAULT; +const uint32_t C_ATTRIBUTE_BADARCH = (uint32_t)SCMP_FLTATR_ACT_BADARCH; +const uint32_t C_ATTRIBUTE_NNP = (uint32_t)SCMP_FLTATR_CTL_NNP; +const uint32_t C_ATTRIBUTE_TSYNC = (uint32_t)SCMP_FLTATR_CTL_TSYNC; +const uint32_t C_ATTRIBUTE_LOG = (uint32_t)SCMP_FLTATR_CTL_LOG; +const uint32_t C_ATTRIBUTE_SSB = (uint32_t)SCMP_FLTATR_CTL_SSB; +const uint32_t C_ATTRIBUTE_OPTIMIZE = (uint32_t)SCMP_FLTATR_CTL_OPTIMIZE; const int C_CMP_NE = (int)SCMP_CMP_NE; const int C_CMP_LT = (int)SCMP_CMP_LT; @@ -275,6 +279,7 @@ const ( filterAttrTsync filterAttrLog filterAttrSSB + filterAttrOptimize ) const ( @@ -681,6 +686,8 @@ func (a scmpFilterAttr) toNative() uint32 { return uint32(C.C_ATTRIBUTE_LOG) case filterAttrSSB: return uint32(C.C_ATTRIBUTE_SSB) + case filterAttrOptimize: + return uint32(C.C_ATTRIBUTE_OPTIMIZE) default: return 0x0 } diff --git a/seccomp_test.go b/seccomp_test.go index bd0933a..0fc8b67 100644 --- a/seccomp_test.go +++ b/seccomp_test.go @@ -479,6 +479,18 @@ func TestFilterAttributeGettersAndSetters(t *testing.T) { } else if ssb != true { t.Error("SSB bit was not set correctly") } + + err = filter.SetOptimize(2) + if err != nil { + t.Errorf("Error setting optimize level: %v", err) + } + + level, err := filter.GetOptimize() + if err != nil { + t.Errorf("Error getting optimize level: %v", err) + } else if level != 2 { + t.Error("Optimize level was not set correctly") + } } func TestMergeFilters(t *testing.T) { From bb2ca2970f108faccff9ec58d4b934a2d91dc6d8 Mon Sep 17 00:00:00 2001 From: Kir Kolyshkin Date: Thu, 16 Sep 2021 13:00:56 -0700 Subject: [PATCH 4/4] Add support for SCMP_FLTATR_API_SYSRAWRC Introduce (*ScmpFilter).GetRawRC and (*ScmpFilter).SetRawRC methods, together with the documentation and trivial tests. Signed-off-by: Kir Kolyshkin --- seccomp.go | 39 +++++++++++++++++++++++++++++++++++++++ seccomp_internal.go | 5 +++++ seccomp_test.go | 12 ++++++++++++ 3 files changed, 56 insertions(+) diff --git a/seccomp.go b/seccomp.go index a08ebdf..2d517fe 100644 --- a/seccomp.go +++ b/seccomp.go @@ -917,6 +917,26 @@ func (f *ScmpFilter) GetOptimize() (int, error) { return int(level), nil } +// GetRawRC returns the current state of RawRC flag, or an error +// if an issue was encountered retrieving the value. +// See SetRawRC for more details. +func (f *ScmpFilter) GetRawRC() (bool, error) { + rawrc, err := f.getFilterAttr(filterAttrRawRC) + if err != nil { + if e := checkAPI("GetRawRC", 4, 2, 5, 0); e != nil { + err = e + } + + return false, err + } + + if rawrc == 0 { + return false, nil + } + + return true, nil +} + // SetBadArchAction sets the default action taken on a syscall for an // architecture not in the filter, or an error if an issue was encountered // setting the value. @@ -1010,6 +1030,25 @@ func (f *ScmpFilter) SetOptimize(level int) error { return err } +// SetRawRC sets whether libseccomp should pass system error codes back to the +// caller, instead of the default ECANCELED. Defaults to false. +func (f *ScmpFilter) SetRawRC(state bool) error { + var toSet C.uint32_t = 0x0 + + if state { + toSet = 0x1 + } + + err := f.setFilterAttr(filterAttrRawRC, toSet) + if err != nil { + if e := checkAPI("SetRawRC", 4, 2, 5, 0); e != nil { + err = e + } + } + + return err +} + // SetSyscallPriority sets a syscall's priority. // This provides a hint to the filter generator in libseccomp about the // importance of this syscall. High-priority syscalls are placed diff --git a/seccomp_internal.go b/seccomp_internal.go index 8d67f0e..0615cc0 100644 --- a/seccomp_internal.go +++ b/seccomp_internal.go @@ -124,6 +124,7 @@ const uint32_t C_ACT_NOTIFY = SCMP_ACT_NOTIFY; #if SCMP_VER_MAJOR == 2 && SCMP_VER_MINOR < 5 #define SCMP_FLTATR_CTL_SSB _SCMP_FLTATR_MIN #define SCMP_FLTATR_CTL_OPTIMIZE _SCMP_FLTATR_MIN +#define SCMP_FLTATR_API_SYSRAWRC _SCMP_FLTATR_MIN #endif const uint32_t C_ATTRIBUTE_DEFAULT = (uint32_t)SCMP_FLTATR_ACT_DEFAULT; @@ -133,6 +134,7 @@ const uint32_t C_ATTRIBUTE_TSYNC = (uint32_t)SCMP_FLTATR_CTL_TSYNC; const uint32_t C_ATTRIBUTE_LOG = (uint32_t)SCMP_FLTATR_CTL_LOG; const uint32_t C_ATTRIBUTE_SSB = (uint32_t)SCMP_FLTATR_CTL_SSB; const uint32_t C_ATTRIBUTE_OPTIMIZE = (uint32_t)SCMP_FLTATR_CTL_OPTIMIZE; +const uint32_t C_ATTRIBUTE_SYSRAWRC = (uint32_t)SCMP_FLTATR_API_SYSRAWRC; const int C_CMP_NE = (int)SCMP_CMP_NE; const int C_CMP_LT = (int)SCMP_CMP_LT; @@ -280,6 +282,7 @@ const ( filterAttrLog filterAttrSSB filterAttrOptimize + filterAttrRawRC ) const ( @@ -688,6 +691,8 @@ func (a scmpFilterAttr) toNative() uint32 { return uint32(C.C_ATTRIBUTE_SSB) case filterAttrOptimize: return uint32(C.C_ATTRIBUTE_OPTIMIZE) + case filterAttrRawRC: + return uint32(C.C_ATTRIBUTE_SYSRAWRC) default: return 0x0 } diff --git a/seccomp_test.go b/seccomp_test.go index 0fc8b67..793e5b7 100644 --- a/seccomp_test.go +++ b/seccomp_test.go @@ -491,6 +491,18 @@ func TestFilterAttributeGettersAndSetters(t *testing.T) { } else if level != 2 { t.Error("Optimize level was not set correctly") } + + err = filter.SetRawRC(true) + if err != nil { + t.Errorf("Error setting RawRC flag: %v", err) + } + + rawrc, err := filter.GetRawRC() + if err != nil { + t.Errorf("Error getting RawRC flag: %v", err) + } else if rawrc != true { + t.Error("RawRC flag was not set correctly") + } } func TestMergeFilters(t *testing.T) {