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]+)$`)