二 Syzkaller学习笔记---更新syz-manager( 八 )


sys/syz-/fetch.go: ()
功能:将常量字符串与模板C代码结合,并编译结合后的代码,形成一个可执行文件 。
说明:模板C代码存于变量,该模板会将先前从收集到的 、 和字符串全部融合:
func compile(cc string, args []string, data *CompileData) (string, []byte, error) {// 创建填充好后的 C 代码缓冲区src := new(bytes.Buffer)// 使用传入的 data 对代码模板 srcTemplate 进行填充if err := srcTemplate.Execute(src, data); err != nil {return "", nil, fmt.Errorf("failed to generate source: %v", err)}// 创建一个临时可执行文件路径binFile, err := osutil.TempFile("syz-extract-bin")if err != nil {return "", nil, err}// 为编译器添加额外的参数args = append(args, []string{// -x c :指定代码语言为 C 语言// - :指定代码从标准输入而不是从文件中读取"-x", "c", "-",// 指定文件输出的路径"-o", binFile,"-w",}...)if data.ExtractFromELF {// gcc -c 参数:只编译但不链接// 由于我们测试时使用的是 Linux,因此会进入该分支args = append(args, "-c")}// 执行程序cmd := osutil.Command(cc, args...)// 将填充后的代码模板喂给 gcc 编译cmd.Stdin = src// 将 stdin 和 stdout 的输入糅合,使得他俩的输出完全一致// 通俗的说就是让 stdin 和 stdout 都指向同一个管道if out, err := cmd.CombinedOutput(); err != nil {os.Remove(binFile)return "", out, err}return binFile, nil, nil}
执行至 的图
代码模板 如下
var srcTemplate = template.Must(template.New("").Parse(`{{if not .ExtractFromELF}}#define __asm__(...){{end}}{{if .DefineGlibcUse}}#ifndef __GLIBC_USE#define __GLIBC_USE(X) 0#endif{{end}}{{range $incl := $.Includes}}#include <{{$incl}}>{{end}}{{range $name, $val := $.Defines}}#ifndef {{$name}}#define {{$name}} {{$val}}#endif{{end}}{{.AddSource}}{{if .DeclarePrintf}}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[] = {{{range $val := $.Values}}(unsigned long long){{$val}},{{end}}};for (i = 0; i < sizeof(vals)/sizeof(vals[0]); i++) {if (i != 0)printf(" ");printf("%llu", vals[i]);}return 0;}{{end}}`))
可以很容易的看出来,该模板会将先前从收集到的 、 和字符串全部融合:
s
func checkUnsupportedCalls(arches []*Arch) bool {supported := make(map[string]bool)unsupported := make(map[string]string)for _, arch := range arches {for _, f := range arch.files {for name := range f.consts {supported[name] = true}for name := range f.undeclared {unsupported[name] = f.name}}}failed := falsefor name, file := range unsupported {if supported[name] {continue}failed = truefmt.Printf("%v: %v is unsupported on all arches (typo?)\n",file, name)}return failed}
首先,使用 make 函数创建两个 map,一个是 ,一个是。用来存储已经支持的名称, 用来存储未支持的名称 。然后,对于中的每个架构,遍历该架构的所有文件,并对这些文件中的常量和未声明的变量进行处理 。如果是常量,则将其名称添加到中;如果是未声明的变量,则将其名称和对应的文件名添加到中 。最后,对于中的每个未支持的变量,如果该变量的名称在中,则说明该变量是支持的;否则,打印出该变量不支持的错误信息 。
最终,返回该函数是否有失败() 。如果为 true,则说明存在不支持的调用;否则,说明所有的调用都是支持的 。
func archList(OS, arches string) []string {if arches != "" {return strings.Split(arches, ",")}var archArray []stringfor arch := range targets.List[OS] {archArray = append(archArray, arch)}sort.Strings(archArray)return archArray}
用来返回 简单的拆分
小结
syz- 会调用自定义解析为 ast 森林,并依次提取每个 ast 树上的节点,然后将这些节点上的字符串放置进模板中,编译模板生成一个 ELF 或其他可执行文件 。