blob: 11b0645d6ae23b3b4831d6e5f207c0e349654408 [file] [log] [blame]
package common
import (
"sort"
"strings"
"go.skia.org/infra/go/sklog"
)
const (
TEST_HARNESS_NAME = "fuzz"
UNKNOWN_FUNCTION = "UNKNOWN"
UNKNOWN_FILE = "UNKNOWN"
ASSEMBLY_CODE_FILE = "[assembly code]"
UNSYMBOLIZED_RESULT = "UNSYMBOLIZED"
UNKNOWN_LINE = -1
ASAN_OPTIONS = "ASAN_OPTIONS=detect_leaks=0 symbolize=1"
STABLE_FUZZER = "stable"
EXPERIMENTAL_FUZZER = "experimental"
FUZZER_NOT_FOUND = "FUZZER_NOT_FOUND"
UNCLAIMED = "<unclaimed>"
)
// The list of architectures we fuzz on
var ARCHITECTURES = []string{"linux_x64"}
// By default, allow a generous amount of RAM and set a flexible approach to timeouts.
var defaultGenerationArgs = []string{"-m", "5000", "-t", "500+"}
var commonImpl CommonImpl
// FuzzerInfo contains all the configuration needed to display, execute and analyze a fuzzer.
type FuzzerInfo struct {
// PrettyName is the human readable name for this fuzzer.
PrettyName string
// Status should be STABLE_FUZZER or EXPERIMENTAL_FUZZER
Status string
// The Groomer is responsible for stabilizing experimental fuzzers and will be the point
// of contact for the sheriff for triaging any bad fuzzes found in stable fuzzers.
Groomer string
// ExtraBugLabels are any additional labels that should be included when making a bug.
ExtraBugLabels []string
// ArgsAfterExecutable is a slice of arguments that come after the executable
// and before the path to the bytes file, that will be fuzzed.
ArgsAfterExecutable []string
// GenerationArgs is a slice of arguments that are used to adjust timeouts/memory usage
// when generating files.
GenerationArgs []string
}
// fuzzers is a map of fuzzer_name -> FuzzerInfo for all registered fuzzers. This should be a
// centralized location to add a new fuzzer, i.e. adding an entry and information here, combined
// with modifying the fuzzer-be.service should be sufficient to add a new fuzzer into the system.
var fuzzers = map[string]FuzzerInfo{
"android_codec": {
PrettyName: "Android Codec",
Status: STABLE_FUZZER,
Groomer: "scroggo",
ExtraBugLabels: []string{"Area-ImageDecoder"},
ArgsAfterExecutable: []string{"--type", "android_codec", "--bytes"},
GenerationArgs: defaultGenerationArgs,
},
"api_draw_functions": {
PrettyName: "API - CanvasDrawFunctions",
Status: STABLE_FUZZER,
Groomer: "hcm",
ExtraBugLabels: nil,
ArgsAfterExecutable: []string{"--type", "api", "--name", "DrawFunctions", "--bytes"},
GenerationArgs: defaultGenerationArgs,
},
"api_gradient": {
PrettyName: "API - Gradients",
Status: STABLE_FUZZER,
Groomer: "fmalita",
ExtraBugLabels: nil,
ArgsAfterExecutable: []string{"--type", "api", "--name", "Gradients", "--bytes"},
GenerationArgs: defaultGenerationArgs,
},
"api_image_filter": {
PrettyName: "API - SerializedImageFilter",
Status: STABLE_FUZZER,
Groomer: "robertphillips",
ExtraBugLabels: []string{"Area-ImageFilter"},
ArgsAfterExecutable: []string{"--type", "api", "--name", "SerializedImageFilter", "--bytes"},
GenerationArgs: defaultGenerationArgs,
},
"api_parse_path": {
PrettyName: "API - ParsePath",
Status: STABLE_FUZZER,
Groomer: "caryclark",
ExtraBugLabels: nil,
ArgsAfterExecutable: []string{"--type", "api", "--name", "ParsePath", "--bytes"},
GenerationArgs: defaultGenerationArgs,
},
"api_path_measure": {
PrettyName: "API - PathMeasure",
Status: STABLE_FUZZER,
Groomer: "caryclark",
ExtraBugLabels: nil,
ArgsAfterExecutable: []string{"--type", "api", "--name", "PathMeasure", "--bytes"},
GenerationArgs: defaultGenerationArgs,
},
"api_pathop": {
PrettyName: "API - PathOp",
Status: STABLE_FUZZER,
Groomer: "caryclark",
ExtraBugLabels: nil,
ArgsAfterExecutable: []string{"--type", "api", "--name", "Pathop", "--bytes"},
GenerationArgs: defaultGenerationArgs,
},
"api_polyutils": {
PrettyName: "API - PolyUtils",
Status: EXPERIMENTAL_FUZZER,
Groomer: "jvanverth",
ExtraBugLabels: nil,
ArgsAfterExecutable: []string{"--type", "api", "--name", "PolyUtils", "--bytes"},
GenerationArgs: defaultGenerationArgs,
},
"color_deserialize": {
PrettyName: "SkColorSpace - Deserialize",
Status: STABLE_FUZZER,
Groomer: "scroggo",
ExtraBugLabels: []string{"Area-ImageDecoder"},
ArgsAfterExecutable: []string{"--type", "color_deserialize", "--bytes"},
GenerationArgs: defaultGenerationArgs,
},
"image_decode": {
PrettyName: "General Image Decode",
Status: STABLE_FUZZER,
Groomer: "scroggo",
ExtraBugLabels: []string{"Area-ImageDecoder"},
ArgsAfterExecutable: []string{"--type", "image_decode", "--bytes"},
GenerationArgs: defaultGenerationArgs,
},
"image_decode_incremental": {
PrettyName: "Incremental Image Decode",
Status: STABLE_FUZZER,
Groomer: "scroggo",
ExtraBugLabels: []string{"Area-ImageDecoder"},
ArgsAfterExecutable: []string{"--type", "image_decode_incremental", "--bytes"},
GenerationArgs: defaultGenerationArgs,
},
"image_filter_deserialize": {
PrettyName: "FilterFuzz Stub",
Status: STABLE_FUZZER,
Groomer: "reed",
ExtraBugLabels: nil,
ArgsAfterExecutable: []string{"--type", "filter_fuzz", "--bytes"},
GenerationArgs: []string{"-m", "5000", "-t", "200+"},
},
"jpeg_encoder": {
PrettyName: "JPEG encoder",
Status: STABLE_FUZZER,
Groomer: "scroggo",
ExtraBugLabels: nil,
ArgsAfterExecutable: []string{"--type", "api", "--name", "JPEGEncoder", "--bytes"},
GenerationArgs: defaultGenerationArgs,
},
"mock_gpu_canvas": {
PrettyName: "Canvas to mock GL backend",
Status: EXPERIMENTAL_FUZZER,
Groomer: "robertphillips",
ExtraBugLabels: nil,
ArgsAfterExecutable: []string{"--type", "api", "--name", "MockGPUCanvas", "--bytes"},
// For most of the canvases, some of the initial test cases take a long time. If they
// time out (1000+ms ), afl-fuzz aborts during startup. To keep things moving, we
// tell afl-fuzz to set the timout to 500ms and ignore any test cases that take longer
// than that (the plus sign after 500).
GenerationArgs: []string{"-m", "5000", "-t", "500+"},
},
"n32_canvas": {
PrettyName: "Canvas to raster n32 backend",
Status: STABLE_FUZZER,
Groomer: "halcanary",
ExtraBugLabels: nil,
ArgsAfterExecutable: []string{"--type", "api", "--name", "RasterN32Canvas", "--bytes"},
GenerationArgs: []string{"-m", "5000", "-t", "500+"},
},
"null_canvas": {
PrettyName: "Canvas to null canvas backend",
Status: STABLE_FUZZER,
Groomer: "halcanary",
ExtraBugLabels: nil,
ArgsAfterExecutable: []string{"--type", "api", "--name", "NullCanvas", "--bytes"},
GenerationArgs: defaultGenerationArgs,
},
"path_deserialize": {
PrettyName: "SkPath deserialize",
Status: STABLE_FUZZER,
Groomer: "reed",
ExtraBugLabels: nil,
ArgsAfterExecutable: []string{"--type", "path_deserialize", "--bytes"},
GenerationArgs: defaultGenerationArgs,
},
"pdf_canvas": {
PrettyName: "Canvas to PDF backend",
Status: EXPERIMENTAL_FUZZER,
Groomer: "halcanary",
ExtraBugLabels: nil,
ArgsAfterExecutable: []string{"--type", "api", "--name", "PDFCanvas", "--bytes"},
GenerationArgs: []string{"-m", "5000", "-t", "500+"},
},
"png_encoder": {
PrettyName: "PNG encoder",
Status: STABLE_FUZZER,
Groomer: "scroggo",
ExtraBugLabels: nil,
ArgsAfterExecutable: []string{"--type", "api", "--name", "PNGEncoder", "--bytes"},
GenerationArgs: defaultGenerationArgs,
},
"region_deserialize": {
PrettyName: "SkRegion deserialize",
Status: STABLE_FUZZER,
Groomer: "halcanary",
ExtraBugLabels: nil,
ArgsAfterExecutable: []string{"--type", "region_deserialize", "--bytes"},
GenerationArgs: defaultGenerationArgs,
},
"region_set_path": {
PrettyName: "SkRegion set_path",
Status: STABLE_FUZZER,
Groomer: "reed",
ExtraBugLabels: nil,
ArgsAfterExecutable: []string{"--type", "region_set_path", "--bytes"},
GenerationArgs: defaultGenerationArgs,
},
"skcodec_scale": {
PrettyName: "SkCodec (Scaling)",
Status: STABLE_FUZZER,
Groomer: "scroggo",
ExtraBugLabels: []string{"Area-ImageDecoder"},
ArgsAfterExecutable: []string{"--type", "image_scale", "--bytes"},
GenerationArgs: defaultGenerationArgs,
},
"skcodec_mode": {
PrettyName: "SkCodec (Modes)",
Status: STABLE_FUZZER,
Groomer: "scroggo",
ExtraBugLabels: []string{"Area-ImageDecoder"},
ArgsAfterExecutable: []string{"--type", "image_mode", "--bytes"},
GenerationArgs: defaultGenerationArgs,
},
"sksl2glsl": {
PrettyName: "SKSL Compiler (GLSL)",
Status: EXPERIMENTAL_FUZZER,
Groomer: "ethannicholas",
ExtraBugLabels: []string{},
ArgsAfterExecutable: []string{"--type", "sksl2glsl", "--bytes"},
GenerationArgs: defaultGenerationArgs,
},
"skottie_json": {
PrettyName: "Skottie from JSON",
Status: STABLE_FUZZER,
Groomer: "fmalita",
ExtraBugLabels: []string{},
ArgsAfterExecutable: []string{"--type", "skottie_json", "--bytes"},
GenerationArgs: defaultGenerationArgs,
},
"skp": {
PrettyName: "SKP from ReadBuffer",
Status: EXPERIMENTAL_FUZZER,
Groomer: "reed",
ExtraBugLabels: []string{},
ArgsAfterExecutable: []string{"--type", "skp", "--bytes"},
GenerationArgs: defaultGenerationArgs,
},
"textblob": {
PrettyName: "TextBlob deserialize",
Status: STABLE_FUZZER,
Groomer: "fmalita",
ExtraBugLabels: []string{},
ArgsAfterExecutable: []string{"--type", "textblob", "--bytes"},
GenerationArgs: defaultGenerationArgs,
},
"webp_encoder": {
PrettyName: "WEBP encoder",
Status: STABLE_FUZZER,
Groomer: "scroggo",
ExtraBugLabels: nil,
ArgsAfterExecutable: []string{"--type", "api", "--name", "WEBPEncoder", "--bytes"},
GenerationArgs: defaultGenerationArgs,
},
}
// FUZZ_CATEGORIES is an alphabetized list of known fuzz categories.
var FUZZ_CATEGORIES = []string{}
var ANALYSIS_TYPES = []string{"ASAN_RELEASE", "ASAN_DEBUG", "CLANG_RELEASE", "CLANG_DEBUG"}
// TODO(kjlubick): Is it necessary to have two separate lists?
var STACKTRACE_ORDER = []string{"ASAN_RELEASE", "CLANG_RELEASE", "ASAN_DEBUG", "CLANG_DEBUG"}
func init() {
commonImpl = &defaultImpl{}
for k := range fuzzers {
FUZZ_CATEGORIES = append(FUZZ_CATEGORIES, k)
}
sort.Strings(FUZZ_CATEGORIES)
}
func PrettifyCategory(category string) string {
f, found := fuzzers[category]
if !found {
sklog.Errorf("Unknown category %s", category)
return FUZZER_NOT_FOUND
}
return f.PrettyName
}
func ExtraBugLabels(category string) []string {
f, found := fuzzers[category]
if !found {
sklog.Errorf("Unknown category %s", category)
return nil
}
return f.ExtraBugLabels
}
// ReplicationArgs returns a space separated list of the args needed to replicate the crash
// of a fuzz of a given category.
func ReplicationArgs(category string) string {
f, found := fuzzers[category]
if !found {
sklog.Errorf("Unknown category %s", category)
return FUZZER_NOT_FOUND
}
return strings.Join(f.ArgsAfterExecutable, " ")
}
// CategoryReminder returns a short string to remind a human what fuzz category
// a fuzz belongs to. For example "skcodec_scale" -> "image_scale" and
// "pdf_canvas" -> "api-PDFCanvas"
func CategoryReminder(category string) string {
f, found := fuzzers[category]
if !found {
sklog.Errorf("Unknown category %s", category)
return FUZZER_NOT_FOUND
}
// assume ['--type', type, {'--name', api_name}]
cr := f.ArgsAfterExecutable[1]
if cr == "api" {
cr += "-" + f.ArgsAfterExecutable[3]
}
return cr
}
// HasCategory returns if a given string corresponds to a known fuzzer category.
func HasCategory(c string) bool {
_, found := fuzzers[c]
return found
}
// Status returns the status of a fuzz category (i.e. stable, experimental, etc)
func Status(c string) string {
f, found := fuzzers[c]
if !found {
sklog.Errorf("Unknown category %s", c)
return FUZZER_NOT_FOUND
}
return f.Status
}
// Groomer returns the groomer of a fuzz category
func Groomer(c string) string {
f, found := fuzzers[c]
if !found {
sklog.Errorf("Unknown category %s", c)
return FUZZER_NOT_FOUND
}
return f.Groomer
}
// Returns if fuzzer knows about a given architecture
func HasArchitecture(a string) bool {
for _, ar := range ARCHITECTURES {
if a == ar {
return true
}
}
return false
}
func SetMockCommon(c CommonImpl) {
commonImpl = c
}
// Returns the Hostname of this machine. Clients should use this instead of
// os.Hostname because this function can be mocked out.
func Hostname() string {
return commonImpl.Hostname()
}