gvisor.dev/gvisor@v0.0.0-20240520182842-f9d4d51c7e0f/test/secfuzz/secfuzz_covermeup.tmpl.go (about) 1 // Copyright 2023 The gVisor 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 //go:build fuzz 16 // +build fuzz 17 18 package secfuzz 19 20 import ( 21 "gvisor.dev/gvisor/pkg/bpf" 22 ) 23 24 // Go does coverage-based fuzzing, so it discovers inputs that are 25 // "interesting" if they manage to cover new code. 26 // Go does not understand "BPF coverage", and there is no easy way to 27 // tell it that a certain BPF input has covered new lines of code. 28 // So... this approach converts BPF code coverage into native Go code 29 // coverage, by simply enumerating every single line of BPF code that 30 // could possibly exist, and having that be its own branch which Go's 31 // fuzzer then recognizes as being covered. 32 // This is possible because BPF programs are limited to 33 // `bpf.MaxInstructions` (currently 4,096), so all we need to do is to 34 // enumerate them all here. 35 // (Note that if this limit ends up being too small (which is possible; 36 // as the time of writing, our current unoptimized Sentry filters are 37 // around ~1,500 instructions), there is nothing preventing this 38 // file from being expanded to cover more instructions beyond this 39 // limit.) 40 // 41 // Then, because we want to compare the execution of two programs, 42 // we need to do it all over again; we can't reuse the same thing 43 // because this would mean that a line is considered "covered" by Go 44 // if *either* program covers it. 45 // 46 // This is hacky but works great! 47 // 48 // The actual `secfuzz_covermeup.go` file is generated by a genrule. 49 50 // countExecutedLinesProgram1 converts coverage data of the first BPF program 51 // to Go coverage data. 52 func countExecutedLinesProgram1(execution bpf.ExecutionMetrics, fuzzee *Fuzzee) { 53 covered := execution.Coverage 54 switch len(execution.Coverage) { 55 // GENERATED_LINES_INSERTED_HERE_THIS_IS_A_LOAD_BEARING_COMMENT 56 case 1: 57 // The last switch statement is not auto-generated, 58 // because unlike all other cases that are auto-generated 59 // before it, it does not `fallthrough`. 60 if covered[0] { 61 fuzzee.coverage[0].Store(true) 62 } 63 } 64 } 65 66 // countExecutedLinesProgram2 converts coverage data of the second BPF 67 // program to Go coverage data. 68 func countExecutedLinesProgram2(execution bpf.ExecutionMetrics, fuzzee *Fuzzee) { 69 covered := execution.Coverage 70 switch len(execution.Coverage) { 71 // GENERATED_LINES_INSERTED_HERE_THIS_IS_A_LOAD_BEARING_COMMENT 72 case 1: 73 // The last switch statement is not auto-generated, 74 // because unlike all other cases that are auto-generated 75 // before it, it does not `fallthrough`. 76 if covered[0] { 77 fuzzee.coverage[0].Store(true) 78 } 79 } 80 }