Skip to content

Commit f3da3e5

Browse files
henry118AkihiroSuda
authored andcommitted
allow ptrace(2) by default for kernel >= 4.8
Signed-off-by: Henry Wang <[email protected]> (cherry picked from commit 94faa70) Signed-off-by: Akihiro Suda <[email protected]>
1 parent 604d0bd commit f3da3e5

3 files changed

Lines changed: 246 additions & 0 deletions

File tree

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
/*
2+
Copyright The containerd Authors.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
16+
File copied and customized based on
17+
https://github.com/moby/moby/tree/v20.10.14/profiles/seccomp/kernel_linux.go
18+
*/
19+
20+
package kernelversion
21+
22+
import (
23+
"bytes"
24+
"fmt"
25+
"sync"
26+
27+
"golang.org/x/sys/unix"
28+
)
29+
30+
// KernelVersion holds information about the kernel.
31+
type KernelVersion struct {
32+
Kernel uint64 // Version of the Kernel (i.e., the "4" in "4.1.2-generic")
33+
Major uint64 // Major revision of the Kernel (i.e., the "1" in "4.1.2-generic")
34+
}
35+
36+
// String implements fmt.Stringer for KernelVersion
37+
func (k *KernelVersion) String() string {
38+
if k.Kernel > 0 || k.Major > 0 {
39+
return fmt.Sprintf("%d.%d", k.Kernel, k.Major)
40+
}
41+
return ""
42+
}
43+
44+
var (
45+
currentKernelVersion *KernelVersion
46+
kernelVersionError error
47+
once sync.Once
48+
)
49+
50+
// getKernelVersion gets the current kernel version.
51+
func getKernelVersion() (*KernelVersion, error) {
52+
once.Do(func() {
53+
var uts unix.Utsname
54+
if err := unix.Uname(&uts); err != nil {
55+
return
56+
}
57+
// Remove the \x00 from the release for Atoi to parse correctly
58+
currentKernelVersion, kernelVersionError = parseRelease(string(uts.Release[:bytes.IndexByte(uts.Release[:], 0)]))
59+
})
60+
return currentKernelVersion, kernelVersionError
61+
}
62+
63+
// parseRelease parses a string and creates a KernelVersion based on it.
64+
func parseRelease(release string) (*KernelVersion, error) {
65+
var version = KernelVersion{}
66+
67+
// We're only make sure we get the "kernel" and "major revision". Sometimes we have
68+
// 3.12.25-gentoo, but sometimes we just have 3.12-1-amd64.
69+
_, err := fmt.Sscanf(release, "%d.%d", &version.Kernel, &version.Major)
70+
if err != nil {
71+
return nil, fmt.Errorf("failed to parse kernel version %q: %w", release, err)
72+
}
73+
return &version, nil
74+
}
75+
76+
// GreaterEqualThan checks if the host's kernel version is greater than, or
77+
// equal to the given kernel version v. Only "kernel version" and "major revision"
78+
// can be specified (e.g., "3.12") and will be taken into account, which means
79+
// that 3.12.25-gentoo and 3.12-1-amd64 are considered equal (kernel: 3, major: 12).
80+
func GreaterEqualThan(minVersion KernelVersion) (bool, error) {
81+
kv, err := getKernelVersion()
82+
if err != nil {
83+
return false, err
84+
}
85+
if kv.Kernel > minVersion.Kernel {
86+
return true, nil
87+
}
88+
if kv.Kernel == minVersion.Kernel && kv.Major >= minVersion.Major {
89+
return true, nil
90+
}
91+
return false, nil
92+
}
Lines changed: 141 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,141 @@
1+
/*
2+
Copyright The containerd Authors.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
16+
File copied and customized based on
17+
https://github.com/moby/moby/tree/v20.10.14/profiles/seccomp/kernel_linux_test.go
18+
*/
19+
20+
package kernelversion
21+
22+
import (
23+
"fmt"
24+
"testing"
25+
)
26+
27+
func TestGetKernelVersion(t *testing.T) {
28+
version, err := getKernelVersion()
29+
if err != nil {
30+
t.Fatal(err)
31+
}
32+
if version == nil {
33+
t.Fatal("version is nil")
34+
}
35+
if version.Kernel == 0 {
36+
t.Fatal("no kernel version")
37+
}
38+
}
39+
40+
// TestParseRelease tests the ParseRelease() function
41+
func TestParseRelease(t *testing.T) {
42+
tests := []struct {
43+
in string
44+
out KernelVersion
45+
expectedErr error
46+
}{
47+
{in: "3.8", out: KernelVersion{Kernel: 3, Major: 8}},
48+
{in: "3.8.0", out: KernelVersion{Kernel: 3, Major: 8}},
49+
{in: "3.8.0-19-generic", out: KernelVersion{Kernel: 3, Major: 8}},
50+
{in: "3.4.54.longterm-1", out: KernelVersion{Kernel: 3, Major: 4}},
51+
{in: "3.10.0-862.2.3.el7.x86_64", out: KernelVersion{Kernel: 3, Major: 10}},
52+
{in: "3.12.8tag", out: KernelVersion{Kernel: 3, Major: 12}},
53+
{in: "3.12-1-amd64", out: KernelVersion{Kernel: 3, Major: 12}},
54+
{in: "3.12foobar", out: KernelVersion{Kernel: 3, Major: 12}},
55+
{in: "99.999.999-19-generic", out: KernelVersion{Kernel: 99, Major: 999}},
56+
{in: "", expectedErr: fmt.Errorf(`failed to parse kernel version "": EOF`)},
57+
{in: "3", expectedErr: fmt.Errorf(`failed to parse kernel version "3": unexpected EOF`)},
58+
{in: "3.", expectedErr: fmt.Errorf(`failed to parse kernel version "3.": EOF`)},
59+
{in: "3a", expectedErr: fmt.Errorf(`failed to parse kernel version "3a": input does not match format`)},
60+
{in: "3.a", expectedErr: fmt.Errorf(`failed to parse kernel version "3.a": expected integer`)},
61+
{in: "a", expectedErr: fmt.Errorf(`failed to parse kernel version "a": expected integer`)},
62+
{in: "a.a", expectedErr: fmt.Errorf(`failed to parse kernel version "a.a": expected integer`)},
63+
{in: "a.a.a-a", expectedErr: fmt.Errorf(`failed to parse kernel version "a.a.a-a": expected integer`)},
64+
{in: "-3", expectedErr: fmt.Errorf(`failed to parse kernel version "-3": expected integer`)},
65+
{in: "-3.", expectedErr: fmt.Errorf(`failed to parse kernel version "-3.": expected integer`)},
66+
{in: "-3.8", expectedErr: fmt.Errorf(`failed to parse kernel version "-3.8": expected integer`)},
67+
{in: "-3.-8", expectedErr: fmt.Errorf(`failed to parse kernel version "-3.-8": expected integer`)},
68+
{in: "3.-8", expectedErr: fmt.Errorf(`failed to parse kernel version "3.-8": expected integer`)},
69+
}
70+
for _, tc := range tests {
71+
tc := tc
72+
t.Run(tc.in, func(t *testing.T) {
73+
version, err := parseRelease(tc.in)
74+
if tc.expectedErr != nil {
75+
if err == nil {
76+
t.Fatal("expected an error")
77+
}
78+
if err.Error() != tc.expectedErr.Error() {
79+
t.Fatalf("expected: %s, got: %s", tc.expectedErr, err)
80+
}
81+
return
82+
}
83+
if err != nil {
84+
t.Fatal("unexpected error:", err)
85+
}
86+
if version == nil {
87+
t.Fatal("version is nil")
88+
}
89+
if version.Kernel != tc.out.Kernel || version.Major != tc.out.Major {
90+
t.Fatalf("expected: %d.%d, got: %d.%d", tc.out.Kernel, tc.out.Major, version.Kernel, version.Major)
91+
}
92+
})
93+
}
94+
}
95+
96+
func TestGreaterEqualThan(t *testing.T) {
97+
// Get the current kernel version, so that we can make test relative to that
98+
v, err := getKernelVersion()
99+
if err != nil {
100+
t.Fatal(err)
101+
}
102+
103+
tests := []struct {
104+
doc string
105+
in KernelVersion
106+
expected bool
107+
}{
108+
{
109+
doc: "same version",
110+
in: KernelVersion{v.Kernel, v.Major},
111+
expected: true,
112+
},
113+
{
114+
doc: "kernel minus one",
115+
in: KernelVersion{v.Kernel - 1, v.Major},
116+
expected: true,
117+
},
118+
{
119+
doc: "kernel plus one",
120+
in: KernelVersion{v.Kernel + 1, v.Major},
121+
expected: false,
122+
},
123+
{
124+
doc: "major plus one",
125+
in: KernelVersion{v.Kernel, v.Major + 1},
126+
expected: false,
127+
},
128+
}
129+
for _, tc := range tests {
130+
tc := tc
131+
t.Run(tc.doc+": "+tc.in.String(), func(t *testing.T) {
132+
ok, err := GreaterEqualThan(tc.in)
133+
if err != nil {
134+
t.Fatal("unexpected error:", err)
135+
}
136+
if ok != tc.expected {
137+
t.Fatalf("expected: %v, got: %v", tc.expected, ok)
138+
}
139+
})
140+
}
141+
}

contrib/seccomp/seccomp_default.go

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ import (
2424

2525
"golang.org/x/sys/unix"
2626

27+
"github.com/containerd/containerd/contrib/seccomp/kernelversion"
2728
"github.com/opencontainers/runtime-spec/specs-go"
2829
)
2930

@@ -467,6 +468,18 @@ func DefaultProfile(sp *specs.Spec) *specs.LinuxSeccomp {
467468
Syscalls: syscalls,
468469
}
469470

471+
// include by kernel version
472+
if ok, err := kernelversion.GreaterEqualThan(
473+
kernelversion.KernelVersion{Kernel: 4, Major: 8}); err == nil {
474+
if ok {
475+
s.Syscalls = append(s.Syscalls, specs.LinuxSyscall{
476+
Names: []string{"ptrace"},
477+
Action: specs.ActAllow,
478+
Args: []specs.LinuxSeccompArg{},
479+
})
480+
}
481+
}
482+
470483
// include by arch
471484
switch runtime.GOARCH {
472485
case "ppc64le":

0 commit comments

Comments
 (0)