Browse code

bump libseccomp-golang v0.9.1

full diff: https://github.com/seccomp/libseccomp-golang/compare/32f571b70023028bd57d9288c20efbcb237f3ce0...689e3c1541a84461afc49c1c87352a6cedf72e9c

Release notes:

* Version 0.9.1 - May 21, 2019
- Minimum supported version of libseccomp bumped to v2.2.0
- Use Libseccomp's `seccomp_version` API to retrieve library version
- Unconditionally set TSync attribute for filters, due to Go's heavily threaded nature
- Fix CVE-2017-18367 - Multiple syscall arguments were incorrectly combined with logical-OR, instead of logical-AND
- Fix a failure to build on Debian-based distributions due to CGo code
- Fix unit test failures on 32-bit architectures
- Improve several errors to be more verbose about their causes
- Add support for SCMP_ACT_LOG (with libseccomp versions 2.4.x and higher), permitting syscalls but logging their execution
- Add support for SCMP_FLTATR_CTL_LOG (with libseccomp versions 2.4.x and higher), logging not-allowed actions when they are denied

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>

Sebastiaan van Stijn authored on 2019/06/25 17:11:02
Showing 4 changed files
... ...
@@ -83,7 +83,7 @@ google.golang.org/grpc                              25c4f928eaa6d96443009bd84238
83 83
 github.com/opencontainers/runc                      425e105d5a03fabd737a126ad93d62a9eeede87f # v1.0.0-rc8
84 84
 github.com/opencontainers/runtime-spec              29686dbc5559d93fb1ef402eeda3e35c38d75af4 # v1.0.1-59-g29686db
85 85
 github.com/opencontainers/image-spec                d60099175f88c47cd379c4738d158884749ed235 # v1.0.1
86
-github.com/seccomp/libseccomp-golang                32f571b70023028bd57d9288c20efbcb237f3ce0
86
+github.com/seccomp/libseccomp-golang                689e3c1541a84461afc49c1c87352a6cedf72e9c # v0.9.1
87 87
 
88 88
 # libcontainer deps (see src/github.com/opencontainers/runc/Godeps/Godeps.json)
89 89
 github.com/coreos/go-systemd                        39ca1b05acc7ad1220e09f133283b8859a8b71ab # v17
... ...
@@ -24,3 +24,28 @@ please note that a Google account is not required to subscribe to the mailing
24 24
 list.
25 25
 
26 26
 	-> https://groups.google.com/d/forum/libseccomp
27
+
28
+Documentation is also available at:
29
+
30
+	-> https://godoc.org/github.com/seccomp/libseccomp-golang
31
+
32
+* Installing the package
33
+
34
+The libseccomp-golang bindings require at least Go v1.2.1 and GCC v4.8.4;
35
+earlier versions may yield unpredictable results.  If you meet these
36
+requirements you can install this package using the command below:
37
+
38
+	$ go get github.com/seccomp/libseccomp-golang
39
+
40
+* Testing the Library
41
+
42
+A number of tests and lint related recipes are provided in the Makefile, if
43
+you want to run the standard regression tests, you can excute the following:
44
+
45
+	$ make check
46
+
47
+In order to execute the 'make lint' recipe the 'golint' tool is needed, it
48
+can be found at:
49
+
50
+	-> https://github.com/golang/lint
51
+
... ...
@@ -27,6 +27,28 @@ import "C"
27 27
 
28 28
 // Exported types
29 29
 
30
+// VersionError denotes that the system libseccomp version is incompatible
31
+// with this package.
32
+type VersionError struct {
33
+	message string
34
+	minimum string
35
+}
36
+
37
+func (e VersionError) Error() string {
38
+	format := "Libseccomp version too low: "
39
+	if e.message != "" {
40
+		format += e.message + ": "
41
+	}
42
+	format += "minimum supported is "
43
+	if e.minimum != "" {
44
+		format += e.minimum + ": "
45
+	} else {
46
+		format += "2.2.0: "
47
+	}
48
+	format += "detected %d.%d.%d"
49
+	return fmt.Sprintf(format, verMajor, verMinor, verMicro)
50
+}
51
+
30 52
 // ScmpArch represents a CPU architecture. Seccomp can restrict syscalls on a
31 53
 // per-architecture basis.
32 54
 type ScmpArch uint
... ...
@@ -54,8 +76,8 @@ type ScmpSyscall int32
54 54
 
