github.com/google/syzkaller@v0.0.0-20240517125934-c0f1611a36d6/pkg/cover/backend/gvisor.go (about) 1 // Copyright 2020 syzkaller project authors. All rights reserved. 2 // Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file. 3 4 package backend 5 6 import ( 7 "bufio" 8 "fmt" 9 "path/filepath" 10 "regexp" 11 "strconv" 12 13 "github.com/google/syzkaller/pkg/osutil" 14 "github.com/google/syzkaller/sys/targets" 15 ) 16 17 func makeGvisor(target *targets.Target, objDir, srcDir, buildDir string, modules []KernelModule) (*Impl, error) { 18 if len(modules) != 0 { 19 return nil, fmt.Errorf("gvisor coverage does not support modules") 20 } 21 bin := filepath.Join(objDir, target.KernelObject) 22 // pkg/build stores runsc as 'vmlinux' (we pretent to be linux), but a local build will have it as 'runsc'. 23 if !osutil.IsExist(bin) { 24 bin = filepath.Join(filepath.Dir(bin), "runsc") 25 } 26 frames, err := gvisorSymbolize(bin, srcDir) 27 if err != nil { 28 return nil, err 29 } 30 unitMap := make(map[string]*CompileUnit) 31 for _, frame := range frames { 32 unit := unitMap[frame.Name] 33 if unit == nil { 34 unit = &CompileUnit{ 35 ObjectUnit: ObjectUnit{ 36 Name: frame.Name, 37 }, 38 Path: frame.Path, 39 } 40 unitMap[frame.Name] = unit 41 } 42 unit.PCs = append(unit.PCs, frame.PC) 43 } 44 var units []*CompileUnit 45 for _, unit := range unitMap { 46 units = append(units, unit) 47 } 48 impl := &Impl{ 49 Units: units, 50 Frames: frames, 51 RestorePC: func(pc uint32) uint64 { 52 return uint64(pc) 53 }, 54 } 55 return impl, nil 56 } 57 58 func gvisorSymbolize(bin, srcDir string) ([]Frame, error) { 59 cmd := osutil.Command(bin, "symbolize", "-all") 60 stdout, err := cmd.StdoutPipe() 61 if err != nil { 62 return nil, err 63 } 64 defer stdout.Close() 65 if err := cmd.Start(); err != nil { 66 return nil, err 67 } 68 defer cmd.Wait() 69 defer cmd.Process.Kill() 70 var frames []Frame 71 s := bufio.NewScanner(stdout) 72 for s.Scan() { 73 frame, err := gvisorParseLine(s) 74 if err != nil { 75 return nil, err 76 } 77 frame.Path = filepath.Join(srcDir, frame.Name) 78 if !osutil.IsExist(frame.Path) { 79 // Try to locate auto-generated files. 80 // Note: some files are only present under some hashed path, 81 // e.g. bazel-out/k8-fastbuild-ST-4c64f0b3d5c7/bin/pkg/usermem/addr_range.go, 82 // it's unclear how we can locate them. In a local run that may be under objDir, 83 // but this is not the case for syz-ci. 84 path := filepath.Join(srcDir, "bazel-out", "k8-fastbuild", "bin", frame.Name) 85 if osutil.IsExist(path) { 86 frame.Path = path 87 } 88 } 89 frames = append(frames, frame) 90 } 91 if err := s.Err(); err != nil { 92 return nil, err 93 } 94 return frames, nil 95 } 96 97 func gvisorParseLine(s *bufio.Scanner) (Frame, error) { 98 pc, err := strconv.ParseUint(s.Text(), 0, 64) 99 if err != nil { 100 return Frame{}, fmt.Errorf("read pc %q, but no line info", pc) 101 } 102 if !s.Scan() { 103 return Frame{}, fmt.Errorf("read pc %q, but no line info", pc) 104 } 105 match := gvisorLineRe.FindStringSubmatch(s.Text()) 106 if match == nil { 107 return Frame{}, fmt.Errorf("failed to parse line: %q", s.Text()) 108 } 109 var ints [4]int 110 for i := range ints { 111 x, err := strconv.ParseUint(match[i+3], 0, 32) 112 if err != nil { 113 return Frame{}, fmt.Errorf("failed to parse number %q: %w", match[i+3], err) 114 } 115 ints[i] = int(x) 116 } 117 frame := Frame{ 118 PC: pc, 119 Name: match[2], 120 Range: Range{ 121 StartLine: ints[0], 122 StartCol: ints[1], 123 EndLine: ints[2], 124 EndCol: ints[3], 125 }, 126 } 127 return frame, nil 128 } 129 130 var gvisorLineRe = regexp.MustCompile(`^(.*/)?(pkg/[^:]+):([0-9]+).([0-9]+),([0-9]+).([0-9]+)$`)