github.com/google/syzkaller@v0.0.0-20251211124644-a066d2bc4b02/pkg/build/android.go (about) 1 // Copyright 2023 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 build 5 6 import ( 7 "archive/tar" 8 "fmt" 9 "io" 10 "io/fs" 11 "os" 12 "path/filepath" 13 "regexp" 14 "strings" 15 "time" 16 17 "github.com/google/syzkaller/pkg/config" 18 "github.com/google/syzkaller/pkg/osutil" 19 ) 20 21 type android struct{} 22 23 type BuildParams struct { 24 BuildScript string `json:"build_script"` 25 EnvVars []string `json:"env_vars"` 26 Flags []string `json:"flags"` 27 AdditionalImages []string `json:"additional_images"` 28 AutoconfPath string `json:"autoconf_path"` 29 ConfigPath string `json:"config_path"` 30 } 31 32 var ccCompilerRegexp = regexp.MustCompile(`#define\s+CONFIG_CC_VERSION_TEXT\s+"(.*)"`) 33 34 func parseConfig(conf []byte) (*BuildParams, error) { 35 buildCfg := new(BuildParams) 36 if err := config.LoadData(conf, buildCfg); err != nil { 37 return nil, fmt.Errorf("failed to parse build config: %w", err) 38 } 39 40 if buildCfg.BuildScript == "" { 41 return nil, fmt.Errorf("build script not specified for Android build") 42 } 43 44 if buildCfg.ConfigPath == "" { 45 return nil, fmt.Errorf("kernel config path not specified for Android build") 46 } 47 48 return buildCfg, nil 49 } 50 51 func (a android) readCompiler(path string) (string, error) { 52 bytes, err := os.ReadFile(path) 53 if err != nil { 54 return "", err 55 } 56 result := ccCompilerRegexp.FindSubmatch(bytes) 57 if result == nil { 58 return "", fmt.Errorf("%s does not contain build information", path) 59 } 60 return string(result[1]), nil 61 } 62 63 func (a android) build(params Params) (ImageDetails, error) { 64 var details ImageDetails 65 if params.CmdlineFile != "" { 66 return details, fmt.Errorf("cmdline file is not supported for android images") 67 } 68 if params.SysctlFile != "" { 69 return details, fmt.Errorf("sysctl file is not supported for android images") 70 } 71 72 buildCfg, err := parseConfig(params.Build) 73 if err != nil { 74 return details, fmt.Errorf("error parsing android configs: %w", err) 75 } 76 77 // Build kernel. 78 cmd := osutil.Command(fmt.Sprintf("./%v", buildCfg.BuildScript), buildCfg.Flags...) 79 cmd.Dir = params.KernelDir 80 cmd.Env = append(cmd.Env, buildCfg.EnvVars...) 81 82 if _, err := osutil.Run(time.Hour, cmd); err != nil { 83 return details, fmt.Errorf("failed to build kernel: %w", err) 84 } 85 86 buildDistDir := filepath.Join(params.KernelDir, "dist") 87 88 vmlinux := filepath.Join(buildDistDir, "vmlinux") 89 90 if buildCfg.AutoconfPath != "" { 91 details.CompilerID, err = a.readCompiler(filepath.Join(params.KernelDir, buildCfg.AutoconfPath)) 92 if err != nil { 93 return details, fmt.Errorf("failed to read compiler: %w", err) 94 } 95 } 96 97 if err := osutil.CopyFile(vmlinux, filepath.Join(params.OutputDir, "obj", "vmlinux")); err != nil { 98 return details, fmt.Errorf("failed to copy vmlinux: %w", err) 99 } 100 if err := osutil.CopyFile(filepath.Join(params.KernelDir, buildCfg.ConfigPath), 101 filepath.Join(params.OutputDir, "obj", "kernel.config")); err != nil { 102 return details, fmt.Errorf("failed to copy kernel config: %w", err) 103 } 104 105 imageFile, err := os.Create(filepath.Join(params.OutputDir, "image")) 106 if err != nil { 107 return details, fmt.Errorf("failed to create output file: %w", err) 108 } 109 defer imageFile.Close() 110 111 if err := copyModuleFiles(filepath.Join(params.KernelDir, "out"), params.OutputDir); err != nil { 112 return details, fmt.Errorf("failed copying module files: %w", err) 113 } 114 115 images := append(buildCfg.AdditionalImages, "boot.img") 116 if err := a.embedImages(imageFile, buildDistDir, images...); err != nil { 117 return details, fmt.Errorf("failed to embed images: %w", err) 118 } 119 120 details.Signature, err = elfBinarySignature(vmlinux, params.Tracer) 121 if err != nil { 122 return details, fmt.Errorf("failed to generate signature: %w", err) 123 } 124 125 return details, nil 126 } 127 128 func copyModuleFiles(srcDir, dstDir string) error { 129 err := filepath.WalkDir(srcDir, 130 func(path string, d fs.DirEntry, err error) error { 131 if err != nil { 132 return fmt.Errorf("error walking out dir: %w", err) 133 } 134 // Staging directories contain stripped module object files. 135 if strings.Contains(path, "staging") { 136 return nil 137 } 138 139 if filepath.Ext(path) == ".ko" { 140 if err := osutil.CopyFile(path, filepath.Join(dstDir, d.Name())); err != nil { 141 return fmt.Errorf("error copying file: %w", err) 142 } 143 } 144 return nil 145 }) 146 if err != nil { 147 return fmt.Errorf("failed to copy module objects: %w", err) 148 } 149 return nil 150 } 151 152 func (a android) embedImages(w io.Writer, srcDir string, imageNames ...string) error { 153 tw := tar.NewWriter(w) 154 defer tw.Close() 155 156 for _, name := range imageNames { 157 path := filepath.Join(srcDir, name) 158 data, err := os.ReadFile(path) 159 if err != nil { 160 return fmt.Errorf("failed to read %q: %w", name, err) 161 } 162 163 if err := tw.WriteHeader(&tar.Header{ 164 Name: name, 165 Mode: 0600, 166 Size: int64(len(data)), 167 }); err != nil { 168 return fmt.Errorf("failed to write header for %q: %w", name, err) 169 } 170 171 if _, err := tw.Write(data); err != nil { 172 return fmt.Errorf("failed to write data for %q: %w", name, err) 173 } 174 } 175 176 if err := tw.Close(); err != nil { 177 return fmt.Errorf("close archive: %w", err) 178 } 179 180 return nil 181 } 182 183 func (a android) clean(params Params) error { 184 if err := osutil.RemoveAll(filepath.Join(params.KernelDir, "out")); err != nil { 185 return fmt.Errorf("failed to clean 'out' directory: %w", err) 186 } 187 if err := osutil.RemoveAll(filepath.Join(params.KernelDir, "dist")); err != nil { 188 return fmt.Errorf("failed to clean 'dist' directory: %w", err) 189 } 190 return nil 191 }