Skip to content

Commit 56d82fb

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 5f03cdf commit 56d82fb

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
@@ -305,19 +305,23 @@ var (
305305

306306
// Nonexported functions
307307

308-
// checkVersion returns an error if libseccomp version used during runtime
309-
// is less than the one required by major, minor, and micro arguments.
310-
// Argument op is arbitrary non-empty operation description, which
311-
// may is used as a part of the error message returned.
308+
// checkVersion returns an error if the libseccomp version being used
309+
// is less than the one specified by major, minor, and micro arguments.
310+
// Argument op is an arbitrary non-empty operation description, which
311+
// is used as a part of the error message returned.
312+
//
313+
// Most users should use checkAPI instead.
312314
func checkVersion(op string, major, minor, micro uint) error {
313315
if (verMajor > major) ||
314316
(verMajor == major && verMinor > minor) ||
315317
(verMajor == major && verMinor == minor && verMicro >= micro) {
316318
return nil
317319
}
318320
return &VersionError{
319-
op: op,
320-
minVer: fmt.Sprintf("%d.%d.%d", major, minor, micro),
321+
op: op,
322+
major: major,
323+
minor: minor,
324+
micro: micro,
321325
}
322326
}
323327

@@ -724,29 +728,32 @@ func (scmpResp *ScmpNotifResp) toNative(resp *C.struct_seccomp_notif_resp) {
724728
resp.flags = C.__u32(scmpResp.Flags)
725729
}
726730

727-
// checkAPI checks if API level is at least minLevel, and returns an error
728-
// otherwise. Argument op is an arbitrary string description the operation,
729-
// and minVersion is the minimally required libseccomp version.
730-
// Both op and minVersion are only used in an error message.
731-
func checkAPI(op string, minLevel uint, minVersion string) error {
732-
// Ignore error from getAPI -- it returns level == 0 in case of error.
731+
// checkAPI checks that both the API level and the seccomp version is equal to
732+
// or greater than the specified minLevel and major, minor, micro,
733+
// respectively, and returns an error otherwise. Argument op is an arbitrary
734+
// non-empty operation description, used as a part of the error message
735+
// returned.
736+
func checkAPI(op string, minLevel uint, major, minor, micro uint) error {
737+
// Ignore error from getAPI, as it returns level == 0 in case of error.
733738
level, _ := getAPI()
734739
if level >= minLevel {
735-
return nil
740+
return checkVersion(op, major, minor, micro)
736741
}
737742
return &VersionError{
738743
op: op,
739744
curAPI: level,
740745
minAPI: minLevel,
741-
minVer: minVersion,
746+
major: major,
747+
minor: minor,
748+
micro: micro,
742749
}
743750
}
744751

745752
// Userspace Notification API
746753
// Calls to C.seccomp_notify* hidden from seccomp.go
747754

748755
func notifSupported() error {
749-
return checkAPI("seccomp notification", 6, "2.5.0")
756+
return checkAPI("seccomp notification", 6, 2, 5, 0)
750757
}
751758

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