Skip to content

Commit c86754b

Browse files
committed
all: 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 d1ff2ae commit c86754b

File tree

3 files changed

+65
-54
lines changed

3 files changed

+65
-54
lines changed

seccomp.go

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -25,9 +25,9 @@ import "C"
2525
// VersionError represents an error when either the system libseccomp version
2626
// or the kernel version is too old to perform the operation requested.
2727
type VersionError struct {
28-
op string // operation that failed or would fail
29-
minVer string // minimally required libseccomp version
30-
curAPI, minAPI uint // current and minimally required API versions
28+
op string // operation that failed or would fail
29+
major, minor, micro uint // minimally required libseccomp version
30+
curAPI, minAPI uint // current and minimally required API versions
3131
}
3232

3333
func init() {
@@ -39,13 +39,13 @@ func init() {
3939

4040
func (e VersionError) Error() string {
4141
if e.minAPI != 0 {
42-
return fmt.Sprintf("%s requires libseccomp >= %s and API level >= %d "+
42+
return fmt.Sprintf("%s requires libseccomp >= %d.%d.%d and API level >= %d "+
4343
"(current version: %d.%d.%d, API level: %d)",
44-
e.op, e.minVer, e.minAPI,
44+
e.op, e.major, e.minor, e.micro, e.minAPI,
4545
verMajor, verMinor, verMicro, e.curAPI)
4646
}
47-
return fmt.Sprintf("%s requires libseccomp >= %s (current version: %d.%d.%d)",
48-
e.op, e.minVer, verMajor, verMinor, verMicro)
47+
return fmt.Sprintf("%s requires libseccomp >= %d.%d.%d (current version: %d.%d.%d)",
48+
e.op, e.major, e.minor, e.micro, verMajor, verMinor, verMicro)
4949
}
5050

5151
// ScmpArch represents a CPU architecture. Seccomp can restrict syscalls on a
@@ -864,7 +864,7 @@ func (f *ScmpFilter) GetNoNewPrivsBit() (bool, error) {
864864
func (f *ScmpFilter) GetLogBit() (bool, error) {
865865
log, err := f.getFilterAttr(filterAttrLog)
866866
if err != nil {
867-
if e := checkAPI("GetLogBit", 3, "2.4.0"); e != nil {
867+
if e := checkAPI("GetLogBit", 3, 2, 4, 0); e != nil {
868868
err = e
869869
}
870870

@@ -887,7 +887,7 @@ func (f *ScmpFilter) GetLogBit() (bool, error) {
887887
func (f *ScmpFilter) GetSSB() (bool, error) {
888888
ssb, err := f.getFilterAttr(filterAttrSSB)
889889
if err != nil {
890-
if e := checkAPI("GetSSB", 4, "2.5.0"); e != nil {
890+
if e := checkAPI("GetSSB", 4, 2, 5, 0); e != nil {
891891
err = e
892892
}
893893

@@ -940,7 +940,7 @@ func (f *ScmpFilter) SetLogBit(state bool) error {
940940

941941
err := f.setFilterAttr(filterAttrLog, toSet)
942942
if err != nil {
943-
if e := checkAPI("SetLogBit", 3, "2.4.0"); e != nil {
943+
if e := checkAPI("SetLogBit", 3, 2, 4, 0); e != nil {
944944
err = e
945945
}
946946
}
@@ -961,7 +961,7 @@ func (f *ScmpFilter) SetSSB(state bool) error {
961961

962962
err := f.setFilterAttr(filterAttrSSB, toSet)
963963
if err != nil {
964-
if e := checkAPI("SetSSB", 4, "2.5.0"); e != nil {
964+
if e := checkAPI("SetSSB", 4, 2, 5, 0); e != nil {
965965
err = e
966966
}
967967
}

seccomp_internal.go

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

303303
// Nonexported functions
304304

305-
// checkVersion returns an error if libseccomp version used during runtime
306-
// is less than the one required by major, minor, and micro arguments.
307-
// Argument op is arbitrary non-empty operation description, which
308-
// may is used as a part of the error message returned.
305+
// checkVersion returns an error if the libseccomp version being used
306+
// is less than the one specified by major, minor, and micro arguments.
307+
// Argument op is an arbitrary non-empty operation description, which
308+
// is used as a part of the error message returned.
309+
//
310+
// Most users should use checkAPI instead.
309311
func checkVersion(op string, major, minor, micro uint) error {
310312
if (verMajor > major) ||
311313
(verMajor == major && verMinor > minor) ||
312314
(verMajor == major && verMinor == minor && verMicro >= micro) {
313315
return nil
314316
}
315317
return &VersionError{
316-
op: op,
317-
minVer: fmt.Sprintf("%d.%d.%d", major, minor, micro),
318+
op: op,
319+
major: major,
320+
minor: minor,
321+
micro: micro,
318322
}
319323
}
320324

@@ -721,29 +725,32 @@ func (scmpResp *ScmpNotifResp) toNative(resp *C.struct_seccomp_notif_resp) {
721725
resp.flags = C.__u32(scmpResp.Flags)
722726
}
723727

724-
// checkAPI checks if API level is at least minLevel, and returns an error
725-
// otherwise. Argument op is an arbitrary string description the operation,
726-
// and minVersion is the minimally required libseccomp version.
727-
// Both op and minVersion are only used in an error message.
728-
func checkAPI(op string, minLevel uint, minVersion string) error {
729-
// Ignore error from getAPI -- it returns level == 0 in case of error.
728+
// checkAPI checks that both the API level and the seccomp version is equal to
729+
// or greater than the specified minLevel and major, minor, micro,
730+
// respectively, and returns an error otherwise. Argument op is an arbitrary
731+
// non-empty operation description, used as a part of the error message
732+
// returned.
733+
func checkAPI(op string, minLevel uint, major, minor, micro uint) error {
734+
// Ignore error from getAPI, as it returns level == 0 in case of error.
730735
level, _ := getAPI()
731736
if level >= minLevel {
732-
return nil
737+
return checkVersion(op, major, minor, micro)
733738
}
734739
return &VersionError{
735740
op: op,
736741
curAPI: level,
737742
minAPI: minLevel,
738-
minVer: minVersion,
743+
major: major,
744+
minor: minor,
745+
micro: micro,
739746
}
740747
}
741748

742749
// Userspace Notification API
743750
// Calls to C.seccomp_notify* hidden from seccomp.go
744751

745752
func notifSupported() error {
746-
return checkAPI("seccomp notification", 6, "2.5.0")
753+
return checkAPI("seccomp notification", 6, 2, 5, 0)
747754
}
748755

749756
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.
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)