github.com/google/syzkaller@v0.0.0-20240517125934-c0f1611a36d6/sys/syz-extract/fetch.go (about)

     1  // Copyright 2017 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 main
     5  
     6  import (
     7  	"bytes"
     8  	"debug/elf"
     9  	"encoding/binary"
    10  	"fmt"
    11  	"io"
    12  	"os"
    13  	"regexp"
    14  	"strconv"
    15  	"strings"
    16  	"text/template"
    17  
    18  	"github.com/google/syzkaller/pkg/compiler"
    19  	"github.com/google/syzkaller/pkg/osutil"
    20  )
    21  
    22  type extractParams struct {
    23  	AddSource      string
    24  	DeclarePrintf  bool
    25  	DefineGlibcUse bool // workaround for incorrect flags to clang for fuchsia.
    26  	ExtractFromELF bool
    27  	TargetEndian   binary.ByteOrder
    28  }
    29  
    30  func extract(info *compiler.ConstInfo, cc string, args []string, params *extractParams) (
    31  	map[string]uint64, map[string]bool, error) {
    32  	data := &CompileData{
    33  		extractParams: params,
    34  		Defines:       info.Defines,
    35  		Includes:      info.Includes,
    36  	}
    37  	for _, def := range info.Consts {
    38  		data.Values = append(data.Values, def.Name)
    39  	}
    40  	bin := ""
    41  	missingIncludes := make(map[string]bool)
    42  	undeclared := make(map[string]bool)
    43  	valMap := make(map[string]bool)
    44  	for _, def := range info.Consts {
    45  		valMap[def.Name] = true
    46  	}
    47  	for {
    48  		bin1, out, err := compile(cc, args, data)
    49  		if err == nil {
    50  			bin = bin1
    51  			break
    52  		}
    53  		// Some consts and syscall numbers are not defined on some archs.
    54  		// Figure out from compiler output undefined consts,
    55  		// and try to compile again without them.
    56  		// May need to try multiple times because some severe errors terminate compilation.
    57  		tryAgain := false
    58  		for _, errMsg := range []string{
    59  			`error: [‘']([a-zA-Z0-9_]+)[’'] undeclared`,
    60  			`note: in expansion of macro [‘']([a-zA-Z0-9_]+)[’']`,
    61  			`note: expanded from macro [‘']([a-zA-Z0-9_]+)[’']`,
    62  			`error: use of undeclared identifier [‘']([a-zA-Z0-9_]+)[’']`,
    63  		} {
    64  			re := regexp.MustCompile(errMsg)
    65  			matches := re.FindAllSubmatch(out, -1)
    66  			for _, match := range matches {
    67  				val := string(match[1])
    68  				if valMap[val] && !undeclared[val] {
    69  					undeclared[val] = true
    70  					tryAgain = true
    71  				}
    72  			}
    73  		}
    74  		if !tryAgain {
    75  			return nil, nil, fmt.Errorf("failed to run compiler: %v %v\n%w\n%s",
    76  				cc, args, err, out)
    77  		}
    78  		data.Values = nil
    79  		for _, def := range info.Consts {
    80  			if undeclared[def.Name] {
    81  				continue
    82  			}
    83  			data.Values = append(data.Values, def.Name)
    84  		}
    85  		data.Includes = nil
    86  		for _, v := range info.Includes {
    87  			if missingIncludes[v] {
    88  				continue
    89  			}
    90  			data.Includes = append(data.Includes, v)
    91  		}
    92  	}
    93  	defer os.Remove(bin)
    94  
    95  	var flagVals []uint64
    96  	var err error
    97  	if data.ExtractFromELF {
    98  		flagVals, err = extractFromELF(bin, params.TargetEndian)
    99  	} else {
   100  		flagVals, err = extractFromExecutable(bin)
   101  	}
   102  	if err != nil {
   103  		return nil, nil, err
   104  	}
   105  	if len(flagVals) != len(data.Values) {
   106  		return nil, nil, fmt.Errorf("fetched wrong number of values %v, want != %v",
   107  			len(flagVals), len(data.Values))
   108  	}
   109  	res := make(map[string]uint64)
   110  	for i, val := range data.Values {
   111  		res[val] = flagVals[i]
   112  	}
   113  	return res, undeclared, nil
   114  }
   115  
   116  type CompileData struct {
   117  	*extractParams
   118  	Defines  map[string]string
   119  	Includes []string
   120  	Values   []string
   121  }
   122  
   123  func compile(cc string, args []string, data *CompileData) (string, []byte, error) {
   124  	src := new(bytes.Buffer)
   125  	if err := srcTemplate.Execute(src, data); err != nil {
   126  		return "", nil, fmt.Errorf("failed to generate source: %w", err)
   127  	}
   128  	binFile, err := osutil.TempFile("syz-extract-bin")
   129  	if err != nil {
   130  		return "", nil, err
   131  	}
   132  	args = append(args, []string{
   133  		"-x", "c", "-",
   134  		"-o", binFile,
   135  		"-w",
   136  	}...)
   137  	if data.ExtractFromELF {
   138  		args = append(args, "-c")
   139  	}
   140  	cmd := osutil.Command(cc, args...)
   141  	cmd.Stdin = src
   142  	if out, err := cmd.CombinedOutput(); err != nil {
   143  		os.Remove(binFile)
   144  		return "", out, err
   145  	}
   146  	return binFile, nil, nil
   147  }
   148  
   149  func extractFromExecutable(binFile string) ([]uint64, error) {
   150  	out, err := osutil.Command(binFile).CombinedOutput()
   151  	if err != nil {
   152  		return nil, fmt.Errorf("failed to run flags binary: %w\n%s", err, out)
   153  	}
   154  	if len(out) == 0 {
   155  		return nil, nil
   156  	}
   157  	var vals []uint64
   158  	for _, val := range strings.Split(string(out), " ") {
   159  		n, err := strconv.ParseUint(val, 10, 64)
   160  		if err != nil {
   161  			return nil, fmt.Errorf("failed to parse value: %w (%v)", err, val)
   162  		}
   163  		vals = append(vals, n)
   164  	}
   165  	return vals, nil
   166  }
   167  
   168  func extractFromELF(binFile string, targetEndian binary.ByteOrder) ([]uint64, error) {
   169  	f, err := os.Open(binFile)
   170  	if err != nil {
   171  		return nil, err
   172  	}
   173  	ef, err := elf.NewFile(f)
   174  	if err != nil {
   175  		return nil, err
   176  	}
   177  	for _, sec := range ef.Sections {
   178  		if sec.Name != "syz_extract_data" {
   179  			continue
   180  		}
   181  		data, err := io.ReadAll(sec.Open())
   182  		if err != nil {
   183  			return nil, err
   184  		}
   185  		vals := make([]uint64, len(data)/8)
   186  		if err := binary.Read(bytes.NewReader(data), targetEndian, &vals); err != nil {
   187  			return nil, err
   188  		}
   189  		return vals, nil
   190  	}
   191  	return nil, fmt.Errorf("did not find syz_extract_data section")
   192  }
   193  
   194  var srcTemplate = template.Must(template.New("").Parse(`
   195  {{if not .ExtractFromELF}}
   196  #define __asm__(...)
   197  {{end}}
   198  
   199  {{if .DefineGlibcUse}}
   200  #ifndef __GLIBC_USE
   201  #	define __GLIBC_USE(X) 0
   202  #endif
   203  {{end}}
   204  
   205  {{range $incl := $.Includes}}
   206  #include <{{$incl}}>
   207  {{end}}
   208  
   209  {{range $name, $val := $.Defines}}
   210  #ifndef {{$name}}
   211  #	define {{$name}} {{$val}}
   212  #endif
   213  {{end}}
   214  
   215  {{.AddSource}}
   216  
   217  {{if .DeclarePrintf}}
   218  int printf(const char *format, ...);
   219  {{end}}
   220  
   221  {{if .ExtractFromELF}}
   222  __attribute__((section("syz_extract_data")))
   223  unsigned long long vals[] = {
   224  	{{range $val := $.Values}}(unsigned long long){{$val}},
   225  	{{end}}
   226  };
   227  {{else}}
   228  int main() {
   229  	int i;
   230  	unsigned long long vals[] = {
   231  		{{range $val := $.Values}}(unsigned long long){{$val}},
   232  		{{end}}
   233  	};
   234  	for (i = 0; i < sizeof(vals)/sizeof(vals[0]); i++) {
   235  		if (i != 0)
   236  			printf(" ");
   237  		printf("%llu", vals[i]);
   238  	}
   239  	return 0;
   240  }
   241  {{end}}
   242  `))