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


核心代码 是这个
params := &extractParams{ // [2] 准备 extract 参数: params, 准备待使用的CC编译器AddSource:"#include ",ExtractFromELF: true,TargetEndian:arch.target.HostEndian,}cc := arch.target.CCompilerres, undeclared, err := extract(info, cc, args, params) // [3] 执行核心函数 extract,生成 res 映射和 undeclared 集合if err != nil {return nil, nil, err}
编译并搜集常量
位置:sys/syz-/fetch.go
功能:调用编译器来编译代码模板,并根据编译出的二进制文件来获取常量整数 。若编译过程出错,则会尝试自动纠错 。
参数:Info 便是单个文件存放 const 数据的结构体,cc 是编译器名称字符串,args 是编译器执行参数, 是用于执行过程用的选项 。
func extract(info *compiler.ConstInfo, cc string, args []string, params *extractParams) (map[string]uint64, map[string]bool, error) {data := &CompileData{ // [1] 初始化: 声明一系列的 mapextractParams: params,Defines:info.Defines,Includes:info.Includes,Values:info.Consts,}// 编译生成的程序路径bin := ""// 这个字段貌似没有用途,先行忽略missingIncludes := make(map[string]bool)// 未定义的 const,通常是自己定义的常量undeclared := make(map[string]bool)// 声明并初始化 valMap 中各个元素为 truevalMap := make(map[string]bool)for _, val := range info.Consts {valMap[val] = true}for {// [2] 尝试将 consts 常量字符串与模板C代码结合,并编译结合后的代码,形成一个可执行文件bin1, out, err := compile(cc, args, data) // [2-1] 编译操作, 返回结果分别为编译出的可执行文件路径 / 编译器标准输出信息 / 编译器标准错误信息if err == nil {bin = bin1break}// Some consts and syscall numbers are not defined on some archs.// Figure out from compiler output undefined consts,// and try to compile again without them.// May need to try multiple times because some severe errors terminate compilation.tryAgain := falsefor _, errMsg := range []string{ // [2-2] 遍历所有预先定义的错误信息,并使用正则表达式匹配`error: [‘']([a-zA-Z0-9_]+)[’'] undeclared`,`note: in expansion of macro [‘']([a-zA-Z0-9_]+)[’']`,`note: expanded from macro [‘']([a-zA-Z0-9_]+)[’']`,`error: use of undeclared identifier [‘']([a-zA-Z0-9_]+)[’']`,} {re := regexp.MustCompile(errMsg)matches := re.FindAllSubmatch(out, -1)for _, match := range matches { // [2-3] 如果匹配到了,则将出问题的常量存于 undeclared 中val := string(match[1])if valMap[val] && !undeclared[val] {undeclared[val] = truetryAgain = true}}}if !tryAgain {return nil, nil, fmt.Errorf("failed to run compiler: %v %v\n%v\n%s",cc, args, err, out)}data.Values = nil// 重置编译用的 consts 数组for _, v := range info.Consts { // [2-4] 将出错的 consts 剔除,并将剩余没出错的 consts 存入编译用的 consts 数组if undeclared[v] {continue}data.Values = append(data.Values, v)}data.Includes = nilfor _, v := range info.Includes {if missingIncludes[v] {continue}data.Includes = append(data.Includes, v)}}defer os.Remove(bin) // [3] 将新编译出的二进制文件删除var flagVals []uint64var err errorif data.ExtractFromELF { // [4] 从编译出的二进制文件中读取数值,解析并返回flagVals, err = extractFromELF(bin, params.TargetEndian) // [4-1] OS 为 Linux 时, 走这个分支,不会实际执行程序,而是从 ELF 文件中一个名为 syz_extract_data 的 section 中读取常量值} else {flagVals, err = extractFromExecutable(bin) // 若 ExtractFromELF 字段为 false, 实际执行目标程序,解析其输出并转换为整型数组}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",len(flagVals), len(data.Values))}res := make(map[string]uint64)for i, name := range data.Values {res[name] = flagVals[i]}return res, undeclared, nil}
因为上面提到了函数,我们进行查看