1#!/usr/bin/env python 2# -*- coding:utf-8 -*- 3# 4# Copyright (c) 2021 Huawei Device Co., Ltd. 5# Licensed under the Apache License, Version 2.0 (the "License"); 6# you may not use this file except in compliance with the License. 7# You may obtain a copy of the License at 8# 9# http://www.apache.org/licenses/LICENSE-2.0 10# 11# Unless required by applicable law or agreed to in writing, software 12# distributed under the License is distributed on an "AS IS" BASIS, 13# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14# See the License for the specific language governing permissions and 15# limitations under the License. 16# 17 18from __future__ import print_function 19import argparse 20import os 21import re 22import sys 23import subprocess 24import zipfile 25import errno 26import pipes 27import traceback 28import time 29import copy 30import shutil 31from pprint import pprint 32import stat 33 34from tools.colored import Colored 35from tools.templates import GN_ENTRY_TEMPLATE 36from tools.templates import PROJECT_GN_TEMPLATE 37from tools.templates import PROJECT_DEMO_TEMPLATE 38from tools.templates import PROJECT_HEADER_TEMPLATE 39from tools.templates import PROJECT_XML_TEMPLATE 40from tools.run_result import RunResult 41 42FLAGS = os.O_WRONLY | os.O_CREAT | os.O_EXCL 43MODES = stat.S_IWUSR | stat.S_IRUSR 44 45CURRENT_DIR = os.path.dirname(os.path.realpath(__file__)) 46SOURCE_ROOT_DIR = os.path.dirname( 47 os.path.dirname( 48 os.path.dirname(os.path.dirname(CURRENT_DIR)) 49 ) 50 ) 51SOURCE_OUT_DIR = os.path.join(SOURCE_ROOT_DIR, "out") 52 53TDD_BUILD_GN_PATH = os.path.join( 54 SOURCE_ROOT_DIR, 55 "test/testfwk/developer_test/BUILD.gn" 56 ) 57###project name must end with _fuzzer. eg. my_fuzzer,extrator_fuzzer. 58VALID_PROJECT_NAME_REGEX = re.compile(r'^[a-zA-Z0-9_-]+(_fuzzer)+$') 59 60 61def _add_environment_args(parser): 62 """Add common environment args.""" 63 parser.add_argument( 64 '-t', 65 '--target_platform', 66 default='phone', 67 choices=["phone", "ivi", "plato"], 68 help="set target_platform value, default:phone") 69 70 parser.add_argument( 71 '-f', 72 '--filter', 73 default=None, 74 help="subsystem filter") 75 76 77def _get_command_string(command): 78 """Returns a shell escaped command string.""" 79 return ' '.join(pipes.quote(part) for part in command) 80 81 82def parse_projects_path(): 83 path_list = [] 84 with open(TDD_BUILD_GN_PATH, 'r') as gn_file: 85 for line in gn_file.readlines()[4:]: 86 striped_str = line.strip() 87 if striped_str.endswith("]"): 88 break 89 path_list.append(striped_str.split(":")[0][3:]) 90 return path_list 91 92 93def _get_fuzzer_yaml_config(fuzzer_name): 94 project_yaml_path = os.path.join( 95 CURRENT_DIR, 96 "projects", 97 fuzzer_name, 98 "project.yaml") 99 if not os.path.exists(project_yaml_path): 100 return {} 101 #log run stdout to fuzzlog dir 102 with open(project_yaml_path) as filehandle: 103 yaml_config = yaml.safe_load(filehandle) 104 return yaml_config 105 106 107#generate template fuzzer project 108def generate(args): 109 print("project name %s." % args.project_name) 110 print("project path %s." % args.project_path) 111 color_logger = Colored.get_project_logger() 112 113 if not VALID_PROJECT_NAME_REGEX.match(args.project_name): 114 print('Invalid project name.', file=sys.stderr) 115 return 1 116 117 template_args = { 118 'project_name': args.project_name, 119 'author': "", 120 'email': "" 121 } 122 123 project_dir_path = os.path.join(args.project_path, args.project_name) 124 print("project_dir_path %s." % project_dir_path) 125 try: 126 os.mkdir(project_dir_path) 127 except OSError as os_exception: 128 if os_exception.errno != errno.EEXIST: 129 raise 130 print(color_logger.red('%s already exists.' % project_dir_path), 131 file=sys.stderr) 132 return 1 133 color_logger.green('Writing new files to %s' % project_dir_path) 134 135 file_path = os.path.join(project_dir_path, 'project.xml') 136 if os.path.exists(file_path): 137 os.remove(file_path) 138 with os.fdopen(os.open(file_path, FLAGS, MODES), 'w') as filehandle: 139 filehandle.write(PROJECT_XML_TEMPLATE % template_args) 140 141 file_path = os.path.join(project_dir_path, "%s.cpp" % args.project_name) 142 if os.path.exists(file_path): 143 os.remove(file_path) 144 with os.fdopen(os.open(file_path, FLAGS, MODES), 'w') as filehandle: 145 filehandle.write(PROJECT_DEMO_TEMPLATE % template_args) 146 147 file_path = os.path.join(project_dir_path, "%s.h" % args.project_name) 148 if os.path.exists(file_path): 149 os.remove(file_path) 150 with os.fdopen(os.open(file_path, FLAGS, MODES), 'w') as filehandle: 151 filehandle.write(PROJECT_HEADER_TEMPLATE % template_args) 152 file_path = os.path.join(project_dir_path, "BUILD.gn") 153 if os.path.exists(file_path): 154 os.remove(file_path) 155 with os.fdopen(os.open(file_path, FLAGS, MODES), 'w') as filehandle: 156 filehandle.write(PROJECT_GN_TEMPLATE % template_args) 157 158 corpus_dir = os.path.join(project_dir_path, 'corpus') 159 if not os.path.exists(corpus_dir): 160 os.mkdir(corpus_dir) 161 if os.path.exists(os.path.join(corpus_dir, 'init')): 162 os.remove(os.path.join(corpus_dir, 'init')) 163 with os.fdopen(os.open(os.path.join(corpus_dir, 'init'), FLAGS, MODES), 'w') as filehandle: 164 filehandle.write("FUZZ") 165 return 0 166 167 168#complie fuzzer project 169def make(args, stdout=None): 170 """make fuzzer module.""" 171 color_logger = Colored.get_project_logger() 172 173 pre_cmd = ['cd', SOURCE_ROOT_DIR] 174 build_target_platform = "build_platform=\"%s\"" 175 176 build_script = [ 177 './build.sh', 178 '--gn-args', 179 'build_example=true', 180 '--build-target' 181 ] 182 build_script.append(args.project_name) 183 build_script.append("--gn-args") 184 build_script.append(build_target_platform % args.build_platform) 185 build_script.append("--product-name") 186 build_script.append(args.build_platform) 187 build_script.append("--export-para") 188 build_script.append("PYCACHE_ENABLE:true") 189 build_script.append("--export-para") 190 build_script.append("BUILD_AOSP:false") 191 print("BUILD_SCRIPT %s" % build_script) 192 final_cmd = "%s && %s" % ( 193 _get_command_string(pre_cmd), 194 _get_command_string(build_script) 195 ) 196 197 color_logger.green('Running:%s' % final_cmd) 198 199 subsystem_src_flag_file_path = os.path.join( 200 SOURCE_OUT_DIR, 201 "release/current_build_fuzz_target.txt" 202 ) 203 if not os.path.exists(os.path.dirname(subsystem_src_flag_file_path)): 204 os.makedirs(os.path.dirname(subsystem_src_flag_file_path)) 205 if os.path.exists(subsystem_src_flag_file_path): 206 os.remove(subsystem_src_flag_file_path) 207 with os.fdopen(os.open(subsystem_src_flag_file_path, FLAGS, MODES), 'wb') as file_handle: 208 file_handle.write(args.project_name.encode()) 209 210 try: 211 if stdout: 212 ret = subprocess.check_call(build_script, cwd=SOURCE_ROOT_DIR, 213 stdout=stdout) 214 else: 215 ret = subprocess.check_call(build_script, cwd=SOURCE_ROOT_DIR) 216 return ret 217 except subprocess.CalledProcessError: 218 print("*" * 50) 219 print("*" * 50) 220 print( 221 'fuzzers {} build failed.'.format(args.project_name), 222 file=sys.stdout 223 ) 224 return -1 225 226 227def report(args): 228 pass 229 230 231def coverage_all(args): 232 pass 233 234 235def main(): 236 parser = argparse.ArgumentParser( 237 'fuzzer_helper.py', 238 description='hydra-fuzz helpers' 239 ) 240 subparsers = parser.add_subparsers(dest='command') 241 242 generate_parser = subparsers.add_parser( 243 'generate', 244 help='Generate files for new project.name must end with "_fuzzer".') 245 generate_parser.add_argument('project_name') 246 generate_parser.add_argument('project_path') 247 _add_environment_args(generate_parser) 248 249 make_parser = subparsers.add_parser( 250 'make', help='Build a single fuzzer module project. ') 251 make_parser.add_argument('project_name') 252 make_parser.add_argument('build_platform') 253 _add_environment_args(make_parser) 254 255 report_parser = subparsers.add_parser( 256 'report', help='Report fuzzer log' 257 ) 258 _add_environment_args(report_parser) 259 report_parser.add_argument( 260 'subfunc', 261 default="list", 262 choices=["list", "coverage", "all"] 263 ) 264 report_parser.add_argument( 265 "-i", 266 "--id", 267 required=False, 268 help="report ID, e.g. empty_fuzzer.20200211184850" 269 ) 270 271 args = parser.parse_args() 272 273 if args.command == 'generate': 274 return generate(args) 275 elif args.command == 'make': 276 return make(args) 277 278 elif args.command == 'report': 279 report(args) 280 return 1 281 else: 282 return 0 283 284if __name__ == "__main__": 285 main() 286