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 `))