55 55
 const (
56 56
 	// Valid architectures recognized by libseccomp
57
-	// ARM64 and all MIPS architectures are unsupported by versions of the
58
-	// library before v2.2 and will return errors if used
57
+	// PowerPC and S390(x) architectures are unavailable below library version
58
+	// v2.3.0 and will returns errors if used with incompatible libraries
59 59
 
60 60
 	// ArchInvalid is a placeholder to ensure uninitialized ScmpArch
61 61
 	// variables are invalid
... ...
@@ -115,6 +137,10 @@ const (
115 115
 	ActTrace ScmpAction = iota
116 116
 	// ActAllow permits the syscall to continue execution
117 117
 	ActAllow ScmpAction = iota
118
+	// ActLog permits the syscall to continue execution after logging it.
119
+	// This action is only usable when libseccomp API level 3 or higher is
120
+	// supported.
121
+	ActLog ScmpAction = iota
118 122
 )
119 123
 
120 124
 const (
... ...
@@ -151,6 +177,10 @@ const (
151 151
 // GetArchFromString returns an ScmpArch constant from a string representing an
152 152
 // architecture
153 153
 func GetArchFromString(arch string) (ScmpArch, error) {
154
+	if err := ensureSupportedVersion(); err != nil {
155
+		return ArchInvalid, err
156
+	}
157
+
154 158
 	switch strings.ToLower(arch) {
155 159
 	case "x86":
156 160
 		return ArchX86, nil
... ...
@@ -185,7 +215,7 @@ func GetArchFromString(arch string) (ScmpArch, error) {
185 185
 	case "s390x":
186 186
 		return ArchS390X, nil
187 187
 	default:
188
-		return ArchInvalid, fmt.Errorf("cannot convert unrecognized string %s", arch)
188
+		return ArchInvalid, fmt.Errorf("cannot convert unrecognized string %q", arch)
189 189
 	}
190 190
 }
191 191
 
... ...
@@ -229,7 +259,7 @@ func (a ScmpArch) String() string {
229 229
 	case ArchInvalid:
230 230
 		return "Invalid architecture"
231 231
 	default:
232
-		return "Unknown architecture"
232
+		return fmt.Sprintf("Unknown architecture %#x", uint(a))
233 233
 	}
234 234
 }
235 235
 
... ...
@@ -253,7 +283,7 @@ func (a ScmpCompareOp) String() string {
253 253
 	case CompareInvalid:
254 254
 		return "Invalid comparison operator"
255 255
 	default:
256
-		return "Unrecognized comparison operator"
256
+		return fmt.Sprintf("Unrecognized comparison operator %#x", uint(a))
257 257
 	}
258 258
 }
259 259
 
... ...
@@ -269,10 +299,12 @@ func (a ScmpAction) String() string {
269 269
 	case ActTrace:
270 270
 		return fmt.Sprintf("Action: Notify tracing processes with code %d",
271 271
 			(a >> 16))
272
+	case ActLog:
273
+		return "Action: Log system call"
272 274
 	case ActAllow:
273 275
 		return "Action: Allow system call"
274 276
 	default:
275
-		return "Unrecognized Action"
277
+		return fmt.Sprintf("Unrecognized Action %#x", uint(a))
276 278
 	}
277 279
 }
278 280
 
... ...
@@ -298,10 +330,29 @@ func (a ScmpAction) GetReturnCode() int16 {
298 298
 // GetLibraryVersion returns the version of the library the bindings are built
299 299
 // against.
300 300
 // The version is formatted as follows: Major.Minor.Micro
301
-func GetLibraryVersion() (major, minor, micro int) {
301
+func GetLibraryVersion() (major, minor, micro uint) {
302 302
 	return verMajor, verMinor, verMicro
303 303
 }
304 304
 
305
+// GetApi returns the API level supported by the system.
306
+// Returns a positive int containing the API level, or 0 with an error if the
307
+// API level could not be detected due to the library being older than v2.4.0.
308
+// See the seccomp_api_get(3) man page for details on available API levels:
309
+// https://github.com/seccomp/libseccomp/blob/master/doc/man/man3/seccomp_api_get.3
310
+func GetApi() (uint, error) {
311
+	return getApi()
312
+}
313
+
314
+// SetApi forcibly sets the API level. General use of this function is strongly
315
+// discouraged.
316
+// Returns an error if the API level could not be set. An error is always
317
+// returned if the library is older than v2.4.0
318
+// See the seccomp_api_get(3) man page for details on available API levels:
319
+// https://github.com/seccomp/libseccomp/blob/master/doc/man/man3/seccomp_api_get.3
320
+func SetApi(api uint) error {
321
+	return setApi(api)
322
+}
323
+
305 324
 // Syscall functions
306 325
 
307 326
 // GetName retrieves the name of a syscall from its number.
... ...
@@ -324,7 +375,7 @@ func (s ScmpSyscall) GetNameByArch(arch ScmpArch) (string, error) {
324 324
 
325 325
 	cString := C.seccomp_syscall_resolve_num_arch(arch.toNative(), C.int(s))
326 326
 	if cString == nil {
327
-		return "", fmt.Errorf("could not resolve syscall name")
327
+		return "", fmt.Errorf("could not resolve syscall name for %#x", int32(s))
328 328
 	}
329 329
 	defer C.free(unsafe.Pointer(cString))
330 330
 
... ...
@@ -338,12 +389,16 @@ func (s ScmpSyscall) GetNameByArch(arch ScmpArch) (string, error) {
338 338
 // Returns the number of the syscall, or an error if no syscall with that name
339 339
 // was found.
340 340
 func GetSyscallFromName(name string) (ScmpSyscall, error) {
341
+	if err := ensureSupportedVersion(); err != nil {
342
+		return 0, err
343
+	}
344
+
341 345
 	cString := C.CString(name)
342 346
 	defer C.free(unsafe.Pointer(cString))
343 347
 
344 348
 	result := C.seccomp_syscall_resolve_name(cString)
345 349
 	if result == scmpError {
346
-		return 0, fmt.Errorf("could not resolve name to syscall")
350
+		return 0, fmt.Errorf("could not resolve name to syscall: %q", name)
347 351
 	}
348 352
 
349 353
 	return ScmpSyscall(result), nil
... ...
@@ -355,6 +410,9 @@ func GetSyscallFromName(name string) (ScmpSyscall, error) {
355 355
 // Returns the number of the syscall, or an error if an invalid architecture is
356 356
 // passed or a syscall with that name was not found.
357 357
 func GetSyscallFromNameByArch(name string, arch ScmpArch) (ScmpSyscall, error) {
358
+	if err := ensureSupportedVersion(); err != nil {
359
+		return 0, err
360
+	}
358 361
 	if err := sanitizeArch(arch); err != nil {
359 362
 		return 0, err
360 363
 	}
... ...
@@ -364,7 +422,7 @@ func GetSyscallFromNameByArch(name string, arch ScmpArch) (ScmpSyscall, error) {
364 364
 
365 365
 	result := C.seccomp_syscall_resolve_name_arch(arch.toNative(), cString)
366 366
 	if result == scmpError {
367
-		return 0, fmt.Errorf("could not resolve name to syscall")
367
+		return 0, fmt.Errorf("could not resolve name to syscall: %q on %v", name, arch)
368 368
 	}
369 369
 
370 370
 	return ScmpSyscall(result), nil
... ...
@@ -386,12 +444,16 @@ func GetSyscallFromNameByArch(name string, arch ScmpArch) (ScmpSyscall, error) {
386 386
 func MakeCondition(arg uint, comparison ScmpCompareOp, values ...uint64) (ScmpCondition, error) {
387 387
 	var condStruct ScmpCondition
388 388
 
389
+	if err := ensureSupportedVersion(); err != nil {
390
+		return condStruct, err
391
+	}
392
+
389 393
 	if comparison == CompareInvalid {
390 394
 		return condStruct, fmt.Errorf("invalid comparison operator")
391 395
 	} else if arg > 5 {
392
-		return condStruct, fmt.Errorf("syscalls only have up to 6 arguments")
396
+		return condStruct, fmt.Errorf("syscalls only have up to 6 arguments (%d given)", arg)
393 397
 	} else if len(values) > 2 {
394
-		return condStruct, fmt.Errorf("conditions can have at most 2 arguments")
398
+		return condStruct, fmt.Errorf("conditions can have at most 2 arguments (%d given)", len(values))
395 399
 	} else if len(values) == 0 {
396 400
 		return condStruct, fmt.Errorf("must provide at least one value to compare against")
397 401
 	}
... ...
@@ -413,6 +475,10 @@ func MakeCondition(arg uint, comparison ScmpCompareOp, values ...uint64) (ScmpCo
413 413
 // GetNativeArch returns architecture token representing the native kernel
414 414
 // architecture
415 415
 func GetNativeArch() (ScmpArch, error) {
416
+	if err := ensureSupportedVersion(); err != nil {
417
+		return ArchInvalid, err
418
+	}
419
+
416 420
 	arch := C.seccomp_arch_native()
417 421
 
418 422
 	return archFromNative(arch)
... ...
@@ -435,6 +501,10 @@ type ScmpFilter struct {
435 435
 // Returns a reference to a valid filter context, or nil and an error if the
436 436
 // filter context could not be created or an invalid default action was given.
437 437
 func NewFilter(defaultAction ScmpAction) (*ScmpFilter, error) {
438
+	if err := ensureSupportedVersion(); err != nil {
439
+		return nil, err
440
+	}
441
+
438 442
 	if err := sanitizeAction(defaultAction); err != nil {
439 443
 		return nil, err
440 444
 	}
... ...
@@ -449,6 +519,13 @@ func NewFilter(defaultAction ScmpAction) (*ScmpFilter, error) {
449 449
 	filter.valid = true
450 450
 	runtime.SetFinalizer(filter, filterFinalizer)
451 451
 
452
+	// Enable TSync so all goroutines will receive the same rules
453
+	// If the kernel does not support TSYNC, allow us to continue without error
454
+	if err := filter.setFilterAttr(filterAttrTsync, 0x1); err != nil && err != syscall.ENOTSUP {
455
+		filter.Release()
456
+		return nil, fmt.Errorf("could not create filter - error setting tsync bit: %v", err)
457
+	}
458
+
452 459
 	return filter, nil
453 460
 }
454 461
 
... ...
@@ -505,7 +582,7 @@ func (f *ScmpFilter) Release() {
505 505
 // The source filter src will be released as part of the process, and will no
506 506
 // longer be usable or valid after this call.
507 507
 // To be merged, filters must NOT share any architectures, and all their
508
-// attributes (Default Action, Bad Arch Action, No New Privs and TSync bools)
508
+// attributes (Default Action, Bad Arch Action, and No New Privs bools)
509 509
 // must match.
510 510
 // The filter src will be merged into the filter this is called on.
511 511
 // The architectures of the src filter not present in the destination, and all
... ...
@@ -678,24 +755,24 @@ func (f *ScmpFilter) GetNoNewPrivsBit() (bool, error) {
678 678
 	return true, nil
679 679
 }
680 680
 
681
-// GetTsyncBit returns whether Thread Synchronization will be enabled on the
682
-// filter being loaded, or an error if an issue was encountered retrieving the
683
-// value.
684
-// Thread Sync ensures that all members of the thread group of the calling
685
-// process will share the same Seccomp filter set.
686
-// Tsync is a fairly recent addition to the Linux kernel and older kernels
687
-// lack support. If the running kernel does not support Tsync and it is
688
-// requested in a filter, Libseccomp will not enable TSync support and will
689
-// proceed as normal.
690
-// This function is unavailable before v2.2 of libseccomp and will return an
691
-// error.
692
-func (f *ScmpFilter) GetTsyncBit() (bool, error) {
693
-	tSync, err := f.getFilterAttr(filterAttrTsync)
681
+// GetLogBit returns the current state the Log bit will be set to on the filter
682
+// being loaded, or an error if an issue was encountered retrieving the value.
683
+// The Log bit tells the kernel that all actions taken by the filter, with the
684
+// exception of ActAllow, should be logged.
685
+// The Log bit is only usable when libseccomp API level 3 or higher is
686
+// supported.
687
+func (f *ScmpFilter) GetLogBit() (bool, error) {
688
+	log, err := f.getFilterAttr(filterAttrLog)
694 689
 	if err != nil {
690
+		api, apiErr := getApi()
691
+		if (apiErr != nil && api == 0) || (apiErr == nil && api < 3) {
692
+			return false, fmt.Errorf("getting the log bit is only supported in libseccomp 2.4.0 and newer with API level 3 or higher")
693
+		}
694
+
695 695
 		return false, err
696 696
 	}
697 697
 
698
-	if tSync == 0 {
698
+	if log == 0 {
699 699
 		return false, nil
700 700
 	}
701 701
 
... ...
@@ -728,25 +805,26 @@ func (f *ScmpFilter) SetNoNewPrivsBit(state bool) error {
728 728
 	return f.setFilterAttr(filterAttrNNP, toSet)
729 729
 }
730 730
 
731
-// SetTsync sets whether Thread Synchronization will be enabled on the filter
732
-// being loaded. Returns an error if setting Tsync failed, or the filter is
733
-// invalid.
734
-// Thread Sync ensures that all members of the thread group of the calling
735
-// process will share the same Seccomp filter set.
736
-// Tsync is a fairly recent addition to the Linux kernel and older kernels
737
-// lack support. If the running kernel does not support Tsync and it is
738
-// requested in a filter, Libseccomp will not enable TSync support and will
739
-// proceed as normal.
740
-// This function is unavailable before v2.2 of libseccomp and will return an
741
-// error.
742
-func (f *ScmpFilter) SetTsync(enable bool) error {
731
+// SetLogBit sets the state of the Log bit, which will be applied on filter
732
+// load, or an error if an issue was encountered setting the value.
733
+// The Log bit is only usable when libseccomp API level 3 or higher is
734
+// supported.
735
+func (f *ScmpFilter) SetLogBit(state bool) error {
743 736
 	var toSet C.uint32_t = 0x0
744 737
 
745
-	if enable {
738
+	if state {
746 739
 		toSet = 0x1
747 740
 	}
748 741
 
749
-	return f.setFilterAttr(filterAttrTsync, toSet)
742
+	err := f.setFilterAttr(filterAttrLog, toSet)
743
+	if err != nil {
744
+		api, apiErr := getApi()
745
+		if (apiErr != nil && api == 0) || (apiErr == nil && api < 3) {
746
+			return fmt.Errorf("setting the log bit is only supported in libseccomp 2.4.0 and newer with API level 3 or higher")
747
+		}
748
+	}
749
+
750
+	return err
750 751
 }
751 752
 
752 753
 // SetSyscallPriority sets a syscall's priority.
... ...
@@ -7,7 +7,6 @@ package seccomp
7 7
 
8 8
 import (
9 9
 	"fmt"
10
-	"os"
11 10
 	"syscall"
12 11
 )
13 12
 
... ...
@@ -17,47 +16,20 @@ import (
17 17
 
18 18
 // #cgo pkg-config: libseccomp
19 19
 /*
20
+#include <errno.h>
20 21
 #include <stdlib.h>
21 22
 #include <seccomp.h>
22 23
 
23 24
 #if SCMP_VER_MAJOR < 2
24
-#error Minimum supported version of Libseccomp is v2.1.0
25
-#elif SCMP_VER_MAJOR == 2 && SCMP_VER_MINOR < 1
26
-#error Minimum supported version of Libseccomp is v2.1.0
25
+#error Minimum supported version of Libseccomp is v2.2.0
26
+#elif SCMP_VER_MAJOR == 2 && SCMP_VER_MINOR < 2
27
+#error Minimum supported version of Libseccomp is v2.2.0
27 28
 #endif
28 29
 
29 30
 #define ARCH_BAD ~0
30 31
 
31 32
 const uint32_t C_ARCH_BAD = ARCH_BAD;
32 33
 
33
-#ifndef SCMP_ARCH_AARCH64
34
-#define SCMP_ARCH_AARCH64 ARCH_BAD
35
-#endif
36
-
37
-#ifndef SCMP_ARCH_MIPS
38
-#define SCMP_ARCH_MIPS ARCH_BAD
39
-#endif
40
-
41
-#ifndef SCMP_ARCH_MIPS64
42
-#define SCMP_ARCH_MIPS64 ARCH_BAD
43
-#endif
44
-
45
-#ifndef SCMP_ARCH_MIPS64N32
46
-#define SCMP_ARCH_MIPS64N32 ARCH_BAD
47
-#endif
48
-
49
-#ifndef SCMP_ARCH_MIPSEL
50
-#define SCMP_ARCH_MIPSEL ARCH_BAD
51
-#endif
52
-
53
-#ifndef SCMP_ARCH_MIPSEL64
54
-#define SCMP_ARCH_MIPSEL64 ARCH_BAD
55
-#endif
56
-
57
-#ifndef SCMP_ARCH_MIPSEL64N32
58
-#define SCMP_ARCH_MIPSEL64N32 ARCH_BAD
59
-#endif
60
-
61 34
 #ifndef SCMP_ARCH_PPC
62 35
 #define SCMP_ARCH_PPC ARCH_BAD
63 36
 #endif
... ...
@@ -96,22 +68,29 @@ const uint32_t C_ARCH_PPC64LE      = SCMP_ARCH_PPC64LE;
96 96
 const uint32_t C_ARCH_S390         = SCMP_ARCH_S390;
97 97
 const uint32_t C_ARCH_S390X        = SCMP_ARCH_S390X;
98 98
 
99
+#ifndef SCMP_ACT_LOG
100
+#define SCMP_ACT_LOG 0x7ffc0000U
101
+#endif
102
+
99 103
 const uint32_t C_ACT_KILL          = SCMP_ACT_KILL;
100 104
 const uint32_t C_ACT_TRAP          = SCMP_ACT_TRAP;
101 105
 const uint32_t C_ACT_ERRNO         = SCMP_ACT_ERRNO(0);
102 106
 const uint32_t C_ACT_TRACE         = SCMP_ACT_TRACE(0);
107
+const uint32_t C_ACT_LOG           = SCMP_ACT_LOG;
103 108
 const uint32_t C_ACT_ALLOW         = SCMP_ACT_ALLOW;
104 109
 
105
-// If TSync is not supported, make sure it doesn't map to a supported filter attribute
106
-// Don't worry about major version < 2, the minimum version checks should catch that case
107
-#if SCMP_VER_MAJOR == 2 && SCMP_VER_MINOR < 2
108
-#define SCMP_FLTATR_CTL_TSYNC _SCMP_CMP_MIN
110
+// The libseccomp SCMP_FLTATR_CTL_LOG member of the scmp_filter_attr enum was
111
+// added in v2.4.0
112
+#if (SCMP_VER_MAJOR < 2) || \
113
+    (SCMP_VER_MAJOR == 2 && SCMP_VER_MINOR < 4)
114
+#define SCMP_FLTATR_CTL_LOG _SCMP_FLTATR_MIN
109 115
 #endif
110 116
 
111 117
 const uint32_t C_ATTRIBUTE_DEFAULT = (uint32_t)SCMP_FLTATR_ACT_DEFAULT;
112 118
 const uint32_t C_ATTRIBUTE_BADARCH = (uint32_t)SCMP_FLTATR_ACT_BADARCH;
113 119
 const uint32_t C_ATTRIBUTE_NNP     = (uint32_t)SCMP_FLTATR_CTL_NNP;
114 120
 const uint32_t C_ATTRIBUTE_TSYNC   = (uint32_t)SCMP_FLTATR_CTL_TSYNC;
121
+const uint32_t C_ATTRIBUTE_LOG     = (uint32_t)SCMP_FLTATR_CTL_LOG;
115 122
 
116 123
 const int      C_CMP_NE            = (int)SCMP_CMP_NE;
117 124
 const int      C_CMP_LT            = (int)SCMP_CMP_LT;
... ...
@@ -125,25 +104,80 @@ const int      C_VERSION_MAJOR     = SCMP_VER_MAJOR;
125 125
 const int      C_VERSION_MINOR     = SCMP_VER_MINOR;
126 126
 const int      C_VERSION_MICRO     = SCMP_VER_MICRO;
127 127
 
128
+#if SCMP_VER_MAJOR == 2 && SCMP_VER_MINOR >= 3
129
+unsigned int get_major_version()
130
+{
131
+        return seccomp_version()->major;
132
+}
133
+
134
+unsigned int get_minor_version()
135
+{
136
+        return seccomp_version()->minor;
137
+}
138
+
139
+unsigned int get_micro_version()
140
+{
141
+        return seccomp_version()->micro;
142
+}
143
+#else
144
+unsigned int get_major_version()
145
+{
146
+        return (unsigned int)C_VERSION_MAJOR;
147
+}
148
+
149
+unsigned int get_minor_version()
150
+{
151
+        return (unsigned int)C_VERSION_MINOR;
152
+}
153
+
154
+unsigned int get_micro_version()
155
+{
156
+        return (unsigned int)C_VERSION_MICRO;
157
+}
158
+#endif
159
+
160
+// The libseccomp API level functions were added in v2.4.0
161
+#if (SCMP_VER_MAJOR < 2) || \
162
+    (SCMP_VER_MAJOR == 2 && SCMP_VER_MINOR < 4)
163
+const unsigned int seccomp_api_get(void)
164
+{
165
+	// libseccomp-golang requires libseccomp v2.2.0, at a minimum, which
166
+	// supported API level 2. However, the kernel may not support API level
167
+	// 2 constructs which are the seccomp() system call and the TSYNC
168
+	// filter flag. Return the "reserved" value of 0 here to indicate that
169
+	// proper API level support is not available in libseccomp.
170
+	return 0;
171
+}
172
+
173
+int seccomp_api_set(unsigned int level)
174
+{
175
+	return -EOPNOTSUPP;
176
+}
177
+#endif
178
+
128 179
 typedef struct scmp_arg_cmp* scmp_cast_t;
129 180
 
130
-// Wrapper to create an scmp_arg_cmp struct
131
-void*
132
-make_struct_arg_cmp(
133
-                    unsigned int arg,
134
-                    int compare,
135
-                    uint64_t a,
136
-                    uint64_t b
137
-                   )
181
+void* make_arg_cmp_array(unsigned int length)
138 182
 {
139
-	struct scmp_arg_cmp *s = malloc(sizeof(struct scmp_arg_cmp));
183
+        return calloc(length, sizeof(struct scmp_arg_cmp));
184
+}
140 185
 
141
-	s->arg = arg;
142
-	s->op = compare;
143
-	s->datum_a = a;
144
-	s->datum_b = b;
186
+// Wrapper to add an scmp_arg_cmp struct to an existing arg_cmp array
187
+void add_struct_arg_cmp(
188
+                        struct scmp_arg_cmp* arr,
189
+                        unsigned int pos,
190
+                        unsigned int arg,
191
+                        int compare,
192
+                        uint64_t a,
193
+                        uint64_t b
194
+                       )
195
+{
196
+        arr[pos].arg = arg;
197
+        arr[pos].op = compare;
198
+        arr[pos].datum_a = a;
199
+        arr[pos].datum_b = b;
145 200
 
146
-	return s;
201
+        return;
147 202
 }
148 203
 */
149 204
 import "C"
... ...
@@ -158,6 +192,7 @@ const (
158 158
 	filterAttrActBadArch scmpFilterAttr = iota
159 159
 	filterAttrNNP        scmpFilterAttr = iota
160 160
 	filterAttrTsync      scmpFilterAttr = iota
161
+	filterAttrLog        scmpFilterAttr = iota
161 162
 )
162 163
 
163 164
 const (
... ...
@@ -168,7 +203,7 @@ const (
168 168
 	archEnd   ScmpArch = ArchS390X
169 169
 	// Comparison boundaries to check for action validity
170 170
 	actionStart ScmpAction = ActKill
171
-	actionEnd   ScmpAction = ActAllow
171
+	actionEnd   ScmpAction = ActLog
172 172
 	// Comparison boundaries to check for comparison operator validity
173 173
 	compareOpStart ScmpCompareOp = CompareNotEqual
174 174
 	compareOpEnd   ScmpCompareOp = CompareMaskedEqual
... ...
@@ -178,26 +213,49 @@ var (
178 178
 	// Error thrown on bad filter context
179 179
 	errBadFilter = fmt.Errorf("filter is invalid or uninitialized")
180 180
 	// Constants representing library major, minor, and micro versions
181
-	verMajor = int(C.C_VERSION_MAJOR)
182
-	verMinor = int(C.C_VERSION_MINOR)
183
-	verMicro = int(C.C_VERSION_MICRO)
181
+	verMajor = uint(C.get_major_version())
182
+	verMinor = uint(C.get_minor_version())
183
+	verMicro = uint(C.get_micro_version())
184 184
 )
185 185
 
186 186
 // Nonexported functions
187 187
 
188 188
 // Check if library version is greater than or equal to the given one
189
-func checkVersionAbove(major, minor, micro int) bool {
189
+func checkVersionAbove(major, minor, micro uint) bool {
190 190
 	return (verMajor > major) ||
191 191
 		(verMajor == major && verMinor > minor) ||
192 192
 		(verMajor == major && verMinor == minor && verMicro >= micro)
193 193
 }
194 194
 
195
-// Init function: Verify library version is appropriate
196
-func init() {
197
-	if !checkVersionAbove(2, 1, 0) {
198
-		fmt.Fprintf(os.Stderr, "Libseccomp version too low: minimum supported is 2.1.0, detected %d.%d.%d", C.C_VERSION_MAJOR, C.C_VERSION_MINOR, C.C_VERSION_MICRO)
199
-		os.Exit(-1)
195
+// Ensure that the library is supported, i.e. >= 2.2.0.
196
+func ensureSupportedVersion() error {
197
+	if !checkVersionAbove(2, 2, 0) {
198
+		return VersionError{}
199
+	}
200
+	return nil
201
+}
202
+
203
+// Get the API level
204
+func getApi() (uint, error) {
205
+	api := C.seccomp_api_get()
206
+	if api == 0 {
207
+		return 0, fmt.Errorf("API level operations are not supported")
200 208
 	}
209
+
210
+	return uint(api), nil
211
+}
212
+
213
+// Set the API level
214
+func setApi(api uint) error {
215
+	if retCode := C.seccomp_api_set(C.uint(api)); retCode != 0 {
216
+		if syscall.Errno(-1*retCode) == syscall.EOPNOTSUPP {
217
+			return fmt.Errorf("API level operations are not supported")
218
+		}
219
+
220
+		return fmt.Errorf("could not set API level: %v", retCode)
221
+	}
222
+
223
+	return nil
201 224
 }
202 225
 
203 226
 // Filter helpers
... ...
@@ -216,10 +274,6 @@ func (f *ScmpFilter) getFilterAttr(attr scmpFilterAttr) (C.uint32_t, error) {
216 216
 		return 0x0, errBadFilter
217 217
 	}
218 218
 
219
-	if !checkVersionAbove(2, 2, 0) && attr == filterAttrTsync {
220
-		return 0x0, fmt.Errorf("the thread synchronization attribute is not supported in this version of the library")
221
-	}
222
-
223 219
 	var attribute C.uint32_t
224 220
 
225 221
 	retCode := C.seccomp_attr_get(f.filterCtx, attr.toNative(), &attribute)
... ...
@@ -239,10 +293,6 @@ func (f *ScmpFilter) setFilterAttr(attr scmpFilterAttr, value C.uint32_t) error
239 239
 		return errBadFilter
240 240
 	}
241 241
 
242
-	if !checkVersionAbove(2, 2, 0) && attr == filterAttrTsync {
243
-		return fmt.Errorf("the thread synchronization attribute is not supported in this version of the library")
244
-	}
245
-
246 242
 	retCode := C.seccomp_attr_set(f.filterCtx, attr.toNative(), value)
247 243
 	if retCode != 0 {
248 244
 		return syscall.Errno(-1 * retCode)
... ...
@@ -254,12 +304,9 @@ func (f *ScmpFilter) setFilterAttr(attr scmpFilterAttr, value C.uint32_t) error
254 254
 // DOES NOT LOCK OR CHECK VALIDITY
255 255
 // Assumes caller has already done this
256 256
 // Wrapper for seccomp_rule_add_... functions
257
-func (f *ScmpFilter) addRuleWrapper(call ScmpSyscall, action ScmpAction, exact bool, cond C.scmp_cast_t) error {
258
-	var length C.uint
259
-	if cond != nil {
260
-		length = 1
261
-	} else {
262
-		length = 0
257
+func (f *ScmpFilter) addRuleWrapper(call ScmpSyscall, action ScmpAction, exact bool, length C.uint, cond C.scmp_cast_t) error {
258
+	if length != 0 && cond == nil {
259
+		return fmt.Errorf("null conditions list, but length is nonzero")
263 260
 	}
264 261
 
265 262
 	var retCode C.int
... ...
@@ -270,9 +317,11 @@ func (f *ScmpFilter) addRuleWrapper(call ScmpSyscall, action ScmpAction, exact b
270 270
 	}
271 271
 
272 272
 	if syscall.Errno(-1*retCode) == syscall.EFAULT {
273
-		return fmt.Errorf("unrecognized syscall")
273
+		return fmt.Errorf("unrecognized syscall %#x", int32(call))
274 274
 	} else if syscall.Errno(-1*retCode) == syscall.EPERM {
275 275
 		return fmt.Errorf("requested action matches default action of filter")
276
+	} else if syscall.Errno(-1*retCode) == syscall.EINVAL {
277
+		return fmt.Errorf("two checks on same syscall argument")
276 278
 	} else if retCode != 0 {
277 279
 		return syscall.Errno(-1 * retCode)
278 280
 	}
... ...
@@ -290,22 +339,32 @@ func (f *ScmpFilter) addRuleGeneric(call ScmpSyscall, action ScmpAction, exact b
290 290
 	}
291 291
 
292 292
 	if len(conds) == 0 {
293
-		if err := f.addRuleWrapper(call, action, exact, nil); err != nil {
293
+		if err := f.addRuleWrapper(call, action, exact, 0, nil); err != nil {
294 294
 			return err
295 295
 		}
296 296
 	} else {
297 297
 		// We don't support conditional filtering in library version v2.1
298 298
 		if !checkVersionAbove(2, 2, 1) {
299
-			return fmt.Errorf("conditional filtering requires libseccomp version >= 2.2.1")
299
+			return VersionError{
300
+				message: "conditional filtering is not supported",
301
+				minimum: "2.2.1",
302
+			}
303
+		}
304
+
305
+		argsArr := C.make_arg_cmp_array(C.uint(len(conds)))
306
+		if argsArr == nil {
307
+			return fmt.Errorf("error allocating memory for conditions")
300 308
 		}
309
+		defer C.free(argsArr)
301 310
 
302
-		for _, cond := range conds {
303
-			cmpStruct := C.make_struct_arg_cmp(C.uint(cond.Argument), cond.Op.toNative(), C.uint64_t(cond.Operand1), C.uint64_t(cond.Operand2))
304
-			defer C.free(cmpStruct)
311
+		for i, cond := range conds {
312
+			C.add_struct_arg_cmp(C.scmp_cast_t(argsArr), C.uint(i),
313
+				C.uint(cond.Argument), cond.Op.toNative(),
314
+				C.uint64_t(cond.Operand1), C.uint64_t(cond.Operand2))
315
+		}
305 316
 
306
-			if err := f.addRuleWrapper(call, action, exact, C.scmp_cast_t(cmpStruct)); err != nil {
307
-				return err
308
-			}
317
+		if err := f.addRuleWrapper(call, action, exact, C.uint(len(conds)), C.scmp_cast_t(argsArr)); err != nil {
318
+			return err
309 319
 		}
310 320
 	}
311 321
 
... ...
@@ -317,11 +376,11 @@ func (f *ScmpFilter) addRuleGeneric(call ScmpSyscall, action ScmpAction, exact b
317 317
 // Helper - Sanitize Arch token input
318 318
 func sanitizeArch(in ScmpArch) error {
319 319
 	if in < archStart || in > archEnd {
320
-		return fmt.Errorf("unrecognized architecture")
320
+		return fmt.Errorf("unrecognized architecture %#x", uint(in))
321 321
 	}
322 322
 
323 323
 	if in.toNative() == C.C_ARCH_BAD {
324
-		return fmt.Errorf("architecture is not supported on this version of the library")
324
+		return fmt.Errorf("architecture %v is not supported on this version of the library", in)
325 325
 	}
326 326
 
327 327
 	return nil
... ...
@@ -330,7 +389,7 @@ func sanitizeArch(in ScmpArch) error {
330 330
 func sanitizeAction(in ScmpAction) error {
331 331
 	inTmp := in & 0x0000FFFF
332 332
 	if inTmp < actionStart || inTmp > actionEnd {
333
-		return fmt.Errorf("unrecognized action")
333
+		return fmt.Errorf("unrecognized action %#x", uint(inTmp))
334 334
 	}
335 335
 
336 336
 	if inTmp != ActTrace && inTmp != ActErrno && (in&0xFFFF0000) != 0 {
... ...
@@ -342,7 +401,7 @@ func sanitizeAction(in ScmpAction) error {
342 342
 
343 343
 func sanitizeCompareOp(in ScmpCompareOp) error {
344 344
 	if in < compareOpStart || in > compareOpEnd {
345
-		return fmt.Errorf("unrecognized comparison operator")
345
+		return fmt.Errorf("unrecognized comparison operator %#x", uint(in))
346 346
 	}
347 347
 
348 348
 	return nil
... ...
@@ -385,7 +444,7 @@ func archFromNative(a C.uint32_t) (ScmpArch, error) {
385 385
 	case C.C_ARCH_S390X:
386 386
 		return ArchS390X, nil
387 387
 	default:
388
-		return 0x0, fmt.Errorf("unrecognized architecture")
388
+		return 0x0, fmt.Errorf("unrecognized architecture %#x", uint32(a))
389 389
 	}
390 390
 }
391 391
 
... ...
@@ -464,10 +523,12 @@ func actionFromNative(a C.uint32_t) (ScmpAction, error) {
464 464
 		return ActErrno.SetReturnCode(int16(aTmp)), nil
465 465
 	case C.C_ACT_TRACE:
466 466
 		return ActTrace.SetReturnCode(int16(aTmp)), nil
467
+	case C.C_ACT_LOG:
468
+		return ActLog, nil
467 469
 	case C.C_ACT_ALLOW:
468 470
 		return ActAllow, nil
469 471
 	default:
470
-		return 0x0, fmt.Errorf("unrecognized action")
472
+		return 0x0, fmt.Errorf("unrecognized action %#x", uint32(a))
471 473
 	}
472 474
 }
473 475
 
... ...
@@ -482,6 +543,8 @@ func (a ScmpAction) toNative() C.uint32_t {
482 482
 		return C.C_ACT_ERRNO | (C.uint32_t(a) >> 16)
483 483
 	case ActTrace:
484 484
 		return C.C_ACT_TRACE | (C.uint32_t(a) >> 16)
485
+	case ActLog:
486
+		return C.C_ACT_LOG
485 487
 	case ActAllow:
486 488
 		return C.C_ACT_ALLOW
487 489
 	default:
... ...
@@ -500,6 +563,8 @@ func (a scmpFilterAttr) toNative() uint32 {
500 500
 		return uint32(C.C_ATTRIBUTE_NNP)
501 501
 	case filterAttrTsync:
502 502
 		return uint32(C.C_ATTRIBUTE_TSYNC)
503
+	case filterAttrLog:
504
+		return uint32(C.C_ATTRIBUTE_LOG)
503 505
 	default:
504 506
 		return 0x0
505 507
 	}