istio.io/istio@v0.0.0-20240520182934-d79c90f27776/tests/fuzz/analyzer_fuzzer.go (about)

     1  // Copyright Istio Authors
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package fuzz
    16  
    17  import (
    18  	"bytes"
    19  	"os"
    20  
    21  	fuzz "github.com/AdaLogics/go-fuzz-headers"
    22  
    23  	"istio.io/istio/pkg/config"
    24  	"istio.io/istio/pkg/config/analysis"
    25  	"istio.io/istio/pkg/config/analysis/analyzers"
    26  	"istio.io/istio/pkg/config/analysis/local"
    27  	"istio.io/istio/pkg/config/analysis/scope"
    28  	"istio.io/istio/pkg/log"
    29  )
    30  
    31  var availableAnalyzers = analyzers.All()
    32  
    33  // createRandomConfigFile creates a single fuzzed config file
    34  func createRandomConfigFile(f *fuzz.ConsumeFuzzer) (string, error) {
    35  	data, err := f.GetBytes()
    36  	if err != nil {
    37  		return "nobytes", err
    38  	}
    39  	tmpfile, err := os.CreateTemp("", "example")
    40  	if err != nil {
    41  		return "nofile", err
    42  	}
    43  	if _, err := tmpfile.Write(data); err != nil {
    44  		return "nofile", err
    45  	}
    46  	if err := tmpfile.Close(); err != nil {
    47  		return "nofile", err
    48  	}
    49  	return tmpfile.Name(), nil
    50  }
    51  
    52  // createRandomConfigFiles creates a slice of ReaderSources
    53  func createRandomConfigFiles(f *fuzz.ConsumeFuzzer) ([]local.ReaderSource, error) {
    54  	var files []local.ReaderSource
    55  
    56  	numberOfFiles, err := f.GetInt()
    57  	if err != nil {
    58  		return files, err
    59  	}
    60  	maxFiles := numberOfFiles % 10
    61  
    62  	// Gather test files
    63  	for i := 0; i < maxFiles; i++ {
    64  		name, err := f.GetString()
    65  		if err != nil {
    66  			return files, err
    67  		}
    68  		rBytes, err := f.GetBytes()
    69  		if err != nil {
    70  			return files, err
    71  		}
    72  		r := bytes.NewReader(rBytes)
    73  		files = append(files, local.ReaderSource{Name: name, Reader: r})
    74  	}
    75  	return files, nil
    76  }
    77  
    78  // runAnalyzer runs the analyzer
    79  func runAnalyzer(sa *local.IstiodAnalyzer) (local.AnalysisResult, error) {
    80  	prevLogLevel := scope.Processing.GetOutputLevel()
    81  	scope.Processing.SetOutputLevel(log.NoneLevel)
    82  	defer scope.Processing.SetOutputLevel(prevLogLevel)
    83  
    84  	cancel := make(chan struct{})
    85  	result, err := sa.Analyze(cancel)
    86  	if err != nil {
    87  		return local.AnalysisResult{}, err
    88  	}
    89  	return result, err
    90  }
    91  
    92  // FuzzAnalyzer implements the fuzzer
    93  func FuzzAnalyzer(data []byte) int {
    94  	f := fuzz.NewConsumer(data)
    95  	analyzerIndex, err := f.GetInt()
    96  	if err != nil {
    97  		return 0
    98  	}
    99  	analyzer := availableAnalyzers[analyzerIndex%len(availableAnalyzers)]
   100  
   101  	requestedInputsByAnalyzer := make(map[string]map[config.GroupVersionKind]struct{})
   102  	analyzerName := analyzer.Metadata().Name
   103  	cr := func(col config.GroupVersionKind) {
   104  		if _, ok := requestedInputsByAnalyzer[analyzerName]; !ok {
   105  			requestedInputsByAnalyzer[analyzerName] = make(map[config.GroupVersionKind]struct{})
   106  		}
   107  		requestedInputsByAnalyzer[analyzerName][col] = struct{}{}
   108  	}
   109  
   110  	// Mesh config file
   111  	addMeshConfig, err := f.GetBool()
   112  	if err != nil {
   113  		return 0
   114  	}
   115  	meshConfigFile := ""
   116  	if addMeshConfig {
   117  		meshConfigFile, err = createRandomConfigFile(f)
   118  		if err != nil {
   119  			return 0
   120  		}
   121  		defer os.Remove(meshConfigFile)
   122  	}
   123  
   124  	// Mesh networks file
   125  	addMeshNetworks, err := f.GetBool()
   126  	if err != nil {
   127  		return 0
   128  	}
   129  	meshNetworkFile := ""
   130  	if addMeshNetworks {
   131  		meshNetworkFile, err = createRandomConfigFile(f)
   132  		if err != nil {
   133  			return 0
   134  		}
   135  		defer os.Remove(meshNetworkFile)
   136  	}
   137  
   138  	configFiles, err := createRandomConfigFiles(f)
   139  	if err != nil {
   140  		return 0
   141  	}
   142  
   143  	sa := local.NewSourceAnalyzer(analysis.Combine("testCase", analyzer), "", "istio-system", cr)
   144  	if addMeshConfig {
   145  		err = sa.AddFileKubeMeshConfig(meshConfigFile)
   146  		if err != nil {
   147  			return 0
   148  		}
   149  	}
   150  	if addMeshNetworks {
   151  		err = sa.AddFileKubeMeshNetworks(meshNetworkFile)
   152  		if err != nil {
   153  			return 0
   154  		}
   155  	}
   156  
   157  	// Include default resources
   158  	err = sa.AddDefaultResources()
   159  	if err != nil {
   160  		return 0
   161  	}
   162  
   163  	// Include resources from test files
   164  	err = sa.AddReaderKubeSource(configFiles)
   165  	if err != nil {
   166  		return 0
   167  	}
   168  	_, _ = runAnalyzer(sa)
   169  	return 1
   170  }