diff --git a/cgroup1/hugetlb.go b/cgroup1/hugetlb.go index 75519d9d..8d9be500 100644 --- a/cgroup1/hugetlb.go +++ b/cgroup1/hugetlb.go @@ -17,6 +17,7 @@ package cgroup1 import ( + "fmt" "os" "path/filepath" "strconv" @@ -56,8 +57,12 @@ func (h *hugetlbController) Create(path string, resources *specs.LinuxResources) return err } for _, limit := range resources.HugepageLimits { + filename := strings.Join([]string{"hugetlb", limit.Pagesize, "limit_in_bytes"}, ".") + if filepath.Base(filename) != filename { + return fmt.Errorf("invalid hugepage size %q", limit.Pagesize) + } if err := os.WriteFile( - filepath.Join(h.Path(path), strings.Join([]string{"hugetlb", limit.Pagesize, "limit_in_bytes"}, ".")), + filepath.Join(h.Path(path), filename), []byte(strconv.FormatUint(limit.Limit, 10)), defaultFilePerm, ); err != nil { diff --git a/cgroup1/hugetlb_test.go b/cgroup1/hugetlb_test.go new file mode 100644 index 00000000..6c42358b --- /dev/null +++ b/cgroup1/hugetlb_test.go @@ -0,0 +1,54 @@ +/* + Copyright The containerd Authors. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +package cgroup1 + +import ( + "os" + "path/filepath" + "testing" + + "github.com/opencontainers/runtime-spec/specs-go" +) + +func TestHugetlbInvalidPageSize(t *testing.T) { + mock, err := newMock(t) + if err != nil { + t.Fatal(err) + } + defer func() { + if err := mock.delete(); err != nil { + t.Errorf("failed delete: %v", err) + } + }() + h, err := NewHugetlb(mock.root) + if err != nil { + t.Fatal(err) + } + resources := specs.LinuxResources{ + HugepageLimits: []specs.LinuxHugepageLimit{ + // resolves to /escaped.limit_in_bytes + {Pagesize: "/../../../../escaped", Limit: 1337}, + }, + } + if err := h.Create("/a", &resources); err == nil { + t.Error("expected error for pagesize containing path separators") + } + escaped := filepath.Join(filepath.Dir(mock.root), "escaped.limit_in_bytes") + if _, err := os.Stat(escaped); !os.IsNotExist(err) { + t.Errorf("file written outside of cgroup root: %s", escaped) + } +} diff --git a/cgroup2/hugetlbv2_test.go b/cgroup2/hugetlbv2_test.go index d0252db5..88cd0242 100644 --- a/cgroup2/hugetlbv2_test.go +++ b/cgroup2/hugetlbv2_test.go @@ -19,6 +19,7 @@ package cgroup2 import ( "fmt" "os" + "path/filepath" "strconv" "testing" @@ -53,3 +54,17 @@ func TestCgroupv2HugetlbStats(t *testing.T) { } } } + +func TestCgroupv2HugetlbInvalidPageSize(t *testing.T) { + path := filepath.Join(t.TempDir(), "hugetlb-test-cg") + require.NoError(t, os.Mkdir(path, defaultDirPerm)) + res := Resources{ + // resolves to /escaped.max + HugeTlb: &HugeTlb{HugeTlbEntry{HugePageSize: "/../../escaped", Limit: 1337}}, + } + err := setResources(path, &res) + require.Error(t, err) + + _, err = os.Stat(filepath.Join(filepath.Dir(path), "escaped.max")) + assert.True(t, os.IsNotExist(err), "file written outside of cgroup path") +} diff --git a/cgroup2/manager.go b/cgroup2/manager.go index 8638ab92..351ee321 100644 --- a/cgroup2/manager.go +++ b/cgroup2/manager.go @@ -153,6 +153,9 @@ type Value struct { // write the value to the full, absolute path, of a unified hierarchy func (c *Value) write(path string, perm os.FileMode) error { + if filepath.Base(c.filename) != c.filename { + return fmt.Errorf("invalid cgroup filename: %q", c.filename) + } var data []byte switch t := c.value.(type) { case uint64: