From 3aa380090f35529e58e4a393e8e0dee79dd0d491 Mon Sep 17 00:00:00 2001 From: Dmitry Vyukov Date: Mon, 2 Dec 2019 14:36:47 +0100 Subject: sys/syz-extract: extract constants from ELF Add a second mode that extracts constant values from ELF object, instead of running the executable. This allows to not (1) link binaries, (2) use proper cross-compiler. It finally fixes 386/arm extracts for my distro. Hopefully not makes things worse for others, should generally be safer/more reliable. The current mode is left b/c I can't test all OSes, windows binaries are not ELF, so we may need it anyway. But later we may switch more OSes to this new mode if they break (fuchsia?). --- sys/syz-extract/akaros.go | 5 +- sys/syz-extract/extract.go | 4 -- sys/syz-extract/fetch.go | 114 ++++++++++++++++++++++++++++++++++----------- sys/syz-extract/freebsd.go | 6 ++- sys/syz-extract/fuchsia.go | 6 ++- sys/syz-extract/linux.go | 30 +++--------- sys/syz-extract/netbsd.go | 5 +- sys/syz-extract/openbsd.go | 5 +- sys/syz-extract/trusty.go | 5 +- sys/syz-extract/windows.go | 5 +- 10 files changed, 123 insertions(+), 62 deletions(-) (limited to 'sys') diff --git a/sys/syz-extract/akaros.go b/sys/syz-extract/akaros.go index 294152e4f..00375f195 100644 --- a/sys/syz-extract/akaros.go +++ b/sys/syz-extract/akaros.go @@ -41,5 +41,8 @@ func (*akaros) processFile(arch *Arch, info *compiler.ConstInfo) (map[string]uin args = append(args, "-I"+dir) } } - return extract(info, "gcc", args, "", true, false) + params := &extractParams{ + DeclarePrintf: true, + } + return extract(info, "gcc", args, params) } diff --git a/sys/syz-extract/extract.go b/sys/syz-extract/extract.go index 70c203b8d..64776bb96 100644 --- a/sys/syz-extract/extract.go +++ b/sys/syz-extract/extract.go @@ -13,7 +13,6 @@ import ( "runtime" "sort" "strings" - "sync" "github.com/google/syzkaller/pkg/ast" "github.com/google/syzkaller/pkg/compiler" @@ -39,9 +38,6 @@ type Arch struct { files []*File err error done chan bool - // Used by OS implementations: - once sync.Once - cc string } type File struct { diff --git a/sys/syz-extract/fetch.go b/sys/syz-extract/fetch.go index 58910d577..405b0167f 100644 --- a/sys/syz-extract/fetch.go +++ b/sys/syz-extract/fetch.go @@ -5,7 +5,10 @@ package main import ( "bytes" + "debug/elf" + "encoding/binary" "fmt" + "io/ioutil" "os" "regexp" "strconv" @@ -16,15 +19,20 @@ import ( "github.com/google/syzkaller/pkg/osutil" ) -func extract(info *compiler.ConstInfo, cc string, args []string, addSource string, declarePrintf, defineGlibcUse bool) ( +type extractParams struct { + AddSource string + DeclarePrintf bool + DefineGlibcUse bool // workaround for incorrect flags to clang for fuchsia. + ExtractFromELF bool +} + +func extract(info *compiler.ConstInfo, cc string, args []string, params *extractParams) ( map[string]uint64, map[string]bool, error) { data := &CompileData{ - AddSource: addSource, - Defines: info.Defines, - Includes: info.Includes, - Values: info.Consts, - DeclarePrintf: declarePrintf, - DefineGlibcUse: defineGlibcUse, + extractParams: params, + Defines: info.Defines, + Includes: info.Includes, + Values: info.Consts, } undeclared := make(map[string]bool) bin, out, err := compile(cc, args, data) @@ -59,19 +67,20 @@ func extract(info *compiler.ConstInfo, cc string, args []string, addSource strin } bin, out, err = compile(cc, args, data) if err != nil { - return nil, nil, fmt.Errorf("failed to run compiler: %v %v\n%v\n%v", - cc, args, err, string(out)) + return nil, nil, fmt.Errorf("failed to run compiler: %v %v\n%v\n%s", + cc, args, err, out) } } defer os.Remove(bin) - out, err = osutil.Command(bin).CombinedOutput() - if err != nil { - return nil, nil, fmt.Errorf("failed to run flags binary: %v\n%v", err, string(out)) + var flagVals []uint64 + if data.ExtractFromELF { + flagVals, err = extractFromELF(bin) + } else { + flagVals, err = extractFromExecutable(bin) } - flagVals := strings.Split(string(out), " ") - if len(out) == 0 { - flagVals = nil + if err != nil { + return nil, nil, err } if len(flagVals) != len(data.Values) { return nil, nil, fmt.Errorf("fetched wrong number of values %v, want != %v", @@ -79,26 +88,19 @@ func extract(info *compiler.ConstInfo, cc string, args []string, addSource strin } res := make(map[string]uint64) for i, name := range data.Values { - val := flagVals[i] - n, err := strconv.ParseUint(val, 10, 64) - if err != nil { - return nil, nil, fmt.Errorf("failed to parse value: %v (%v)", err, val) - } - res[name] = n + res[name] = flagVals[i] } return res, undeclared, nil } type CompileData struct { - AddSource string - Defines map[string]string - Includes []string - Values []string - DeclarePrintf bool - DefineGlibcUse bool // workaround for incorrect flags to clang for fuchsia. + *extractParams + Defines map[string]string + Includes []string + Values []string } -func compile(cc string, args []string, data *CompileData) (bin string, out []byte, err error) { +func compile(cc string, args []string, data *CompileData) (string, []byte, error) { src := new(bytes.Buffer) if err := srcTemplate.Execute(src, data); err != nil { return "", nil, fmt.Errorf("failed to generate source: %v", err) @@ -112,6 +114,9 @@ func compile(cc string, args []string, data *CompileData) (bin string, out []byt "-o", binFile, "-w", }...) + if data.ExtractFromELF { + args = append(args, "-c") + } cmd := osutil.Command(cc, args...) cmd.Stdin = src if out, err := cmd.CombinedOutput(); err != nil { @@ -121,6 +126,51 @@ func compile(cc string, args []string, data *CompileData) (bin string, out []byt return binFile, nil, nil } +func extractFromExecutable(binFile string) ([]uint64, error) { + out, err := osutil.Command(binFile).CombinedOutput() + if err != nil { + return nil, fmt.Errorf("failed to run flags binary: %v\n%s", err, out) + } + if len(out) == 0 { + return nil, nil + } + var vals []uint64 + for _, val := range strings.Split(string(out), " ") { + n, err := strconv.ParseUint(val, 10, 64) + if err != nil { + return nil, fmt.Errorf("failed to parse value: %v (%v)", err, val) + } + vals = append(vals, n) + } + return vals, nil +} + +func extractFromELF(binFile string) ([]uint64, error) { + f, err := os.Open(binFile) + if err != nil { + return nil, err + } + ef, err := elf.NewFile(f) + if err != nil { + return nil, err + } + for _, sec := range ef.Sections { + if sec.Name != "syz_extract_data" { + continue + } + data, err := ioutil.ReadAll(sec.Open()) + if err != nil { + return nil, err + } + vals := make([]uint64, len(data)/8) + if err := binary.Read(bytes.NewReader(data), binary.LittleEndian, &vals); err != nil { + return nil, err + } + return vals, nil + } + return nil, fmt.Errorf("did not find syz_extract_data section") +} + var srcTemplate = template.Must(template.New("").Parse(` #define __asm__(...) @@ -146,6 +196,13 @@ var srcTemplate = template.Must(template.New("").Parse(` int printf(const char *format, ...); {{end}} +{{if .ExtractFromELF}} +__attribute__((section("syz_extract_data"))) +unsigned long long vals[] = { + {{range $val := $.Values}}(unsigned long long){{$val}}, + {{end}} +}; +{{else}} int main() { int i; unsigned long long vals[] = { @@ -159,4 +216,5 @@ int main() { } return 0; } +{{end}} `)) diff --git a/sys/syz-extract/freebsd.go b/sys/syz-extract/freebsd.go index e5256521b..7bd02afeb 100644 --- a/sys/syz-extract/freebsd.go +++ b/sys/syz-extract/freebsd.go @@ -56,5 +56,9 @@ func (*freebsd) processFile(arch *Arch, info *compiler.ConstInfo) (map[string]ui args = append(args, "-I"+dir) } } - return extract(info, "gcc", args, "#include ", true, false) + params := &extractParams{ + AddSource: "#include ", + DeclarePrintf: true, + } + return extract(info, "gcc", args, params) } diff --git a/sys/syz-extract/fuchsia.go b/sys/syz-extract/fuchsia.go index a2be49202..6c0a41e88 100644 --- a/sys/syz-extract/fuchsia.go +++ b/sys/syz-extract/fuchsia.go @@ -37,5 +37,9 @@ func (*fuchsia) processFile(arch *Arch, info *compiler.ConstInfo) (map[string]ui for _, incdir := range info.Incdirs { args = append(args, "-I"+filepath.Join(dir, incdir)) } - return extract(info, cc, args, "", true, true) + params := &extractParams{ + DeclarePrintf: true, + DefineGlibcUse: true, + } + return extract(info, cc, args, params) } diff --git a/sys/syz-extract/linux.go b/sys/syz-extract/linux.go index 4c759a19d..b4bfd0b1e 100644 --- a/sys/syz-extract/linux.go +++ b/sys/syz-extract/linux.go @@ -5,7 +5,6 @@ package main import ( "fmt" - "os/exec" "path/filepath" "runtime" "strings" @@ -111,13 +110,6 @@ func (*linux) prepareArch(arch *Arch) error { } func (*linux) processFile(arch *Arch, info *compiler.ConstInfo) (map[string]uint64, map[string]bool, error) { - arch.once.Do(func() { - arch.cc = "gcc" - if !checkCompiler("gcc", arch.target.CFlags) && - checkCompiler("clang", arch.target.CFlags) { - arch.cc = "clang" - } - }) headerArch := arch.target.KernelHeaderArch sourceDir := arch.sourceDir buildDir := arch.buildDir @@ -143,7 +135,7 @@ func (*linux) processFile(arch *Arch, info *compiler.ConstInfo) (map[string]uint "-I" + buildDir + "/syzkaller", "-include", sourceDir + "/include/linux/kconfig.h", } - args = append(args, arch.target.CFlags...) + args = append(args, arch.target.CrossCFlags...) for _, incdir := range info.Incdirs { args = append(args, "-I"+sourceDir+"/"+incdir) } @@ -152,14 +144,12 @@ func (*linux) processFile(arch *Arch, info *compiler.ConstInfo) (map[string]uint args = append(args, "-I"+dir) } } - const addSource = ` -#include -unsigned long phys_base; -#ifndef __phys_addr -unsigned long __phys_addr(unsigned long addr) { return 0; } -#endif -` - res, undeclared, err := extract(info, arch.cc, args, addSource, true, false) + params := &extractParams{ + AddSource: "#include ", + ExtractFromELF: true, + } + cc := arch.target.CCompilerPrefix + "gcc" + res, undeclared, err := extract(info, cc, args, params) if err != nil { return nil, nil, err } @@ -179,9 +169,3 @@ unsigned long __phys_addr(unsigned long addr) { return 0; } } return res, undeclared, nil } - -func checkCompiler(cc string, args []string) bool { - cmd := exec.Command(cc, append(args, "-x", "c", "-", "-o", "/dev/null")...) - cmd.Stdin = strings.NewReader("int main(){}") - return cmd.Run() == nil -} diff --git a/sys/syz-extract/netbsd.go b/sys/syz-extract/netbsd.go index 0ff12680d..61c4716cd 100644 --- a/sys/syz-extract/netbsd.go +++ b/sys/syz-extract/netbsd.go @@ -91,7 +91,10 @@ func (*netbsd) processFile(arch *Arch, info *compiler.ConstInfo) (map[string]uin info.Consts = append(info.Consts, compat) } } - res, undeclared, err := extract(info, "gcc", args, "#include ", false, false) + params := &extractParams{ + AddSource: "#include ", + } + res, undeclared, err := extract(info, "gcc", args, params) for orig, compats := range compatNames { for _, compat := range compats { if undeclared[orig] && !undeclared[compat] { diff --git a/sys/syz-extract/openbsd.go b/sys/syz-extract/openbsd.go index 1a56c18db..715131f28 100644 --- a/sys/syz-extract/openbsd.go +++ b/sys/syz-extract/openbsd.go @@ -76,7 +76,10 @@ func (*openbsd) processFile(arch *Arch, info *compiler.ConstInfo) (map[string]ui info.Consts = append(info.Consts, compat) } } - res, undeclared, err := extract(info, "gcc", args, "#include ", false, false) + params := &extractParams{ + AddSource: "#include ", + } + res, undeclared, err := extract(info, "gcc", args, params) for orig, compats := range compatNames { for _, compat := range compats { if undeclared[orig] && !undeclared[compat] { diff --git a/sys/syz-extract/trusty.go b/sys/syz-extract/trusty.go index 6bd5d6bfa..d9d78dded 100644 --- a/sys/syz-extract/trusty.go +++ b/sys/syz-extract/trusty.go @@ -39,5 +39,8 @@ func (*trusty) processFile(arch *Arch, info *compiler.ConstInfo) (map[string]uin args = append(args, "-I"+dir) } } - return extract(info, "gcc", args, "", true, false) + params := &extractParams{ + DeclarePrintf: true, + } + return extract(info, "gcc", args, params) } diff --git a/sys/syz-extract/windows.go b/sys/syz-extract/windows.go index d74ef505d..d4f86f60e 100644 --- a/sys/syz-extract/windows.go +++ b/sys/syz-extract/windows.go @@ -18,5 +18,8 @@ func (*windows) prepareArch(arch *Arch) error { } func (*windows) processFile(arch *Arch, info *compiler.ConstInfo) (map[string]uint64, map[string]bool, error) { - return extract(info, "cl", nil, "", true, false) + params := &extractParams{ + DeclarePrintf: true, + } + return extract(info, "cl", nil, params) } -- cgit mrf-deployment