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 print("BUILD_SCRIPT %s" % build_script) 190 final_cmd = "%s && %s" % ( 191 _get_command_string(pre_cmd), 192 _get_command_string(build_script) 193 ) 194 195 color_logger.green('Running:%s' % final_cmd) 196 197 subsystem_src_flag_file_path = os.path.join( 198 SOURCE_OUT_DIR, 199 "release/current_build_fuzz_target.txt" 200 ) 201 if not os.path.exists(os.path.dirname(subsystem_src_flag_file_path)): 202 os.makedirs(os.path.dirname(subsystem_src_flag_file_path)) 203 if os.path.exists(subsystem_src_flag_file_path): 204 os.remove(subsystem_src_flag_file_path) 205 with os.fdopen(os.open(subsystem_src_flag_file_path, FLAGS, MODES), 'wb') as file_handle: 206 file_handle.write(args.project_name.encode()) 207 208 try: 209 if stdout: 210 ret = subprocess.check_call(build_script, cwd=SOURCE_ROOT_DIR, 211 stdout=stdout) 212 else: 213 ret = subprocess.check_call(build_script, cwd=SOURCE_ROOT_DIR) 214 return ret 215 except subprocess.CalledProcessError: 216 print("*" * 50) 217 print("*" * 50) 218 print( 219 'fuzzers {} build failed.'.format(args.project_name), 220 file=sys.stdout 221 ) 222 return -1 223 224 225def report(args): 226 pass 227 228 229def coverage_all(args): 230 pass 231 232 233def main(): 234 parser = argparse.ArgumentParser( 235 'fuzzer_helper.py', 236 description='hydra-fuzz helpers' 237 ) 238 subparsers = parser.add_subparsers(dest='command') 239 240 generate_parser = subparsers.add_parser( 241 'generate', 242 help='Generate files for new project.name must end with "_fuzzer".') 243 generate_parser.add_argument('project_name') 244 generate_parser.add_argument('project_path') 245 _add_environment_args(generate_parser) 246 247 make_parser = subparsers.add_parser( 248 'make', help='Build a single fuzzer module project. ') 249 make_parser.add_argument('project_name') 250 make_parser.add_argument('build_platform') 251 _add_environment_args(make_parser) 252 253 report_parser = subparsers.add_parser( 254 'report', help='Report fuzzer log' 255 ) 256 _add_environment_args(report_parser) 257 report_parser.add_argument( 258 'subfunc', 259 default="list", 260 choices=["list", "coverage", "all"] 261 ) 262 report_parser.add_argument( 263 "-i", 264 "--id", 265 required=False, 266 help="report ID, e.g. empty_fuzzer.20200211184850" 267 ) 268 269 args = parser.parse_args() 270 271 if args.command == 'generate': 272 return generate(args) 273 elif args.command == 'make': 274 return make(args) 275 276 elif args.command == 'report': 277 report(args) 278 return 1 279 else: 280 return 0 281 282if __name__ == "__main__": 283 main() 284