Skip to content

Commit 6e53b43

Browse files
committed
checkAPI: also check version
While testing this package against a recent kernel and older libseccomp library (2.2.x to 2.4.x) it appears that every time we perform an API level check, libseccomp version check is also needed. Modify checkAPI to also call checkVersion. Unify both functions, as well as VersionError, to use minor, major, micro triad everywhere. While at it: - fix checkAPI and checkVersion docs; - simplify their tests, add more test cases. Signed-off-by: Kir Kolyshkin <[email protected]>
1 parent 8f5d66b commit 6e53b43

File tree

3 files changed

+65
-55
lines changed

3 files changed

+65
-55
lines changed

seccomp.go

Lines changed: 11 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -37,9 +37,9 @@ import "C"
3737
// VersionError represents an error when either the system libseccomp version
3838
// or the kernel version is too old to perform the operation requested.
3939
type VersionError struct {
40-
op string // operation that failed or would fail
41-
minVer string // minimally required libseccomp version
42-
curAPI, minAPI uint // current and minimally required API versions
40+
op string // operation that failed or would fail
41+
major, minor, micro uint // minimally required libseccomp version
42+
curAPI, minAPI uint // current and minimally required API versions
4343
}
4444

4545
func init() {
@@ -51,14 +51,13 @@ func init() {
5151

5252
func (e VersionError) Error() string {
5353
if e.minAPI != 0 {
54-
return fmt.Sprintf("%s requires libseccomp >= %s and API level >= %d "+
54+
return fmt.Sprintf("%s requires libseccomp >= %d.%d.%d and API level >= %d "+
5555
"(current version: %d.%d.%d, API level: %d)",
56-
e.op, e.minVer, e.minAPI,
56+
e.op, e.major, e.minor, e.micro, e.minAPI,
5757
verMajor, verMinor, verMicro, e.curAPI)
5858
}
59-
return fmt.Sprintf("%s requires libseccomp >= %s (current version: %d.%d.%d)",
60-
e.op, e.minVer, verMajor, verMinor, verMicro)
61-
59+
return fmt.Sprintf("%s requires libseccomp >= %d.%d.%d (current version: %d.%d.%d)",
60+
e.op, e.major, e.minor, e.micro, verMajor, verMinor, verMicro)
6261
}
6362

6463
// ScmpArch represents a CPU architecture. Seccomp can restrict syscalls on a
@@ -879,7 +878,7 @@ func (f *ScmpFilter) GetNoNewPrivsBit() (bool, error) {
879878
func (f *ScmpFilter) GetLogBit() (bool, error) {
880879
log, err := f.getFilterAttr(filterAttrLog)
881880
if err != nil {
882-
if e := checkAPI("GetLogBit", 3, "2.4.0"); e != nil {
881+
if e := checkAPI("GetLogBit", 3, 2, 4, 0); e != nil {
883882
err = e
884883
}
885884

@@ -902,7 +901,7 @@ func (f *ScmpFilter) GetLogBit() (bool, error) {
902901
func (f *ScmpFilter) GetSSB() (bool, error) {
903902
ssb, err := f.getFilterAttr(filterAttrSSB)
904903
if err != nil {
905-
if e := checkAPI("GetSSB", 4, "2.5.0"); e != nil {
904+
if e := checkAPI("GetSSB", 4, 2, 5, 0); e != nil {
906905
err = e
907906
}
908907

@@ -955,7 +954,7 @@ func (f *ScmpFilter) SetLogBit(state bool) error {
955954

956955
err := f.setFilterAttr(filterAttrLog, toSet)
957956
if err != nil {
958-
if e := checkAPI("SetLogBit", 3, "2.4.0"); e != nil {
957+
if e := checkAPI("SetLogBit", 3, 2, 4, 0); e != nil {
959958
err = e
960959
}
961960
}
@@ -976,7 +975,7 @@ func (f *ScmpFilter) SetSSB(state bool) error {
976975

977976
err := f.setFilterAttr(filterAttrSSB, toSet)
978977
if err != nil {
979-
if e := checkAPI("SetSSB", 4, "2.5.0"); e != nil {
978+
if e := checkAPI("SetSSB", 4, 2, 5, 0); e != nil {
980979
err = e
981980
}
982981
}

seccomp_internal.go

Lines changed: 22 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -308,19 +308,23 @@ var (
308308

309309
// Nonexported functions
310310

311-
// checkVersion returns an error if libseccomp version used during runtime
312-
// is less than the one required by major, minor, and micro arguments.
313-
// Argument op is arbitrary non-empty operation description, which
314-
// may is used as a part of the error message returned.
311+
// checkVersion returns an error if the libseccomp version being used
312+
// is less than the one specified by major, minor, and micro arguments.
313+
// Argument op is an arbitrary non-empty operation description, which
314+
// is used as a part of the error message returned.
315+
//
316+
// Most users should use checkAPI instead.
315317
func checkVersion(op string, major, minor, micro uint) error {
316318
if (verMajor > major) ||
317319
(verMajor == major && verMinor > minor) ||
318320
(verMajor == major && verMinor == minor && verMicro >= micro) {
319321
return nil
320322
}
321323
return &VersionError{
322-
op: op,
323-
minVer: fmt.Sprintf("%d.%d.%d", major, minor, micro),
324+
op: op,
325+
major: major,
326+
minor: minor,
327+
micro: micro,
324328
}
325329
}
326330

@@ -727,29 +731,32 @@ func (scmpResp *ScmpNotifResp) toNative(resp *C.struct_seccomp_notif_resp) {
727731
resp.flags = C.__u32(scmpResp.Flags)
728732
}
729733

730-
// checkAPI checks if API level is at least minLevel, and returns an error
731-
// otherwise. Argument op is an arbitrary string description the operation,
732-
// and minVersion is the minimally required libseccomp version.
733-
// Both op and minVersion are only used in an error message.
734-
func checkAPI(op string, minLevel uint, minVersion string) error {
735-
// Ignore error from getAPI -- it returns level == 0 in case of error.
734+
// checkAPI checks that both the API level and the seccomp version is equal to
735+
// or greater than the specified minLevel and major, minor, micro,
736+
// respectively, and returns an error otherwise. Argument op is an arbitrary
737+
// non-empty operation description, used as a part of the error message
738+
// returned.
739+
func checkAPI(op string, minLevel uint, major, minor, micro uint) error {
740+
// Ignore error from getAPI, as it returns level == 0 in case of error.
736741
level, _ := getAPI()
737742
if level >= minLevel {
738-
return nil
743+
return checkVersion(op, major, minor, micro)
739744
}
740745
return &VersionError{
741746
op: op,
742747
curAPI: level,
743748
minAPI: minLevel,
744-
minVer: minVersion,
749+
major: major,
750+
minor: minor,
751+
micro: micro,
745752
}
746753
}
747754

748755
// Userspace Notification API
749756
// Calls to C.seccomp_notify* hidden from seccomp.go
750757

751758
func notifSupported() error {
752-
return checkAPI("seccomp notification", 6, "2.5.0")
759+
return checkAPI("seccomp notification", 6, 2, 5, 0)
753760
}
754761

755762
func (f *ScmpFilter) getNotifFd() (ScmpFd, error) {

seccomp_ver_test.go

Lines changed: 32 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -6,25 +6,26 @@ import (
66

77
func TestCheckVersion(t *testing.T) {
88
for _, tc := range []struct {
9-
// test input
9+
// input
1010
op string
1111
x, y, z uint
12-
// test output
13-
res string // empty string if no error is expected
12+
// expectations
13+
isErr bool
1414
}{
15-
{
16-
op: "frobnicate", x: 100, y: 99, z: 7,
17-
res: "frobnicate requires libseccomp >= 100.99.7 (current version: ",
18-
},
19-
{
20-
op: "old-ver", x: 2, y: 2, z: 0, // 2.2.0 is guaranteed to succeed
21-
},
15+
{op: "verNew", x: 100, y: 99, z: 7, isErr: true},
16+
{op: "verMajor+1", x: verMajor + 1, isErr: true},
17+
{op: "verMinor+1", x: verMajor, y: verMinor + 1, isErr: true},
18+
{op: "verMicro+1", x: verMajor, y: verMinor, z: verMicro + 1, isErr: true},
19+
// Current version is guaranteed to succeed.
20+
{op: "verCur", x: verMajor, y: verMinor, z: verMicro},
21+
// 2.2.0 is guaranteed to succeed as it's minimally supported version.
22+
{op: "verOld", x: 2, y: 2, z: 0},
2223
} {
2324
err := checkVersion(tc.op, tc.x, tc.y, tc.z)
2425
t.Log(err)
25-
if tc.res != "" { // error expected
26+
if tc.isErr {
2627
if err == nil {
27-
t.Errorf("case %s: expected %q-like error, got nil", tc.op, tc.res)
28+
t.Errorf("case %s: expected error, got nil", tc.op)
2829
}
2930
continue
3031
}
@@ -35,27 +36,30 @@ func TestCheckVersion(t *testing.T) {
3536
}
3637

3738
func TestCheckAPI(t *testing.T) {
39+
curAPI, _ := getAPI()
3840
for _, tc := range []struct {
39-
// test input
40-
op string
41-
level uint
42-
ver string
43-
// test output
44-
res string // empty string if no error is expected
41+
// input
42+
op string
43+
level uint
44+
x, y, z uint
45+
// expectations
46+
isErr bool
4547
}{
46-
{
47-
op: "deviate", level: 99, ver: "100.99.88",
48-
res: "frobnicate requires libseccomp >= 100.99.7 (current version: ",
49-
},
50-
{
51-
op: "api-0", level: 0, // API 0 will succeed
52-
},
48+
{op: "apiHigh", level: 99, isErr: true},
49+
{op: "api+1", level: curAPI + 1, isErr: true},
50+
// Cases that should succeed.
51+
{op: "apiCur", level: curAPI},
52+
{op: "api0", level: 0},
53+
{op: "apiCur_verCur", level: curAPI, x: verMajor, y: verMinor, z: verMicro},
54+
// Adequate API level but version is too high.
55+
{op: "verHigh", level: 0, x: 99, isErr: true},
56+
// Other cases with version are checked by testCheckVersion.
5357
} {
54-
err := checkAPI(tc.op, tc.level, tc.ver)
58+
err := checkAPI(tc.op, tc.level, tc.x, tc.y, tc.z)
5559
t.Log(err)
56-
if tc.res != "" { // error expected
60+
if tc.isErr {
5761
if err == nil {
58-
t.Errorf("case %s: expected %q-like error, got nil", tc.op, tc.res)
62+
t.Errorf("case %s: expected error, got nil", tc.op)
5963
}
6064
continue
6165
}

0 commit comments

Comments
 (0)