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 32 33from tools.colored import Colored 34from tools.templates import GN_ENTRY_TEMPLATE 35from tools.templates import PROJECT_GN_TEMPLATE 36from tools.templates import PROJECT_DEMO_TEMPLATE 37from tools.templates import PROJECT_HEADER_TEMPLATE 38from tools.templates import PROJECT_XML_TEMPLATE 39from tools.run_result import RunResult 40 41CURRENT_DIR = os.path.dirname(os.path.realpath(__file__)) 42SOURCE_ROOT_DIR = os.path.dirname( 43 os.path.dirname( 44 os.path.dirname(os.path.dirname(CURRENT_DIR)) 45 ) 46 ) 47SOURCE_OUT_DIR = os.path.join(SOURCE_ROOT_DIR, "out") 48 49TDD_BUILD_GN_PATH = os.path.join( 50 SOURCE_ROOT_DIR, 51 "test/testfwk/developer_test/BUILD.gn" 52 ) 53###project name must end with _fuzzer. eg. my_fuzzer,extrator_fuzzer. 54VALID_PROJECT_NAME_REGEX = re.compile(r'^[a-zA-Z0-9_-]+(_fuzzer)+$') 55 56 57def _add_environment_args(parser): 58 """Add common environment args.""" 59 parser.add_argument( 60 '-t', 61 '--target_platform', 62 default='phone', 63 choices=["phone", "ivi", "plato"], 64 help="set target_platform value, default:phone") 65 66 parser.add_argument( 67 '-f', 68 '--filter', 69 default=None, 70 help="subsystem filter") 71 72 73def _get_command_string(command): 74 """Returns a shell escaped command string.""" 75 return ' '.join(pipes.quote(part) for part in command) 76 77 78def parse_projects_path(): 79 path_list = [] 80 with open(TDD_BUILD_GN_PATH, 'r') as gn_file: 81 for line in gn_file.readlines()[4:]: 82 striped_str = line.strip() 83 if striped_str.endswith("]"): 84 break 85 path_list.append(striped_str.split(":")[0][3:]) 86 return path_list 87 88 89def _get_fuzzer_yaml_config(fuzzer_name): 90 project_yaml_path = os.path.join( 91 CURRENT_DIR, 92 "projects", 93 fuzzer_name, 94 "project.yaml") 95 if not os.path.exists(project_yaml_path): 96 return 97 #log run stdout to fuzzlog dir 98 with open(project_yaml_path) as filehandle: 99 yaml_config = yaml.safe_load(filehandle) 100 return yaml_config 101 102 103#generate template fuzzer project 104def generate(args): 105 print("project name %s." % args.project_name) 106 print("project path %s." % args.project_path) 107 color_logger = Colored.get_project_logger() 108 109 if not VALID_PROJECT_NAME_REGEX.match(args.project_name): 110 print('Invalid project name.', file=sys.stderr) 111 return 1 112 113 template_args = { 114 'project_name': args.project_name, 115 'author': "", 116 'email': "" 117 } 118 119 project_dir_path = os.path.join(args.project_path, args.project_name) 120 print("project_dir_path %s." % project_dir_path) 121 try: 122 os.mkdir(project_dir_path) 123 except OSError as os_exception: 124 if os_exception.errno != errno.EEXIST: 125 raise 126 print(color_logger.red('%s already exists.' % project_dir_path), 127 file=sys.stderr) 128 return 1 129 color_logger.green('Writing new files to %s' % project_dir_path) 130 131 file_path = os.path.join(project_dir_path, 'project.xml') 132 with open(file_path, 'w') as filehandle: 133 filehandle.write(PROJECT_XML_TEMPLATE % template_args) 134 135 file_path = os.path.join(project_dir_path, "%s.cpp" % args.project_name) 136 with open(file_path, 'w') as filehandle: 137 filehandle.write(PROJECT_DEMO_TEMPLATE % template_args) 138 139 file_path = os.path.join(project_dir_path, "%s.h" % args.project_name) 140 with open(file_path, 'w') as filehandle: 141 filehandle.write(PROJECT_HEADER_TEMPLATE % template_args) 142 file_path = os.path.join(project_dir_path, "BUILD.gn") 143 with open(file_path, 'w') as filehandle: 144 filehandle.write(PROJECT_GN_TEMPLATE % template_args) 145 146 corpus_dir = os.path.join(project_dir_path, 'corpus') 147 if not os.path.exists(corpus_dir): 148 os.mkdir(corpus_dir) 149 with open(os.path.join(corpus_dir, 'init'), 'w') as filehandle: 150 filehandle.write("FUZZ") 151 152 153#complie fuzzer project 154def make(args, stdout=None): 155 """make fuzzer module.""" 156 color_logger = Colored.get_project_logger() 157 158 pre_cmd = ['cd', SOURCE_ROOT_DIR] 159 build_target_platform = "build_platform=\"%s\"" 160 161 build_script = [ 162 './build.sh', 163 '--gn-args', 164 'build_example=true', 165 '--build-target' 166 ] 167 build_script.append(args.project_name) 168 build_script.append("--gn-args") 169 build_script.append(build_target_platform % args.build_platform) 170 build_script.append("--product-name") 171 build_script.append(args.build_platform) 172 build_script.append("--export-para") 173 build_script.append("PYCACHE_ENABLE:true") 174 build_script.append("--export-para") 175 build_script.append("BUILD_AOSP:false") 176 print("BUILD_SCRIPT %s" % build_script) 177 final_cmd = "%s && %s" % ( 178 _get_command_string(pre_cmd), 179 _get_command_string(build_script) 180 ) 181 182 color_logger.green('Running:%s' % final_cmd) 183 184 subsystem_src_flag_file_path = os.path.join( 185 SOURCE_OUT_DIR, 186 "release/current_build_fuzz_target.txt" 187 ) 188 if not os.path.exists(os.path.dirname(subsystem_src_flag_file_path)): 189 os.makedirs(os.path.dirname(subsystem_src_flag_file_path)) 190 with open(subsystem_src_flag_file_path, "wb") as file_handle: 191 file_handle.write(args.project_name.encode()) 192 193 try: 194 if stdout: 195 ret = subprocess.check_call(build_script, cwd=SOURCE_ROOT_DIR, 196 stdout=stdout) 197 else: 198 ret = subprocess.check_call(build_script, cwd=SOURCE_ROOT_DIR) 199 return ret 200 except subprocess.CalledProcessError: 201 print("*" * 50) 202 print("*" * 50) 203 print( 204 'fuzzers {} build failed.'.format(args.project_name), 205 file=sys.stdout 206 ) 207 return -1 208 209 210def report(args): 211 pass 212 213 214def coverage_all(args): 215 pass 216 217 218def main(): 219 parser = argparse.ArgumentParser( 220 'fuzzer_helper.py', 221 description='hydra-fuzz helpers' 222 ) 223 subparsers = parser.add_subparsers(dest='command') 224 225 generate_parser = subparsers.add_parser( 226 'generate', 227 help='Generate files for new project.name must end with "_fuzzer".') 228 generate_parser.add_argument('project_name') 229 generate_parser.add_argument('project_path') 230 _add_environment_args(generate_parser) 231 232 make_parser = subparsers.add_parser( 233 'make', help='Build a single fuzzer module project. ') 234 make_parser.add_argument('project_name') 235 make_parser.add_argument('build_platform') 236 _add_environment_args(make_parser) 237 238 report_parser = subparsers.add_parser( 239 'report', help='Report fuzzer log' 240 ) 241 _add_environment_args(report_parser) 242 report_parser.add_argument( 243 'subfunc', 244 default="list", 245 choices=["list", "coverage", "all"] 246 ) 247 report_parser.add_argument( 248 "-i", 249 "--id", 250 required=False, 251 help="report ID, e.g. empty_fuzzer.20200211184850" 252 ) 253 254 args = parser.parse_args() 255 256 if args.command == 'generate': 257 return generate(args) 258 elif args.command == 'make': 259 return make(args) 260 261 elif args.command == 'report': 262 report(args) 263 264if __name__ == "__main__": 265 main() 266