Skip to content

Commit 6cfc04e

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 f31d034 commit 6cfc04e

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
@@ -35,9 +35,9 @@ import "C"
3535
// VersionError represents an error when either the system libseccomp version
3636
// or the kernel version is too old to perform the operation requested.
3737
type VersionError struct {
38-
op string // operation that failed or would fail
39-
minVer string // minimally required libseccomp version
40-
curAPI, minAPI uint // current and minimally required API versions
38+
op string // operation that failed or would fail
39+
major, minor, micro uint // minimally required libseccomp version
40+
curAPI, minAPI uint // current and minimally required API versions
4141
}
4242

4343
func init() {
@@ -49,13 +49,13 @@ func init() {
4949

5050
func (e VersionError) Error() string {
5151
if e.minAPI != 0 {
52-
return fmt.Sprintf("%s requires libseccomp >= %s and API level >= %d "+
52+
return fmt.Sprintf("%s requires libseccomp >= %d.%d.%d and API level >= %d "+
5353
"(current version: %d.%d.%d, API level: %d)",
54-
e.op, e.minVer, e.minAPI,
54+
e.op, e.major, e.minor, e.micro, e.minAPI,
5555
verMajor, verMinor, verMicro, e.curAPI)
5656
}
57-
return fmt.Sprintf("%s requires libseccomp >= %s (current version: %d.%d.%d)",
58-
e.op, e.minVer, verMajor, verMinor, verMicro)
57+
return fmt.Sprintf("%s requires libseccomp >= %d.%d.%d (current version: %d.%d.%d)",
58+
e.op, e.major, e.minor, e.micro, verMajor, verMinor, verMicro)
5959
}
6060

6161
// ScmpArch represents a CPU architecture. Seccomp can restrict syscalls on a
@@ -874,7 +874,7 @@ func (f *ScmpFilter) GetNoNewPrivsBit() (bool, error) {
874874
func (f *ScmpFilter) GetLogBit() (bool, error) {
875875
log, err := f.getFilterAttr(filterAttrLog)
876876
if err != nil {
877-
if e := checkAPI("GetLogBit", 3, "2.4.0"); e != nil {
877+
if e := checkAPI("GetLogBit", 3, 2, 4, 0); e != nil {
878878
err = e
879879
}
880880

@@ -897,7 +897,7 @@ func (f *ScmpFilter) GetLogBit() (bool, error) {
897897
func (f *ScmpFilter) GetSSB() (bool, error) {
898898
ssb, err := f.getFilterAttr(filterAttrSSB)
899899
if err != nil {
900-
if e := checkAPI("GetSSB", 4, "2.5.0"); e != nil {
900+
if e := checkAPI("GetSSB", 4, 2, 5, 0); e != nil {
901901
err = e
902902
}
903903

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

951951
err := f.setFilterAttr(filterAttrLog, toSet)
952952
if err != nil {
953-
if e := checkAPI("SetLogBit", 3, "2.4.0"); e != nil {
953+
if e := checkAPI("SetLogBit", 3, 2, 4, 0); e != nil {
954954
err = e
955955
}
956956
}
@@ -971,7 +971,7 @@ func (f *ScmpFilter) SetSSB(state bool) error {
971971

972972
err := f.setFilterAttr(filterAttrSSB, toSet)
973973
if err != nil {
974-
if e := checkAPI("SetSSB", 4, "2.5.0"); e != nil {
974+
if e := checkAPI("SetSSB", 4, 2, 5, 0); e != nil {
975975
err = e
976976
}
977977
}

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