From 6019bcdfbbed387b366e4e368c30475f5c31f054 Mon Sep 17 00:00:00 2001 From: Jin Dong Date: Sat, 11 Jan 2025 17:40:28 +0000 Subject: [PATCH 1/2] move FuzzContainerdImport to go native fuzz Signed-off-by: Jin Dong --- ...zzer.go => containerd_import_fuzz_test.go} | 51 ++++++++++--------- contrib/fuzz/daemon.go | 2 - 2 files changed, 28 insertions(+), 25 deletions(-) rename contrib/fuzz/{containerd_import_fuzzer.go => containerd_import_fuzz_test.go} (62%) diff --git a/contrib/fuzz/containerd_import_fuzzer.go b/contrib/fuzz/containerd_import_fuzz_test.go similarity index 62% rename from contrib/fuzz/containerd_import_fuzzer.go rename to contrib/fuzz/containerd_import_fuzz_test.go index 33edbefbb0..abdd1496c9 100644 --- a/contrib/fuzz/containerd_import_fuzzer.go +++ b/contrib/fuzz/containerd_import_fuzz_test.go @@ -1,5 +1,3 @@ -//go:build gofuzz - /* Copyright The containerd Authors. Licensed under the Apache License, Version 2.0 (the "License"); @@ -18,6 +16,8 @@ package fuzz import ( "bytes" "context" + "os" + "testing" fuzz "github.com/AdaLogics/go-fuzz-headers" @@ -31,30 +31,35 @@ func fuzzContext() (context.Context, context.CancelFunc) { return ctx, cancel } -func FuzzContainerdImport(data []byte) int { - initDaemon.Do(startDaemon) - - client, err := containerd.New(defaultAddress) - if err != nil { - return 0 +func FuzzContainerdImport(f *testing.F) { + if os.Getuid() != 0 { + f.Skip("skipping fuzz test that requires root") } - defer client.Close() - f := fuzz.NewConsumer(data) + f.Fuzz(func(t *testing.T, data []byte) { + initDaemon.Do(startDaemon) - noOfImports, err := f.GetInt() - if err != nil { - return 0 - } - maxImports := 20 - ctx, cancel := fuzzContext() - defer cancel() - for i := 0; i < noOfImports%maxImports; i++ { - tarBytes, err := f.GetBytes() + client, err := containerd.New(defaultAddress) if err != nil { - return 0 + return } - _, _ = client.Import(ctx, bytes.NewReader(tarBytes)) - } - return 1 + defer client.Close() + + f := fuzz.NewConsumer(data) + + noOfImports, err := f.GetInt() + if err != nil { + return + } + maxImports := 20 + ctx, cancel := fuzzContext() + defer cancel() + for i := 0; i < noOfImports%maxImports; i++ { + tarBytes, err := f.GetBytes() + if err != nil { + return + } + _, _ = client.Import(ctx, bytes.NewReader(tarBytes)) + } + }) } diff --git a/contrib/fuzz/daemon.go b/contrib/fuzz/daemon.go index 96f7e74ca0..59e0ee0360 100644 --- a/contrib/fuzz/daemon.go +++ b/contrib/fuzz/daemon.go @@ -1,5 +1,3 @@ -//go:build gofuzz - /* Copyright The containerd Authors. From b49df6af11dbf7e4fc715e972c8e816edcb02309 Mon Sep 17 00:00:00 2001 From: Jin Dong Date: Sat, 11 Jan 2025 17:41:43 +0000 Subject: [PATCH 2/2] move FuzzCRIServer to go native fuzz Signed-off-by: Jin Dong --- contrib/fuzz/containerd_import_fuzz_test.go | 2 +- ...{cri_fuzzer.go => cri_server_fuzz_test.go} | 251 ++++++++---------- contrib/fuzz/cri_server_fuzzer.go | 92 ------- internal/cri/server/fuzz.go | 33 --- 4 files changed, 107 insertions(+), 271 deletions(-) rename contrib/fuzz/{cri_fuzzer.go => cri_server_fuzz_test.go} (75%) delete mode 100644 contrib/fuzz/cri_server_fuzzer.go delete mode 100644 internal/cri/server/fuzz.go diff --git a/contrib/fuzz/containerd_import_fuzz_test.go b/contrib/fuzz/containerd_import_fuzz_test.go index abdd1496c9..a15221a140 100644 --- a/contrib/fuzz/containerd_import_fuzz_test.go +++ b/contrib/fuzz/containerd_import_fuzz_test.go @@ -41,7 +41,7 @@ func FuzzContainerdImport(f *testing.F) { client, err := containerd.New(defaultAddress) if err != nil { - return + t.Fatal(err) } defer client.Close() diff --git a/contrib/fuzz/cri_fuzzer.go b/contrib/fuzz/cri_server_fuzz_test.go similarity index 75% rename from contrib/fuzz/cri_fuzzer.go rename to contrib/fuzz/cri_server_fuzz_test.go index ff67439b4d..8f9e0941f2 100644 --- a/contrib/fuzz/cri_fuzzer.go +++ b/contrib/fuzz/cri_server_fuzz_test.go @@ -1,5 +1,3 @@ -//go:build gofuzz - /* Copyright The containerd Authors. Licensed under the Apache License, Version 2.0 (the "License"); @@ -18,46 +16,93 @@ package fuzz import ( "context" "fmt" + "os" golangruntime "runtime" "strings" + "testing" fuzz "github.com/AdaLogics/go-fuzz-headers" - runtime "k8s.io/cri-api/pkg/apis/runtime/v1" - + containerd "github.com/containerd/containerd/v2/client" + criconfig "github.com/containerd/containerd/v2/internal/cri/config" + "github.com/containerd/containerd/v2/internal/cri/instrument" "github.com/containerd/containerd/v2/internal/cri/server" - sandboxstore "github.com/containerd/containerd/v2/internal/cri/store/sandbox" + "github.com/containerd/containerd/v2/internal/cri/server/images" + "github.com/containerd/containerd/v2/pkg/oci" + "github.com/containerd/errdefs" + "google.golang.org/grpc" + runtime "k8s.io/cri-api/pkg/apis/runtime/v1" ) -var ( - // The APIs the fuzzer can call: - ops = map[int]string{ - 0: "createContainer", - 1: "removeContainer", - 2: "addSandboxes", - 3: "listContainers", - 4: "startContainer", - 5: "containerStats", - 6: "listContainerStats", - 7: "containerStatus", - 8: "stopContainer", - 9: "updateContainerResources", - 10: "listImages", - 11: "removeImages", - 12: "imageStatus", - 13: "imageFsInfo", - 14: "listPodSandbox", - 15: "portForward", - 16: "removePodSandbox", - 17: "runPodSandbox", - 18: "podSandboxStatus", - 19: "stopPodSandbox", - 20: "status", - 21: "updateRuntimeConfig", +func FuzzCRIServer(f *testing.F) { + if os.Getuid() != 0 { + f.Skip("skipping fuzz test that requires root") } + + f.Fuzz(func(t *testing.T, data []byte) { + initDaemon.Do(startDaemon) + + f := fuzz.NewConsumer(data) + + client, err := containerd.New(defaultAddress) + if err != nil { + t.Fatal(err) + } + defer client.Close() + + imageConfig := criconfig.ImageConfig{} + + imageService, err := images.NewService(imageConfig, &images.CRIImageServiceOptions{ + Client: client, + }) + if err != nil { + t.Fatal(err) + } + + c, rs, err := server.NewCRIService(&server.CRIServiceOptions{ + RuntimeService: &fakeRuntimeService{}, + ImageService: imageService, + Client: client, + }) + if err != nil { + t.Fatal(err) + } + + fuzzCRI(t, f, &service{ + CRIService: c, + RuntimeServiceServer: rs, + ImageServiceServer: imageService.GRPCService(), + }) + }) +} + +type fakeRuntimeService struct{} + +func (fakeRuntimeService) Config() criconfig.Config { + return criconfig.Config{} +} + +func (fakeRuntimeService) LoadOCISpec(string) (*oci.Spec, error) { + return nil, errdefs.ErrNotFound +} + +type service struct { + server.CRIService + runtime.RuntimeServiceServer + runtime.ImageServiceServer +} + +func (c *service) Register(s *grpc.Server) error { + instrumented := instrument.NewService(c) + runtime.RegisterRuntimeServiceServer(s, instrumented) + runtime.RegisterImageServiceServer(s, instrumented) + return nil +} + +var ( executionOrder []string ) -func printExecutions() { +func printExecutions(t *testing.T) { if r := recover(); r != nil { var err string switch res := r.(type) { @@ -70,11 +115,11 @@ func printExecutions() { default: err = "uknown error type" } - fmt.Println("Executions:") + t.Log("Executions:") for _, eo := range executionOrder { - fmt.Println(eo) + t.Log(eo) } - panic(err) + t.Fatal(err) } } @@ -84,68 +129,45 @@ type fuzzCRIService interface { runtime.ImageServiceServer } -func fuzzCRI(f *fuzz.ConsumeFuzzer, c fuzzCRIService) int { +func fuzzCRI(t *testing.T, f *fuzz.ConsumeFuzzer, c fuzzCRIService) int { + ops := []func(c fuzzCRIService, f *fuzz.ConsumeFuzzer) error{ + createContainerFuzz, + removeContainerFuzz, + listContainersFuzz, + startContainerFuzz, + containerStatsFuzz, + listContainerStatsFuzz, + containerStatusFuzz, + stopContainerFuzz, + updateContainerResourcesFuzz, + listImagesFuzz, + removeImagesFuzz, + imageStatusFuzz, + imageFsInfoFuzz, + listPodSandboxFuzz, + portForwardFuzz, + removePodSandboxFuzz, + runPodSandboxFuzz, + podSandboxStatusFuzz, + stopPodSandboxFuzz, + statusFuzz, + updateRuntimeConfigFuzz, + } + calls, err := f.GetInt() if err != nil { return 0 } executionOrder = make([]string, 0) - defer printExecutions() + defer printExecutions(t) for i := 0; i < calls%40; i++ { op, err := f.GetInt() if err != nil { return 0 } - opType := op % len(ops) - - switch ops[opType] { - case "createContainer": - createContainerFuzz(c, f) - case "removeContainer": - removeContainerFuzz(c, f) - case "addSandboxes": - addSandboxesFuzz(c, f) - case "listContainers": - listContainersFuzz(c, f) - case "startContainer": - startContainerFuzz(c, f) - case "containerStats": - containerStatsFuzz(c, f) - case "listContainerStats": - listContainerStatsFuzz(c, f) - case "containerStatus": - containerStatusFuzz(c, f) - case "stopContainer": - stopContainerFuzz(c, f) - case "updateContainerResources": - updateContainerResourcesFuzz(c, f) - case "listImages": - listImagesFuzz(c, f) - case "removeImages": - removeImagesFuzz(c, f) - case "imageStatus": - imageStatusFuzz(c, f) - case "imageFsInfo": - imageFsInfoFuzz(c, f) - case "listPodSandbox": - listPodSandboxFuzz(c, f) - case "portForward": - portForwardFuzz(c, f) - case "removePodSandbox": - removePodSandboxFuzz(c, f) - case "runPodSandbox": - runPodSandboxFuzz(c, f) - case "podSandboxStatus": - podSandboxStatusFuzz(c, f) - case "stopPodSandbox": - stopPodSandboxFuzz(c, f) - case "status": - statusFuzz(c, f) - case "updateRuntimeConfig": - updateRuntimeConfigFuzz(c, f) - } + ops[op%len(ops)](c, f) } return 1 } @@ -184,67 +206,6 @@ func removeContainerFuzz(c fuzzCRIService, f *fuzz.ConsumeFuzzer) error { return nil } -func sandboxStore(cs fuzzCRIService) (*sandboxstore.Store, error) { - var ( - ss *sandboxstore.Store - err error - ) - - ss, err = server.SandboxStore(cs) - if err != nil { - ss, err = server.SandboxStore(cs) - if err != nil { - return nil, err - } - return ss, nil - } - return ss, nil -} - -// addSandboxesFuzz creates a sandbox and adds it to the sandboxstore -func addSandboxesFuzz(c fuzzCRIService, f *fuzz.ConsumeFuzzer) error { - quantity, err := f.GetInt() - if err != nil { - return err - } - - ss, err := sandboxStore(c) - if err != nil { - return err - } - - for i := 0; i < quantity%20; i++ { - newSandbox, err := getSandboxFuzz(f) - if err != nil { - return err - } - err = ss.Add(newSandbox) - if err != nil { - return err - } - } - return nil -} - -// getSandboxFuzz creates a sandbox -func getSandboxFuzz(f *fuzz.ConsumeFuzzer) (sandboxstore.Sandbox, error) { - metadata := sandboxstore.Metadata{} - status := sandboxstore.Status{} - err := f.GenerateStruct(&metadata) - if err != nil { - return sandboxstore.Sandbox{}, err - } - err = f.GenerateStruct(&status) - if err != nil { - return sandboxstore.Sandbox{}, err - } - - reqString := fmt.Sprintf("metadata: %+v\nstatus: %+v\n", metadata, status) - logExecution("sandboxstore.NewSandbox", reqString) - - return sandboxstore.NewSandbox(metadata, status), nil -} - // listContainersFuzz creates a ListContainersRequest and passes // it to c.ListContainers func listContainersFuzz(c fuzzCRIService, f *fuzz.ConsumeFuzzer) error { diff --git a/contrib/fuzz/cri_server_fuzzer.go b/contrib/fuzz/cri_server_fuzzer.go deleted file mode 100644 index 3eaaa514b5..0000000000 --- a/contrib/fuzz/cri_server_fuzzer.go +++ /dev/null @@ -1,92 +0,0 @@ -//go: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 ( - fuzz "github.com/AdaLogics/go-fuzz-headers" - "google.golang.org/grpc" - runtime "k8s.io/cri-api/pkg/apis/runtime/v1" - - containerd "github.com/containerd/containerd/v2/client" - criconfig "github.com/containerd/containerd/v2/internal/cri/config" - "github.com/containerd/containerd/v2/internal/cri/instrument" - "github.com/containerd/containerd/v2/internal/cri/server" - "github.com/containerd/containerd/v2/internal/cri/server/images" - "github.com/containerd/containerd/v2/pkg/oci" - "github.com/containerd/errdefs" -) - -func FuzzCRIServer(data []byte) int { - initDaemon.Do(startDaemon) - - f := fuzz.NewConsumer(data) - - client, err := containerd.New(defaultAddress) - if err != nil { - return 0 - } - defer client.Close() - - imageConfig := criconfig.ImageConfig{} - - imageService, err := images.NewService(imageConfig, &images.CRIImageServiceOptions{ - Client: client, - }) - if err != nil { - panic(err) - } - - c, rs, err := server.NewCRIService(&server.CRIServiceOptions{ - RuntimeService: &fakeRuntimeService{}, - ImageService: imageService, - Client: client, - }) - if err != nil { - panic(err) - } - - return fuzzCRI(f, &service{ - CRIService: c, - RuntimeServiceServer: rs, - ImageServiceServer: imageService.GRPCService(), - }) -} - -type fakeRuntimeService struct{} - -func (fakeRuntimeService) Config() criconfig.Config { - return criconfig.Config{} -} - -func (fakeRuntimeService) LoadOCISpec(string) (*oci.Spec, error) { - return nil, errdefs.ErrNotFound -} - -type service struct { - server.CRIService - runtime.RuntimeServiceServer - runtime.ImageServiceServer -} - -func (c *service) Register(s *grpc.Server) error { - instrumented := instrument.NewService(c) - runtime.RegisterRuntimeServiceServer(s, instrumented) - runtime.RegisterImageServiceServer(s, instrumented) - return nil -} diff --git a/internal/cri/server/fuzz.go b/internal/cri/server/fuzz.go deleted file mode 100644 index d217d0ec76..0000000000 --- a/internal/cri/server/fuzz.go +++ /dev/null @@ -1,33 +0,0 @@ -//go: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 server - -import ( - "fmt" - - "github.com/containerd/containerd/v2/internal/cri/store/sandbox" -) - -func SandboxStore(cs CRIService) (*sandbox.Store, error) { - s, ok := cs.(*criService) - if !ok { - return nil, fmt.Errorf("%+v is not sbserver.criService", cs) - } - return s.sandboxStore, nil -}