diff --git a/.golangci.yml b/.golangci.yml index 4c213b1e1..aad6acff2 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -27,6 +27,8 @@ issues: exclude-rules: - path: 'cmd[\\/]containerd[\\/]builtins[\\/]' text: "blank-imports:" + - path: 'contrib[\\/]fuzz[\\/]' + text: "exported: func name will be used as fuzz.Fuzz" linters-settings: gosec: diff --git a/archive/compression/compression_fuzzer.go b/archive/compression/compression_fuzzer.go new file mode 100644 index 000000000..b8374799d --- /dev/null +++ b/archive/compression/compression_fuzzer.go @@ -0,0 +1,29 @@ +//go:build gofuzz +// +build gofuzz + +/* + 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 compression + +import ( + "bytes" +) + +func FuzzDecompressStream(data []byte) int { + _, _ = DecompressStream(bytes.NewReader(data)) + return 1 +} diff --git a/content/local/content_local_fuzzer.go b/content/local/content_local_fuzzer.go new file mode 100644 index 000000000..49a44f375 --- /dev/null +++ b/content/local/content_local_fuzzer.go @@ -0,0 +1,76 @@ +//go:build gofuzz +// +build gofuzz + +/* + 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 local + +import ( + "bufio" + "bytes" + "context" + "io" + "testing" + + "github.com/opencontainers/go-digest" + + "github.com/containerd/containerd/content" +) + +func FuzzContentStoreWriter(data []byte) int { + t := &testing.T{} + ctx := context.Background() + ctx, _, cs, cleanup := contentStoreEnv(t) + defer cleanup() + + cw, err := cs.Writer(ctx, content.WithRef("myref")) + if err != nil { + return 0 + } + if err := cw.Close(); err != nil { + return 0 + } + + // reopen, so we can test things + cw, err = cs.Writer(ctx, content.WithRef("myref")) + if err != nil { + return 0 + } + + err = checkCopyFuzz(int64(len(data)), cw, bufio.NewReader(io.NopCloser(bytes.NewReader(data)))) + if err != nil { + return 0 + } + expected := digest.FromBytes(data) + + if err = cw.Commit(ctx, int64(len(data)), expected); err != nil { + return 0 + } + return 1 +} + +func checkCopyFuzz(size int64, dst io.Writer, src io.Reader) error { + nn, err := io.Copy(dst, src) + if err != nil { + return err + } + + if nn != size { + return err + } + return nil +} diff --git a/content/local/store_test.go b/content/local/store_test.go index fe206bc13..83263e012 100644 --- a/content/local/store_test.go +++ b/content/local/store_test.go @@ -291,20 +291,6 @@ func populateBlobStore(ctx context.Context, t checker, cs content.Store, nblobs, return blobs } -func contentStoreEnv(t testing.TB) (context.Context, string, content.Store, func()) { - tmpdir := t.TempDir() - - cs, err := NewStore(tmpdir) - if err != nil { - t.Fatal(err) - } - - ctx, cancel := context.WithCancel(context.Background()) - return ctx, tmpdir, cs, func() { - cancel() - } -} - func checkCopy(t checker, size int64, dst io.Writer, src io.Reader) { nn, err := io.Copy(dst, src) if err != nil { diff --git a/content/local/test_helper.go b/content/local/test_helper.go new file mode 100644 index 000000000..42de01cae --- /dev/null +++ b/content/local/test_helper.go @@ -0,0 +1,38 @@ +/* + 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 local + +import ( + "context" + "testing" + + "github.com/containerd/containerd/content" +) + +func contentStoreEnv(t testing.TB) (context.Context, string, content.Store, func()) { + tmpdir := t.TempDir() + + cs, err := NewStore(tmpdir) + if err != nil { + t.Fatal(err) + } + + ctx, cancel := context.WithCancel(context.Background()) + return ctx, tmpdir, cs, func() { + cancel() + } +} diff --git a/contrib/apparmor/apparmor_fuzzer.go b/contrib/apparmor/apparmor_fuzzer.go new file mode 100644 index 000000000..c4a037bfe --- /dev/null +++ b/contrib/apparmor/apparmor_fuzzer.go @@ -0,0 +1,37 @@ +//go:build gofuzz +// +build gofuzz + +// Copyright 2022 ADA Logics Ltd +// +// 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 apparmor + +import ( + "os" +) + +func FuzzLoadDefaultProfile(data []byte) int { + f, err := os.Create("fuzz_file") + if err != nil { + return 0 + } + defer os.Remove("fuzz_file") + _, err = f.Write(data) + if err != nil { + return 0 + } + _ = LoadDefaultProfile("fuzz_file") + return 1 +} diff --git a/contrib/fuzz/containerd_import_fuzzer.go b/contrib/fuzz/containerd_import_fuzzer.go index f471b731b..5fbccde60 100644 --- a/contrib/fuzz/containerd_import_fuzzer.go +++ b/contrib/fuzz/containerd_import_fuzzer.go @@ -19,14 +19,11 @@ package fuzz import ( "bytes" "context" - "sync" - "time" fuzz "github.com/AdaLogics/go-fuzz-headers" "github.com/containerd/containerd" _ "github.com/containerd/containerd/cmd/containerd/builtins" - "github.com/containerd/containerd/cmd/containerd/command" "github.com/containerd/containerd/namespaces" ) @@ -36,27 +33,6 @@ const ( defaultAddress = "/tmp/containerd/containerd.sock" ) -var ( - initDaemon sync.Once -) - -func startDaemonForFuzzing(arguments []string) { - app := command.App() - _ = app.Run(arguments) -} - -func startDaemon() { - args := []string{"--log-level", "debug"} - go func() { - // This is similar to invoking the - // containerd binary. - // See contrib/fuzz/oss_fuzz_build.sh - // for more info. - startDaemonForFuzzing(args) - }() - time.Sleep(time.Second * 4) -} - func fuzzContext() (context.Context, context.CancelFunc) { ctx, cancel := context.WithCancel(context.Background()) ctx = namespaces.WithNamespace(ctx, "fuzzing-namespace") diff --git a/contrib/fuzz/daemon.go b/contrib/fuzz/daemon.go new file mode 100644 index 000000000..5df2ac8fc --- /dev/null +++ b/contrib/fuzz/daemon.go @@ -0,0 +1,44 @@ +//go:build gofuzz +// +build gofuzz + +/* + 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 fuzz + +import ( + "sync" + "time" + + "github.com/containerd/containerd/cmd/containerd/command" +) + +var ( + initDaemon sync.Once +) + +func startDaemonForFuzzing(arguments []string) { + app := command.App() + _ = app.Run(arguments) +} + +func startDaemon() { + args := []string{"--log-level", "debug"} + go func() { + // This is similar to invoking the + // containerd binary. + // See contrib/fuzz/oss_fuzz_build.sh + // for more info. + startDaemonForFuzzing(args) + }() + time.Sleep(time.Second * 4) +} diff --git a/contrib/fuzz/diff_fuzzer.go b/contrib/fuzz/diff_fuzzer.go new file mode 100644 index 000000000..08dbf31ac --- /dev/null +++ b/contrib/fuzz/diff_fuzzer.go @@ -0,0 +1,108 @@ +// Copyright 2021 ADA Logics Ltd +// +// 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 fuzz + +import ( + "context" + _ "crypto/sha256" // required by go-digest + "os" + + fuzz "github.com/AdaLogics/go-fuzz-headers" + "github.com/containerd/containerd/content/local" + "github.com/containerd/containerd/diff/apply" + "github.com/containerd/containerd/diff/walking" + "github.com/containerd/containerd/mount" + ocispec "github.com/opencontainers/image-spec/specs-go/v1" +) + +func FuzzDiffApply(data []byte) int { + f := fuzz.NewConsumer(data) + + mountsQty, err := f.GetInt() + if err != nil { + return 0 + } + mounts := make([]mount.Mount, 0) + for i := 0; i < mountsQty%30; i++ { + m := mount.Mount{} + err = f.GenerateStruct(&m) + if err != nil { + return 0 + } + mounts = append(mounts, m) + } + desc := ocispec.Descriptor{} + err = f.GenerateStruct(&desc) + if err != nil { + return 0 + } + tmpdir, err := os.MkdirTemp("", "fuzzing-") + if err != nil { + return 0 + } + cs, err := local.NewStore(tmpdir) + if err != nil { + return 0 + } + fsa := apply.NewFileSystemApplier(cs) + _, _ = fsa.Apply(context.Background(), desc, mounts) + return 1 +} + +func FuzzDiffCompare(data []byte) int { + f := fuzz.NewConsumer(data) + + lowerQty, err := f.GetInt() + if err != nil { + return 0 + } + lower := make([]mount.Mount, 0) + for i := 0; i < lowerQty%30; i++ { + m := mount.Mount{} + err = f.GenerateStruct(&m) + if err != nil { + return 0 + } + lower = append(lower, m) + } + + upperQty, err := f.GetInt() + if err != nil { + return 0 + } + upper := make([]mount.Mount, 0) + for i := 0; i < upperQty%30; i++ { + m := mount.Mount{} + err = f.GenerateStruct(&m) + if err != nil { + return 0 + } + upper = append(upper, m) + } + + ctx := context.Background() + tmpdir, err := os.MkdirTemp("", "fuzzing-") + if err != nil { + return 0 + } + cs, err := local.NewStore(tmpdir) + if err != nil { + return 0 + } + walker := walking.NewWalkingDiff(cs) + _, _ = walker.Compare(ctx, lower, upper) + return 1 +} diff --git a/contrib/fuzz/exchange_fuzzer.go b/contrib/fuzz/exchange_fuzzer.go new file mode 100644 index 000000000..24954c7ad --- /dev/null +++ b/contrib/fuzz/exchange_fuzzer.go @@ -0,0 +1,54 @@ +// Copyright 2021 ADA Logics Ltd +// +// 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 fuzz + +import ( + "context" + + fuzz "github.com/AdaLogics/go-fuzz-headers" + eventstypes "github.com/containerd/containerd/api/events" + "github.com/containerd/containerd/events" + "github.com/containerd/containerd/events/exchange" + "github.com/containerd/containerd/namespaces" +) + +func FuzzExchange(data []byte) int { + f := fuzz.NewConsumer(data) + namespace, err := f.GetString() + if err != nil { + return 0 + } + event := &eventstypes.ContainerCreate{} + err = f.GenerateStruct(event) + if err != nil { + return 0 + } + input, err := f.GetString() + if err != nil { + return 0 + } + + env := &events.Envelope{} + err = f.GenerateStruct(env) + if err != nil { + return 0 + } + ctx := namespaces.WithNamespace(context.Background(), namespace) + exch := exchange.NewExchange() + exch.Publish(ctx, input, event) + exch.Forward(ctx, env) + return 1 +} diff --git a/contrib/fuzz/fuzz_images.go b/contrib/fuzz/fuzz_images.go new file mode 100644 index 000000000..bf5a02a12 --- /dev/null +++ b/contrib/fuzz/fuzz_images.go @@ -0,0 +1,46 @@ +// Copyright 2021 ADA Logics Ltd +// +// 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 fuzz + +import ( + "context" + "os" + + fuzz "github.com/AdaLogics/go-fuzz-headers" + "github.com/containerd/containerd/content/local" + "github.com/containerd/containerd/images" + "github.com/containerd/containerd/platforms" + ocispec "github.com/opencontainers/image-spec/specs-go/v1" +) + +func FuzzImagesCheck(data []byte) int { + f := fuzz.NewConsumer(data) + desc := ocispec.Descriptor{} + err := f.GenerateStruct(&desc) + if err != nil { + return 0 + } + tmpdir, err := os.MkdirTemp("", "fuzzing-") + if err != nil { + return 0 + } + cs, err := local.NewStore(tmpdir) + if err != nil { + return 0 + } + _, _, _, _, _ = images.Check(context.Background(), cs, desc, platforms.Default()) + return 1 +} diff --git a/contrib/fuzz/uuid_fuzzer.go b/contrib/fuzz/uuid_fuzzer.go new file mode 100644 index 000000000..bca469adf --- /dev/null +++ b/contrib/fuzz/uuid_fuzzer.go @@ -0,0 +1,25 @@ +// Copyright 2021 ADA Logics Ltd +// +// 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 fuzz + +import ( + "github.com/google/uuid" +) + +func FuzzUUIDParse(data []byte) int { + _, _ = uuid.Parse(string(data)) + return 1 +} diff --git a/remotes/docker/config/docker_fuzzer_internal.go b/remotes/docker/config/docker_fuzzer_internal.go new file mode 100644 index 000000000..03553890e --- /dev/null +++ b/remotes/docker/config/docker_fuzzer_internal.go @@ -0,0 +1,46 @@ +//go:build gofuzz +// +build gofuzz + +/* + 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 config + +import ( + "io/ioutil" + "os" + + fuzz "github.com/AdaLogics/go-fuzz-headers" +) + +func FuzzParseHostsFile(data []byte) int { + f := fuzz.NewConsumer(data) + dir, err := ioutil.TempDir("", "fuzz-") + if err != nil { + return 0 + } + err = f.CreateFiles(dir) + if err != nil { + return 0 + } + defer os.RemoveAll(dir) + b, err := f.GetBytes() + if err != nil { + return 0 + } + _, _ = parseHostsFile(dir, b) + return 1 +}