github.com/google/syzkaller@v0.0.0-20251211124644-a066d2bc4b02/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/mgrconfig"
    14  	"github.com/google/syzkaller/pkg/osutil"
    15  	"github.com/google/syzkaller/pkg/vminfo"
    16  	"github.com/google/syzkaller/sys/targets"
    17  )
    18  
    19  func makeGvisor(target *targets.Target, kernelDirs *mgrconfig.KernelDirs, modules []*vminfo.KernelModule) (*Impl,
    20  	error) {
    21  	if len(modules) != 0 {
    22  		return nil, fmt.Errorf("gvisor coverage does not support modules")
    23  	}
    24  	bin := filepath.Join(kernelDirs.Obj, target.KernelObject)
    25  	// pkg/build stores runsc as 'vmlinux' (we pretent to be linux), but a local build will have it as 'runsc'.
    26  	if !osutil.IsExist(bin) {
    27  		bin = filepath.Join(filepath.Dir(bin), "runsc")
    28  	}
    29  	frames, err := gvisorSymbolize(bin, kernelDirs.Src)
    30  	if err != nil {
    31  		return nil, err
    32  	}
    33  	unitMap := make(map[string]*CompileUnit)
    34  	for _, frame := range frames {
    35  		unit := unitMap[frame.Name]
    36  		if unit == nil {
    37  			unit = &CompileUnit{
    38  				ObjectUnit: ObjectUnit{
    39  					Name: frame.Name,
    40  				},
    41  				Path: frame.Path,
    42  			}
    43  			unitMap[frame.Name] = unit
    44  		}
    45  		unit.PCs = append(unit.PCs, frame.PC)
    46  	}
    47  	var units []*CompileUnit
    48  	for _, unit := range unitMap {
    49  		units = append(units, unit)
    50  	}
    51  	impl := &Impl{
    52  		Units:  units,
    53  		Frames: frames,
    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 nil, fmt.Errorf("read pc %q, but no line info", pc)
   101  	}
   102  	if !s.Scan() {
   103  		return nil, fmt.Errorf("read pc %q, but no line info", pc)
   104  	}
   105  	match := gvisorLineRe.FindStringSubmatch(s.Text())
   106  	if match == nil {
   107  		return nil, 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 nil, 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]+)$`)